Skip to main content
Opcodes define the type of Gateway message being sent or received. All Gateway messages include an op field that specifies the opcode.

Opcode Reference

The following opcodes are defined in fluxer_gateway/src/utils/constants.erl:
OpcodeNameDirectionDescription
0DispatchServer → ClientEvent dispatched to client
1HeartbeatBothHeartbeat request/response
2IdentifyClient → ServerStart a new session
3Presence UpdateClient → ServerUpdate client presence
4Voice State UpdateClient → ServerJoin/leave/update voice
5Voice Server PingClient → ServerVoice server ping (reserved)
6ResumeClient → ServerResume a disconnected session
7ReconnectServer → ClientServer requests reconnect
8Request Guild MembersClient → ServerRequest guild member chunks
9Invalid SessionServer → ClientSession is invalid
10HelloServer → ClientInitial handshake
11Heartbeat ACKServer → ClientHeartbeat acknowledgement
12Gateway ErrorServer → ClientGateway error occurred
14Lazy RequestClient → ServerRequest lazy-loaded data
Opcode 13 is intentionally skipped in the specification.

Server → Client Opcodes

These opcodes are sent by the Gateway to clients.

Opcode 0: Dispatch

Dispatches events to the client. This is the most common opcode. Payload:
{
  "op": 0,
  "t": "MESSAGE_CREATE",
  "s": 42,
  "d": {
    "id": "1234567890",
    "content": "Hello!",
    "channel_id": "9876543210"
  }
}
Fields:
FieldTypeDescription
op0Dispatch opcode
tstringEvent name (see Events)
sintegerSequence number (monotonically increasing)
dobjectEvent data (varies by event type)
Store the sequence number (s) to enable session resumption.

Opcode 7: Reconnect

The server requests that the client reconnect to the Gateway. Payload:
{
  "op": 7
}
Client Action:
  1. Immediately close the connection
  2. Reconnect to the Gateway
  3. Send a Resume (opcode 6) with the last sequence number
Do NOT send a new Identify. Always attempt to Resume when receiving this opcode.

Opcode 9: Invalid Session

Indicates the session is invalid and cannot be resumed. Payload:
{
  "op": 9,
  "d": false
}
Fields:
FieldTypeDescription
dbooleanWhether the session is resumable (always false in practice)
Client Action:
  1. Wait 1-5 seconds (randomized)
  2. Send a new Identify (opcode 2)
  3. Do NOT attempt to Resume
Causes:
  • Session not found (expired or invalid session_id)
  • Identify rate limited
  • Invalid token during Resume
Implementation: gateway_handler.erl:send_invalid_session/1

Opcode 10: Hello

Sent immediately after connecting. Contains heartbeat configuration. Payload:
{
  "op": 10,
  "d": {
    "heartbeat_interval": 41250
  }
}
Fields:
FieldTypeDescription
d.heartbeat_intervalintegerMilliseconds between heartbeats (41,250ms)
Client Action:
  1. Start heartbeat timer with the given interval
  2. Send Identify or Resume
The heartbeat interval is always 41,250 milliseconds in the current implementation.

Opcode 11: Heartbeat ACK

Acknowledges receipt of a client heartbeat. Payload:
{
  "op": 11
}
Client Action: Update last ACK timestamp to prevent timeout.
If you don’t receive a Heartbeat ACK within 45 seconds, the connection may be dead. Consider reconnecting.

Opcode 12: Gateway Error

Indicates a Gateway-level error occurred. Payload:
{
  "op": 12,
  "d": {
    "code": "VOICE_CHANNEL_NOT_FOUND",
    "message": "Voice channel not found"
  }
}
This opcode is used for errors that don’t warrant connection closure.

Client → Server Opcodes

These opcodes are sent by clients to the Gateway.

Opcode 1: Heartbeat

Responds to server heartbeat requests or sends proactive heartbeats. Payload:
{
  "op": 1,
  "d": null
}
Fields:
FieldTypeDescription
dinteger | nullLast sequence number received (null before Ready)
Server Response: Opcode 11 (Heartbeat ACK) Rate Limit: Counted towards the 120 events/60s limit. Implementation: gateway_handler.erl:handle_heartbeat/4

Opcode 2: Identify

Starts a new Gateway session. Payload:
{
  "op": 2,
  "d": {
    "token": "your.auth.token",
    "properties": {
      "os": "linux",
      "browser": "fluxer-client",
      "device": "fluxer-client"
    },
    "presence": {
      "status": "online",
      "afk": false
    },
    "ignored_events": ["TYPING_START", "PRESENCE_UPDATE"],
    "flags": 0,
    "initial_guild_id": "1234567890"
  }
}
Required Fields:
FieldTypeDescription
tokenstringBot or user authentication token
propertiesobjectClient properties
properties.osstringOperating system
properties.browserstringClient/browser name
properties.devicestringDevice name
Optional Fields:
FieldTypeDefaultDescription
presenceobjectnullInitial presence state
ignored_eventsarray[]Event names to filter out
flagsinteger0Connection flags
initial_guild_idsnowflakenullGuild to prioritize loading
Server Response:
  • Success: Opcode 0 with event “READY”
  • Failure: Close code 4004 (Authentication Failed) or Opcode 9 (Invalid Session)
Errors:
  • Close 4004: Invalid token
  • Close 4005: Already authenticated
  • Opcode 9: Rate limited
Implementation: gateway_handler.erl:validate_identify_data/1

Opcode 3: Presence Update

Updates the client’s presence status. Payload:
{
  "op": 3,
  "d": {
    "status": "dnd",
    "afk": true,
    "mobile": false
  }
}
Fields:
FieldTypeDescription
statusstringOne of: online, idle, dnd, invisible
afkbooleanWhether the user is AFK
mobilebooleanWhether connecting from mobile
Status Conversion: The Gateway automatically converts "offline" to "invisible". Requirements:
  • Must be authenticated (sent Identify)
  • Rate limited: 120/60s
Implementation: gateway_handler.erl:handle_presence_update/3

Opcode 4: Voice State Update

Joins, leaves, or updates voice channel state. Payload:
{
  "op": 4,
  "d": {
    "guild_id": "1234567890",
    "channel_id": "9876543210",
    "self_mute": false,
    "self_deaf": false
  }
}
Fields:
FieldTypeDescription
guild_idsnowflakeGuild containing the voice channel
channel_idsnowflake | nullVoice channel ID (null to disconnect)
self_mutebooleanWhether to self-mute
self_deafbooleanWhether to self-deafen
Rate Limit:
10 updates per second. Exceeding this queues updates (max 64). Queue processed every 100ms.
Requirements:
  • Must be authenticated
Implementation: gateway_handler.erl:handle_voice_state_update/3

Opcode 6: Resume

Resumes a disconnected session. Payload:
{
  "op": 6,
  "d": {
    "token": "your.auth.token",
    "session_id": "abc123def456",
    "seq": 42
  }
}
Fields:
FieldTypeDescription
tokenstringSame token used for Identify
session_idstringSession ID from Ready event
seqintegerLast sequence number received
Server Response:
  • Success: Replays missed events, then sends RESUMED event
  • Failure: Opcode 9 (Invalid Session) or close codes
Errors:
  • Opcode 9: Session not found
  • Close 4007: Invalid sequence number
  • Close 4004: Token mismatch
Implementation: gateway_handler.erl:handle_resume/2

Opcode 8: Request Guild Members

Requests chunks of guild members. Payload:
{
  "op": 8,
  "d": {
    "guild_id": "1234567890",
    "query": "search",
    "limit": 100
  }
}
Rate Limit:
3 requests per 10 seconds. Exceeding this closes the connection with code 4008.
Requirements:
  • Must be authenticated
  • Only one concurrent request allowed per connection
Implementation: gateway_handler.erl:handle_request_guild_members/3

Opcode 14: Lazy Request

Requests lazy-loaded data (guild subscriptions, channels, etc.). Payload:
{
  "op": 14,
  "d": {
    "guild_id": "1234567890",
    "channels": {
      "9876543210": [[0, 99]]
    }
  }
}
Requirements:
  • Must be authenticated
  • Rate limited: 120/60s
Implementation: gateway_handler.erl:handle_lazy_request/3

Error Handling

Unknown Opcode

Sending an unknown opcode results in: Close Code: 4001 (Unknown Opcode) Reason: "Unknown opcode"

Before Authentication

Sending opcodes 3, 4, 8, or 14 before authentication results in: Close Code: 4003 (Not Authenticated) Reason: "Not authenticated"

Rate Limiting

Exceeding rate limits results in: Close Code: 4008 (Rate Limited) Reason: "Rate limited"

Implementation Notes

Opcode mapping is defined in constants.erl:
%% Integer to atom
gateway_opcode(0) -> dispatch;
gateway_opcode(1) -> heartbeat;
gateway_opcode(2) -> identify;
...

%% Atom to integer  
opcode_to_num(dispatch) -> 0;
opcode_to_num(heartbeat) -> 1;
opcode_to_num(identify) -> 2;
...
See fluxer_gateway/src/utils/constants.erl:44-74 for the complete implementation.

Build docs developers (and LLMs) love