---
title: Stickers | API Docs
description: Peel an emoji or image onto a message, send a sticker of its own, place one inside text, and reposition it.
---

A sticker is an image or emoji stuck to a conversation rather than sent as a photo. You control its position, size and orientation.

Stickers are an iMessage feature. See [Protocol Selection](/channel/imessage/guides/messaging/protocol-selection/index.md) for what each protocol supports.

## Three ways to send a sticker

|                             | What the recipient sees                                                           | Endpoint                                                                      |
| --------------------------- | --------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| **Peel onto a message**     | an emoji or image stuck to an existing bubble, positionable                       | `POST /v3/messages/{messageId}/reactions` with `type: "sticker"`              |
| **Send it as a message**    | a sticker in the conversation, no bubble, peelable by the recipient onto anything | `POST /v3/chats/{chatId}/messages` with a media part carrying `sticker: true` |
| **Place it inline in text** | sticker images in the line with the words, in place of the characters they cover  | `POST /v3/chats/{chatId}/messages` with `inline_stickers` on a text part      |

Placement applies only to a peel. A sticker sent as its own message isn’t attached to anything, and an [inline sticker](#inline-stickers) is positioned by the character range it replaces.

## Peel a sticker onto a message

Pass `type: "sticker"` with what to peel — an emoji, or an image you’ve uploaded:

- [cURL](#tab-panel-0-0)
- [TypeScript](#tab-panel-0-1)
- [Python](#tab-panel-0-2)
- [Go](#tab-panel-0-3)

Terminal window

```
curl -X POST https://api.linqapp.com/api/partner/v3/messages/{messageId}/reactions \
  -H "Authorization: Bearer $LINQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
      "operation": "add",
      "type": "sticker",
      "emoji": "🎉"
    }'
```

```
await client.messages.addReaction({messageId}, {
  operation: "add",
  type: "sticker",
  emoji: "🎉",
});
```

```
client.messages.add_reaction(
    {message_id},
    operation="add",
    type="sticker",
    emoji="🎉",
)
```

```
client.Messages.AddReaction(context.TODO(), {messageId}, linq.MessageAddReactionParams{
  Operation: linq.F("add"),
  Type: linq.F("sticker"),
  Emoji: linq.F("🎉"),
})
```

| Field           | Required | Type            | Description                                                                                                                                                                                                                                                                                                                                                                                  |
| --------------- | -------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `operation`     | Yes      | `add \| remove` | Whether to add or remove the reaction                                                                                                                                                                                                                                                                                                                                                        |
| `type`          | Yes      | `object`        | —                                                                                                                                                                                                                                                                                                                                                                                            |
| `custom_emoji`  | No       | `string`        | Custom emoji string. Required when type is "custom". This is a \*\*tapback\*\* — the emoji sits in the tapback bubble on the corner of the message. To peel an emoji onto the message as a draggable sticker instead, use type "sticker" with \`emoji\`.                                                                                                                                     |
| `emoji`         | No       | `string`        | A single emoji to peel onto the message as a sticker. Only valid when type is "sticker". Exactly one of \`emoji\`, \`url\` or \`attachment\_id\` is required when type is "sticker". Not to be confused with \`custom\_emoji\`, which produces a tapback.                                                                                                                                    |
| `url`           | No       | `string (uri)`  | Linq attachment URL of the sticker image — the \`download\_url\` returned by \`POST /v3/attachments\`. Only valid when type is "sticker". The image must already be stored with us. To send a sticker from elsewhere, upload it with \`POST /v3/attachments\` first and pass \`attachment\_id\`. Exactly one of \`emoji\`, \`url\` or \`attachment\_id\` is required when type is "sticker". |
| `attachment_id` | No       | `string (uuid)` | Reference to a sticker image pre-uploaded via \`POST /v3/attachments\`. Only valid when type is "sticker". Exactly one of \`emoji\`, \`url\` or \`attachment\_id\` is required when type is "sticker".                                                                                                                                                                                       |
| `part_index`    | No       | `integer`       | Optional index of the message part to react to. If not provided, reacts to the entire message (part 0).                                                                                                                                                                                                                                                                                      |
| `placement`     | No       | `object`        | —                                                                                                                                                                                                                                                                                                                                                                                            |

### Choosing what to peel

Pick one of `emoji`, `url` or `attachment_id`.

| Field           | What it takes                                                                                               | Pre-upload needed |
| --------------- | ----------------------------------------------------------------------------------------------------------- | ----------------- |
| `emoji`         | a single emoji                                                                                              | No                |
| `attachment_id` | an image from [`POST /v3/attachments`](/channel/imessage/api/resources/attachments/methods/create/index.md) | Yes               |
| `url`           | the `download_url` from that same upload                                                                    | Yes               |

`attachment_id` and `url` are two ways of naming the same stored image, so use whichever you have on hand.

**Animated images work.** A GIF peels onto the bubble and plays there, in whatever shape the file already has.

`url` takes a Linq attachment address only — upload the image first.

- [cURL](#tab-panel-1-0)
- [TypeScript](#tab-panel-1-1)
- [Python](#tab-panel-1-2)
- [Go](#tab-panel-1-3)

Terminal window

```
curl -X POST https://api.linqapp.com/api/partner/v3/messages/{messageId}/reactions \
  -H "Authorization: Bearer $LINQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
      "operation": "add",
      "type": "sticker",
      "attachment_id": "550e8400-e29b-41d4-a716-446655440000"
    }'
```

```
await client.messages.addReaction({messageId}, {
  operation: "add",
  type: "sticker",
  attachment_id: "550e8400-e29b-41d4-a716-446655440000",
});
```

```
client.messages.add_reaction(
    {message_id},
    operation="add",
    type="sticker",
    attachment_id="550e8400-e29b-41d4-a716-446655440000",
)
```

```
client.Messages.AddReaction(context.TODO(), {messageId}, linq.MessageAddReactionParams{
  Operation: linq.F("add"),
  Type: linq.F("sticker"),
  AttachmentId: linq.F("550e8400-e29b-41d4-a716-446655440000"),
})
```

A message can hold several parts — text, an image, a link. `part_index` picks which one the sticker lands on, counting from 0. Omit it and the sticker goes on the first. It works the same way for a [tapback](/channel/imessage/guides/messaging/reactions#targeting-multipart-messages/index.md).

### Emoji stickers vs. emoji tapbacks

Both take an emoji and they look nothing alike:

|                                   | What you get                                                                                                                          |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `type: "sticker"` + `emoji`       | the emoji peeled onto the bubble — draggable, resizable, rotatable                                                                    |
| `type: "custom"` + `custom_emoji` | a [tapback](/channel/imessage/guides/messaging/reactions#custom-emoji-reactions/index.md) in the small bubble at the message’s corner |

### Reuse one upload

For images, the attachments API doubles as your sticker library: upload once, then peel the same `attachment_id` as often as you like. A sticker you send repeatedly — a logo, a mascot, a seasonal set — should be uploaded once and referenced by id, not re-uploaded on every send. (Emoji need none of this; there’s nothing to store.)

One caveat on tiers — a sticker meant for reuse belongs on the **persistent** tier, which is the default. Attachments on the [ephemeral tier](/channel/imessage/guides/platform/zero-day-retention/index.md) are removed within roughly 24–48 hours of upload, taking the sticker with them.

## Placement

`placement` sets where the sticker lands on the target bubble. Every field is optional — omit the object entirely for the native-looking default: centred, unrotated, at the size that source gets natively.

| Field      | Required | Type              | Description                                                                                                                                                                                                                                      |
| ---------- | -------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `x`        | No       | `number (double)` | Horizontal position on the target bubble, from -1 (far left) to 1 (far right). 0 is centred.                                                                                                                                                     |
| `y`        | No       | `number (double)` | Vertical position on the target bubble, from -1 (top) to 1 (bottom). 0 is centred.                                                                                                                                                               |
| `scale`    | No       | `number (double)` | How large the sticker is drawn. Omit it for the default size — equivalent to \`1\` for an image, or \`0.5\` for an emoji. Values outside 0.05–2.5 are clamped rather than rejected. Scale is linear, so 2.5 is a little over six times the area. |
| `rotation` | No       | `number (double)` | Clockwise rotation in degrees.                                                                                                                                                                                                                   |

The default size depends on what you peeled: an emoji lands smaller than an image, so `scale: 1` is not the same size for both. Out-of-range values clamp rather than reject, so a size or angle never fails a send.

## Move a sticker after sending

`PATCH /v3/messages/{messageId}/reactions/{reactionId}`

Repositioning reaches every device in the conversation, exactly as dragging the sticker by hand would. Omitted fields keep their current value.

- [cURL](#tab-panel-2-0)
- [TypeScript](#tab-panel-2-1)
- [Python](#tab-panel-2-2)
- [Go](#tab-panel-2-3)

Terminal window

```
curl -X PATCH https://api.linqapp.com/api/partner/v3/messages/{messageId}/reactions/{reactionId} \
  -H "Authorization: Bearer $LINQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
      "placement": {
        "x": 0.6,
        "y": 0.5,
        "scale": 0.75
      }
    }'
```

```
await client.messages.updateStickerPlacement({messageId}, {reactionId}, {
  placement: {
    x: 0.6,
    y: 0.5,
    scale: 0.75,
  },
});
```

```
client.messages.update_sticker_placement(
    {message_id},
    {reaction_id},
    placement={
        "x": 0.6,
        "y": 0.5,
        "scale": 0.75,
    },
)
```

```
client.Messages.UpdateStickerPlacement(context.TODO(), {messageId}, {reactionId}, linq.MessageUpdateStickerPlacementParams{
  Placement: linq.F(map[string]any{
    X: linq.F(0.6),
    Y: linq.F(0.5),
    Scale: linq.F(0.75),
  }),
})
```

`reactionId` is the `id` on the reaction as it appears on the message, or `reaction_id` on the [`reaction.added`](/channel/imessage/guides/webhooks/events#reaction-events/index.md) webhook. Stickers **stack**, so the target message alone can’t identify one — this id is the only thing that distinguishes one sticker from another on the same bubble.

**Only stickers can be repositioned.** A tapback has no placement, so a non-sticker `reactionId` is rejected.

A move fires no webhook

Repositioning updates the message in place. Nothing is emitted in either direction — you aren’t notified when someone moves a sticker of their own, and your own moves fire no event either.

## Standalone stickers

Set `sticker: true` on a media part. The image arrives as a sticker with no bubble, and the recipient can peel it onto any message in the conversation.

- [cURL](#tab-panel-3-0)
- [TypeScript](#tab-panel-3-1)
- [Python](#tab-panel-3-2)
- [Go](#tab-panel-3-3)

Terminal window

```
curl -X POST https://api.linqapp.com/api/partner/v3/chats/{chatId}/messages \
  -H "Authorization: Bearer $LINQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
      "message": {
        "parts": [
          {
            "type": "media",
            "url": "https://cdn.linqapp.com/attachments/example/sticker.png",
            "sticker": true
          }
        ]
      }
    }'
```

```
await client.chats.messages.send({chatId}, {
  message: {
    parts: [
      {
        type: "media",
        url: "https://cdn.linqapp.com/attachments/example/sticker.png",
        sticker: true,
      },
    ],
  },
});
```

```
client.chats.messages.send(
    {chat_id},
    message={
        "parts": [
            {
                "type": "media",
                "url": "https://cdn.linqapp.com/attachments/example/sticker.png",
                "sticker": True,
            },
        ],
    },
)
```

```
client.Chats.Messages.Send(context.TODO(), {chatId}, linq.ChatMessageSendParams{
  Message: linq.F(map[string]any{
    Parts: linq.F([]any{
      map[string]any{
        Type: linq.F("media"),
        Url: linq.F("https://cdn.linqapp.com/attachments/example/sticker.png"),
        Sticker: linq.F(true),
      },
    }),
  }),
})
```

- **Photos are cut out automatically.** The subject is lifted off its background. An image that’s already cut out is sent as-is.
- **If no subject is found**, the image sends as a photo.
- **Animated images** send as animated stickers, without a cutout.
- **iMessage only.** On SMS and RCS the image sends as a photo.

## Inline stickers

`inline_stickers` places sticker images in the line of text. Each entry replaces a character range of the text part’s `value`, and takes its image from exactly one of `url` or `attachment_id`.

- [cURL](#tab-panel-4-0)
- [TypeScript](#tab-panel-4-1)
- [Python](#tab-panel-4-2)
- [Go](#tab-panel-4-3)

Terminal window

```
curl -X POST https://api.linqapp.com/api/partner/v3/chats/{chatId}/messages \
  -H "Authorization: Bearer $LINQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
      "message": {
        "parts": [
          {
            "type": "text",
            "value": "Happy birthday 🎂! 🎉🎉",
            "inline_stickers": [
              {
                "range": [
                  15,
                  17
                ],
                "attachment_id": "550e8400-e29b-41d4-a716-446655440000"
              },
              {
                "range": [
                  19,
                  21
                ],
                "attachment_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7"
              },
              {
                "range": [
                  21,
                  23
                ],
                "attachment_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7"
              }
            ]
          }
        ]
      }
    }'
```

```
await client.chats.messages.send({chatId}, {
  message: {
    parts: [
      {
        type: "text",
        value: "Happy birthday 🎂! 🎉🎉",
        inline_stickers: [
          {
            range: [15, 17],
            attachment_id: "550e8400-e29b-41d4-a716-446655440000",
          },
          {
            range: [19, 21],
            attachment_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7",
          },
          {
            range: [21, 23],
            attachment_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7",
          },
        ],
      },
    ],
  },
});
```

```
client.chats.messages.send(
    {chat_id},
    message={
        "parts": [
            {
                "type": "text",
                "value": "Happy birthday 🎂! 🎉🎉",
                "inline_stickers": [
                    {
                        "range": [15, 17],
                        "attachment_id": "550e8400-e29b-41d4-a716-446655440000",
                    },
                    {
                        "range": [19, 21],
                        "attachment_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
                    },
                    {
                        "range": [21, 23],
                        "attachment_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
                    },
                ],
            },
        ],
    },
)
```

```
client.Chats.Messages.Send(context.TODO(), {chatId}, linq.ChatMessageSendParams{
  Message: linq.F(map[string]any{
    Parts: linq.F([]any{
      map[string]any{
        Type: linq.F("text"),
        Value: linq.F("Happy birthday 🎂! 🎉🎉"),
        InlineStickers: linq.F([]any{
          map[string]any{
            Range: linq.F([]int{15, 17}),
            AttachmentId: linq.F("550e8400-e29b-41d4-a716-446655440000"),
          },
          map[string]any{
            Range: linq.F([]int{19, 21}),
            AttachmentId: linq.F("7c9e6679-7425-40de-944b-e07fc1f90ae7"),
          },
          map[string]any{
            Range: linq.F([]int{21, 23}),
            AttachmentId: linq.F("7c9e6679-7425-40de-944b-e07fc1f90ae7"),
          },
        }),
      },
    }),
  }),
})
```

The same array works on `POST /v3/chats` and `POST /v3/messages`, on any text part.

| Field           | Required | Type             | Description                                                                                                                                                                                                                                                                                                           |
| --------------- | -------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `range`         | Yes      | `array<integer>` | Character range \`\[start, end)\` in the \`value\` string that the sticker replaces. \`start\` is inclusive, \`end\` is exclusive. Those characters are hidden on iMessage and sent as written on SMS and RCS. \*Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.\*   |
| `url`           | No       | `string (uri)`   | Linq attachment URL of the sticker image — the \`download\_url\` returned by \`POST /v3/attachments\`. The image must already be stored with us. To use an image from elsewhere, upload it with \`POST /v3/attachments\` first and pass \`attachment\_id\`. Exactly one of \`url\` or \`attachment\_id\` is required. |
| `attachment_id` | No       | `string (uuid)`  | Reference to a sticker image pre-uploaded via \`POST /v3/attachments\`. Exactly one of \`url\` or \`attachment\_id\` is required.                                                                                                                                                                                     |

### Choosing the range

The sticker replaces the characters in its range. On SMS and RCS, recipients see those characters instead, and VoiceOver reads them aloud.

Use a placeholder that fits in the sentence, like an emoji or `[cake]`. For example, `"Sip cup 🥤"` with the range on `🥤` shows “Sip cup” followed by the sticker.

Ranges are `[start, end)` in UTF-16 code units, the same as [text decorations](/channel/imessage/guides/messaging/sending-messages#text-decorations/index.md). Most characters count as 1; some emoji count as 2.

### Limits

- **100 stickers and 10 different images per part.** Copies of one image count as one.
- **Ranges can’t overlap** each other or a `text_decorations` range, and can’t split a character. Can’t be combined with `mention` on the same part.
- **Animated images arrive as a still.**
- **No error on SMS or RCS.** The stickers are dropped and `value` is sent as written.

### Reading them back

Text parts include an `inline_stickers` array in the send response, `GET /v3/messages/{messageId}`, `GET /v3/chats/{chatId}/messages`, and the [`message.*`](/channel/imessage/guides/webhooks/events#message-events/index.md) webhooks. Each entry has the `range` plus the image’s `id`, `url`, `mime_type` and `file_name`. It’s `null` on a part with no stickers.

`inline_stickers` is only set on messages you send. An inline sticker you receive arrives as a separate media part.

## Important notes

- **A peel can’t be removed.** `operation: "remove"` with `type: "sticker"` is rejected, and there is no `reaction.removed` counterpart.
- **Stickers stack.** Unlike a tapback, a second sticker doesn’t replace the first.
- **Only the sender can move a sticker.** A recipient can’t drag, resize or rotate one you peeled. Placement is yours alone, on send or via `PATCH`.
- **iMessage only.** A peel onto a chat on SMS or RCS is rejected with [`2029`](/channel/imessage/error/codes/2xxx/2029/index.md). [Check iMessage capability](/channel/imessage/guides/messaging/protocol-selection#protocol-capabilities/index.md) first, or send a tapback, which SMS and RCS do support.

## Related

- [Reactions](/channel/imessage/guides/messaging/reactions/index.md) — tapbacks and custom emoji on the same endpoint
- [Sending Messages](/channel/imessage/guides/messaging/sending-messages#text-decorations/index.md) — text parts, decorations, and the range convention inline stickers share
- [Attachments](/channel/imessage/guides/messaging/attachments/index.md) — pre-upload, reuse, size limits
- [Polls](/channel/imessage/guides/messaging/polls/index.md) — poll votes arrive as sticker reactions
- [Webhook Events](/channel/imessage/guides/webhooks/events#reaction-events/index.md) — the `reaction.added` payload
- [API Reference: Add a reaction](/channel/imessage/api/resources/messages/methods/add_reaction/index.md)
- [API Reference: Move a sticker](/channel/imessage/api/resources/messages/methods/update_sticker_placement/index.md)
- [Error 2029 — stickers are iMessage only](/channel/imessage/error/codes/2xxx/2029/index.md)
