Skip to main content

Overview

The Fluxer Gateway is a WebSocket-based connection that provides real-time event delivery for your bot. It enables your bot to receive messages, member updates, voice state changes, and other events as they happen.

Getting Gateway Information

Before connecting, retrieve the Gateway URL and connection parameters:
curl -H "Authorization: Bot {bot_token}" \
  https://api.fluxer.app/v1/gateway/bot
Response:
{
  "url": "wss://gateway.fluxer.app",
  "shards": 1,
  "session_start_limit": {
    "total": 1000,
    "remaining": 999,
    "reset_after": 14400000,
    "max_concurrency": 1
  }
}
url
string
WebSocket URL to connect to
shards
number
Recommended number of shards for your bot (always 1 for most bots)
session_start_limit
object
Connection rate limit information
The session_start_limit prevents abuse by limiting how many Gateway connections you can establish within a time window.

Connection Flow

Establishing Connection

1. Connect to WebSocket

import WebSocket from 'ws';

const ws = new WebSocket('wss://gateway.fluxer.app');

ws.on('open', () => {
  console.log('Connected to Gateway');
});

ws.on('message', (data) => {
  const payload = JSON.parse(data.toString());
  handleGatewayPayload(payload);
});

ws.on('close', (code, reason) => {
  console.log(`Disconnected: ${code} - ${reason}`);
});

ws.on('error', (error) => {
  console.error('WebSocket error:', error);
});

2. Receive Hello Event

The Gateway sends a Hello event immediately after connection:
{
  "op": 10,
  "d": {
    "heartbeat_interval": 41250
  }
}
op
number
Opcode 10 indicates a Hello event
d.heartbeat_interval
number
Milliseconds between heartbeat messages (typically 41250ms)

3. Send Identify

Authenticate your bot by sending an Identify payload:
{
  "op": 2,
  "d": {
    "token": "Bot 123456789012345678.dGhpcyBpcyBhIHNlY3JldCB0b2tlbiBleGFtcGxl",
    "properties": {
      "os": "linux",
      "browser": "my-bot",
      "device": "my-bot"
    },
    "intents": 513,
    "presence": {
      "status": "online",
      "afk": false
    }
  }
}
token
string
required
Your bot token with “Bot ” prefix
properties
object
required
Connection properties for identification
intents
number
required
Gateway intents bitfield (see Intents section)
presence
object
Initial presence status
Include the “Bot ” prefix before your token, or the connection will be rejected.

4. Receive Ready Event

Upon successful authentication, the Gateway sends a Ready event:
{
  "op": 0,
  "t": "READY",
  "d": {
    "v": 1,
    "user": {
      "id": "123456789012345678",
      "username": "my_awesome_bot",
      "discriminator": "0001",
      "avatar": null,
      "bot": true
    },
    "guilds": [
      {"id": "987654321098765432", "unavailable": false}
    ],
    "session_id": "abc123def456",
    "application_id": "123456789012345678"
  }
}
The Ready event contains your bot’s user information and a list of guilds it’s a member of.

Heartbeat

Maintain the connection by sending heartbeat messages at the specified interval:
let heartbeatInterval: NodeJS.Timeout;
let lastSequence: number | null = null;

function handleHello(payload: any) {
  const interval = payload.d.heartbeat_interval;
  
  heartbeatInterval = setInterval(() => {
    ws.send(JSON.stringify({
      op: 1,
      d: lastSequence
    }));
  }, interval);
}

function handleDispatch(payload: any) {
  lastSequence = payload.s;
}
Failure to send heartbeats will cause the Gateway to close your connection with code 4000.

Heartbeat ACK

The Gateway acknowledges each heartbeat:
{
  "op": 11
}
If you don’t receive a heartbeat ACK before sending the next heartbeat, consider the connection zombie and reconnect.

Gateway Intents

Intents control which events your bot receives. Calculate the bitfield:
// Gateway Intents
const Intents = {
  GUILDS: 1 << 0,                    // 1
  GUILD_MEMBERS: 1 << 1,             // 2
  GUILD_BANS: 1 << 2,                // 4
  GUILD_EMOJIS: 1 << 3,              // 8
  GUILD_INTEGRATIONS: 1 << 4,        // 16
  GUILD_WEBHOOKS: 1 << 5,            // 32
  GUILD_INVITES: 1 << 6,             // 64
  GUILD_VOICE_STATES: 1 << 7,        // 128
  GUILD_PRESENCES: 1 << 8,           // 256
  GUILD_MESSAGES: 1 << 9,            // 512
  GUILD_MESSAGE_REACTIONS: 1 << 10,  // 1024
  GUILD_MESSAGE_TYPING: 1 << 11,     // 2048
  DIRECT_MESSAGES: 1 << 12,          // 4096
  DIRECT_MESSAGE_REACTIONS: 1 << 13, // 8192
  DIRECT_MESSAGE_TYPING: 1 << 14,    // 16384
};

// Example: Guilds + Guild Messages
const intents = Intents.GUILDS | Intents.GUILD_MESSAGES;
// Result: 513

Common Intent Combinations

const intents = 
  Intents.GUILDS |
  Intents.GUILD_MESSAGES;
// 513
Receives guild events and messages
Privileged intents (GUILD_PRESENCES, GUILD_MEMBERS) may require approval for verified bots with 100+ guilds.

Receiving Events

All events use opcode 0 (Dispatch) with an event type:
{
  "op": 0,
  "s": 42,
  "t": "MESSAGE_CREATE",
  "d": {
    "id": "234567890123456789",
    "channel_id": "345678901234567890",
    "author": {
      "id": "456789012345678901",
      "username": "user123",
      "discriminator": "0001"
    },
    "content": "Hello, bot!",
    "timestamp": "2026-03-04T12:00:00.000Z"
  }
}
op
number
Always 0 for dispatch events
s
number
Sequence number (use for heartbeats)
t
string
Event type name (e.g., MESSAGE_CREATE)
d
object
Event data (structure varies by event type)

Event Handler Example

function handleGatewayPayload(payload: any) {
  const { op, t, d, s } = payload;
  
  if (s !== null) {
    lastSequence = s;
  }
  
  switch (op) {
    case 10: // Hello
      handleHello(payload);
      sendIdentify();
      break;
      
    case 11: // Heartbeat ACK
      console.log('Heartbeat acknowledged');
      break;
      
    case 0: // Dispatch
      handleEvent(t, d);
      break;
      
    default:
      console.log('Unknown opcode:', op);
  }
}

function handleEvent(type: string, data: any) {
  switch (type) {
    case 'READY':
      console.log(`Logged in as ${data.user.username}`);
      break;
      
    case 'MESSAGE_CREATE':
      handleMessage(data);
      break;
      
    case 'GUILD_MEMBER_ADD':
      handleMemberJoin(data);
      break;
      
    // ... handle other events
  }
}

Common Gateway Events

MESSAGE_CREATE

Received when a message is sent in a channel your bot can see:
{
  "op": 0,
  "t": "MESSAGE_CREATE",
  "d": {
    "id": "234567890123456789",
    "channel_id": "345678901234567890",
    "guild_id": "456789012345678901",
    "author": {...},
    "content": "!help",
    "timestamp": "2026-03-04T12:00:00.000Z",
    "mention_everyone": false,
    "mentions": [],
    "attachments": [],
    "embeds": []
  }
}

GUILD_MEMBER_ADD

Received when a user joins a guild:
{
  "op": 0,
  "t": "GUILD_MEMBER_ADD",
  "d": {
    "user": {
      "id": "567890123456789012",
      "username": "newuser",
      "discriminator": "0001"
    },
    "guild_id": "456789012345678901",
    "joined_at": "2026-03-04T12:00:00.000Z",
    "roles": []
  }
}

VOICE_STATE_UPDATE

Received when a user’s voice state changes:
{
  "op": 0,
  "t": "VOICE_STATE_UPDATE",
  "d": {
    "guild_id": "456789012345678901",
    "channel_id": "678901234567890123",
    "user_id": "567890123456789012",
    "session_id": "xyz789",
    "mute": false,
    "deaf": false,
    "self_mute": false,
    "self_deaf": false
  }
}

All Gateway Events

Complete reference of Gateway events

Event Filtering

Control which events you receive

Resuming Sessions

If your connection drops, resume instead of re-identifying:
{
  "op": 6,
  "d": {
    "token": "Bot {bot_token}",
    "session_id": "abc123def456",
    "seq": 42
  }
}
token
string
required
Your bot token with “Bot ” prefix
session_id
string
required
Session ID from the Ready event
seq
number
required
Last sequence number received

Resume vs Identify

  • Connection was closed unexpectedly
  • Close code is 4000-4999 (except 4004, 4010, 4011, 4014)
  • You have a valid session_id and sequence number
  • Less than 5 minutes since disconnection

Close Codes

CodeDescriptionReconnect
4000Unknown errorYes (resume)
4001Unknown opcodeYes (resume)
4002Decode errorYes (resume)
4003Not authenticatedNo
4004Authentication failedNo
4005Already authenticatedYes (resume)
4007Invalid sequenceYes (identify)
4008Rate limitedYes (identify)
4009Session timeoutYes (identify)
4010Invalid shardNo
4011Sharding requiredNo
4014Disallowed intentsNo
Do not attempt to reconnect if the close code is 4003, 4004, 4010, 4011, or 4014.

Rate Limits

Gateway Rate Limits

  • Identify: 1 per 5 seconds
  • Presence Updates: 5 per 60 seconds
  • Gateway Commands: 120 per 60 seconds

Handling Rate Limits

let commandCount = 0;
let resetTime = Date.now() + 60000;

function canSendCommand(): boolean {
  const now = Date.now();
  
  if (now >= resetTime) {
    commandCount = 0;
    resetTime = now + 60000;
  }
  
  if (commandCount >= 120) {
    console.warn('Gateway rate limit reached');
    return false;
  }
  
  commandCount++;
  return true;
}

Best Practices

1

Implement Reconnection Logic

Always handle disconnections gracefully with exponential backoff:
let reconnectAttempts = 0;
const maxReconnectDelay = 60000; // 60 seconds

function reconnect() {
  const delay = Math.min(
    1000 * Math.pow(2, reconnectAttempts),
    maxReconnectDelay
  );
  
  setTimeout(() => {
    reconnectAttempts++;
    connect();
  }, delay);
}
2

Store Session Information

Persist session_id and sequence number for resumption:
let sessionId: string | null = null;
let lastSequence: number | null = null;

function handleReady(data: any) {
  sessionId = data.session_id;
  // Save to file or database
}
3

Monitor Heartbeat ACKs

Detect zombie connections by tracking heartbeat acknowledgments:
let lastHeartbeatAck = Date.now();
let heartbeatAckReceived = true;

function sendHeartbeat() {
  if (!heartbeatAckReceived) {
    console.warn('Heartbeat not acknowledged, reconnecting');
    reconnect();
    return;
  }
  
  heartbeatAckReceived = false;
  ws.send(JSON.stringify({ op: 1, d: lastSequence }));
}

function handleHeartbeatAck() {
  heartbeatAckReceived = true;
  lastHeartbeatAck = Date.now();
}
4

Handle Large Payloads

Some events can be very large (e.g., READY with many guilds). Use streaming JSON parsers for memory efficiency.
5

Request Minimal Intents

Only request the intents your bot actually needs to reduce bandwidth and processing.

Troubleshooting

Your bot token is invalid or malformed. Ensure you’re using the correct format:
Bot 123456789012345678.dGhpcyBpcyBhIHNlY3JldCB0b2tlbiBleGFtcGxl
Ensure you’ve requested the GUILD_MESSAGES intent (512) in your Identify payload.
You’re not sending heartbeats at the correct interval. Check that your heartbeat timer is working.
Sessions expire after 5 minutes or when invalidated. Fall back to sending a new Identify payload.

Next Steps

Handle Commands

Process user commands from MESSAGE_CREATE events

Gateway Events Reference

Complete list of all Gateway events

Presence & Status

Update your bot’s online status

Voice Connections

Connect to voice channels (advanced)

Build docs developers (and LLMs) love