Bot Gateway

The NexusGuild Bot Gateway delivers real-time events to your bot over a persistent Socket.io connection. Your bot connects once and receives events for every guild it's installed in, no polling required.

Overview

PropertyValue
TransportSocket.io (WebSocket + HTTP long-poll fallback)
Namespace/bot-gateway
Auth methodBot token via Socket.io handshake auth
Connections per bot1 (connecting again disconnects the previous session)
Gateway versionv: 1 (returned in READY)

Connecting

Use the Socket.io client library to connect to the /bot-gateway namespace:

import { io } from 'socket.io-client';

const gateway = io('https://nexusguild.gg/bot-gateway', {
  auth: {
    token: 'YOUR_BOT_TOKEN',
  },
  transports: ['websocket'],    // prefer WebSocket
  reconnection: true,
  reconnectionDelay: 5000,
});

gateway.on('connect', () => {
  console.log('Connected to NexusGuild gateway');
});

gateway.on('connect_error', (err) => {
  console.error('Gateway connection failed:', err.message);
});

Authentication

Pass your bot token in the Socket.io auth object at connection time. If the token is missing or invalid, the connection is rejected with an error message.

⚠️
Connecting a second time with the same token will disconnect the existing session. Only one gateway connection per bot is allowed.

Heartbeat

Send a HEARTBEAT event periodically to verify connectivity. The server responds with HEARTBEAT_ACK.

// Send heartbeat every 30 seconds
setInterval(() => {
  gateway.emit('HEARTBEAT');
}, 30000);

gateway.on('HEARTBEAT_ACK', () => {
  // Connection is alive
});
ℹ️
Socket.io handles its own ping/pong internally. The HEARTBEAT event is an optional application-level health check, not a requirement for staying connected.

Gateway Events

After connecting and authenticating, the gateway delivers these events to your bot.

READY

READY

Sent immediately after a successful connection. Contains bot identity and gateway version.

{
  "bot": {
    "id":   "snowflake",
    "name": "MyBot"
  },
  "v": 1
}

Following READY, one GUILD_CREATE is emitted per guild the bot is installed in.

GUILD_CREATE

GUILD_CREATE

Sent once per guild on connection, providing a complete snapshot of each guild's state.

{
  "id":                       "snowflake",
  "name":                     "My Server",
  "icon":                     null,
  "owner_id":                 "snowflake",
  "approximate_member_count":  42,
  "channels": [
    {
      "id":        "snowflake",
      "type":      0,
      "name":      "general",
      "topic":     null,
      "position":  0,
      "parent_id": null
    }
  ],
  "roles": [
    {
      "id":          "snowflake",
      "name":        "@everyone",
      "color":       0,
      "permissions": "103926848",
      "position":    0,
      "mentionable": false
    }
  ],
  "members": [
    {
      "user": { "id": "snowflake", "username": "Alice", "bot": false },
      "nick": null,
      "roles": [],
      "joined_at": "2026-01-01T00:00:00.000Z"
    }
  ]
}

MESSAGE_CREATE

MESSAGE_CREATE

Fired when any message is sent in a guild the bot is in. Payload mirrors the REST message object.

{
  "id":               "snowflake",
  "channel_id":       "snowflake",
  "user_id":          "snowflake",
  "username":         "Alice",
  "avatar":           null,
  "content":          "Hello!",
  "created_at":       "2026-03-07T12:00:00.000Z",
  "attachments":      null,
  "reply_to_content": null,
  "thread_channel_id": null
}

MESSAGE_UPDATE

MESSAGE_UPDATE

Fired when a message is edited. Payload is the updated message object (same shape as MESSAGE_CREATE).

MESSAGE_DELETE

MESSAGE_DELETE

Fired when a message is deleted (including bulk deletes — one event per deleted message).

{
  "id":         "snowflake",   // message ID
  "channel_id": "snowflake",
  "guild_id":   "snowflake"
}

MEMBER_JOIN

MEMBER_JOIN

Fired when a user joins a guild.

{
  "user": {
    "id":       "snowflake",
    "username": "Bob",
    "avatar":   null
  },
  "guild_id":  "snowflake",
  "joined_at": "2026-03-07T12:00:00.000Z"
}

MEMBER_LEAVE

MEMBER_LEAVE

Fired when a user leaves or is removed from a guild.

{
  "user":     { "id": "snowflake", "username": "Bob" },
  "guild_id": "snowflake"
}

CHANNEL_CREATE

CHANNEL_CREATE

Fired when a channel is created in a guild. Payload is a channel object.

{
  "id":        "snowflake",
  "type":      0,
  "guild_id":  "snowflake",
  "name":      "announcements",
  "topic":     null,
  "position":  1,
  "parent_id": null
}

CHANNEL_UPDATE

CHANNEL_UPDATE

Fired when a channel is updated. Same payload shape as CHANNEL_CREATE.

CHANNEL_DELETE

CHANNEL_DELETE

Fired when a channel is deleted.

{
  "id":       "snowflake",
  "guild_id": "snowflake"
}

ROLE_UPDATE

ROLE_UPDATE

Fired when a role is created, edited, or deleted. Check for a deleted: true field to distinguish deletion from creation/update.

// Created or updated:
{
  "id":          "snowflake",
  "name":        "Mod",
  "color":       3447003,
  "permissions": "8",
  "position":    1,
  "mentionable": false,
  "hoist":       false
}

// Deleted:
{
  "deleted":  true,
  "id":       "snowflake",
  "guild_id": "snowflake"
}

INTERACTION_CREATE

INTERACTION_CREATE

Fired when a user invokes one of your bot's slash commands. Respond using the interaction callback endpoint with the interactionId and token from this event. You have 3 seconds to respond (or send a deferred response first).

{
  "id":         "snowflake",          // interactionId
  "token":      "interaction_token",  // use in callback URL
  "type":       2,                    // APPLICATION_COMMAND
  "guild_id":   "snowflake",
  "channel_id": "snowflake",
  "member": {
    "user": { "id": "snowflake", "username": "Alice" },
    "roles": []
  },
  "data": {
    "name":    "ping",              // command name
    "options": []                    // command arguments
  }
}

GUILD_MEMBER_UPDATE

GUILD_MEMBER_UPDATE

Fired when a member's roles change (via the role management REST endpoints).

{
  "guild_id": "snowflake",
  "user":     { "id": "snowflake", "username": "Alice" },
  "nick":     null,
  "roles":    ["roleSnowflake"],
  "joined_at": "2026-01-01T00:00:00.000Z"
}

Example: Basic Bot

A minimal bot that connects, logs guild names, and echoes messages:

import { io } from 'socket.io-client';

const BOT_TOKEN = 'your_token_here';
const BASE_URL  = 'https://nexusguild.gg';

const guilds = new Map();

const gateway = io(`${BASE_URL}/bot-gateway`, {
  auth: { token: BOT_TOKEN },
  transports: ['websocket'],
});

gateway.on('READY', ({ bot }) => {
  console.log(`Logged in as ${bot.name} (${bot.id})`);
});

gateway.on('GUILD_CREATE', (guild) => {
  guilds.set(guild.id, guild);
  console.log(`In guild: ${guild.name} (${guild.members.length} members)`);
});

gateway.on('MESSAGE_CREATE', async (msg) => {
  if (msg.content === '!ping') {
    await fetch(`${BASE_URL}/api/v1/channels/${msg.channel_id}/messages`, {
      method: 'POST',
      headers: {
        'Authorization': `Bot ${BOT_TOKEN}`,
        'Content-Type':  'application/json',
      },
      body: JSON.stringify({ content: 'Pong!' }),
    });
  }
});

Example: Slash Command Bot

Registering a command and responding to it:

// 1. Register the command (one-time setup, session auth required)
await fetch(`/api/bots/${botId}/servers/${guildId}/commands`, {
  method: 'PUT',
  headers: { 'Content-Type': 'application/json' },
  // session cookie sent automatically
  body: JSON.stringify({
    name: 'roll',
    description: 'Roll a dice',
    options: [
      {
        name: 'sides',
        description: 'Number of sides',
        type: 4,       // INTEGER
        required: false,
      }
    ]
  }),
});

// 2. Handle the INTERACTION_CREATE event
gateway.on('INTERACTION_CREATE', async (interaction) => {
  if (interaction.data.name !== 'roll') return;

  const sides = interaction.data.options?.[0]?.value ?? 6;
  const result = Math.floor(Math.random() * sides) + 1;

  await fetch(
    `/api/interactions/${interaction.id}/${interaction.token}/callback`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        type: 4,
        data: { content: `🎲 You rolled a **${result}** (d${sides})` },
      }),
    }
  );
});