Client is an event emitter. You subscribe with client.on(event, listener). The event map is fully typed, so your listener’s arguments are inferred.
Overview
On an older Cloak server, the
serverId and channelId on the message, reaction, pin, and lifecycle events can fall back to the bot’s currently selected server or channel. An up-to-date server carries them on every event.Messages
messageCreate
Fires for every message your bot can see. Because delivery is a firehose, that covers all visible channels across all servers. It also fires for direct messages. A DM arrives on this same event withisDM: true and a null serverId, and its channelId holds the dm id.
msg.reply() is a true reply. It renders a quote header and fires a “replied to you” notification that pierces mutes. For a plain answer, like a ping bot, use msg.channel.send(). See Mentions and replies.systemMessage
Fires for system events rather than user messages. Thetype field is member_join, bot_add, or unknown. See SystemMessageEvent.
messageUpdate
Fires when a message is edited.content is the decrypted new text, best-effort. It is '' when the key is missing, never raw ciphertext.
messageDelete
Fires when a message is deleted.sendRejected
Fires when the server denies asend(). This is the only way a denied send becomes visible: send() is fire-and-forget, so its promise already resolved when the frame went out.
sendRejected does not also fire error. A backend denial is an expected outcome, not a plumbing failure. If you want one funnel, forward it yourself.
Reactions and pins
reactionUpdate
Fires when a reaction is added or removed on a visible message.count is the new total for that reaction. The event does not say whether it was an add or a remove. userId is the reactor, so filter with userId === client.user?.id to ignore your bot’s own reactions. See Reactions.
pinUpdate
Fires when a message is pinned or unpinned.pinned is a boolean.
Typing
typing
Fires when someone starts or stops typing. A firehose across every channel your bot can see, exactly likemessageCreate.
true, which is what the shipped Cloak clients do. See Typing, presence, and profile.
Channels, groups, and servers
These fire as channels, groups, and the server itself change, and they fire for your bot’s own mutations too. See Channels and groups.channelCreate
Payload{ channelId, name, groupId, type, serverId }.
channelUpdate
Payload{ channelId, name, serverId }. Fires when a channel is renamed.
channelDelete
Payload{ channelId, serverId }.
groupCreate
Payload{ groupId, name, serverId }.
groupUpdate
Payload{ groupId, name, serverId }. Fires when a group is renamed.
groupDelete
Payload{ groupId, serverId }.
serverUpdate
Payload{ serverId, name }. Fires when the server is renamed by its owner. There is no method to rename a server, so this is how you learn about it.
serverDelete
Payload{ serverId }. Fires when the server is deleted by its owner.
Lifecycle
ready
Fires once, afterlogin() finishes. Permission grants are already populated, so can() works inside this handler.
guildCreate
Fires when your bot is added to a server, and once per existing server during login. Payload is aGuild.
guildDelete
Fires when your bot is removed from a server. The SDK drops that server’s cached conversation key, its voice key, and its permission state.permissionsUpdate
Fires when a server resolves or changes your bot’s grant. The payload is theserverId plus the full PermissionSet, spread flat.
Commands
commandError
Fires when a slash command could not be run.reason: 'parse' means the readable text matched a registered command but did not fit its declaration, for example a missing required option or an uncoercible number. The handler is not invoked, and the SDK does not auto-reply, because it never speaks unprompted.
reason: 'handler' means your own handler threw or rejected.
An unregistered command emits nothing at all. See Slash commands.
Voice
voiceKeyUpdate
Fires when the bot’s voice key for a server is provisioned again: a new key version, a new scope, or both.voice_listen can now hear everyone, and one that lost it cannot. Never poll for this. See Voice keys.
voiceJoin
Fires when someone joins a voice channel, including your own bot. Filter withp.userId === client.user?.id.
The payload is the live roster entry, so later updates mutate it in place. Copy it if you need a snapshot.
voiceLeave
Payload{ channelId, userId }. Fires for your bot’s own leave too.
voiceStateUpdate
Payload{ channelId, userId, state, value }. Fires when a member’s mute, deafen, webcam, or stream flag changes. state is a VoiceStateFlag.
The
VoiceStateFlag union still lists 'p2pWebcam' and 'p2pStream'. Peer-to-peer video is deleted from the Cloak platform, so those two values can no longer arrive. Ignore them.voiceConnectionState
Payload{ channelId, userId, reconnecting }. Fires when a member’s voice session enters or leaves the backend’s 60 second reconnect grace window.
voiceDisconnect
Payload{ channelId, reason: 'moved' | 'forced', toChannelId? }. Fires when a moderator moves or disconnects your bot. toChannelId is set only for a move.
voiceConnection() is already cleared when this fires, and the SDK does not auto-rejoin. Rejoin yourself if that is what you want.
Connection
These let you observe the automatic reconnect. You do not usually need to act on them. See Connection lifecycle.disconnect
reconnecting
reconnected
Diagnostics
error
Fires when the SDK catches a failure that would otherwise have been silent: one of your handlers threw or returned a rejecting promise, a key pull failed, or an inbound frame could not be dispatched. The payload is aCloakClientError.
Server denials do not come through here. An awaited action rejects with
CloakActionError, and a denied send fires sendRejected.raw
Every inbound frame, verbatim, including the ones the SDK consumes itself. A frame is a positional array, typed asunknown[].
client.raw.
Next
Types
The shape of each event payload.
Client
The methods you call in response.
Errors
Every error class, and what reaches which channel.
Direct messages
Why messageCreate needs a guard.