Skip to content

Reactions

Add emoji reactions to messages — standard Unicode emoji or a space's custom emoji. Reactions are delivered in real time via the Reactions intent.

How Reactions Work #

Any user or bot with the AddReactions permission can react to a message with a Unicode emoji or with a custom emoji. Each message supports up to 20 unique emoji. Each user can react once per emoji — attempting to react twice returns already_reacted.

Performance note: Reactions are buffered in-memory on the server and flushed to the database periodically. Real-time events (ReactionAdd / ReactionRemove) fire instantly — there's zero latency for connected clients even under heavy load.

Reaction Schema #

When you query messages or receive them via events, each message includes a reactions array. Each entry represents one emoji with its count and a preview of who reacted.

Field Type Description
emoji string Unicode emoji (e.g. "👍", "🔥"), or :name: for a custom emoji.
count number Total number of users who reacted with this emoji.
userIds string[] Preview of user IDs (up to 3). Use count for the real total.
customEmojiId string | null For a custom emoji, its item ID; null for a Unicode emoji.

Example — message with reactions

{
  "messageId": 42,
  "text": "Hello everyone!",
  "reactions": [
    {
      "emoji": "👍",
      "count": 128,
      "userIds": ["d3b07384-...", "a1b2c3d4-...", "e5f6a7b8-..."],
      "customEmojiId": null
    },
    {
      "emoji": ":party:",
      "count": 3,
      "userIds": ["d3b07384-...", "a1b2c3d4-...", "e5f6a7b8-..."],
      "customEmojiId": "9b2f4a1e-..."
    }
  ]
}

userIds is capped at 3. For display purposes (avatars), use the IDs in the array. For the actual count, always use the count field — it reflects the real total even when hundreds of users react.

Adding Reactions #

Send a POST to /IReactions/v1/Add with the channel, message, and Unicode emoji.

Request

POST /IReactions/v1/Add
Authorization: Bot YOUR_TOKEN
Content-Type: application/json

{
  "channelId": "c0ffee00-...",
  "messageId": 42,
  "emoji": "👍"
}
Status Code Description
200 — Reaction added successfully.
403 insufficient_permissions Bot lacks AddReactions permission.
403 reactions_disabled Reactions are turned off in this announcement channel.
404 message_not_found Message doesn't exist in this channel.
409 already_reacted Bot already reacted with this emoji.
422 reaction_limit_reached Message already has 20 unique emoji.

Removing Reactions #

Remove the bot's own reaction with DELETE /IReactions/v1/Remove. Bots can only remove their own reactions — not those of other users.

Request

DELETE /IReactions/v1/Remove
Authorization: Bot YOUR_TOKEN
Content-Type: application/json

{
  "channelId": "c0ffee00-...",
  "messageId": 42,
  "emoji": "👍"
}

Custom Emoji Reactions #

POST /IReactions/v1/AddCustom reacts with a custom emoji, named by its itemId — an emoji item from IExpressions/List. The body is spaceId (the space the message is in), channelId, messageId and itemId. The reaction then reads :name: as its emoji and carries the item ID as customEmojiId.

Request

curl -X POST \
     -H "Authorization: Bot YOUR_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"spaceId":"aaaabbbb-...","channelId":"c0ffee00-...","messageId":42,"itemId":"9b2f4a1e-..."}' \
     https://gateway.argon.zone/IReactions/v1/AddCustom

An emoji of the message's space — any bot with AddReactions may use it.

An emoji of another space — only a verified bot may use it; any other bot gets 403 insufficient_permissions.

POST /IReactions/v1/RemoveCustom takes the same body and removes the bot's own custom reaction — also after the emoji was deleted. Remove with :name: does not reach a custom reaction; it answers reaction_not_found.

Status Code Description
403 insufficient_permissions Bot lacks AddReactions, or the emoji belongs to another space and the bot is not verified.
403 reactions_disabled Reactions are turned off in this announcement channel.
404 not_found The emoji does not exist or was deleted, or the channel takes no reactions.
404 message_not_found Message doesn't exist in this channel.
404 reaction_not_found RemoveCustom: the bot has not reacted with this emoji.
409 already_reacted Bot already reacted with this emoji.
422 reaction_limit_reached Message already has 20 unique emoji; custom ones count too.

Listing Reactions #

Query all reactions on a specific message with GET /IReactions/v1/List. A custom emoji comes with its customEmojiId.

Request

GET /IReactions/v1/List?channelId=c0ffee00-...&messageId=42
Authorization: Bot YOUR_TOKEN

Response

{
  "reactions": [
    {
      "emoji": "👍",
      "count": 42,
      "userIds": ["d3b07384-...", "a1b2c3d4-...", "e5f6a7b8-..."],
      "customEmojiId": null
    },
    {
      "emoji": ":party:",
      "count": 1,
      "userIds": ["d3b07384-..."],
      "customEmojiId": "9b2f4a1e-..."
    }
  ]
}

Batch Refresh #

After a reconnect or when a tab regains focus, the client may need to refresh reactions for all currently visible messages. Instead of calling /List per message, use POST /IReactions/v1/BatchGet to fetch reactions for up to 50 messages in a single request.

When to use BatchGet: All messages must belong to the same channel. The server resolves reactions from an in-memory LRU cache, so hot messages return instantly with no DB hit. Ideal for refreshing the 15–30 messages visible in the viewport.

Request

POST /IReactions/v1/BatchGet
Authorization: Bot YOUR_TOKEN
Content-Type: application/json

{
  "channelId": "c0ffee00-...",
  "messageIds": [42, 43, 44, 45, 46]
}

Response

{
  "messages": [
    {
      "messageId": 42,
      "reactions": [
        { "emoji": "👍", "count": 128, "userIds": ["...", "...", "..."], "customEmojiId": null }
      ]
    },
    {
      "messageId": 43,
      "reactions": []
    }
  ]
}

Recommended client pattern (TypeScript)

// On reconnect or visibilitychange → "visible"
async function refreshVisibleReactions(channelId: string, visibleIds: number[]) {
  const res = await fetch("https://gateway.argon.zone/IReactions/v1/BatchGet", {
    method: "POST",
    headers: { Authorization: "Bot " + token, "Content-Type": "application/json" },
    body: JSON.stringify({ channelId, messageIds: visibleIds }),
  });
  const { messages } = await res.json();
  // Replace local reaction state for each returned message
  for (const { messageId, reactions } of messages) {
    store.setReactions(messageId, reactions);
  }
}

Reactions in Messages #

Reactions are included automatically when fetching messages via GET /IMessages/v1/History and in MessageCreate events, custom ones with their customEmojiId. No extra API call needed — reactions travel with the message.

Example — GET /IMessages/v1/History?channelId=c0ffee00-...&limit=1

{
  "messages": [
    {
      "messageId": 42,
      "text": "Ship it!",
      "creatorId": "...",
      "reactions": [
        { "emoji": "🚀", "count": 15, "userIds": ["...", "...", "..."], "customEmojiId": null }
      ]
    }
  ]
}

Real-time Events #

Subscribe to the Reactions intent (bit 3, value 8) to receive reaction events via SSE. Add it to your intents bitmask: intents = Messages | Reactions = 1 | 8 = 9.

Field Type Description
spaceId, channelId string Where the message is.
messageId int64 The message reacted to.
userId string Who added or removed the reaction.
emoji string The Unicode emoji, or :name: for a custom one.
customEmojiId string | null The custom emoji's item ID; null for a Unicode emoji.
emojiName string | null The custom emoji's name, without colons.
emojiUrl string | null The custom emoji's file; null once the emoji is deleted.

reactionAdd and reactionRemove carry the same fields. For a Unicode emoji the last three are null.

SSE example — event: reactionAdd, data:

{
  "spaceId": "aaaabbbb-...",
  "channelId": "c0ffee00-...",
  "messageId": 42,
  "userId": "d3b07384-...",
  "emoji": ":party:",
  "customEmojiId": "9b2f4a1e-...",
  "emojiName": "party",
  "emojiUrl": ".../files/0d6c1f2e-..."
}

Handling reaction events (TypeScript)

// One reaction per emoji: a unicode one by its text, a custom one by its item ID.
const keyOf = (r: { emoji: string; customEmojiId?: string | null }) => r.customEmojiId ?? r.emoji;

es.addEventListener("reactionAdd", (e) => {
  const data = JSON.parse(e.data);
  // Increment the count for keyOf(data) on data.messageId and add data.userId to the preview.
  // Draw a custom emoji from data.emojiUrl; once the emoji is deleted it is null, so show data.emoji (":name:").
});

es.addEventListener("reactionRemove", (e) => {
  const data = JSON.parse(e.data);
  // Decrement the count for keyOf(data) on data.messageId and remove data.userId from the preview.
  // If the count reaches 0, remove the reaction entirely.
});

Limits & Permissions #

Limit Value Description
Unique emoji per message 20 Maximum distinct emoji on a single message, custom ones included.
Reactions per user per emoji 1 Each user can react once with the same emoji. Duplicates return 409.
userIds preview 3 Max user IDs returned per reaction. Use count for the real total.
Rate limit 60/min Sliding window per bot.
BatchGet max messages 50 Maximum message IDs per /BatchGet request. Excess IDs are silently truncated.
Required permission AddReactions Checked on /Add and /AddCustom. Removing own reaction has no permission requirement.
Another space's emoji verified bots An emoji of the message's space works for every bot.
SSE intent Reactions (bit 3 = 8) Required to receive ReactionAdd / ReactionRemove events.