Skip to main content

Stickers

iMessage supports stickers — Memojis, Genmojis, and pack stickers — sent as their own message (a sticker bubble).

Inbound stickers (Mac → CRM) are forwarded via the standard POST /conversations/messages/inbound call with a structured sticker block in the bluesendr-metadata:v1 footer embedded in the message body.

Outbound stickers (CRM → Mac) ride inside the existing POST /crm/webhook request — a sticker block in the metadata footer tells the Mac to send a sticker, indistinguishable from a sticker the user would have sent themselves. See Outbound: CRM → Mac below.

Standalone vs Tapback​

This guide covers standalone stickers — a sticker sent as its own message. A sticker can also be applied as a Tapback reaction on another message — that's a separate surface covered in the Reactions guide.

CaseFooter blockThis guide?
Standalone sticker (its own message)top-level sticker: {...}Yes
Sticker Tapback (reaction on another message)reaction: { kind: "sticker", ... }See Reactions

The two are mutually exclusive — a single message row is either one or the other, never both. The discriminator is whether the message references a target message (Tapback) or stands alone (this guide).

Inbound: Mac → CRM​

Standalone stickers arrive as a POST /conversations/messages/inbound call. The metadata footer in the message body carries the structured sticker object; the rendered image is delivered via attachments[]:

POST /conversations/messages/inbound
{
"type": "SMS",
"contactId": "<crm contact id>",
"locationId": "<crm location id>",
"attachments": ["https://.../sticker.png"],
"message": "Sticker\n\n-- bluesendr-metadata:v1 --\n{\"chat_id\":680,\"is_group\":false,\"message_guid\":\"9B1A60E2-C341-4DA7-8E22-1FB39C5A87D0\",\"recipients\":[\"+15550003333\"],\"sender\":\"+15550003333\",\"service_name\":\"iMessage\",\"sticker\":{\"guid\":\"BCAEE2CB-30A8-40DF-A639-8983ADFC4D58\",\"mime_type\":\"image/heic\",\"name\":\"sticker_42.heic\",\"short_description\":\"Smiling Memoji\"}}\n-- end bluesendr-metadata --"
}

The body text is always the literal string Sticker — locale- independent, never translated. iMessage doesn't surface a stable "Bob sent a sticker" label, so the body is overridden with a stable placeholder. CRMs that don't render sticker bubbles structurally can fall back to displaying the body as-is.

sticker object​

FieldAlways presentNotes
guidyesStable identifier for this sticker file on the originating Mac. Not portable across Macs — do not echo back as a reference for outbound.
mime_typebest-effortE.g. "image/heic", "image/png".
namebest-effortFilename as it arrived.
short_descriptionbest-effortHuman-readable description (alt-text-like; useful for accessibility).

Sticker image rendering​

The sticker image is uploaded by BlueSender and exposed via the standard attachments[] array — the same pipeline used for inbound photos. CRMs render the sticker by displaying that URL inline.

BlueSender converts non-web-friendly formats before upload so CRMs don't have to handle native iMessage formats:

InputOutputNotes
HEIC (Memoji / Genmoji)PNGLossless conversion.
Animated PNG (APNG)Animated GIFPreserves the animation. GIF has 1-bit alpha and a 256-color palette — fine for sticker-pack art, occasionally visible artifacts on photo-derived stickers. Falls back to a static first-frame PNG if the GIF would exceed ~5 MB.
Static PNG / JPEG / etc.Pass-throughNo conversion.

If the sticker file isn't on disk yet when BlueSender forwards the message (an iCloud download is still in progress), BlueSender waits up to ~3 seconds with a partial-write stability gate before giving up. On timeout, the structured sticker block is still emitted but attachments[] is empty — render a placeholder using sticker.short_description and the Sticker body string.

Inbound CRM responsibilities checklist​

  • Strip the metadata footer from displayed message bodies.
  • Branch on the top-level sticker field to render a sticker bubble.
  • Render the sticker image from the first entry in attachments[].
  • Fall back gracefully when attachments[] is empty (file unavailable) — show a placeholder using sticker.short_description.
  • Treat sticker and reaction as mutually exclusive — never both on the same message.

Outbound: CRM → Mac​

CRM-originated stickers ship inside the existing POST /crm/webhook request — there is no separate endpoint to call. The Mac detects a sticker block in the bluesendr-metadata:v1 footer of the message body, downloads or decodes the sticker, re-encodes it to the iMessage native format, and sends it as a real sticker — indistinguishable from a sticker the user would have sent themselves. Acks return on the standard message_status channel.

Wire contract​

POST /crm/webhook
{
"locationId": "<crm location id>",
"contactId": "<crm contact id>",
"phone": "<recipient E.164>",
"messageId": "<your correlation id, becomes hlMessageId on the ack>",
"message": "-- bluesendr-metadata:v1 --\n{\"chat_id\":649,\"sticker\":{\"url\":\"https://cdn.example.com/stickers/party.png\",\"name\":\"Party Popper\",\"mime_type\":\"image/png\"}}\n-- end bluesendr-metadata --"
}

Top-level fields are identical to a regular send-message webhook — phone, contactId, and messageId are still required for routing and ack correlation. The message field can be the metadata block on its own, or you can prefix any display text that the CRM should show in its conversation log — anything before the -- bluesendr-metadata:v1 -- line is purely for CRM display and is not sent on iMessage.

sticker block fields​

FieldWhenNotes
urlrequired if bytes is absentHTTPS URL to the sticker image. Mac downloads via the standard attachment pipeline (no BE-side size cap). Recommended for anything beyond a few KB.
bytesrequired if url is absentBase64-encoded sticker image. Counts against the webhook's message-body cap of 20,000 characters (MESSAGE_BODY_MAX_LENGTH_CHARS), since the base64 rides inside the message string. Practical decoded ceiling is ~14 KB after base64 inflation and metadata-wrapper overhead. Use url for anything larger.
nameoptionalHuman-readable sticker name. Surfaces as emoji_image_short_description on the resulting message row (used by macOS VoiceOver and Apple's quoted-reply rendering) and appears in BlueSender's logs. Does not cause a lookup against the user's installed stickers; the Mac always renders the image supplied via bytes / url. Defaults to Sticker if omitted.
mime_typeoptionalHint only (e.g. "image/png", "image/jpeg"). The Mac re-encodes regardless.

url and bytes are mutually exclusive in practice — provide exactly one. If both are present the Mac prefers the inline bytes fast path.

The chat_id field on the metadata footer is required for group sends so the Mac knows which conversation to open. For 1:1 sends, the top-level phone field is sufficient — chat_id is optional.

Acks​

Acks reuse the standard message_status channel keyed by hlMessageId (= the messageId you sent in the request). Status values:

  • delivered — sticker sent successfully.
  • failed with one of the codes below.

Error codes​

CodeMeaningRetry
INVALID_FRAMESticker block missing, both url and bytes absent, or recipient resolution failed (no phone and no chat_id-resolvable group).No — fix the request.
UI_AUTOMATION_DISABLEDOutbound sticker sends are disabled at the Mac (operator config or user preference).No — out-of-band fix.
STICKER_DOWNLOAD_FAILEDThe Mac could not download the URL (network, 4xx/5xx, TLS).Yes, after backoff.
STICKER_ENCODE_FAILEDThe downloaded/decoded image could not be re-encoded into the iMessage native format (corrupt input, unsupported format, OS encoder rejection).No — try a different source image.
COMPOSE_FAILEDThe Mac could not compose the sticker send. Common cause: outbound sticker sends require macOS 15+.Sometimes — depends on the underlying cause.
INTERNAL_ERRORUnexpected error in the dispatch pipeline.Yes — typically transient.

Constraints​

  • Static image input only. Animation is flattened to a single frame during re-encode; Apple's sticker pipeline supports animated stickers but BlueSender's outbound path does not construct them.
  • macOS 15+ is required on the Mac for outbound sticker sends. Older macOS versions ack COMPOSE_FAILED.
  • Inline bytes size: enforced coarsely via the WebSocket frame cap (WS_MAX_PAYLOAD_BYTES, default 1 MB). Base64 inflates by ~33%, so a 750 KB image is roughly the practical inline ceiling — prefer url for larger stickers.

Outbound example: URL form​

POST /crm/webhook
{
"locationId": "loc_abc",
"contactId": "ct_123",
"phone": "+15555550100",
"messageId": "sticker-001",
"message": "-- bluesendr-metadata:v1 --\n{\"chat_id\":649,\"sticker\":{\"url\":\"https://cdn.example.com/stickers/party.png\",\"name\":\"Party Popper\",\"mime_type\":\"image/png\"}}\n-- end bluesendr-metadata --"
}

Outbound example: inline bytes form​

sticker block
{
"bytes": "<base64-encoded image>",
"name": "Party Popper",
"mime_type": "image/png"
}

Pasting into a CRM message composer​

If you're triggering stickers manually from the CRM's iMessage composer (or copy-pasting a test payload during integration), paste the literal multi-line body below. Replace chat_id with the chat the message belongs to (carry it over from a recent inbound message's metadata, or omit it for 1:1 sends and rely on the top-level recipient phone) and url with a publicly-reachable sticker image.

URL form
-- bluesendr-metadata:v1 --
{
"chat_id": 660,
"sticker": {
"url": "https://cdn.example.com/stickers/party.png",
"name": "Party Popper",
"mime_type": "image/png"
}
}
-- end bluesendr-metadata --
Inline bytes form — working 64×64 yellow-orange gradient sticker (no setup required)
-- bluesendr-metadata:v1 --
{
"chat_id": 660,
"sticker": {
"bytes": "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAAeUExURf+MAP+RAP+YAP+gAP+pAP+yAP+6AP/AAP/JAP/QAFUzFJcAAAAHdElNRQfqBQcEKQ/9zA8rAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDI2LTA1LTA3VDA0OjQxOjE1KzAwOjAwvC4UrgAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyNi0wNS0wN1QwNDo0MToxNSswMDowMM1zrBIAAAAodEVYdGRhdGU6dGltZXN0YW1wADIwMjYtMDUtMDdUMDQ6NDE6MTUrMDA6MDCaZo3NAAABl0lEQVRYw82X0ZLCIAxFi6WU/v8Pr3YK5CYhhNZx9j754DkGEAjL8o8TIE/YeUPoZQ5/kfgNQcMnFDoMkjFPiPXMa8UybB5ZEoeC/P6qZGiwcaaw+PL1WLNGUOgCxkeRkaEIejw3uPjtCij0EqB+hhNF1wC/L/HmoAZF0PhCpXdUA5ZACyB4KqEKtQSVTxBhgBIUQRKxSmgzUAs4mf2KaiCCVgDwO0kdxWXAEmQBnG8GdRaEoPH5HTBoAjmCwucrxdAZgxCUAnKmhjlB5Y+jGMgsWIINBMcZUQJbSJjDJshUkMU0GoJUBcdBSxCTcEOQvjQEn2B2EnEVklzG3VrG4P8jeQQbF/D92BWsYjOKzbTpm+n5dpZjGB4oAQ4Ux5GmnKrmqX7jUJ071rkADELB7iZ5O3avto/FcbUpl6u4WyXPBfx6jhIfNAjCELG9MHnWYtxocZ43WVabFz1tHm1UaXMZyWdPq/qk1cVeX8dH/ToqarvPXh2LlVPQe3FMvFlCj3Y8m5RXU5h4dIGCx4f3JTO8MMzBP80fSyAoCWP/HgwAAAAASUVORK5CYII=",
"name": "Sun",
"mime_type": "image/png"
}
}
-- end bluesendr-metadata --

The base64 above is a real, working 64×64 PNG (~720 bytes raw, ~960 chars base64) that you can paste verbatim. Replace chat_id: 660 with one of your own chats and you should see the sun sticker appear in Messages.app. For your own custom stickers, base64-encode any small image (base64 -i sticker.png on macOS) and substitute it in.

Optionally prefix any display text before the -- bluesendr-metadata:v1 -- line — that text shows up in the CRM conversation log but is not sent on iMessage.