reactionId: a unicode emoji for the common case, or a custom emoji id for server-specific emoji. This page covers both sides, adding reactions and listening for them.
Add a reaction
Callmsg.react(reactionId) on a Message to add the bot’s reaction. It returns Promise<void> and requires the reaction_add permission. For a plain emoji, pass the unicode character directly.
if (msg.isDM || !msg.serverId) return; guard narrows msg.serverId from string | null to string so can() accepts it, since a permission grant is a server concept. Without it, TypeScript rejects the can() call and plain JavaScript silently returns false for a null server. A bot that also reacts inside DMs skips can() on that branch and calls msg.react() directly.
The reactionId is opaque. The SDK does not validate it and passes it straight through to the server. An invalid id is refused by the server, not caught locally.
Remove the bot’s reaction
Callmsg.unreact(reactionId) to remove the bot’s own reaction from a message. It only affects the reaction the bot added, not anyone else’s.
React with a custom emoji
For a server’s custom emoji, pass its id instead of a unicode character. Get ids fromclient.fetchEmojis(), which returns Promise<{ emojis, category }>, the global emoji catalog. Its ids are exactly the custom-emoji ids that react() accepts.
The shape of
emojis and category is defined by the server, so treat them as data to look ids up in rather than a fixed structure.Listen for reaction changes
ThereactionUpdate event fires when a reaction is added or removed on a message your bot can see. It is not scoped to a channel you selected: on a current Cloak server it arrives for every server the bot is in, the same way messageCreate does. Filter on channelId if you care about one place.
string
The message whose reactions changed.
string
The reaction that changed, the same opaque id you pass to
react().number
The new total for that reaction on the message. The wire carries only this total, never an add-or-remove flag.
'add' | 'remove' | 'unknown'
Inferred by comparing
count with previousCount. 'unknown' when the SDK has no earlier count for this message and reaction (the first hook for it since the bot started, or an evicted entry) or when the total did not change.number | null
The last total this session saw for the same message and reaction, or
null.string
The user who added or removed the reaction.
string
The server the message belongs to.
string
The channel the message belongs to.
count is the new total and the wire includes no add-or-remove flag, operation is only as good as the SDK’s memory of the previous total. It is exact for any reaction the bot has watched since it started; the first hook for a reaction after a restart reads 'unknown'. Treat the event as “this reaction now has this many”, and use operation when a direction is needed and 'unknown' is acceptable.
Filter out the bot’s own reactions
reactionUpdate fires for the bot’s own reactions too, including the ones it adds with react(). When that matters, filter on userId.
On an older Cloak server,
serverId and channelId on reactionUpdate can fall back to the bot’s currently selected server and channel. An up-to-date Cloak server always reports the message’s own location.React to being mentioned
A reaction acknowledges something without adding a message to the channel, which makes it a good pairing withmsg.mentionsMe.
mentionsMe does and does not cover.
Handling errors
Three different failures are worth telling apart.Plain Error, before the wire
Plain Error, before the wire
react() and unreact() reject with a plain Error when the message has no createdAt, which the server needs to locate the row. Live server messages and messages loaded with fetchMessages always carry it. A DM message carries it only on a backend that honors wire version 3, so in practice the case that comes up is a DM on an older backend.CloakActionError, from the server
CloakActionError, from the server
Both methods reject with
CloakActionError when the server refuses. Read .code for the reason and .permission for the missing permission when the code maps to one.A timeout on an older server
A timeout on an older server
Confirming a reaction needs a Cloak server new enough to send the bot a success acknowledgement. Against an older backend the call cannot confirm and times out even though the reaction landed. See What the server must support.
can('reaction_add', serverId) to skip actions the server would only refuse, and still catch the promise for the cases you cannot predict.
Next
Editing, deleting, and pinning
The other four message actions, under the same DM rule.
The Message object
Every field and method a received message carries.
Handling errors
Working with
CloakActionError and friends.Permissions
Why
reaction_add must be granted first.