Skip to main content
This guide covers the complete lifecycle of a Gateway connection, including connecting, identifying, heartbeating, resuming, and disconnecting.

Initial Connection

1. Establish WebSocket Connection

Connect to the Gateway URL with required query parameters:
const ws = new WebSocket('wss://gateway.fluxer.example/?v=1&encoding=json');

2. Receive Hello

Immediately after connecting, the Gateway sends a Hello message (opcode 10):
{
  "op": 10,
  "d": {
    "heartbeat_interval": 41250
  }
}
The heartbeat_interval is always 41,250 milliseconds (41.25 seconds) in the current implementation.

3. Start Heartbeating

Upon receiving Hello, start a timer to send heartbeats. The server also initiates heartbeat checks every heartbeat_interval / 3 (~13.75s).

Heartbeat Mechanism

Heartbeats keep the connection alive and allow the Gateway to detect dead connections.

Server-Initiated Heartbeat

The Gateway sends heartbeat requests (opcode 1) approximately every 13.75 seconds:
{
  "op": 1,
  "d": null
}

Client Response

Clients must respond with their current sequence number:
{
  "op": 1,
  "d": null
}
The Gateway responds with a heartbeat ACK (opcode 11):
{
  "op": 11
}
If the Gateway doesn’t receive a heartbeat ACK within 45 seconds (heartbeat timeout), it will close the connection with code 4009 (Session Timeout).

Heartbeat Validation

The Gateway validates heartbeat sequence numbers:
  • null is always accepted
  • Integer sequence numbers are verified against the session’s current state
  • Invalid sequences result in close code 4007 (Invalid Sequence)
Implementation reference: gateway_handler.erl:verify_heartbeat_ack/2

Authentication

Identify (New Session)

To start a new session, send an Identify message (opcode 2):
{
  "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"],
    "flags": 0,
    "initial_guild_id": "1234567890"
  }
}

Identify Payload Fields

FieldTypeRequiredDescription
tokenstringYesAuthentication token
propertiesobjectYesClient properties object
properties.osstringYesOperating system name
properties.browserstringYesBrowser/client name
properties.devicestringYesDevice name
presenceobjectNoInitial presence state
ignored_eventsarrayNoEvent names to not receive
flagsintegerNoConnection flags (default: 0)
initial_guild_idsnowflakeNoGuild to prioritize loading
Event names in ignored_events are normalized to uppercase (e.g., "typing_start" becomes "TYPING_START").

Identify Responses

Success: Receive a Ready event (opcode 0, event READY) Failure cases:
  • Close 4004: Authentication failed (invalid token)
  • Close 4005: Already authenticated (sent Identify twice)
  • Opcode 9: Invalid session (rate limited or other issue)
Implementation reference: gateway_handler.erl:handle_identify/3

Ready Event

After successful authentication, the Gateway sends a Ready event:
{
  "op": 0,
  "t": "READY",
  "s": 1,
  "d": {
    "user": { /* user object */ },
    "guilds": [ /* array of guild objects */ ],
    "session_id": "abc123def456",
    "resume_gateway_url": "wss://gateway.fluxer.example"
  }
}
Store the session_id to resume this session if the connection drops.

Session Resumption

If your connection drops unexpectedly, you can resume the session to avoid re-receiving events.

Resume Request

Send a Resume message (opcode 6) instead of Identify:
{
  "op": 6,
  "d": {
    "token": "your.auth.token",
    "session_id": "abc123def456",
    "seq": 42
  }
}

Resume Payload

FieldTypeDescription
tokenstringSame token used for Identify
session_idstringSession ID from Ready event
seqintegerLast sequence number received

Resume Responses

Success:
  1. Gateway replays all missed events since seq
  2. Gateway sends a RESUMED event:
{
  "op": 0,
  "t": "RESUMED",
  "s": 45,
  "d": null
}
Failure:
{
  "op": 9,
  "d": false
}
Session not found or expired. Send a new Identify.
Implementation reference: gateway_handler.erl:handle_resume/2

Event Replay

When resuming successfully, the Gateway:
  1. Retrieves all events between seq and current sequence
  2. Dispatches them in order to the client
  3. Sends the RESUMED event
  4. Continues with new events
This ensures no events are missed during the disconnection.

Presence Updates

Update your presence while connected using opcode 3:
{
  "op": 3,
  "d": {
    "status": "dnd",
    "afk": false,
    "mobile": false
  }
}

Status Values

StatusDescription
onlineAvailable and active
idleAway from keyboard
dndDo Not Disturb
invisibleAppear offline
offlineConverted to invisible by Gateway
The Gateway automatically converts "offline" status to "invisible" (see gateway_handler.erl:adjust_status/1).

Voice State Updates

Join or leave voice channels with opcode 4:
{
  "op": 4,
  "d": {
    "guild_id": "1234567890",
    "channel_id": "9876543210",
    "self_mute": false,
    "self_deaf": false
  }
}
To leave a voice channel, set channel_id to null.

Voice Update Rate Limiting

Voice state updates are limited to 10 per second. Exceeding this rate will queue updates (max queue: 64).
The queue is processed automatically every 100ms. If the queue exceeds 64 entries, the oldest entry is dropped. Implementation: gateway_handler.erl:handle_voice_state_update/3

Disconnection

Graceful Closure

Clients can close the connection using standard WebSocket close frames. The Gateway will:
  1. Preserve the session for potential resumption
  2. Clean up compression contexts
  3. Update connection metrics

Server-Initiated Closure

The Gateway may close connections for various reasons (see Close Codes):
WebSocket Close Frame:
{
  "code": 4008,
  "reason": "Rate limited"
}
Close codes 4004, 4010, 4011, 4012, and 4013 are not resumable. You must create a new session.

Connection State Machine

Best Practices

When reconnecting after errors, use exponential backoff:
  • First retry: 1-2 seconds
  • Second retry: 2-4 seconds
  • Third retry: 4-8 seconds
  • Max: 60 seconds
Always persist:
  • Session ID from Ready
  • Last sequence number received
  • Resume Gateway URL
This enables seamless resumption after disconnects.
Track heartbeat round-trip time and failures:
  • Warn if RTT exceeds 10 seconds
  • Reconnect if 3 consecutive failures
  • The Gateway tracks this server-side too
If you receive close code 4008:
  • Do NOT immediately reconnect
  • Wait at least 60 seconds
  • Review your event sending rate

Implementation References

Key source files for connection lifecycle:
  • gateway_handler.erl - Main WebSocket handler
  • session_manager.erl - Session creation and lookup
  • session.erl - Session state management
  • constants.erl - Timing and limit constants

Build docs developers (and LLMs) love