Gateway events are sent via Dispatch messages (opcode 0). Each event has a type (t field) and contains specific data in the d field.
Event Structure
All events follow this format:
{
"op": 0,
"t": "EVENT_NAME",
"s": 42,
"d": {
// Event-specific data
}
}
Event Categories
Messages
MESSAGE_CREATE, MESSAGE_UPDATE, MESSAGE_DELETE
Guilds
GUILD_CREATE, GUILD_UPDATE, GUILD_DELETE
Channels
CHANNEL_CREATE, CHANNEL_UPDATE, CHANNEL_DELETE
Members
GUILD_MEMBER_UPDATE, GUILD_MEMBER_ADD, GUILD_MEMBER_REMOVE
Reactions
MESSAGE_REACTION_ADD, MESSAGE_REACTION_REMOVE
Voice
VOICE_STATE_UPDATE, VOICE_SERVER_UPDATE
Other
TYPING_START, RELATIONSHIP_ADD, and more
Session Events
READY
Sent after successful authentication. Contains initial state data.
Example:
{
"op": 0,
"t": "READY",
"s": 1,
"d": {
"v": 1,
"user": {
"id": "1234567890",
"username": "fluxer_user",
"discriminator": "0001"
},
"guilds": [
{
"id": "9876543210",
"name": "My Guild",
"unavailable": false
}
],
"session_id": "abc123def456",
"resume_gateway_url": "wss://gateway.fluxer.example",
"relationships": [],
"private_channels": []
}
}
Key Fields:
| Field | Type | Description |
|---|
v | integer | Gateway version (1) |
user | object | The authenticated user |
guilds | array | Guilds the user is in |
session_id | string | Session ID for resuming |
resume_gateway_url | string | URL to use when resuming |
relationships | array | User relationships (friends, blocked) |
private_channels | array | DM and group DM channels |
Store session_id to enable session resumption after disconnects.
RESUMED
Sent after successfully resuming a session.
Example:
{
"op": 0,
"t": "RESUMED",
"s": 45,
"d": null
}
After this event, the Gateway continues sending new events normally.
Implementation: gateway_handler.erl:handle_resume_success/5
Message Events
MESSAGE_CREATE
A message was created.
Example:
{
"op": 0,
"t": "MESSAGE_CREATE",
"s": 42,
"d": {
"id": "1234567890",
"channel_id": "9876543210",
"guild_id": "1111111111",
"author": {
"id": "2222222222",
"username": "sender",
"discriminator": "0001"
},
"content": "Hello, world!",
"timestamp": "2026-03-04T12:00:00.000Z",
"edited_timestamp": null,
"mentions": [],
"attachments": []
}
}
MESSAGE_UPDATE
A message was edited.
Example:
{
"op": 0,
"t": "MESSAGE_UPDATE",
"s": 43,
"d": {
"id": "1234567890",
"channel_id": "9876543210",
"content": "Edited content",
"edited_timestamp": "2026-03-04T12:01:00.000Z"
}
}
MESSAGE_UPDATE may only include changed fields, not the complete message object.
MESSAGE_DELETE
A message was deleted.
Example:
{
"op": 0,
"t": "MESSAGE_DELETE",
"s": 44,
"d": {
"id": "1234567890",
"channel_id": "9876543210",
"guild_id": "1111111111"
}
}
MESSAGE_DELETE_BULK
Multiple messages were deleted at once.
Example:
{
"op": 0,
"t": "MESSAGE_DELETE_BULK",
"s": 45,
"d": {
"ids": ["1234567890", "1234567891", "1234567892"],
"channel_id": "9876543210",
"guild_id": "1111111111"
}
}
Reaction Events
MESSAGE_REACTION_ADD
A reaction was added to a message.
Example:
{
"op": 0,
"t": "MESSAGE_REACTION_ADD",
"s": 46,
"d": {
"user_id": "2222222222",
"channel_id": "9876543210",
"message_id": "1234567890",
"guild_id": "1111111111",
"emoji": {
"id": null,
"name": "👍"
},
"member": {
"user": { /* user object */ },
"roles": ["3333333333"]
}
}
}
The Gateway may buffer rapid reactions and send them as MESSAGE_REACTION_ADD_MANY instead.
MESSAGE_REACTION_ADD_MANY
Multiple reactions were added (batched by Gateway).
Example:
{
"op": 0,
"t": "MESSAGE_REACTION_ADD_MANY",
"s": 47,
"d": {
"channel_id": "9876543210",
"message_id": "1234567890",
"guild_id": "1111111111",
"reactions": [
{
"user_id": "2222222222",
"emoji": { "id": null, "name": "👍" }
},
{
"user_id": "3333333333",
"emoji": { "id": null, "name": "❤️" }
}
]
}
}
Implementation: session_dispatch.erl:dispatch_reaction_add_many/2
MESSAGE_REACTION_REMOVE
A reaction was removed from a message.
Example:
{
"op": 0,
"t": "MESSAGE_REACTION_REMOVE",
"s": 48,
"d": {
"user_id": "2222222222",
"channel_id": "9876543210",
"message_id": "1234567890",
"guild_id": "1111111111",
"emoji": {
"id": null,
"name": "👍"
}
}
}
Guild Events
GUILD_CREATE
Sent when joining a guild or during initial Ready sequence.
Example:
{
"op": 0,
"t": "GUILD_CREATE",
"s": 10,
"d": {
"id": "1111111111",
"name": "My Guild",
"owner_id": "2222222222",
"channels": [ /* array of channels */ ],
"members": [ /* array of members */ ],
"roles": [ /* array of roles */ ]
}
}
GUILD_UPDATE
Guild properties were updated.
Example:
{
"op": 0,
"t": "GUILD_UPDATE",
"s": 50,
"d": {
"id": "1111111111",
"name": "Updated Guild Name",
"icon": "new_icon_hash"
}
}
GUILD_DELETE
You left a guild or the guild became unavailable.
Example:
{
"op": 0,
"t": "GUILD_DELETE",
"s": 51,
"d": {
"id": "1111111111",
"unavailable": false
}
}
Fields:
| Field | Type | Description |
|---|
id | snowflake | Guild ID |
unavailable | boolean | true if outage, false if kicked/left |
Channel Events
CHANNEL_CREATE
A channel was created.
Example:
{
"op": 0,
"t": "CHANNEL_CREATE",
"s": 52,
"d": {
"id": "9876543210",
"type": 0,
"guild_id": "1111111111",
"name": "new-channel",
"position": 1
}
}
Implementation: session_dispatch.erl:update_channels_map/3
CHANNEL_UPDATE
A channel was updated.
Example:
{
"op": 0,
"t": "CHANNEL_UPDATE",
"s": 53,
"d": {
"id": "9876543210",
"name": "renamed-channel",
"topic": "New topic"
}
}
CHANNEL_DELETE
A channel was deleted.
Example:
{
"op": 0,
"t": "CHANNEL_DELETE",
"s": 54,
"d": {
"id": "9876543210",
"type": 0,
"guild_id": "1111111111"
}
}
Member Events
GUILD_MEMBER_ADD
A user joined a guild.
Example:
{
"op": 0,
"t": "GUILD_MEMBER_ADD",
"s": 55,
"d": {
"guild_id": "1111111111",
"user": {
"id": "4444444444",
"username": "newmember"
},
"roles": [],
"joined_at": "2026-03-04T12:00:00.000Z"
}
}
GUILD_MEMBER_UPDATE
A guild member was updated (roles, nickname, etc.).
Example:
{
"op": 0,
"t": "GUILD_MEMBER_UPDATE",
"s": 56,
"d": {
"guild_id": "1111111111",
"user": {
"id": "4444444444"
},
"roles": ["5555555555"],
"nick": "NewNickname"
}
}
Implementation: guild_presence.erl (dispatches member updates on presence changes)
GUILD_MEMBER_REMOVE
A user left or was removed from a guild.
Example:
{
"op": 0,
"t": "GUILD_MEMBER_REMOVE",
"s": 57,
"d": {
"guild_id": "1111111111",
"user": {
"id": "4444444444"
}
}
}
Voice Events
VOICE_STATE_UPDATE
A user’s voice state changed.
Example:
{
"op": 0,
"t": "VOICE_STATE_UPDATE",
"s": 58,
"d": {
"guild_id": "1111111111",
"channel_id": "9876543210",
"user_id": "2222222222",
"session_id": "voice_session_123",
"self_mute": false,
"self_deaf": false,
"mute": false,
"deaf": false
}
}
VOICE_SERVER_UPDATE
Voice server information for connecting.
Example:
{
"op": 0,
"t": "VOICE_SERVER_UPDATE",
"s": 59,
"d": {
"token": "voice_token_abc123",
"guild_id": "1111111111",
"endpoint": "voice.fluxer.example"
}
}
Presence Events
PRESENCE_UPDATE
A user’s presence changed (status, activity, etc.).
Example:
{
"op": 0,
"t": "PRESENCE_UPDATE",
"s": 60,
"d": {
"user": {
"id": "2222222222"
},
"guild_id": "1111111111",
"status": "idle",
"activities": [],
"client_status": {
"desktop": "idle"
}
}
}
Presence Buffering
The Gateway may buffer presence updates for users who are not:
- In a guild channel
- Friends (relationship type 1 or 3)
- In a group DM with you
Buffered presences are flushed when you add them as a friend.
Implementation: session_dispatch.erl:should_buffer_presence/3
Relationship Events
RELATIONSHIP_ADD
A relationship was created (friend request, friend, blocked).
Example:
{
"op": 0,
"t": "RELATIONSHIP_ADD",
"s": 61,
"d": {
"id": "3333333333",
"type": 1,
"user": {
"id": "3333333333",
"username": "newfriend"
}
}
}
Relationship Types:
| Type | Description |
|---|
| 0 | None |
| 1 | Friend |
| 2 | Blocked |
| 3 | Incoming friend request |
| 4 | Outgoing friend request |
RELATIONSHIP_UPDATE
A relationship was updated.
Example:
{
"op": 0,
"t": "RELATIONSHIP_UPDATE",
"s": 62,
"d": {
"id": "3333333333",
"type": 1
}
}
RELATIONSHIP_REMOVE
A relationship was removed.
Example:
{
"op": 0,
"t": "RELATIONSHIP_REMOVE",
"s": 63,
"d": {
"id": "3333333333",
"type": 0
}
}
Other Events
TYPING_START
A user started typing.
Example:
{
"op": 0,
"t": "TYPING_START",
"s": 64,
"d": {
"channel_id": "9876543210",
"user_id": "2222222222",
"timestamp": 1709553600
}
}
You can filter out TYPING_START events by including "TYPING_START" in the ignored_events array during Identify.
CHANNEL_RECIPIENT_ADD
A user was added to a group DM.
Example:
{
"op": 0,
"t": "CHANNEL_RECIPIENT_ADD",
"s": 65,
"d": {
"channel_id": "9876543210",
"user": {
"id": "4444444444",
"username": "newuser"
}
}
}
Implementation: session_dispatch.erl:update_recipient_membership/3
CHANNEL_RECIPIENT_REMOVE
A user was removed from a group DM.
Example:
{
"op": 0,
"t": "CHANNEL_RECIPIENT_REMOVE",
"s": 66,
"d": {
"channel_id": "9876543210",
"user": {
"id": "4444444444"
}
}
}
Event Filtering
You can filter unwanted events during Identify:
{
"op": 2,
"d": {
"token": "...",
"properties": { /* ... */ },
"ignored_events": [
"TYPING_START",
"PRESENCE_UPDATE"
]
}
}
Filtered events will not be sent to your client, reducing bandwidth.
Implementation: session_dispatch.erl:should_ignore_event/2
Event Normalization
Event names are normalized to uppercase. The Gateway accepts both:
- Uppercase:
"MESSAGE_CREATE"
- Lowercase:
"message_create"
Both are converted to the atom message_create internally.
Implementation: event_atoms.erl:normalize/1