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.
:name: for a custom emoji. count for the real total. 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": "👍"
} AddReactions permission. 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.
AddReactions, or the emoji belongs to another space and the bot is not verified. RemoveCustom: the bot has not reacted with this emoji. 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.
:name: for a custom one. null for a Unicode emoji. 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 #
count for the real total. /BatchGet request. Excess IDs are silently truncated. /Add and /AddCustom. Removing own reaction has no permission requirement.