Skip to content

Developer docs

REST API reference

Every route of the Hubs developer API with its authentication, parameters, examples and error codes.

All routes live under https://hubs.example.com/api/developer/v1 on your Hubs address (incoming webhooks under /api/webhooks/in). Requests and responses are JSON in UTF-8. Ids are UUIDs and times are ISO 8601 in UTC.

Authentication

KindSendUsed for
Bot tokenAuthorization: Bot hbot_...The bot's own token from the developer portal. Calls act as the bot's account.
Hub API keyAuthorization: Bearer hubs_...A key a hub owner made in Manage, Developer tools, for chosen channels. Calls act for that owner.
Signed requestX-Hubs-Timestamp and X-Hubs-SignatureAn HMAC-SHA256 signature made with the incoming webhook's secret.

Rate limits

Each bot may make 120 calls a minute across every hub, and at most 30 of them may be writes (sends, edits, deletes, reactions, interaction answers and follow-ups). The window slides, so calls free up as each one ages past a minute, and refused calls do not count. Every response carries X-RateLimit-Limit and X-RateLimit-Remaining; a 429 adds Retry-After and X-RateLimit-Reset-After in seconds.

Pagination

Lists return a page and a cursor (nextBefore or nextAfter). Pass the cursor back to get the next page; null means there is no more.

Routes

Bot and hubs

Who the bot is and where it is installed.

Get the bot's own account

GET/api/developer/v1/users/@me

The bot's id, handle and name, and its application.

Auth
Bot token
Counts as
A call
Example request
curl https://hubs.example.com/api/developer/v1/users/@me \
  -H "Authorization: Bot $HUBS_BOT_TOKEN"
Example response
200
{
  "id": "0b1c2d3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e",
  "handle": "dice_bot",
  "displayName": "Dice",
  "bot": true,
  "application": {
    "id": "c4d5e6f7-a8b9-4c0d-8e1f-2a3b4c5d6e7f",
    "name": "Dice"
  }
}

Errors: BOT_TOKEN_INVALID, BOT_DISABLED, BOT_OWNER_RESTRICTED, BOT_RATE_LIMITED

List the hubs the bot is in

GET/api/developer/v1/hubs

Every hub where the bot is installed, with the scopes and permissions that hub approved. Up to 200.

Auth
Bot token
Counts as
A call
Example request
curl https://hubs.example.com/api/developer/v1/hubs \
  -H "Authorization: Bot $HUBS_BOT_TOKEN"
Example response
200
{
  "hubs": [
    {
      "id": "6f1c2b9a-3d4e-4f50-8a61-7b8c9d0e1f20",
      "name": "Board Game Night",
      "scopes": [
        "bot",
        "messages:read",
        "messages:write"
      ],
      "permissions": [
        "read_history",
        "send_messages"
      ]
    }
  ]
}

Errors: BOT_TOKEN_INVALID, BOT_DISABLED, BOT_OWNER_RESTRICTED, BOT_RATE_LIMITED

Channels

Chat and announcement channels the caller can open.

List a hub's channels

GET/api/developer/v1/hubs/:id/channels

Chat and announcement channels the caller can open, in sidebar order. Bots also see whether they may post (canPost). A hub API key sees only the channels it was made for.

Auth
Bot token or Hub API key
Bot scope
channels:read
Key scope
channels:read
Counts as
A call

Path

  • id The hub's id.
Example request
curl https://hubs.example.com/api/developer/v1/hubs/6f1c2b9a-3d4e-4f50-8a61-7b8c9d0e1f20/channels \
  -H "Authorization: Bot $HUBS_BOT_TOKEN"
Example response
200
{
  "channels": [
    {
      "id": "2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d",
      "hubId": "6f1c2b9a-3d4e-4f50-8a61-7b8c9d0e1f20",
      "name": "general",
      "kind": "chat",
      "topic": "",
      "position": 0,
      "canPost": true
    }
  ]
}

Errors: BOT_TOKEN_INVALID, BOT_DISABLED, BOT_OWNER_RESTRICTED, BOT_RATE_LIMITED, BOT_NOT_IN_HUB, BOT_SCOPE_MISSING, BOT_PERMISSION_MISSING, KEY_INVALID, KEY_SCOPE_MISSING, KEY_OUT_OF_SCOPE, KEY_RATE_LIMITED, NOT_FOUND

Messages

Read history, send, edit and delete. Bots edit and delete only their own messages.

Read message history

GET/api/developer/v1/channels/:channelId/messages

Newest first. Pass nextBefore back as before to page further. Bots page by message id; hub API keys page by sequence number.

Auth
Bot token or Hub API key
Bot scope
messages:read
Key scope
messages:read
Counts as
A call

Path

  • channelId The channel's id.

Query, with a bot token

FieldTypeRules
beforeid
limitinteger1 to 100, default 50

Query, with a hub API key

FieldTypeRules
beforeintegerat least 1
limitinteger1 to 50, default 25
Example request
curl https://hubs.example.com/api/developer/v1/channels/2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d/messages \
  -H "Authorization: Bot $HUBS_BOT_TOKEN"
Example response
200
{
  "messages": [
    {
      "id": "9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b",
      "channelId": "2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d",
      "hubId": "6f1c2b9a-3d4e-4f50-8a61-7b8c9d0e1f20",
      "author": {
        "id": "0b1c2d3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e",
        "handle": "dice_bot",
        "displayName": "Dice",
        "bot": true
      },
      "content": "You rolled a 17.",
      "createdAt": "2026-10-11T16:00:00.000Z",
      "editedAt": null,
      "replyToId": null,
      "attachments": [],
      "reactions": [],
      "applicationId": "c4d5e6f7-a8b9-4c0d-8e1f-2a3b4c5d6e7f",
      "embeds": [],
      "components": []
    }
  ],
  "nextBefore": null
}

Errors: BOT_TOKEN_INVALID, BOT_DISABLED, BOT_OWNER_RESTRICTED, BOT_RATE_LIMITED, BOT_NOT_IN_HUB, BOT_SCOPE_MISSING, BOT_PERMISSION_MISSING, KEY_INVALID, KEY_SCOPE_MISSING, KEY_OUT_OF_SCOPE, KEY_RATE_LIMITED, NOT_FOUND, BAD_REQUEST

Send a message

POST/api/developer/v1/channels/:channelId/messages

Posts as the bot (201), or as a labeled integration post for a hub API key. Channel rules, slow mode and content rules apply exactly as for a person. Bots may send embeds and components; with an embed, content may be empty.

Auth
Bot token or Hub API key
Bot scope
messages:write
Key scope
messages:write
Counts as
A write

Path

  • channelId The channel's id.

Headers

  • Idempotency-Key Required. 8 to 128 letters, numbers, dots, dashes, underscores or colons. A retry with the same key and the same body returns the first result; a different body with a used key is refused (409).

Body, with a bot token

FieldTypeRules
contentstringup to 4000 characters, default ""
replyToIdid or null
embedsEmbed[]up to 5 items, default []
componentsActionRow[]up to 5 items, default []

Body, with a hub API key

FieldTypeRules
contentrequiredstring1 to 4000 characters
Example request
curl -X POST https://hubs.example.com/api/developer/v1/channels/2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d/messages \
  -H "Authorization: Bot $HUBS_BOT_TOKEN" \
  -H "Idempotency-Key: roll-0001" \
  -H "Content-Type: application/json" \
  -d '{"content":"You rolled a 17."}'
Example response
201
{
  "id": "9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b",
  "channelId": "2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d",
  "hubId": "6f1c2b9a-3d4e-4f50-8a61-7b8c9d0e1f20",
  "author": {
    "id": "0b1c2d3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e",
    "handle": "dice_bot",
    "displayName": "Dice",
    "bot": true
  },
  "content": "You rolled a 17.",
  "createdAt": "2026-10-11T16:00:00.000Z",
  "editedAt": null,
  "replyToId": null,
  "attachments": [],
  "reactions": [],
  "applicationId": "c4d5e6f7-a8b9-4c0d-8e1f-2a3b4c5d6e7f",
  "embeds": [],
  "components": []
}

Errors: BOT_TOKEN_INVALID, BOT_DISABLED, BOT_OWNER_RESTRICTED, BOT_RATE_LIMITED, BOT_NOT_IN_HUB, BOT_SCOPE_MISSING, BOT_PERMISSION_MISSING, KEY_INVALID, KEY_SCOPE_MISSING, KEY_OUT_OF_SCOPE, KEY_RATE_LIMITED, NOT_FOUND, BAD_REQUEST, IDEMPOTENCY_CONFLICT

Get one message

GET/api/developer/v1/channels/:channelId/messages/:messageId

Auth
Bot token
Bot scope
messages:read
Counts as
A call

Path

  • channelId The channel's id.
  • messageId The message's id.
Example request
curl https://hubs.example.com/api/developer/v1/channels/2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d/messages/9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b \
  -H "Authorization: Bot $HUBS_BOT_TOKEN"
Example response
200
{
  "id": "9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b",
  "channelId": "2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d",
  "hubId": "6f1c2b9a-3d4e-4f50-8a61-7b8c9d0e1f20",
  "author": {
    "id": "0b1c2d3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e",
    "handle": "dice_bot",
    "displayName": "Dice",
    "bot": true
  },
  "content": "You rolled a 17.",
  "createdAt": "2026-10-11T16:00:00.000Z",
  "editedAt": null,
  "replyToId": null,
  "attachments": [],
  "reactions": [],
  "applicationId": "c4d5e6f7-a8b9-4c0d-8e1f-2a3b4c5d6e7f",
  "embeds": [],
  "components": []
}

Errors: BOT_TOKEN_INVALID, BOT_DISABLED, BOT_OWNER_RESTRICTED, BOT_RATE_LIMITED, BOT_NOT_IN_HUB, BOT_SCOPE_MISSING, BOT_PERMISSION_MISSING, NOT_FOUND

Edit the bot's own message

PATCH/api/developer/v1/channels/:channelId/messages/:messageId

Send any of content, embeds and components. Fields you leave out stay as they are.

Auth
Bot token
Bot scope
messages:write
Counts as
A write

Path

  • channelId The channel's id.
  • messageId The message's id.

Body

FieldTypeRules
contentstringup to 4000 characters
embedsEmbed[]up to 5 items
componentsActionRow[]up to 5 items
Example request
curl -X PATCH https://hubs.example.com/api/developer/v1/channels/2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d/messages/9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b \
  -H "Authorization: Bot $HUBS_BOT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"content":"You rolled an 18. The dice slipped."}'
Example response
200
{
  "id": "9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b",
  "channelId": "2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d",
  "hubId": "6f1c2b9a-3d4e-4f50-8a61-7b8c9d0e1f20",
  "author": {
    "id": "0b1c2d3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e",
    "handle": "dice_bot",
    "displayName": "Dice",
    "bot": true
  },
  "content": "You rolled a 17.",
  "createdAt": "2026-10-11T16:00:00.000Z",
  "editedAt": "2026-10-11T16:00:00.000Z",
  "replyToId": null,
  "attachments": [],
  "reactions": [],
  "applicationId": "c4d5e6f7-a8b9-4c0d-8e1f-2a3b4c5d6e7f",
  "embeds": [],
  "components": []
}

Errors: BOT_TOKEN_INVALID, BOT_DISABLED, BOT_OWNER_RESTRICTED, BOT_RATE_LIMITED, BOT_NOT_IN_HUB, BOT_SCOPE_MISSING, BOT_PERMISSION_MISSING, BOT_NOT_MESSAGE_AUTHOR, NOT_FOUND, BAD_REQUEST

Delete the bot's own message

DELETE/api/developer/v1/channels/:channelId/messages/:messageId

Auth
Bot token
Bot scope
messages:write
Counts as
A write

Path

  • channelId The channel's id.
  • messageId The message's id.
Example request
curl -X DELETE https://hubs.example.com/api/developer/v1/channels/2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d/messages/9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b \
  -H "Authorization: Bot $HUBS_BOT_TOKEN"
Example response
204 (no body)

Errors: BOT_TOKEN_INVALID, BOT_DISABLED, BOT_OWNER_RESTRICTED, BOT_RATE_LIMITED, BOT_NOT_IN_HUB, BOT_SCOPE_MISSING, BOT_PERMISSION_MISSING, BOT_NOT_MESSAGE_AUTHOR, NOT_FOUND

Reactions

A bot's own reactions on messages it can read.

Add the bot's reaction

PUT/api/developer/v1/channels/:channelId/messages/:messageId/reactions/:emoji

URL-encode the emoji. Adding a reaction twice is harmless.

Auth
Bot token
Bot scope
reactions:write
Counts as
A write

Path

  • channelId The channel's id.
  • messageId The message's id.
  • emoji The emoji, 1 to 64 characters, URL-encoded.
Example request
curl -X PUT https://hubs.example.com/api/developer/v1/channels/2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d/messages/9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b/reactions/%F0%9F%8E%B2 \
  -H "Authorization: Bot $HUBS_BOT_TOKEN"
Example response
204 (no body)

Errors: BOT_TOKEN_INVALID, BOT_DISABLED, BOT_OWNER_RESTRICTED, BOT_RATE_LIMITED, BOT_NOT_IN_HUB, BOT_SCOPE_MISSING, BOT_PERMISSION_MISSING, NOT_FOUND

Remove the bot's reaction

DELETE/api/developer/v1/channels/:channelId/messages/:messageId/reactions/:emoji

Auth
Bot token
Bot scope
reactions:write
Counts as
A write

Path

  • channelId The channel's id.
  • messageId The message's id.
  • emoji The emoji, URL-encoded.
Example request
curl -X DELETE https://hubs.example.com/api/developer/v1/channels/2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d/messages/9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b/reactions/%F0%9F%8E%B2 \
  -H "Authorization: Bot $HUBS_BOT_TOKEN"
Example response
204 (no body)

Errors: BOT_TOKEN_INVALID, BOT_DISABLED, BOT_OWNER_RESTRICTED, BOT_RATE_LIMITED, BOT_NOT_IN_HUB, BOT_SCOPE_MISSING, BOT_PERMISSION_MISSING, NOT_FOUND

Members

Who is in a hub, with hub roles and a bot flag.

List a hub's members

GET/api/developer/v1/hubs/:id/members

Ordered by account id. Pass nextAfter back as after to page on. Deleted accounts are left out.

Auth
Bot token
Bot scope
members:read
Counts as
A call

Path

  • id The hub's id.

Query

FieldTypeRules
afterid
limitinteger1 to 100, default 50
Example request
curl https://hubs.example.com/api/developer/v1/hubs/6f1c2b9a-3d4e-4f50-8a61-7b8c9d0e1f20/members \
  -H "Authorization: Bot $HUBS_BOT_TOKEN"
Example response
200
{
  "members": [
    {
      "id": "5d6e7f80-9a1b-4c2d-9e3f-4a5b6c7d8e9f",
      "handle": "river",
      "displayName": "River",
      "role": "member",
      "bot": false
    }
  ],
  "nextAfter": null
}

Errors: BOT_TOKEN_INVALID, BOT_DISABLED, BOT_OWNER_RESTRICTED, BOT_RATE_LIMITED, BOT_NOT_IN_HUB, BOT_SCOPE_MISSING, BOT_PERMISSION_MISSING

Gateway

Where and how to open the live events connection.

The live events address

GET/api/developer/v1/gateway

Open a WebSocket here over HTTP/1.1. A plain request without the upgrade answers 426, or 401 or 403 for a bad token. See the gateway guide.

Auth
Bot token
Counts as
A call
Example request
curl https://hubs.example.com/api/developer/v1/gateway \
  -H "Authorization: Bot $HUBS_BOT_TOKEN"
Example response
426
{
  "error": "Open a WebSocket here over HTTP/1.1 (Connection: Upgrade, Upgrade: websocket).",
  "code": "BOT_GATEWAY_UPGRADE_REQUIRED"
}

Errors: BOT_TOKEN_INVALID, BOT_DISABLED, BOT_OWNER_RESTRICTED, BOT_GATEWAY_UPGRADE_REQUIRED

Get connection details

GET/api/developer/v1/gateway/bot

Where to connect, how many shards to open, the heartbeat interval and what each intent needs.

Auth
Bot token
Bot scope
gateway
Counts as
A call
Example request
curl https://hubs.example.com/api/developer/v1/gateway/bot \
  -H "Authorization: Bot $HUBS_BOT_TOKEN"
Example response
200
{
  "url": "wss://hubs.example.com/api/developer/v1/gateway",
  "shards": 1,
  "maxShards": 16,
  "heartbeatInterval": 41250,
  "intents": {
    "HUB_MESSAGES": {
      "bit": 512,
      "scope": "messages:read",
      "permission": "Read message history"
    }
  }
}

Errors: BOT_TOKEN_INVALID, BOT_DISABLED, BOT_OWNER_RESTRICTED, BOT_RATE_LIMITED, BOT_SCOPE_MISSING

Slash commands

Global commands, and commands for one hub.

List global commands

GET/api/developer/v1/applications/@me/commands

Auth
Bot token
Bot scope
commands
Counts as
A call
Example request
curl https://hubs.example.com/api/developer/v1/applications/@me/commands \
  -H "Authorization: Bot $HUBS_BOT_TOKEN"
Example response
200
{
  "commands": [
    {
      "id": "3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f",
      "applicationId": "c4d5e6f7-a8b9-4c0d-8e1f-2a3b4c5d6e7f",
      "hubId": null,
      "name": "roll",
      "description": "Roll some dice",
      "options": [
        {
          "type": "integer",
          "name": "sides",
          "description": "How many sides",
          "required": true,
          "minValue": 2,
          "maxValue": 100
        }
      ],
      "updatedAt": "2026-10-11T16:00:00.000Z"
    }
  ]
}

Errors: BOT_TOKEN_INVALID, BOT_DISABLED, BOT_OWNER_RESTRICTED, BOT_RATE_LIMITED, BOT_SCOPE_MISSING

Replace global commands

PUT/api/developer/v1/applications/@me/commands

Send the whole list, as an array or as {"commands": [...]}. Commands that keep their name keep their id; commands left out are removed.

Auth
Bot token
Bot scope
commands
Counts as
A call

Body

A list of Command objects.

Example request
curl -X PUT https://hubs.example.com/api/developer/v1/applications/@me/commands \
  -H "Authorization: Bot $HUBS_BOT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '[{"name":"roll","description":"Roll some dice","options":[{"type":"integer","name":"sides","description":"How many sides","required":true,"minValue":2,"maxValue":100}]}]'
Example response
200
{
  "commands": [
    {
      "id": "3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f",
      "applicationId": "c4d5e6f7-a8b9-4c0d-8e1f-2a3b4c5d6e7f",
      "hubId": null,
      "name": "roll",
      "description": "Roll some dice",
      "options": [
        {
          "type": "integer",
          "name": "sides",
          "description": "How many sides",
          "required": true,
          "minValue": 2,
          "maxValue": 100
        }
      ],
      "updatedAt": "2026-10-11T16:00:00.000Z"
    }
  ]
}

Errors: BOT_TOKEN_INVALID, BOT_DISABLED, BOT_OWNER_RESTRICTED, BOT_RATE_LIMITED, BOT_SCOPE_MISSING, BAD_REQUEST

List one hub's commands

GET/api/developer/v1/hubs/:id/commands

Auth
Bot token
Bot scope
commands
Counts as
A call

Path

  • id The hub's id.
Example request
curl https://hubs.example.com/api/developer/v1/hubs/6f1c2b9a-3d4e-4f50-8a61-7b8c9d0e1f20/commands \
  -H "Authorization: Bot $HUBS_BOT_TOKEN"
Example response
200
{
  "commands": [
    {
      "id": "3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f",
      "applicationId": "c4d5e6f7-a8b9-4c0d-8e1f-2a3b4c5d6e7f",
      "hubId": "6f1c2b9a-3d4e-4f50-8a61-7b8c9d0e1f20",
      "name": "roll",
      "description": "Roll some dice",
      "options": [
        {
          "type": "integer",
          "name": "sides",
          "description": "How many sides",
          "required": true,
          "minValue": 2,
          "maxValue": 100
        }
      ],
      "updatedAt": "2026-10-11T16:00:00.000Z"
    }
  ]
}

Errors: BOT_TOKEN_INVALID, BOT_DISABLED, BOT_OWNER_RESTRICTED, BOT_RATE_LIMITED, BOT_NOT_IN_HUB, BOT_SCOPE_MISSING, BOT_PERMISSION_MISSING

Replace one hub's commands

PUT/api/developer/v1/hubs/:id/commands

A hub command replaces a global command with the same name in that hub. The change goes to the hub's audit trail.

Auth
Bot token
Bot scope
commands
Counts as
A call

Path

  • id The hub's id.

Body

A list of Command objects.

Example request
curl -X PUT https://hubs.example.com/api/developer/v1/hubs/6f1c2b9a-3d4e-4f50-8a61-7b8c9d0e1f20/commands \
  -H "Authorization: Bot $HUBS_BOT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '[{"name":"scores","description":"This week's scores"}]'
Example response
200
{
  "commands": [
    {
      "id": "3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f",
      "applicationId": "c4d5e6f7-a8b9-4c0d-8e1f-2a3b4c5d6e7f",
      "hubId": "6f1c2b9a-3d4e-4f50-8a61-7b8c9d0e1f20",
      "name": "roll",
      "description": "Roll some dice",
      "options": [
        {
          "type": "integer",
          "name": "sides",
          "description": "How many sides",
          "required": true,
          "minValue": 2,
          "maxValue": 100
        }
      ],
      "updatedAt": "2026-10-11T16:00:00.000Z"
    }
  ]
}

Errors: BOT_TOKEN_INVALID, BOT_DISABLED, BOT_OWNER_RESTRICTED, BOT_RATE_LIMITED, BOT_NOT_IN_HUB, BOT_SCOPE_MISSING, BOT_PERMISSION_MISSING, BAD_REQUEST

Interactions

Answering commands, buttons, menus and forms.

Answer an interaction

POST/api/developer/v1/interactions/:id/callback

Once, within 3 seconds. The type decides what the person sees; see the interactions guide for which types fit which interaction.

Auth
Bot token
Bot scope
commands
Counts as
A write

Path

  • id The interaction's id, from INTERACTION_CREATE.

Body type: "reply"

FieldTypeRules
typerequired"reply"
datarequiredobject
data.contentstringup to 4000 characters, default ""
data.embedsEmbed[]up to 5 items, default []
data.componentsActionRow[]up to 5 items, default []
data.ephemeralboolean

Body type: "defer"

FieldTypeRules
typerequired"defer"
dataobjectdefault {}
data.ephemeralboolean

Body type: "update"

FieldTypeRules
typerequired"update"
datarequiredobject
data.contentstringup to 4000 characters
data.embedsEmbed[]up to 5 items
data.componentsActionRow[]up to 5 items

Body type: "defer_update"

FieldTypeRules
typerequired"defer_update"

Body type: "modal"

FieldTypeRules
typerequired"modal"
datarequiredModal

Body type: "autocomplete"

FieldTypeRules
typerequired"autocomplete"
datarequiredobject
data.choicesrequiredobject[]up to 25 items
data.choices[].namerequiredstring1 to 100 characters
data.choices[].valuerequiredstring or number
Example request
curl -X POST https://hubs.example.com/api/developer/v1/interactions/7f8e9d0c-1b2a-4c3d-9e4f-5a6b7c8d9e0f/callback \
  -H "Authorization: Bot $HUBS_BOT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"type":"reply","data":{"content":"You rolled a 17.","ephemeral":false}}'
Example response
204 (no body)

Errors: BOT_TOKEN_INVALID, BOT_DISABLED, BOT_OWNER_RESTRICTED, BOT_RATE_LIMITED, BOT_INTERACTION_UNKNOWN, BOT_INTERACTION_EXPIRED, BOT_INTERACTION_ACKNOWLEDGED, BAD_REQUEST

Download a file someone gave a command

GET/api/developer/v1/interaction-files/:token

The url in an attachment option's value. The address is the credential: no token header, and it works for 10 minutes.

Auth

Path

  • token Part of the url in the option's value.
Example request
curl https://hubs.example.com/api/developer/v1/interaction-files/token
Example response
200 (no body)

Errors: BOT_FILE_EXPIRED

Send a follow-up

POST/api/developer/v1/interactions/:id/followups

For 15 minutes after the first answer. Public follow-ups are messages from the bot; ephemeral ones only the person sees.

Auth
Bot token
Bot scope
commands
Counts as
A write

Path

  • id The interaction's id, from INTERACTION_CREATE.

Headers

  • Idempotency-Key Optional. 8 to 128 letters, numbers, dots, dashes, underscores or colons. A retry with the same key posts once.

Body

FieldTypeRules
contentstringup to 4000 characters, default ""
embedsEmbed[]up to 5 items, default []
componentsActionRow[]up to 5 items, default []
ephemeralboolean
Example request
curl -X POST https://hubs.example.com/api/developer/v1/interactions/7f8e9d0c-1b2a-4c3d-9e4f-5a6b7c8d9e0f/followups \
  -H "Authorization: Bot $HUBS_BOT_TOKEN" \
  -H "Idempotency-Key: roll-0001" \
  -H "Content-Type: application/json" \
  -d '{"content":"Rolling again for the table."}'
Example response
201
{
  "id": "9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b",
  "channelId": "2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d",
  "hubId": "6f1c2b9a-3d4e-4f50-8a61-7b8c9d0e1f20",
  "author": {
    "id": "0b1c2d3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e",
    "handle": "dice_bot",
    "displayName": "Dice",
    "bot": true
  },
  "content": "You rolled a 17.",
  "createdAt": "2026-10-11T16:00:00.000Z",
  "editedAt": null,
  "replyToId": null,
  "attachments": [],
  "reactions": [],
  "applicationId": "c4d5e6f7-a8b9-4c0d-8e1f-2a3b4c5d6e7f",
  "embeds": [],
  "components": []
}

Errors: BOT_TOKEN_INVALID, BOT_DISABLED, BOT_OWNER_RESTRICTED, BOT_RATE_LIMITED, BOT_INTERACTION_UNKNOWN, BOT_INTERACTION_EXPIRED, BOT_INTERACTION_NOT_ACKNOWLEDGED, BAD_REQUEST

Edit the first answer

PATCH/api/developer/v1/interactions/:id/original

After a public defer, the first edit posts the answer. Fields you leave out stay as they are.

Auth
Bot token
Bot scope
commands
Counts as
A write

Path

  • id The interaction's id, from INTERACTION_CREATE.

Body

FieldTypeRules
contentstringup to 4000 characters
embedsEmbed[]up to 5 items
componentsActionRow[]up to 5 items
Example request
curl -X PATCH https://hubs.example.com/api/developer/v1/interactions/7f8e9d0c-1b2a-4c3d-9e4f-5a6b7c8d9e0f/original \
  -H "Authorization: Bot $HUBS_BOT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"content":"You rolled a 17 (best of three)."}'
Example response
200
{
  "id": "9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b",
  "channelId": "2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d",
  "hubId": "6f1c2b9a-3d4e-4f50-8a61-7b8c9d0e1f20",
  "author": {
    "id": "0b1c2d3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e",
    "handle": "dice_bot",
    "displayName": "Dice",
    "bot": true
  },
  "content": "You rolled a 17.",
  "createdAt": "2026-10-11T16:00:00.000Z",
  "editedAt": null,
  "replyToId": null,
  "attachments": [],
  "reactions": [],
  "applicationId": "c4d5e6f7-a8b9-4c0d-8e1f-2a3b4c5d6e7f",
  "embeds": [],
  "components": []
}

Errors: BOT_TOKEN_INVALID, BOT_DISABLED, BOT_OWNER_RESTRICTED, BOT_RATE_LIMITED, BOT_INTERACTION_UNKNOWN, BOT_INTERACTION_EXPIRED, BOT_INTERACTION_NOT_ACKNOWLEDGED, NOT_FOUND, BAD_REQUEST

Inline results

People type the bot's handle in the message box and pick from its results.

Turn inline results on or off

PUT/api/developer/v1/applications/@me/inline

When on, people type the bot's handle in the message box to ask it for results. The placeholder is the hint they see, up to 60 characters.

Auth
Bot token
Counts as
A call
Example request
curl -X PUT https://hubs.example.com/api/developer/v1/applications/@me/inline \
  -H "Authorization: Bot $HUBS_BOT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"enabled":true,"placeholder":"Search GIFs"}'
Example response
200
{
  "enabled": true,
  "placeholder": "Search GIFs"
}

Errors: BOT_TOKEN_INVALID, BOT_DISABLED, BOT_OWNER_RESTRICTED, BOT_RATE_LIMITED, BAD_REQUEST

Answer an inline query

POST/api/developer/v1/inline-queries/:id/answer

Once, within 3 seconds of INLINE_QUERY on the gateway. Up to 20 results, each with its own id; a GIF needs its url.

Auth
Bot token
Bot scope
commands
Counts as
A call

Path

  • id The inline query's id, from INLINE_QUERY.
Example request
curl -X POST https://hubs.example.com/api/developer/v1/inline-queries/6f1c2b9a-3d4e-4f50-8a61-7b8c9d0e1f20/answer \
  -H "Authorization: Bot $HUBS_BOT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"results":[{"id":"r1","type":"article","title":"Rules","content":"Be kind. No spoilers."}]}'
Example response
200
{
  "ok": true
}

Errors: BOT_TOKEN_INVALID, BOT_DISABLED, BOT_OWNER_RESTRICTED, BOT_RATE_LIMITED, BOT_NOT_IN_HUB, BOT_SCOPE_MISSING, BOT_PERMISSION_MISSING, BOT_INLINE_UNKNOWN, BOT_INLINE_EXPIRED, BOT_INLINE_ANSWERED, BAD_REQUEST

Incoming webhooks

Post into a channel from another tool with a signed request.

Post through an incoming webhook

POST/api/webhooks/in/:hookId

Sign the exact bytes you send. The post carries the Webhook label and the hook's name. Each signature is accepted once.

Auth
Signed request

Path

  • hookId From the webhook's address, shown when it was made.

Headers

  • X-Hubs-Timestamp Unix seconds. Within 5 minutes of the server's clock.
  • X-Hubs-Signature v1= and the hex HMAC-SHA256 of "<timestamp>.<raw body>" with the secret.

Body

FieldTypeRules
contentrequiredstring1 to 2000 characters
Example request
curl -X POST https://hubs.example.com/api/webhooks/in/e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b \
  -H "X-Hubs-Timestamp: $TIMESTAMP" \
  -H "X-Hubs-Signature: $SIGNATURE" \
  -H "Content-Type: application/json" \
  -d '{"content":"Deploy finished."}'
Example response
201
{
  "id": "9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b"
}

Errors: WEBHOOK_UNKNOWN, WEBHOOK_SIGNATURE, WEBHOOK_REPLAY, WEBHOOK_RATE_LIMITED, BAD_REQUEST

Objects

Embed

A card of text under a message. Up to 5 per message and 6,000 characters in all. Pictures from other sites are left out on purpose.

Fields

FieldTypeRules
titlestring1 to 256 characters
descriptionstring1 to 4096 characters
urlstringup to 512 characters, an https address
colorinteger0 to 16777215
authorobject
author.namerequiredstring1 to 256 characters
fieldsobject[]up to 25 items
fields[].namerequiredstring1 to 256 characters
fields[].valuerequiredstring1 to 1024 characters
fields[].inlineboolean
footerobject
footer.textrequiredstring1 to 2048 characters
timestamptimestamp

Action row

Up to 5 rows per message. A row holds up to 5 buttons or one select menu. Custom ids are unique within a message.

Fields

FieldTypeRules
typerequired"row"
componentsrequired(Button or SelectMenu)[]1 to 5 items

Button

Link buttons take an https url and never reach the bot; every other style takes a customId.

Fields

FieldTypeRules
typerequired"button"
stylerequired"primary" | "secondary" | "success" | "danger" | "link" | "panel"
labelrequiredstring1 to 80 characters
customIdstring1 to 100 characters, printable ASCII
urlstringup to 512 characters, an https address
emojistring1 to 32 characters
disabledboolean

Select menu

Up to 25 options with unique values.

Fields

FieldTypeRules
typerequired"select"
customIdrequiredstring1 to 100 characters, printable ASCII
placeholderstring1 to 150 characters
minValuesinteger0 to 25
maxValuesinteger1 to 25
optionsrequiredobject[]1 to 25 items
options[].labelrequiredstring1 to 100 characters
options[].valuerequiredstring1 to 100 characters
options[].descriptionstring1 to 100 characters
options[].defaultboolean
disabledboolean

Form

Up to 5 fields, opened as a dialog for the person.

Fields

FieldTypeRules
customIdrequiredstring1 to 100 characters, printable ASCII
titlerequiredstring1 to 45 characters
fieldsrequiredobject[]1 to 5 items
fields[].customIdrequiredstring1 to 100 characters, printable ASCII
fields[].labelrequiredstring1 to 45 characters
fields[].stylerequired"short" | "paragraph"
fields[].requiredboolean
fields[].placeholderstring1 to 100 characters
fields[].minLengthinteger0 to 4000
fields[].maxLengthinteger1 to 4000
fields[].valuestringup to 4000 characters

Command

A slash command. Required options come first.

Fields

FieldTypeRules
namerequiredstring1 to 32 characters
descriptionstringup to 100 characters, default ""
optionsCommandOption[]up to 25 items, default []
access"everyone" | "admins"default "everyone"
type"chat_input" | "message" | "user"default "chat_input"
contexts("hub" | "dm" | "group_dm")[]1 to 3 items, default ["hub"]

Command option

An option has choices or autocomplete, not both. Only text and number options take either.

Fields

FieldTypeRules
typerequired"string" | "integer" | "number" | "boolean" | "user" | "channel" | "role" | "mentionable" | "attachment"
namerequiredstringmatches ^[a-z0-9_-]{1,32}$
descriptionrequiredstring1 to 100 characters
requiredboolean
choicesobject[]1 to 25 items
choices[].namerequiredstring1 to 100 characters
choices[].valuerequiredstring or number
autocompleteboolean
minValuenumber
maxValuenumber
minLengthinteger0 to 6000
maxLengthinteger1 to 6000
channelTypes("chat" | "announcements" | "voice")[]1 to 3 items

Fields type: "subcommand"

FieldTypeRules
typerequired"subcommand"
namerequiredstringmatches ^[a-z0-9_-]{1,32}$
descriptionrequiredstring1 to 100 characters
optionsobject[]up to 25 items, default []
options[].typerequired"string" | "integer" | "number" | "boolean" | "user" | "channel" | "role" | "mentionable" | "attachment"
options[].namerequiredstringmatches ^[a-z0-9_-]{1,32}$
options[].descriptionrequiredstring1 to 100 characters
options[].requiredboolean
options[].choicesobject[]1 to 25 items
options[].choices[].namerequiredstring1 to 100 characters
options[].choices[].valuerequiredstring or number
options[].autocompleteboolean
options[].minValuenumber
options[].maxValuenumber
options[].minLengthinteger0 to 6000
options[].maxLengthinteger1 to 6000
options[].channelTypes("chat" | "announcements" | "voice")[]1 to 3 items

Fields type: "subcommand_group"

FieldTypeRules
typerequired"subcommand_group"
namerequiredstringmatches ^[a-z0-9_-]{1,32}$
descriptionrequiredstring1 to 100 characters
optionsrequiredobject[]1 to 25 items
options[].typerequired"subcommand"
options[].namerequiredstringmatches ^[a-z0-9_-]{1,32}$
options[].descriptionrequiredstring1 to 100 characters
options[].optionsobject[]up to 25 items, default []
options[].options[].typerequired"string" | "integer" | "number" | "boolean" | "user" | "channel" | "role" | "mentionable" | "attachment"
options[].options[].namerequiredstringmatches ^[a-z0-9_-]{1,32}$
options[].options[].descriptionrequiredstring1 to 100 characters
options[].options[].requiredboolean
options[].options[].choicesobject[]1 to 25 items
options[].options[].choices[].namerequiredstring1 to 100 characters
options[].options[].choices[].valuerequiredstring or number
options[].options[].autocompleteboolean
options[].options[].minValuenumber
options[].options[].maxValuenumber
options[].options[].minLengthinteger0 to 6000
options[].options[].maxLengthinteger1 to 6000
options[].options[].channelTypes("chat" | "announcements" | "voice")[]1 to 3 items

Errors

Errors are JSON with a sentence for people and, for bot errors, a stable code for your program: { "error": "This bot was not granted the messages:write scope in this hub.", "code": "BOT_SCOPE_MISSING" }. Match on code and the status, never on the sentence.

CodeStatusMeansWhat to do
BOT_TOKEN_INVALID401The token is missing, malformed, reset or revoked.Send Authorization: Bot followed by the current token. Reset it in the portal if it leaked.
BOT_DISABLED403Hubs switched the application off.Read the notice in the developer portal for the reason.
BOT_OWNER_RESTRICTED403The developer's own account is paused or suspended.The bot acts again once the account is back in good standing.
BOT_NOT_IN_HUB404The bot is not installed in that hub. Hubs it was never in look the same.Share the install link with an owner or admin of the hub.
BOT_SCOPE_MISSING403The application does not list the scope, or the hub did not approve it.Add the scope in the portal, then ask the hub to approve a new install link.
BOT_PERMISSION_MISSING403The bot's role in that hub lacks the permission the scope needs.Ask the hub to approve the permission.
BOT_RATE_LIMITED429Over 120 calls, or 30 writes, in the last minute.Wait Retry-After seconds, then try again.
BOT_NOT_MESSAGE_AUTHOR403Editing or deleting a message the bot did not send.Bots change only their own messages.
BOT_GATEWAY_UPGRADE_REQUIRED426The gateway address was reached without a WebSocket upgrade.Connect over HTTP/1.1 with Connection: Upgrade and Upgrade: websocket.
BOT_INTERACTION_UNKNOWN404No interaction with that id belongs to this bot.Use the id from INTERACTION_CREATE.
BOT_INTERACTION_EXPIRED404The first answer came after 3 seconds, or a follow-up after 15 minutes.Answer or defer at once, then follow up.
BOT_FILE_EXPIRED404The address of a file someone gave a command is unknown, or its 10 minutes have passed.Download the file when the interaction arrives.
BOT_INTERACTION_ACKNOWLEDGED409The interaction was already answered.Use a follow-up or edit the first answer.
BOT_INLINE_UNKNOWN404No inline query with that id is waiting for this bot.Answer with the id from INLINE_QUERY.
BOT_INLINE_EXPIRED404The inline query is more than 3 seconds old.Answer within 3 seconds. The person can type again for a new query.
BOT_INLINE_ANSWERED409The inline query was already answered.Answer each inline query once.
BOT_INTERACTION_NOT_ACKNOWLEDGED400A follow-up or edit before the first answer.Answer with the callback first.
BAD_REQUESTmessage only400The body, query or a header does not match the schema.The error sentence names the first problem.
NOT_FOUNDmessage only403 or 404The hub's own rules said no: a channel the role cannot see or post in, or a message that is gone.Ask the hub to open the channel to the bot's role.
IDEMPOTENCY_CONFLICTmessage only409The Idempotency-Key was used before with a different body.Use a new key for new content.
KEY_INVALIDmessage only401The hub API key is missing, expired or revoked.Ask the hub owner for a new key.
KEY_SCOPE_MISSINGmessage only403The key was not made with this scope.Ask the hub owner for a key with the scope.
KEY_OUT_OF_SCOPEmessage only404The hub or channel is not one the key was made for.Use one of the key's channels.
KEY_RATE_LIMITEDmessage only429Over 60 calls in a minute with this key.Slow down and try again in a minute.
WEBHOOK_UNKNOWNmessage only404No such incoming webhook, its hub switched webhooks off, or the hub left the organization.Check the address, or ask for a new webhook.
WEBHOOK_SIGNATUREmessage only401The signature is missing or wrong, or the timestamp is more than 5 minutes off.Sign the exact bytes you send, with the current secret and clock.
WEBHOOK_REPLAYmessage only409This signature was already received, or the channel cannot take posts now.Sign each post with a fresh timestamp.
WEBHOOK_RATE_LIMITEDmessage only429More than 30 posts in a minute through this webhook.Wait a minute.