Gateways
Gateways are Discord's form of real-time communication over secure WebSockets. Clients will receive events and data over the gateway they are connected to and send data over the REST API. The API for interacting with Gateways is complex and fairly unforgiving, therefore it's highly recommended you read all of the following documentation before writing a custom implementation.
The Discord Gateway has a versioning system separate from the HTTP APIs. The documentation herein is only for the latest version in the following table, unless otherwise specified.
Important note: Not all event fields are documented, in particular, fields prefixed with an underscore are considered internal fields and should not be relied on. We may change the format at any time.
Gateway Versions
Version | Status |
---|---|
9 | Available |
8 | Available |
7 | Doesn't look like anything to me |
6 | Deprecated |
5 | Discontinued |
4 | Discontinued |
Payloads
Gateway Payload Structure
Field | Type | Description |
---|---|---|
op | integer | opcode for the payload |
d | ?mixed (any JSON value) | event data |
s | ?integer * | sequence number, used for resuming sessions and heartbeats |
t | ?string * | the event name for this payload |
* s
and t
are null
when op
is not 0
(Gateway Dispatch Opcode).
Sending Payloads
Packets sent from the client to the Gateway API are encapsulated within a gateway payload object and must have the proper opcode and data object set. The payload object can then be serialized in the format of choice (see ETF/JSON), and sent over the websocket. Payloads to the gateway are limited to a maximum of 4096 bytes sent, going over this will cause a connection termination with error code 4002.
Example Gateway Dispatch
json
{"op": 0,"d": {},"s": 42,"t": "GATEWAY_EVENT_NAME"}
Receiving Payloads
Receiving payloads with the Gateway API is slightly more complex than sending. When using the JSON encoding with Payload Compression enabled, the Gateway has the option of sending payloads as compressed JSON binaries using zlib, meaning your library must detect (see RFC1950 2.2) and decompress these payloads before attempting to parse them. Otherwise the gateway does implement a shared compression context between messages sent, see Transport Compression.
Encoding and Compression
ETF/JSON
When initially creating and handshaking connections to the Gateway, a user can choose whether they wish to communicate over plain-text JSON or binary ETF.
Using ETF
While using ETF there are some additional constraints to note:
- Snowflake IDs are transmitted as 64-bit integers over ETF.
- The client must not send compressed messages to the server.
- Payloads must use string keys, atom keys will lead to a 4002 decode error.
See erlpack for an ETF implementation example.
Payload Compression
When using JSON encoding with payload compression enabled (compress: true
in identify), the Gateway may optionally send zlib-compressed payloads (see RFC1950 2.2). Your library must detect and decompress these payloads to plain-text JSON before attempting to parse them. If you are using payload compression, the gateway does not implement a shared compression context between messages sent. Payload compression will be disabled if you use transport compression (see below).
Transport Compression
Currently the only available transport compression option is zlib-stream
. You will need to run all received packets through a shared zlib context, as seen in the example below. Every connection to the gateway should use its own unique zlib context.
Transport Compression Example
python
# Z_SYNC_FLUSH suffixZLIB_SUFFIX = b'\x00\x00\xff\xff'# initialize a buffer to store chunksbuffer = bytearray()# create a zlib inflation context to run chunks throughinflator = zlib.decompressobj()# ...def on_websocket_message(msg):# always push the message data to your cachebuffer.extend(msg)# check if the last four bytes are equal to ZLIB_SUFFIXif len(msg) < 4 or msg[-4:] != ZLIB_SUFFIX:return# if the message *does* end with ZLIB_SUFFIX,# get the full message by decompressing the buffers# NOTE: the message is utf-8 encoded.msg = inflator.decompress(buffer)buffer = bytearray()# here you can treat `msg` as either JSON or ETF encoded,# depending on your `encoding` param
Connecting to the Gateway
Connecting
Gateway URL Query String Params
Field | Type | Description | Accepted Values |
---|---|---|---|
v | integer | Gateway Version to use | see Gateway versions |
encoding | string | The encoding of received gateway packets | 'json' or 'etf' |
compress? | string | The (optional) compression of gateway packets | 'zlib-stream' |
The first step in establishing connectivity to the gateway is requesting a valid websocket endpoint from the API. This can be done through either the Get Gateway or the Get Gateway Bot endpoint.
With the resulting payload, you can now open a websocket connection to the "url" (or endpoint) specified. Generally, it is a good idea to explicitly pass the gateway version and encoding. For example, we may connect to wss://gateway.discord.gg/?v=9&encoding=json
.
Once connected, the client should immediately receive an Opcode 10 Hello payload, with information on the connection's heartbeat interval:
Example Gateway Hello
json
{"op": 10,"d": {"heartbeat_interval": 45000}}
Heartbeating
After receiving Opcode 10 Hello, the client may begin sending Opcode 1 Heartbeat payloads after heartbeat_interval * random.random()
milliseconds, and every heartbeat_interval
milliseconds thereafter. You may send heartbeats before this interval elapses, but you should avoid doing so unless necessary. There is already tolerance in the heartbeat_interval
that will cover network latency, so you do not need to account for it in your own implementation - waiting the precise interval will suffice.
The gateway may request a heartbeat from the client in some situations by sending an Opcode 1 Heartbeat. When this occurs, the client should immediately send an Opcode 1 Heartbeat without waiting the remainder of the current interval.
Any time the client sends a heartbeat, the gateway will respond with Opcode 11 Heartbeat ACK, a successful acknowledgement of their last heartbeat:
Example Gateway Heartbeat ACK
json
{"op": 11}
If a client does not receive a heartbeat ack between its attempts at sending heartbeats, this may be due to a failed or "zombied" connection. The client should then immediately terminate the connection with a non-1000 close code, reconnect, and attempt to Resume.
Identifying
Next, the client is expected to send an Opcode 2 Identify:
Example Gateway Identify
This is a minimal IDENTIFY
payload. IDENTIFY
supports additional optional fields for other session properties, such as payload compression, or an initial presence state. See the Identify Structure for a more complete example of all options you can pass in.
json
{"op": 2,"d": {"token": "my_token","intents": 513,"properties": {"$os": "linux","$browser": "my_library","$device": "my_library"}}}
If the payload is valid, the gateway will respond with a Ready event. Your client is now considered in a "connected" state. Clients are limited by maximum concurrency when Identifying; if they exceed this limit, the gateway will respond with an Opcode 9 Invalid Session. It is important to note that although the ready event contains a large portion of the required initial state, some information (such as guilds and their members) is sent asynchronously (see Guild Create event).
Resuming
The Internet is a scary place. Disconnections happen, especially with persistent connections. Due to Discord's architecture, this is a semi-regular event and should be expected and handled. Discord has a process for "resuming" (or reconnecting) a connection that allows the client to replay any lost events from the last sequence number they received in the exact same way they would receive them normally.
Your client should store the session_id
from the Ready, and the sequence number of the last event it received. When your client detects that it has been disconnected, it should completely close the connection and open a new one (following the same strategy as Connecting). Once the new connection has been opened, the client should send a Gateway Resume:
Example Gateway Resume
json
{"op": 6,"d": {"token": "my_token","session_id": "session_id_i_stored","seq": 1337}}
If successful, the gateway will respond by replaying all missed events in order, finishing with a Resumed event to signal replay has finished, and all subsequent events are new. It's also possible that your client cannot reconnect in time to resume, in which case the client will receive a Opcode 9 Invalid Session and is expected to wait a random amount of time—between 1 and 5 seconds—then send a fresh Opcode 2 Identify.
Disconnections
If the gateway ever issues a disconnect to your client, it will provide a close event code that you can use to properly handle the disconnection. A full list of these close codes can be found in the Response Codes documentation.
When you close the connection to the gateway with the close code 1000 or 1001, your session will be invalidated and your bot will appear offline. If you simply close the TCP connection, or use a different close code, the bot session will remain active and timeout after a few minutes. This can be useful for a reconnect, which will resume the previous session.
Gateway Intents
Maintaining a stateful application can be difficult when it comes to the amount of data you're expected to process, especially at scale. Gateway Intents are a system to help you lower that computational burden.
When identifying to the gateway, you can specify an intents
parameter which allows you to conditionally subscribe to pre-defined "intents", groups of events defined by Discord. If you do not specify a certain intent, you will not receive any of the gateway events that are batched into that group. The valid intents are:
List of Intents
GUILDS (1 << 0)- GUILD_CREATE- GUILD_UPDATE- GUILD_DELETE- GUILD_ROLE_CREATE- GUILD_ROLE_UPDATE- GUILD_ROLE_DELETE- CHANNEL_CREATE- CHANNEL_UPDATE- CHANNEL_DELETE- CHANNEL_PINS_UPDATE- THREAD_CREATE- THREAD_UPDATE- THREAD_DELETE- THREAD_LIST_SYNC- THREAD_MEMBER_UPDATE- THREAD_MEMBERS_UPDATE *- STAGE_INSTANCE_CREATE- STAGE_INSTANCE_UPDATE- STAGE_INSTANCE_DELETEGUILD_MEMBERS (1 << 1)- GUILD_MEMBER_ADD- GUILD_MEMBER_UPDATE- GUILD_MEMBER_REMOVE- THREAD_MEMBERS_UPDATE *GUILD_BANS (1 << 2)- GUILD_BAN_ADD- GUILD_BAN_REMOVEGUILD_EMOJIS_AND_STICKERS (1 << 3)- GUILD_EMOJIS_UPDATE- GUILD_STICKERS_UPDATEGUILD_INTEGRATIONS (1 << 4)- GUILD_INTEGRATIONS_UPDATE- INTEGRATION_CREATE- INTEGRATION_UPDATE- INTEGRATION_DELETEGUILD_WEBHOOKS (1 << 5)- WEBHOOKS_UPDATEGUILD_INVITES (1 << 6)- INVITE_CREATE- INVITE_DELETEGUILD_VOICE_STATES (1 << 7)- VOICE_STATE_UPDATEGUILD_PRESENCES (1 << 8)- PRESENCE_UPDATEGUILD_MESSAGES (1 << 9)- MESSAGE_CREATE- MESSAGE_UPDATE- MESSAGE_DELETE- MESSAGE_DELETE_BULKGUILD_MESSAGE_REACTIONS (1 << 10)- MESSAGE_REACTION_ADD- MESSAGE_REACTION_REMOVE- MESSAGE_REACTION_REMOVE_ALL- MESSAGE_REACTION_REMOVE_EMOJIGUILD_MESSAGE_TYPING (1 << 11)- TYPING_STARTDIRECT_MESSAGES (1 << 12)- MESSAGE_CREATE- MESSAGE_UPDATE- MESSAGE_DELETE- CHANNEL_PINS_UPDATEDIRECT_MESSAGE_REACTIONS (1 << 13)- MESSAGE_REACTION_ADD- MESSAGE_REACTION_REMOVE- MESSAGE_REACTION_REMOVE_ALL- MESSAGE_REACTION_REMOVE_EMOJIDIRECT_MESSAGE_TYPING (1 << 14)- TYPING_START
* Thread Members Update contains different data depending on which intents are used.
Caveats
Any events not defined in an intent are considered "passthrough" and will always be sent to you.
Guild Member Update is sent for current-user updates regardless of whether the GUILD_MEMBERS
intent is set.
Guild Create and Request Guild Members are uniquely affected by intents. See these sections for more information.
Thread Members Update by default only includes if the current user was added to or removed from a thread. To receive these updates for other users, request the GUILD_MEMBERS
Gateway Intent.
If you specify an intent
value in your IDENTIFY
payload that is invalid, the socket will close with a 4013
close code. An invalid intent is one that is not meaningful and not documented above.
If you specify an intent
value in your IDENTIFY
payload that is disallowed, the socket will close with a 4014
close code. A disallowed intent is a privileged intent that has not been approved for your bot.
Bots in under 100 guilds can enable these intents in the bot tab of the developer dashboard. Verified bots can get access to privileged intents when getting verified, or by writing into support after getting verified.
Privileged Intents
Some intents are defined as "Privileged" due to the sensitive nature of the data. Those intents are:
GUILD_PRESENCES
GUILD_MEMBERS
To specify these intents in your IDENTIFY
payload, you must visit your application page in the Developer Portal and enable the toggle for each Privileged Intent that you wish to use. If your bot qualifies for verification, you must first verify your bot and request access to these intents during the verification process. If your bot is already verified and you need to request additional privileged intents, contact support.
Events under the GUILD_PRESENCES
and GUILD_MEMBERS
intents are turned off by default on all gateway versions. If you are using Gateway v6, you will receive those events if you are authorized to receive them and have enabled the intents in the Developer Portal. You do not need to use Intents on Gateway v6 to receive these events; you just need to enable the flags.
If you are using Gateway v8 or above, Intents are mandatory and must be specified when identifying.
In addition to the gateway restrictions described here, Discord's REST API is also affected by Privileged Intents. Specifically, to use the List Guild Members endpoint, you must have the GUILD_MEMBERS
intent enabled for your application. This behavior is independent of whether the intent is set during IDENTIFY
.
Rate Limiting
Clients are allowed to send 120 gateway commands every 60 seconds, meaning you can send an average of 2 commands per second. Clients who surpass this limit are immediately disconnected from the Gateway, and similarly to the HTTP API, repeat offenders will have their API access revoked. Clients also have a limit of concurrent Identify requests allowed per 5 seconds. If you hit this limit, the Gateway will respond with an Opcode 9 Invalid Session.
Tracking State
Most of a client's state is provided during the initial Ready event and the Guild Create events that immediately follow. As objects are further created/updated/deleted, other events are sent to notify the client of these changes and to provide the new or updated data. To avoid excessive API calls, Discord expects clients to locally cache as many relevant object states as possible, and to update them as gateway events are received.
An example of state tracking can be found with member status caching. When initially connecting to the gateway, the client receives information regarding the online status of guild members (online, idle, dnd, offline). To keep this state updated, a client must track and parse Presence Update events as they are received, and apply the provided data to the cached member objects.
For larger bots, client state can grow to be quite large. We recommend only storing objects in memory that are needed for a bot's operation. Many bots, for example, just respond to user input through chat commands. These bots may only need to keep guild information (like guild/channel roles and permissions) in memory, since MESSAGE_CREATE and MESSAGE_UPDATE events have the full member object available.
Guild Availability
When connecting to the gateway as a bot user, guilds that the bot is a part of will start out as unavailable. Don't fret! The gateway will automatically attempt to reconnect on your behalf. As guilds become available to you, you will receive Guild Create events.
Sharding
As bots grow and are added to an increasing number of guilds, some developers may find it necessary to break or split portions of their bots operations into separate logical processes. As such, Discord gateways implement a method of user-controlled guild sharding which allows for splitting events across a number of gateway connections. Guild sharding is entirely user controlled, and requires no state-sharing between separate connections to operate.
To enable sharding on a connection, the user should send the shard
array in the Identify payload. The first item in this array should be the zero-based integer value of the current shard, while the second represents the total number of shards. DMs will only be sent to shard 0. To calculate what events will be sent to what shard, the following formula can be used:
Sharding Formula
python
shard_id = (guild_id >> 22) % num_shards
As an example, if you wanted to split the connection between three shards, you'd use the following values for shard
for each connection: [0, 3]
, [1, 3]
, and [2, 3]
. Note that only the first shard ([0, 3]
) would receive DMs.
Note that num_shards
does not relate to, or limit, the total number of potential sessions—it is only used for routing traffic. As such, sessions do not have to be identified in an evenly distributed manner when sharding. You can establish multiple sessions with the same [shard_id, num_shards]
, or sessions with different num_shards
values. This allows you to create sessions that will handle more or less traffic than others for more fine-tuned load balancing, or orchestrate "zero-downtime" scaling/updating by handing off traffic to a new deployment of sessions with a higher or lower num_shards
count that are prepared in parallel.
Max Concurrency
If you have multiple shards, you may start them concurrently based on the max_concurrency
value returned to you on session start. Which shards you can start concurrently are assigned based on a key for each shard. The rate limit key for a given shard can be computed with
rate_limit_key = shard_id % max_concurrency
This puts your shards into "buckets" of max_concurrency
size. When you start your bot, you may start up to max_concurrency
shards at a time, and you must start them by "bucket" in order. To explain another way, let's say you have 16 shards, and your max_concurrency
is 16:
shard_id: 0, rate limit key (0 % 16): 0shard_id: 1, rate limit key (1 % 16): 1shard_id: 2, rate limit key (2 % 16): 2shard_id: 3, rate limit key (3 % 16): 3shard_id: 4, rate limit key (4 % 16): 4shard_id: 5, rate limit key (5 % 16): 5shard_id: 6, rate limit key (6 % 16): 6shard_id: 7, rate limit key (7 % 16): 7shard_id: 8, rate limit key (8 % 16): 8shard_id: 9, rate limit key (9 % 16): 9shard_id: 10, rate limit key (10 % 16): 10shard_id: 11, rate limit key (11 % 16): 11shard_id: 12, rate limit key (12 % 16): 12shard_id: 13, rate limit key (13 % 16): 13shard_id: 14, rate limit key (14 % 16): 14shard_id: 15, rate limit key (15 % 16): 15
You may start all 16 of your shards at once, because each has a rate_limit_key
which fills the bucket of 16 shards. However, let's say you had 32 shards:
shard_id: 0, rate limit key (0 % 16): 0shard_id: 1, rate limit key (1 % 16): 1shard_id: 2, rate limit key (2 % 16): 2shard_id: 3, rate limit key (3 % 16): 3shard_id: 4, rate limit key (4 % 16): 4shard_id: 5, rate limit key (5 % 16): 5shard_id: 6, rate limit key (6 % 16): 6shard_id: 7, rate limit key (7 % 16): 7shard_id: 8, rate limit key (8 % 16): 8shard_id: 9, rate limit key (9 % 16): 9shard_id: 10, rate limit key (10 % 16): 10shard_id: 11, rate limit key (11 % 16): 11shard_id: 12, rate limit key (12 % 16): 12shard_id: 13, rate limit key (13 % 16): 13shard_id: 14, rate limit key (14 % 16): 14shard_id: 15, rate limit key (15 % 16): 15shard_id: 16, rate limit key (16 % 16): 0shard_id: 17, rate limit key (17 % 16): 1shard_id: 18, rate limit key (18 % 16): 2shard_id: 19, rate limit key (19 % 16): 3shard_id: 20, rate limit key (20 % 16): 4shard_id: 21, rate limit key (21 % 16): 5shard_id: 22, rate limit key (22 % 16): 6shard_id: 23, rate limit key (23 % 16): 7shard_id: 24, rate limit key (24 % 16): 8shard_id: 25, rate limit key (25 % 16): 9shard_id: 26, rate limit key (26 % 16): 10shard_id: 27, rate limit key (27 % 16): 11shard_id: 28, rate limit key (28 % 16): 12shard_id: 29, rate limit key (29 % 16): 13shard_id: 30, rate limit key (30 % 16): 14shard_id: 31, rate limit key (31 % 16): 15
In this case, you must start the shard buckets in "order". That means that you can start shard 0 -> shard 15 concurrently, and then you can start shard 16 -> shard 31.
Sharding for Very Large Bots
If you own a bot that is near or in over 150,000 guilds, there are some additional considerations you must take around sharding. Please file a support-ticket to get moved to the sharding for big bots, when you reach near this amount of servers. You can contact the discord support using https://dis.gd/contact.
The number of shards you run must be a multiple of a fixed number we will determine when reaching out to you. If you attempt to start your bot with an invalid number of shards, your websocket connection will close with a 4010 Invalid Shard opcode. The gateway bot bootstrap endpoint will return the correct amount of shards, so if you're already using this endpoint to determine your number of shards, you shouldn't require any further changes.
The session start limit for these bots will also be increased from 1000 to max(2000, (guild_count / 1000) * 3)
per day. You also receive an increased max_concurrency
, the number of shards you can concurrently start.
Finally, the Get Current User Guilds endpoint will no longer return results for your bot. We will be creating a new endpoint that is more shard-aware to iterate through your bot's guilds if needed.
Commands and Events
Commands are requests made to the gateway socket by a client.
Gateway Commands
name | description |
---|---|
Identify | triggers the initial handshake with the gateway |
Resume | resumes a dropped gateway connection |
Heartbeat | maintains an active gateway connection |
Request Guild Members | requests members for a guild |
Update Voice State | joins, moves, or disconnects the client from a voice channel |
Update Presence | updates a client's presence |
Events are payloads sent over the socket to a client that correspond to events in Discord.
Gateway Events
name | description |
---|---|
Hello | defines the heartbeat interval |
Ready | contains the initial state information |
Resumed | response to Resume |
Reconnect | server is going away, client should reconnect to gateway and resume |
Invalid Session | failure response to Identify or Resume or invalid active session |
Channel Create | new guild channel created |
Channel Update | channel was updated |
Channel Delete | channel was deleted |
Channel Pins Update | message was pinned or unpinned |
Thread Create | thread created, also sent when being added to a private thread |
Thread Update | thread was updated |
Thread Delete | thread was deleted |
Thread List Sync | sent when gaining access to a channel, contains all active threads in that channel |
Thread Member Update | thread member for the current user was updated |
Thread Members Update | some user(s) were added to or removed from a thread |
Guild Create | lazy-load for unavailable guild, guild became available, or user joined a new guild |
Guild Update | guild was updated |
Guild Delete | guild became unavailable, or user left/was removed from a guild |
Guild Ban Add | user was banned from a guild |
Guild Ban Remove | user was unbanned from a guild |
Guild Emojis Update | guild emojis were updated |
Guild Stickers Update | guild stickers were updated |
Guild Integrations Update | guild integration was updated |
Guild Member Add | new user joined a guild |
Guild Member Remove | user was removed from a guild |
Guild Member Update | guild member was updated |
Guild Members Chunk | response to Request Guild Members |
Guild Role Create | guild role was created |
Guild Role Update | guild role was updated |
Guild Role Delete | guild role was deleted |
Integration Create | guild integration was created |
Integration Update | guild integration was updated |
Integration Delete | guild integration was deleted |
Interaction Create | user used an interaction, such as an Application Command |
Invite Create | invite to a channel was created |
Invite Delete | invite to a channel was deleted |
Message Create | message was created |
Message Update | message was edited |
Message Delete | message was deleted |
Message Delete Bulk | multiple messages were deleted at once |
Message Reaction Add | user reacted to a message |
Message Reaction Remove | user removed a reaction from a message |
Message Reaction Remove All | all reactions were explicitly removed from a message |
Message Reaction Remove Emoji | all reactions for a given emoji were explicitly removed from a message |
Presence Update | user was updated |
Stage Instance Create | stage instance was created |
Stage Instance Delete | stage instance was deleted or closed |
Stage Instance Update | stage instance was updated |
Typing Start | user started typing in a channel |
User Update | properties about the user changed |
Voice State Update | someone joined, left, or moved a voice channel |
Voice Server Update | guild's voice server was updated |
Webhooks Update | guild channel webhook was created, update, or deleted |
Event Names
Event names are in standard constant form, fully upper-cased and replacing all spaces with underscores. For instance, Channel Create would be CHANNEL_CREATE
and Voice State Update would be VOICE_STATE_UPDATE
. Within the following documentation, they have been left in standard English form to aid in readability.
Identify
Used to trigger the initial handshake with the gateway.
Identify Structure
Field | Type | Description | Default |
---|---|---|---|
token | string | authentication token | - |
properties | object | connection properties | - |
compress? | boolean | whether this connection supports compression of packets | false |
large_threshold? | integer | value between 50 and 250, total number of members where the gateway will stop sending offline members in the guild member list | 50 |
shard? | array of two integers (shard_id, num_shards) | used for Guild Sharding | - |
presence? | update presence object | presence structure for initial presence information | - |
intents | integer | the Gateway Intents you wish to receive | - |
Identify Connection Properties
Field | Type | Description |
---|---|---|
\$os | string | your operating system |
\$browser | string | your library name |
\$device | string | your library name |
Example Identify
json
{"op": 2,"d": {"token": "my_token","properties": {"$os": "linux","$browser": "disco","$device": "disco"},"compress": true,"large_threshold": 250,"shard": [0, 1],"presence": {"activities": [{"name": "Cards Against Humanity","type": 0}],"status": "dnd","since": 91879201,"afk": false},// This intent represents 1 << 0 for GUILDS, 1 << 1 for GUILD_MEMBERS, and 1 << 2 for GUILD_BANS// This connection will only receive the events defined in those three intents"intents": 7}}
Resume
Used to replay missed events when a disconnected client resumes.
Resume Structure
Field | Type | Description |
---|---|---|
token | string | session token |
session_id | string | session id |
seq | integer | last sequence number received |
Example Resume
json
{"op": 6,"d": {"token": "randomstring","session_id": "evenmorerandomstring","seq": 1337}}
Heartbeat
Used to maintain an active gateway connection. Must be sent every heartbeat_interval
milliseconds after the Opcode 10 Hello payload is received. The inner d
key is the last sequence number—s
—received by the client. If you have not yet received one, send null
.
Example Heartbeat
{"op": 1,"d": 251}
Request Guild Members
Used to request all members for a guild or a list of guilds. When initially connecting, if you don't have the GUILD_PRESENCES
Gateway Intent, or if the guild is over 75k members, it will only send members who are in voice, plus the member for you (the connecting user). Otherwise, if a guild has over large_threshold
members (value in the Gateway Identify), it will only send members who are online, have a role, have a nickname, or are in a voice channel, and if it has under large_threshold
members, it will send all members. If a client wishes to receive additional members, they need to explicitly request them via this operation. The server will send Guild Members Chunk events in response with up to 1000 members per chunk until all members that match the request have been sent.
Due to our privacy and infrastructural concerns with this feature, there are some limitations that apply:
GUILD_PRESENCES
intent is required to setpresences = true
. Otherwise, it will always be falseGUILD_MEMBERS
intent is required to request the entire member list—(query=‘’, limit=0<=n)
- You will be limited to requesting 1
guild_id
per request - Requesting a prefix (
query
parameter) will return a maximum of 100 members - Requesting
user_ids
will continue to be limited to returning 100 members
Guild Request Members Structure
Field | Type | Description | Required |
---|---|---|---|
guild_id | snowflake | id of the guild to get members for | true |
query? | string | string that username starts with, or an empty string to return all members | one of query or user_ids |
limit | integer | maximum number of members to send matching the query ; a limit of 0 can be used with an empty string query to return all members | true when specifying query |
presences? | boolean | used to specify if we want the presences of the matched members | false |
user_ids? | snowflake or array of snowflakes | used to specify which users you wish to fetch | one of query or user_ids |
nonce? | string | nonce to identify the Guild Members Chunk response | false |
Guild Request Members
json
{"op": 8,"d": {"guild_id": "41771983444115456","query": "","limit": 0}}
Update Voice State
Sent when a client wants to join, move, or disconnect from a voice channel.
Gateway Voice State Update Structure
Field | Type | Description |
---|---|---|
guild_id | snowflake | id of the guild |
channel_id | ?snowflake | id of the voice channel client wants to join (null if disconnecting) |
self_mute | boolean | is the client muted |
self_deaf | boolean | is the client deafened |
Example Gateway Voice State Update
json
{"op": 4,"d": {"guild_id": "41771983423143937","channel_id": "127121515262115840","self_mute": false,"self_deaf": false}}
Update Presence
Sent by the client to indicate a presence or status update.
Gateway Presence Update Structure
Field | Type | Description |
---|---|---|
since | ?integer | unix time (in milliseconds) of when the client went idle, or null if the client is not idle |
activities | array of activity objects | the user's activities |
status | string | the user's new status |
afk | boolean | whether or not the client is afk |
Status Types
Status | Description |
---|---|
online | Online |
dnd | Do Not Disturb |
idle | AFK |
invisible | Invisible and shown as offline |
offline | Offline |
Example Gateway Presence Update
json
{"op": 3,"d": {"since": 91879201,"activities": [{"name": "Save the Oxford Comma","type": 0}],"status": "online","afk": false}}
Connecting and Resuming
Hello
Sent on connection to the websocket. Defines the heartbeat interval that the client should heartbeat to.
Hello Structure
Field | Type | Description |
---|---|---|
heartbeat_interval | integer | the interval (in milliseconds) the client should heartbeat with |
Example Hello
json
{"op": 10,"d": {"heartbeat_interval": 45000}}
Ready
The ready event is dispatched when a client has completed the initial handshake with the gateway (for new sessions). The ready event can be the largest and most complex event the gateway will send, as it contains all the state required for a client to begin interacting with the rest of the platform.
guilds
are the guilds of which your bot is a member. They start out as unavailable when you connect to the gateway. As they become available, your bot will be notified via Guild Create events.
Ready Event Fields
Field | Type | Description |
---|---|---|
v | integer | gateway version |
user | user object | information about the user including email |
guilds | array of Unavailable Guild objects | the guilds the user is in |
session_id | string | used for resuming connections |
shard? | array of two integers (shard_id, num_shards) | the shard information associated with this session, if sent when identifying |
application | partial application object | contains id and flags |
Resumed
The resumed event is dispatched when a client has sent a resume payload to the gateway (for resuming existing sessions).
Reconnect
The reconnect event is dispatched when a client should reconnect to the gateway (and resume their existing session, if they have one). This event usually occurs during deploys to migrate sessions gracefully off old hosts.
Example Gateway Reconnect
json
{"op": 7,"d": null}
Invalid Session
Sent to indicate one of at least three different situations:
- the gateway could not initialize a session after receiving an Opcode 2 Identify
- the gateway could not resume a previous session after receiving an Opcode 6 Resume
- the gateway has invalidated an active session and is requesting client action
The inner d
key is a boolean that indicates whether the session may be resumable. See Connecting and Resuming for more information.
Example Gateway Invalid Session
json
{"op": 9,"d": false}
Channels
Channel Create
Sent when a new guild channel is created, relevant to the current user. The inner payload is a channel object.
Channel Update
Sent when a channel is updated. The inner payload is a channel object. This is not sent when the field last_message_id
is altered. To keep track of the last_message_id changes, you must listen for Message Create events.
Channel Delete
Sent when a channel relevant to the current user is deleted. The inner payload is a channel object.
Thread Create
Sent when a thread is created, relevant to the current user, or when the current user is added to a thread. The inner payload is a channel object. When being added to an existing private thread, includes a thread member object.
Thread Update
Sent when a thread is updated. The inner payload is a channel object. This is not sent when the field last_message_id
is altered. To keep track of the last_message_id changes, you must listen for Message Create events.
Thread Delete
Sent when a thread relevant to the current user is deleted. The inner payload is a subset of the channel object, containing just the id
, guild_id
, parent_id
, and type
fields.
Thread List Sync
Sent when the current user gains access to a channel.
Thread List Sync Event Fields
Field | Type | Description |
---|---|---|
guild_id | snowflake | the id of the guild |
channel_ids? | array of snowflakes | the parent channel ids whose threads are being synced. If omitted, then threads were synced for the entire guild. This array may contain channel_ids that have no active threads as well, so you know to clear that data. |
threads | array of channel objects | all active threads in the given channels that the current user can access |
members | array of thread member objects | all thread member objects from the synced threads for the current user, indicating which threads the current user has been added to |
Thread Member Update
Sent when the thread member object for the current user is updated. The inner payload is a thread member object. This event is documented for completeness, but unlikely to be used by most bots. For bots, this event largely is just a signal that you are a member of the thread. See the threads docs for more details.
Thread Members Update
Sent when anyone is added to or removed from a thread. If the current user does not have the GUILD_MEMBERS
Gateway Intent, then this event will only be sent if the current user was added to or removed from the thread.
Thread Members Update Event Fields
Field | Type | Description |
---|---|---|
id | snowflake | the id of the thread |
guild_id | snowflake | the id of the guild |
member_count | integer | the approximate number of members in the thread, capped at 50 |
added_members?* | array of thread member objects | the users who were added to the thread |
removed_member_ids? | array of snowflakes | the id of the users who were removed from the thread |
* In this gateway event, the thread member objects will also include the guild member and nullable presence objects for each added thread member.
Channel Pins Update
Sent when a message is pinned or unpinned in a text channel. This is not sent when a pinned message is deleted.
Channel Pins Update Event Fields
Field | Type | Description |
---|---|---|
guild_id? | snowflake | the id of the guild |
channel_id | snowflake | the id of the channel |
last_pin_timestamp? | ?ISO8601 timestamp | the time at which the most recent pinned message was pinned |
Guilds
Guild Create
This event can be sent in three different scenarios:
- When a user is initially connecting, to lazily load and backfill information for all unavailable guilds sent in the Ready event. Guilds that are unavailable due to an outage will send a Guild Delete event.
- When a Guild becomes available again to the client.
- When the current user joins a new Guild.
The inner payload is a guild object, with all the extra fields specified.
Guild Update
Sent when a guild is updated. The inner payload is a guild object.
Guild Delete
Sent when a guild becomes or was already unavailable due to an outage, or when the user leaves or is removed from a guild. The inner payload is an unavailable guild object. If the unavailable
field is not set, the user was removed from the guild.
Guild Ban Add
Sent when a user is banned from a guild.
Guild Ban Add Event Fields
Field | Type | Description |
---|---|---|
guild_id | snowflake | id of the guild |
user | a user object | the banned user |
Guild Ban Remove
Sent when a user is unbanned from a guild.
Guild Ban Remove Event Fields
Field | Type | Description |
---|---|---|
guild_id | snowflake | id of the guild |
user | a user object | the unbanned user |
Guild Emojis Update
Sent when a guild's emojis have been updated.
Guild Emojis Update Event Fields
Field | Type | Description |
---|---|---|
guild_id | snowflake | id of the guild |
emojis | array | array of emojis |
Guild Stickers Update
Sent when a guild's stickers have been updated.
Guild Stickers Update Event Fields
Field | Type | Description |
---|---|---|
guild_id | snowflake | id of the guild |
stickers | array | array of stickers |
Guild Integrations Update
Sent when a guild integration is updated.
Guild Integrations Update Event Fields
Field | Type | Description |
---|---|---|
guild_id | snowflake | id of the guild whose integrations were updated |
Guild Member Add
Sent when a new user joins a guild. The inner payload is a guild member object with an extra guild_id
key:
Guild Member Add Extra Fields
Field | Type | Description |
---|---|---|
guild_id | snowflake | id of the guild |
Guild Member Remove
Sent when a user is removed from a guild (leave/kick/ban).
Guild Member Remove Event Fields
Field | Type | Description |
---|---|---|
guild_id | snowflake | the id of the guild |
user | a user object | the user who was removed |
Guild Member Update
Sent when a guild member is updated. This will also fire when the user object of a guild member changes.
Guild Member Update Event Fields
Field | Type | Description |
---|---|---|
guild_id | snowflake | the id of the guild |
roles | array of snowflakes | user role ids |
user | a user object | the user |
nick? | ?string | nickname of the user in the guild |
joined_at | ?ISO8601 timestamp | when the user joined the guild |
premium_since? | ?ISO8601 timestamp | when the user starting boosting the guild |
deaf? | boolean | whether the user is deafened in voice channels |
mute? | boolean | whether the user is muted in voice channels |
pending? | boolean | whether the user has not yet passed the guild's Membership Screening requirements |
Guild Members Chunk
Sent in response to Guild Request Members.
You can use the chunk_index
and chunk_count
to calculate how many chunks are left for your request.
Guild Members Chunk Event Fields
Field | Type | Description |
---|---|---|
guild_id | snowflake | the id of the guild |
members | array of guild member objects | set of guild members |
chunk_index | integer | the chunk index in the expected chunks for this response (0 <= chunk_index < chunk_count) |
chunk_count | integer | the total number of expected chunks for this response |
not_found? | array | if passing an invalid id to REQUEST_GUILD_MEMBERS , it will be returned here |
presences? | array of presence objects | if passing true to REQUEST_GUILD_MEMBERS , presences of the returned members will be here |
nonce? | string | the nonce used in the Guild Members Request |
Guild Role Create
Sent when a guild role is created.
Guild Role Create Event Fields
Field | Type | Description |
---|---|---|
guild_id | snowflake | the id of the guild |
role | a role object | the role created |
Guild Role Update
Sent when a guild role is updated.
Guild Role Update Event Fields
Field | Type | Description |
---|---|---|
guild_id | snowflake | the id of the guild |
role | a role object | the role updated |
Guild Role Delete
Sent when a guild role is deleted.
Guild Role Delete Event Fields
Field | Type | Description |
---|---|---|
guild_id | snowflake | id of the guild |
role_id | snowflake | id of the role |
Integrations
Integration Create
Sent when an integration is created. The inner payload is a integration object with an additional guild_id
key:
Integration Create Event Additional Fields
Field | Type | Description |
---|---|---|
guild_id | snowflake | id of the guild |
Integration Update
Sent when an integration is updated. The inner payload is a integration object with an additional guild_id
key:
Integration Update Event Additional Fields
Field | Type | Description |
---|---|---|
guild_id | snowflake | id of the guild |
Integration Delete
Sent when an integration is deleted.
Integration Delete Event Fields
Field | Type | Description |
---|---|---|
id | snowflake | integration id |
guild_id | snowflake | id of the guild |
application_id? | snowflake | id of the bot/OAuth2 application for this discord integration |
Invites
Invite Create
Sent when a new invite to a channel is created.
Invite Create Event Fields
Field | Type | Description |
---|---|---|
channel_id | snowflake | the channel the invite is for |
code | string | the unique invite code |
created_at | ISO8601 timestamp | the time at which the invite was created |
guild_id? | snowflake | the guild of the invite |
inviter? | user object | the user that created the invite |
max_age | integer | how long the invite is valid for (in seconds) |
max_uses | integer | the maximum number of times the invite can be used |
target_type? | integer | the type of target for this voice channel invite |
target_user? | user object | the user whose stream to display for this voice channel stream invite |
target_application? | partial application object | the embedded application to open for this voice channel embedded application invite |
temporary | boolean | whether or not the invite is temporary (invited users will be kicked on disconnect unless they're assigned a role) |
uses | integer | how many times the invite has been used (always will be 0) |
Invite Delete
Sent when an invite is deleted.
Invite Delete Event Fields
Field | Type | Description |
---|---|---|
channel_id | snowflake | the channel of the invite |
guild_id? | snowflake | the guild of the invite |
code | string | the unique invite code |
Messages
Message Create
Sent when a message is created. The inner payload is a message object.
Message Update
Sent when a message is updated. The inner payload is a message object.
Message Delete
Sent when a message is deleted.
Message Delete Event Fields
Field | Type | Description |
---|---|---|
id | snowflake | the id of the message |
channel_id | snowflake | the id of the channel |
guild_id? | snowflake | the id of the guild |
Message Delete Bulk
Sent when multiple messages are deleted at once.
Message Delete Bulk Event Fields
Field | Type | Description |
---|---|---|
ids | array of snowflakes | the ids of the messages |
channel_id | snowflake | the id of the channel |
guild_id? | snowflake | the id of the guild |
Message Reaction Add
Sent when a user adds a reaction to a message.
Message Reaction Add Event Fields
Field | Type | Description |
---|---|---|
user_id | snowflake | the id of the user |
channel_id | snowflake | the id of the channel |
message_id | snowflake | the id of the message |
guild_id? | snowflake | the id of the guild |
member? | member object | the member who reacted if this happened in a guild |
emoji | a partial emoji object | the emoji used to react - example |
Message Reaction Remove
Sent when a user removes a reaction from a message.
Message Reaction Remove Event Fields
Field | Type | Description |
---|---|---|
user_id | snowflake | the id of the user |
channel_id | snowflake | the id of the channel |
message_id | snowflake | the id of the message |
guild_id? | snowflake | the id of the guild |
emoji | a partial emoji object | the emoji used to react - example |
Message Reaction Remove All
Sent when a user explicitly removes all reactions from a message.
Message Reaction Remove All Event Fields
Field | Type | Description |
---|---|---|
channel_id | snowflake | the id of the channel |
message_id | snowflake | the id of the message |
guild_id? | snowflake | the id of the guild |
Message Reaction Remove Emoji
Sent when a bot removes all instances of a given emoji from the reactions of a message.
Message Reaction Remove Emoji
Field | Type | Description |
---|---|---|
channel_id | snowflake | the id of the channel |
guild_id? | snowflake | the id of the guild |
message_id | snowflake | the id of the message |
emoji | partial emoji object | the emoji that was removed |
Presence
Presence Update
A user's presence is their current state on a guild. This event is sent when a user's presence or info, such as name or avatar, is updated.
Presence Update Event Fields
Field | Type | Description |
---|---|---|
user | user object | the user presence is being updated for |
guild_id | snowflake | id of the guild |
status | string | either "idle", "dnd", "online", or "offline" |
activities | array of activity objects | user's current activities |
client_status | client_status object | user's platform-dependent status |
Client Status Object
Active sessions are indicated with an "online", "idle", or "dnd" string per platform. If a user is offline or invisible, the corresponding field is not present.
Field | Type | Description |
---|---|---|
desktop? | string | the user's status set for an active desktop (Windows, Linux, Mac) application session |
mobile? | string | the user's status set for an active mobile (iOS, Android) application session |
web? | string | the user's status set for an active web (browser, bot account) application session |
Activity Object
Activity Structure
Field | Type | Description |
---|---|---|
name | string | the activity's name |
type | integer | activity type |
url? | ?string | stream url, is validated when type is 1 |
created_at | integer | unix timestamp (in milliseconds) of when the activity was added to the user's session |
timestamps? | timestamps object | unix timestamps for start and/or end of the game |
application_id? | snowflake | application id for the game |
details? | ?string | what the player is currently doing |
state? | ?string | the user's current party status |
emoji? | ?emoji object | the emoji used for a custom status |
party? | party object | information for the current party of the player |
assets? | assets object | images for the presence and their hover texts |
secrets? | secrets object | secrets for Rich Presence joining and spectating |
instance? | boolean | whether or not the activity is an instanced game session |
flags? | integer | activity flags OR d together, describes what the payload includes |
buttons? | array of buttons | the custom buttons shown in the Rich Presence (max 2) |
Activity Types
ID | Name | Format | Example |
---|---|---|---|
0 | Game | Playing {name} | "Playing Rocket League" |
1 | Streaming | Streaming {details} | "Streaming Rocket League" |
2 | Listening | Listening to {name} | "Listening to Spotify" |
3 | Watching | Watching {name} | "Watching YouTube Together" |
4 | Custom | {emoji} {name} | ":smiley: I am cool" |
5 | Competing | Competing in {name} | "Competing in Arena World Champions" |
Activity Timestamps
Field | Type | Description |
---|---|---|
start? | integer | unix time (in milliseconds) of when the activity started |
end? | integer | unix time (in milliseconds) of when the activity ends |
Activity Emoji
Field | Type | Description |
---|---|---|
name | string | the name of the emoji |
id? | snowflake | the id of the emoji |
animated? | boolean | whether this emoji is animated |
Activity Party
Field | Type | Description |
---|---|---|
id? | string | the id of the party |
size? | array of two integers (current_size, max_size) | used to show the party's current and maximum size |
Activity Assets
Field | Type | Description |
---|---|---|
large_image? | string | the id for a large asset of the activity, usually a snowflake |
large_text? | string | text displayed when hovering over the large image of the activity |
small_image? | string | the id for a small asset of the activity, usually a snowflake |
small_text? | string | text displayed when hovering over the small image of the activity |
Activity Secrets
Field | Type | Description |
---|---|---|
join? | string | the secret for joining a party |
spectate? | string | the secret for spectating a game |
match? | string | the secret for a specific instanced match |
Activity Flags
Name | Value |
---|---|
INSTANCE | 1 << 0 |
JOIN | 1 << 1 |
SPECTATE | 1 << 2 |
JOIN_REQUEST | 1 << 3 |
SYNC | 1 << 4 |
PLAY | 1 << 5 |
Activity Buttons
When received over the gateway, the buttons
field is an array of strings, which are the button labels. Bots cannot access a user's activity button URLs. When sending, the buttons
field must be an array of the below object:
Field | Type | Description |
---|---|---|
label | string | the text shown on the button (1-32 characters) |
url | string | the url opened when clicking the button (1-512 characters) |
Example Activity
json
{"details": "24H RL Stream for Charity","state": "Rocket League","name": "Twitch","type": 1,"url": "https://www.twitch.tv/discord"}
Example Activity with Rich Presence
json
{"name": "Rocket League","type": 0,"application_id": "379286085710381999","state": "In a Match","details": "Ranked Duos: 2-1","timestamps": {"start": 15112000660000},"party": {"id": "9dd6594e-81b3-49f6-a6b5-a679e6a060d3","size": [2, 2]},"assets": {"large_image": "351371005538729000","large_text": "DFH Stadium","small_image": "351371005538729111","small_text": "Silver III"},"secrets": {"join": "025ed05c71f639de8bfaa0d679d7c94b2fdce12f","spectate": "e7eb30d2ee025ed05c71ea495f770b76454ee4e0","match": "4b2fdce12f639de8bfa7e3591b71a0d679d7c93f"}}
Typing Start
Sent when a user starts typing in a channel.
Typing Start Event Fields
Field | Type | Description |
---|---|---|
channel_id | snowflake | id of the channel |
guild_id? | snowflake | id of the guild |
user_id | snowflake | id of the user |
timestamp | integer | unix time (in seconds) of when the user started typing |
member? | member object | the member who started typing if this happened in a guild |
User Update
Sent when properties about the user change. Inner payload is a user object.
Voice
Voice State Update
Sent when someone joins/leaves/moves voice channels. Inner payload is a voice state object.
Voice Server Update
Sent when a guild's voice server is updated. This is sent when initially connecting to voice, and when the current voice instance fails over to a new server.
Voice Server Update Event Fields
Field | Type | Description |
---|---|---|
token | string | voice connection token |
guild_id | snowflake | the guild this voice server update is for |
endpoint | ?string | the voice server host |
Example Voice Server Update Payload
json
{"token": "my_token","guild_id": "41771983423143937","endpoint": "smart.loyal.discord.gg"}
Webhooks
Webhooks Update
Sent when a guild channel's webhook is created, updated, or deleted.
Webhook Update Event Fields
Field | Type | Description |
---|---|---|
guild_id | snowflake | id of the guild |
channel_id | snowflake | id of the channel |
Interactions
Interaction Create
Sent when a user in a guild uses an Application Command. Inner payload is an Interaction.
Stage Instances
Stage Instance Create
Sent when a Stage instance is created (i.e. the stage is now "live"). inner payload is a stage instance
Stage Instance Update
Sent when a Stage instance has been updated. inner payload is a stage instance
Stage Instance Delete
Sent when a Stage instance has been deleted (i.e. the stage has been closed). inner payload is a stage instance
Get Gateway
GET
/gateway
Returns an object with a single valid WSS URL, which the client can use for Connecting. Clients should cache this value and only call this endpoint to retrieve a new URL if they are unable to properly establish a connection using the cached version of the URL.
Example Response
json
{"url": "wss://gateway.discord.gg/"}
Get Gateway Bot
GET
/gateway/bot
Returns an object based on the information in Get Gateway, plus additional metadata that can help during the operation of large or sharded bots. Unlike the Get Gateway, this route should not be cached for extended periods of time as the value is not guaranteed to be the same per-call, and changes as the bot joins/leaves guilds.
JSON Response
Field | Type | Description |
---|---|---|
url | string | The WSS URL that can be used for connecting to the gateway |
shards | integer | The recommended number of shards to use when connecting |
session_start_limit | session_start_limit object | Information on the current session start limit |
Example Response
json
{"url": "wss://gateway.discord.gg/","shards": 9,"session_start_limit": {"total": 1000,"remaining": 999,"reset_after": 14400000,"max_concurrency": 1}}
Session Start Limit Object
Session Start Limit Structure
Field | Type | Description |
---|---|---|
total | integer | The total number of session starts the current user is allowed |
remaining | integer | The remaining number of session starts the current user is allowed |
reset_after | integer | The number of milliseconds after which the limit resets |
max_concurrency | integer | The number of identify requests allowed per 5 seconds |