Bot REST API

The Bot REST API lets your bot read and write data using standard HTTP requests. All endpoints live under /api/v1 and require a bot token header. Bots must be installed in a guild to access its channels and members.

Authentication

Every request to /api/v1 must include your bot token in the Authorization header:

Authorization: Bot YOUR_BOT_TOKEN

Bot tokens are 64-character hex strings generated when you create a bot through the developer portal. You can regenerate a token at any time — the old token immediately stops working.

⚠️
Never expose your bot token in client-side code or commit it to version control. Treat it like a password.

Obtaining a Token

  1. Log in to the NexusGuild developer portal
  2. Create a new application under My Applications
  3. Go to Bot → copy the token
  4. To install the bot in a guild, use the invite link or call PUT /api/bots/:botId/servers/:serverId

Bot User

GET /api/v1/users/@me

Returns the authenticated bot's user object.

Response

{
  "id":       "snowflake",
  "username": "MyBot",
  "avatar":   "/uploads/avatars/..." // or null,
  "bot":      true
}

Get Channel

GET /api/v1/channels/{channelId}

Fetch a single channel. Bot must be a member of the channel's guild and have VIEW_CHANNEL permission.

Response — Channel Object

{
  "id":        "snowflake",
  "type":      0, // 0=text 2=voice 4=category 5=announcement 11=thread 15=forum
  "guild_id":  "snowflake",
  "name":      "general",
  "topic":     "Channel topic" // or null,
  "position":  0,
  "parent_id": "snowflake" // category ID, or null
}

Get Channel Messages

GET /api/v1/channels/{channelId}/messages

Fetch messages from a channel. Requires VIEW_CHANNEL and READ_MESSAGE_HISTORY. Results ordered oldest-first; up to 100 per call.

Query Parameters

ParamDefaultDescription
limitoptional50Max messages to return (1–100)
beforeoptionalReturn messages with ID less than this (cursor before)
afteroptionalReturn messages with ID greater than this (cursor after)

Response — Array of Message Objects

[
  {
    "id":               "snowflake",
    "channel_id":       "snowflake",
    "author": {
      "id":       "snowflake",
      "username": "SomeUser",
      "avatar":   "/uploads/avatars/...",
      "bot":      false
    },
    "content":          "Hello world",
    "timestamp":        "2026-03-07T12:00:00.000Z",
    "edited_timestamp":  null,
    "attachments":      [],
    "embeds":           [],
    "pinned":           false,
    "type":             0
  }
]

Send Message

POST /api/v1/channels/{channelId}/messages

Send a message to a channel. Requires VIEW_CHANNEL and SEND_MESSAGES. The message is broadcast to all clients in the channel via Socket.io.

Request Body (JSON)

FieldDescription
contentrequired*Message text. Required if components is absent.
componentsoptionalArray of action row objects — see Message Components

Example

fetch(`/api/v1/channels/${channelId}/messages`, {
  method: 'POST',
  headers: {
    'Authorization': `Bot ${token}`,
    'Content-Type':  'application/json',
  },
  body: JSON.stringify({ content: 'Hello from my bot!' }),
})

Response — Message Object

Returns the created message object (same shape as Get Messages).

Edit Message

PATCH /api/v1/channels/{channelId}/messages/{messageId}

Edit a message. Bots can only edit their own messages. Requires VIEW_CHANNEL.

Request Body (JSON)

FieldDescription
contentrequiredNew message content (non-empty)

Response — Updated Message Object

Delete Message

DELETE /api/v1/channels/{channelId}/messages/{messageId}

Delete a message. Bots can only delete their own messages. Requires VIEW_CHANNEL.

Response

204 No Content

Bulk Delete Messages

DELETE /api/v1/channels/{channelId}/messages/bulk-delete

Delete up to 100 of the bot's own messages at once. Only deletes messages authored by this bot. Requires VIEW_CHANNEL.

Request Body (JSON)

FieldDescription
messagesrequiredArray of message ID strings (1–100)
{ "messages": ["id1", "id2", "id3"] }

Response

204 No Content

Add Reaction

PUT /api/v1/channels/{channelId}/messages/{messageId}/reactions/{emoji}/@me

Add a reaction to a message. Requires VIEW_CHANNEL and ADD_REACTIONS. The emoji must be URL-encoded (e.g. %F0%9F%91%8D for 👍, or name%3Aid for custom emojis).

Response

204 No Content

Remove Reaction

DELETE /api/v1/channels/{channelId}/messages/{messageId}/reactions/{emoji}/@me

Remove the bot's reaction from a message.

Response

204 No Content

Get Pinned Messages

GET /api/v1/channels/{channelId}/pins

Fetch all pinned messages in a channel. Requires VIEW_CHANNEL.

Response

Array of message objects where pinned: true.

Pin a Message

PUT /api/v1/channels/{channelId}/pins/{messageId}

Pin a message. Requires VIEW_CHANNEL and MANAGE_MESSAGES. Emits message_pinned to all clients in the channel.

Response

204 No Content

Unpin a Message

DELETE /api/v1/channels/{channelId}/pins/{messageId}

Unpin a message. Requires VIEW_CHANNEL and MANAGE_MESSAGES.

Response

204 No Content

Get Guild

GET /api/v1/guilds/{guildId}

Fetch guild information. Bot must be installed in the guild.

Response — Guild Object

{
  "id":                       "snowflake",
  "name":                     "My Server",
  "icon":                     "/uploads/icons/..." // or null,
  "owner_id":                 "snowflake",
  "approximate_member_count":  42
}

Get Guild Channels

GET /api/v1/guilds/{guildId}/channels

List all channels in a guild ordered by position. Returns channel objects (same shape as Get Channel).

List Guild Members

GET /api/v1/guilds/{guildId}/members

List guild members paginated by join date. Max 1000 per page.

Query Parameters

ParamDefaultDescription
limitoptional100Max results (1–1000)
afteroptionalReturn members whose user ID is greater than this

Response — Array of Member Objects

[
  {
    "user": {
      "id":       "snowflake",
      "username": "Alice",
      "avatar":   "/uploads/avatars/...",
      "bot":      false
    },
    "nick":      null,
    "roles":     ["roleSnowflake"],
    "joined_at": "2026-01-01T00:00:00.000Z"
  }
]

Get Guild Member

GET /api/v1/guilds/{guildId}/members/{userId}

Fetch a single member. Returns a member object.

List Guild Roles

GET /api/v1/guilds/{guildId}/roles

List all roles in a guild ordered by position (highest first).

Response — Array of Role Objects

[
  {
    "id":          "snowflake",
    "name":        "Moderator",
    "color":       3447003,       // integer RGB (e.g. 0x3498DB)
    "permissions": "8",           // permission bitmask as string
    "position":    2,
    "mentionable": false,
    "hoist":       false
  }
]

Create Role

POST /api/v1/guilds/{guildId}/roles

Create a new role. Requires MANAGE_ROLES.

Request Body (JSON)

FieldDescription
namerequiredRole name
coloroptionalRGB color as integer (e.g. 0xFF0000) or hex string ("#FF0000")
permissionsoptionalBitmask string (default "0")
positionoptionalHierarchy position (0 = lowest)
mentionableoptionalBoolean — whether users can @mention this role

Response — Role Object

Edit Role

PATCH /api/v1/guilds/{guildId}/roles/{roleId}

Update a role. All body fields are optional — only provided fields are updated. Requires MANAGE_ROLES.

Body fields: same as Create Role (all optional).

Response — Updated Role Object

Delete Role

DELETE /api/v1/guilds/{guildId}/roles/{roleId}

Delete a role. Requires MANAGE_ROLES.

Response

204 No Content

Add Role to Member

PUT /api/v1/guilds/{guildId}/members/{userId}/roles/{roleId}

Assign a role to a guild member. Requires MANAGE_ROLES. Emits a GUILD_MEMBER_UPDATE gateway event with added_roles and removed_roles.

Response

204 No Content

Remove Role from Member

DELETE /api/v1/guilds/{guildId}/members/{userId}/roles/{roleId}

Remove a role from a guild member. Requires MANAGE_ROLES. Emits a GUILD_MEMBER_UPDATE gateway event with added_roles and removed_roles.

Response

204 No Content

Managing Bots (Session Auth)

ℹ️
The following endpoints use session cookie auth (same as the main app), not bot token auth. They are used by the developer portal UI to manage bot configurations.
MethodPathDescription
GET /api/bots List all bots owned by the session user
POST /api/bots Create a new bot application
GET /api/bots/:botId Get bot details
PATCH /api/bots/:botId Update bot (name, description, callback URL, etc.)
DELETE /api/bots/:botId Delete bot and its user account
GET /api/bots/:botId/token Retrieve bot token (masked except last 4 chars)
POST /api/bots/:botId/token/regenerate Regenerate bot token (immediately invalidates old token)
PUT /api/bots/:botId/servers/:serverId Install bot into a guild (you must be Guild Leader or admin)
DELETE /api/bots/:botId/servers/:serverId Remove bot from a guild

Slash Commands (Session Auth)

Register and manage slash commands per guild. Commands are sent to connected clients and trigger INTERACTION_CREATE gateway events when a user invokes them.

MethodPathDescription
GET /api/bots/:botId/servers/:serverId/commands List slash commands registered for this bot in this guild
PUT /api/bots/:botId/servers/:serverId/commands Create or update a slash command (upsert by name)
DELETE /api/bots/:botId/servers/:serverId/commands/:commandId Delete a slash command

Slash Command Object

{
  "id":          "snowflake",
  "bot_id":      "snowflake",
  "server_id":   "snowflake",
  "name":        "ping",          // unique per bot+server
  "description": "Replies with pong",
  "options":      []               // array of command option objects (see below)
}

Command Options

Options define the arguments a slash command accepts. They are stored in the options array on the command object and shown as type hints in the chat input when a user types the command.

Option Object

FieldDescription
namerequiredOption name (shown as the key in key:value input)
descriptionrequiredShort description shown in the autocomplete hint
typerequiredOne of the type integers below
requiredoptionalBoolean — whether the argument is mandatory (default false)

Option Types

ValueNameDescription
3STRINGPlain text argument
4INTEGERWhole number (parsed with parseInt)
5BOOLEANtrue or false
6USERUser mention or ID
7CHANNELChannel mention or ID
8ROLERole mention or ID
10NUMBERDecimal number (parsed with parseFloat)

How arguments are passed to your bot

Users can provide arguments as key:value pairs (e.g. /roll sides:20) or positionally (e.g. /roll 20). The client parses these into typed option objects delivered in the data.options array of the INTERACTION_CREATE event:

// INTERACTION_CREATE data.options example
[
  { "name": "sides", "type": 4, "value": 20 }
]

Responding to Interactions

When a user invokes a slash command, your bot receives an INTERACTION_CREATE gateway event. You must respond within 3 seconds, or use a deferred response.

Immediate Response (Type 4)

POST /api/interactions/{interactionId}/{token}/callback

Respond to an interaction immediately. No auth required — the token in the URL grants access.

{
  "type": 4,           // CHANNEL_MESSAGE_WITH_SOURCE
  "content": "Pong!"
}

Ephemeral Response

Add "flags": 64 to any callback or followup to make the response ephemeral — only the user who triggered the interaction will see it. The message appears inline in the channel as a dismissible notice and is never saved to the database.

{
  "type": 4,
  "content": "Only you can see this.",
  "flags": 64          // EPHEMERAL
}

flags: 64 is also accepted on followup responses.

Deferred Response (Type 5)

POST /api/interactions/{interactionId}/{token}/callback

Acknowledge the interaction immediately, then send the real response later (within 15 minutes).

// Step 1: Acknowledge immediately
{
  "type": 5            // DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE
}

// Step 2: Send follow-up (within 15 minutes)
fetch(`/api/interactions/${interactionId}/${token}/followup`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ content: 'Done!' }),
})

Interaction Type Constants

TypeNameDescription
4CHANNEL_MESSAGE_WITH_SOURCEReply with a message immediately
5DEFERRED_CHANNEL_MESSAGE_WITH_SOURCEAcknowledge now, follow up within 15 minutes

Message Flags

ValueNameDescription
64EPHEMERALOnly the triggering user sees the message; not saved to the channel

Message Components

Messages can include interactive components — currently buttons inside action rows. Pass a components array when sending a message via either the REST API or an interaction callback/followup.

Structure

{
  "content": "Choose an option:",
  "components": [
    {
      "type": 1,          // ACTION_ROW — up to 5 buttons per row
      "components": [
        {
          "type":      2,              // BUTTON
          "style":     1,              // 1=Primary 2=Secondary 3=Success 4=Danger 5=Link
          "label":     "Click me",
          "custom_id": "my_button_1"  // echoed back in the INTERACTION_CREATE event
        }
      ]
    }
  ]
}

Button Styles

ValueNameColor
1PrimaryBlue
2SecondaryGrey
3SuccessGreen
4DangerRed
5LinkGrey (use url instead of custom_id)

Receiving button clicks

When a user clicks a button, your bot receives an INTERACTION_CREATE event with type: 3 (MESSAGE_COMPONENT). The data payload contains the custom_id of the button that was clicked and the message_id of the message it belongs to. Respond using the same callback endpoint as slash commands.

gateway.on('INTERACTION_CREATE', async (interaction) => {
  if (interaction.type !== 3) return; // MESSAGE_COMPONENT

  const { custom_id, message_id } = interaction.data;

  await fetch(
    `/api/interactions/${interaction.id}/${interaction.token}/callback`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ type: 4, content: `You clicked: ${custom_id}` }),
    }
  );
});

Event Subscriptions (Webhook Delivery)

If your bot is not connected to the gateway, you can still receive events via webhook. Register a subscription URL and NexusGuild will POST events to it as they occur. The gateway takes priority — if your bot is connected, webhook delivery is skipped.

ℹ️
Subscription management uses session cookie auth, not bot token auth.
MethodPathDescription
GET /api/bots/:botId/subscriptions Get current subscription (null if none)
PUT /api/bots/:botId/subscriptions Create or replace the subscription
DELETE /api/bots/:botId/subscriptions Remove the subscription

PUT Request Body (JSON)

FieldDescription
webhook_urlrequiredHTTPS URL that receives POST requests
signing_keyrequiredSecret used to sign payloads (HMAC-SHA256)
eventsrequiredArray of event names to subscribe to
// Example PUT body
{
  "webhook_url":  "https://mybot.example.com/nexus-events",
  "signing_key":  "my-secret-signing-key",
  "events": ["MESSAGE_CREATE", "MESSAGE_UPDATE", "REACT_ADD", "REACT_REMOVE"]
}

Available Event Names

MESSAGE_CREATE MESSAGE_UPDATE MESSAGE_DELETE REACT_ADD REACT_REMOVE MEMBER_JOIN MEMBER_LEAVE CHANNEL_CREATE CHANNEL_UPDATE CHANNEL_DELETE ROLE_UPDATE

Webhook Payload

Your endpoint receives a POST with Content-Type: application/json and two custom headers:

HeaderDescription
X-NexusGuild-SignatureHMAC-SHA256 hex digest of the raw body, signed with your signing_key
X-NexusGuild-EventThe event name (e.g. MESSAGE_CREATE)
// Webhook body shape
{
  "event":     "MESSAGE_CREATE",
  "data":      { /* same payload as the gateway event */ },
  "timestamp": 1742400000000
}

Verifying the signature (Node.js)

import crypto from 'crypto';

function verifySignature(rawBody, signature, signingKey) {
  const expected = crypto
    .createHmac('sha256', signingKey)
    .update(rawBody)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}

Rate Limiting

The /api/v1 endpoints enforce a sliding-window rate limit of 50 requests per 10 seconds per bot token. Exceeding the limit returns a 429 Too Many Requests response.

429 Response

HTTP/1.1 429 Too Many Requests
Retry-After: 3

{
  "error":       "Too many requests",
  "retry_after": 3    // seconds until the oldest request leaves the window
}

Always check for a 429 status and wait retry_after seconds before retrying.