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.
| Case | Footer block | This 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[]:
{
"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
| Field | Always present | Notes |
|---|---|---|
guid | yes | Stable identifier for this sticker file on the originating Mac. Not portable across Macs — do not echo back as a reference for outbound. |
mime_type | best-effort | E.g. "image/heic", "image/png". |
name | best-effort | Filename as it arrived. |
short_description | best-effort | Human-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:
| Input | Output | Notes |
|---|---|---|
| HEIC (Memoji / Genmoji) | PNG | Lossless conversion. |
| Animated PNG (APNG) | Animated GIF | Preserves 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-through | No 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
stickerfield 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 usingsticker.short_description. - Treat
stickerandreactionas 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
{
"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
| Field | When | Notes |
|---|---|---|
url | required if bytes is absent | HTTPS URL to the sticker image. Mac downloads via the standard attachment pipeline (no BE-side size cap). Recommended for anything beyond a few KB. |
bytes | required if url is absent | Base64-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. |
name | optional | Human-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_type | optional | Hint 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.failedwith one of the codes below.
Error codes
| Code | Meaning | Retry |
|---|---|---|
INVALID_FRAME | Sticker 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_DISABLED | Outbound sticker sends are disabled at the Mac (operator config or user preference). | No — out-of-band fix. |
STICKER_DOWNLOAD_FAILED | The Mac could not download the URL (network, 4xx/5xx, TLS). | Yes, after backoff. |
STICKER_ENCODE_FAILED | The 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_FAILED | The Mac could not compose the sticker send. Common cause: outbound sticker sends require macOS 15+. | Sometimes — depends on the underlying cause. |
INTERNAL_ERROR | Unexpected 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
bytessize: 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 — preferurlfor larger stickers.
Outbound example: URL form
{
"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
{
"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.
-- 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 --
-- 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.
Related
- HTTP Webhooks — both webhook directions.
- Reactions — sticker as a reaction on another message (different mechanism, different feasibility).
bluesendr-metadata:v1footer — the schema embedded inmessage.