Reactions
Add emoji reactions to messages. Users and bots can react with standard Unicode 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.
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.
count for the real total. Example — message with reactions
{
"messageId": 42,
"text": "Hello everyone!",
"reactions": [
{
"emoji": "👍",
"count": 128,
"userIds": ["d3b07384-...", "a1b2c3d4-...", "e5f6a7b8-..."]
},
{
"emoji": "🔥",
"count": 3,
"userIds": ["d3b07384-...", "a1b2c3d4-...", "e5f6a7b8-..."]
}
]
} 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 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": "👍" }
Listing Reactions #
Query all reactions on a specific message with GET /IReactions/v1/List.
Request
GET /IReactions/v1/List?channelId=c0ffee00-...&messageId=42 Authorization: Bot YOUR_TOKEN
Response
{
"reactions": [
{
"emoji": "👍",
"count": 42,
"userIds": ["d3b07384-...", "a1b2c3d4-...", "e5f6a7b8-..."]
}
]
} 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": ["...", "...", "..."] }
]
},
{
"messageId": 43,
"reactions": []
}
]
} Recommended client pattern (TypeScript)
// On reconnect or visibilitychange → "visible" async function refreshVisibleReactions(channelId: string, visibleIds: number[]) { const res = await fetch("/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.
No extra API call needed — reactions travel with the message.
Example — message history with reactions
GET /IMessages/v1/History?channelId=c0ffee00-...&limit=1 // Response includes reactions on each message { "messages": [ { "messageId": 42, "text": "Ship it!", "creatorId": "...", "reactions": [ { "emoji": "🚀", "count": 15, "userIds": ["...", "...", "..."] } ] } ] }
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.
SSE example
id: 128 event: reactionAdd data: { "spaceId": "aaaabbbb-...", "channelId": "c0ffee00-...", "messageId": 42, "userId": "d3b07384-...", "emoji": "👍" }
Handling reaction events (TypeScript)
// Update local reaction count from delta events eventSource.addEventListener('reactionAdd', (e) => { const data = JSON.parse(e.data); // Increment local count for data.emoji on data.messageId // Add data.userId to the user preview list }); eventSource.addEventListener('reactionRemove', (e) => { const data = JSON.parse(e.data); // Decrement local count for data.emoji on data.messageId // Remove data.userId from the user preview list // If count reaches 0, remove the reaction group entirely });
Limits & Permissions #
count for the real total. /BatchGet request. Excess IDs are silently truncated.