Disband Bot Documentation

Build, deploy, and manage Disband bots

My Applications

Getting Started

Bots are self-hosted: you run the code yourself, a server owner invites the bot, and the bot connects with its token. Disband never hosts or executes your bot.

1. Create a bot

Open Disband → Settings → Bots (or the Developers portal) → click New bot. Pick a name and the scopes it needs, then copy the token — it's shown once.

2. Install the SDK

# Python (zero dependencies)

pip install disband-bot

# JavaScript / TypeScript

npm install @disband/bot

3. Write your bot

Python

from disband_bot import Client

client = Client(token="YOUR_BOT_TOKEN")

@client.on("ready")
def on_ready(me):
    print(f"Logged in as {me['name']}")

@client.on("messageCreate")
def on_message(msg):
    if msg.content == "!ping":
        msg.reply("pong")

client.run()

JavaScript / TypeScript

import { Client } from "@disband/bot";

const client = new Client({ token: process.env.DISBAND_BOT_TOKEN });

client.on("messageCreate", async (message) => {
  if (message.author?.is_bot) return;
  if (message.content !== "!ping") return;
  await message.reply("pong");
});

await client.connect();

4. Invite the bot to a space

In Settings → Bots, click Invite on your bot. Choose a space (or use General invite for a link anyone can use). Copy the invite link and share it — the space owner approves it.

Bot Scopes (Permissions)

Scopes control what a bot can do. Pick the minimum set your bot needs.

ScopeDescription
messages.readReceive message events and read messages and channels.
messages.writeSend and reply to messages.
messages.deleteDelete messages the bot can see.
messages.reactAdd and remove reactions on messages.
members.readSee the member list of spaces the bot has joined.
channels.readList channels and categories.
channels.manageCreate, rename, and delete channels.
webhooks.manageCreate, edit, and delete webhooks.
roles.manageCreate, edit and delete roles, give and take members' roles, and set channel permissions for roles.

Roles and Permissions

With roles.manage and a role that has Manage Roles, a bot can create, edit, reorder and delete roles and give or take members' roles. Like people, it only manages roles below its own highest role, only changes members below it, never the space owner, and can only switch on permissions it has itself. So give your bot a role and drag it above the roles it should look after.

RequestWhat it does
GET /servers/{id}/permissionsThe bot's own permissions, roles, scopes and highest_role_position.
GET /servers/{id}/rolesEvery role, highest first. manageable is true for roles below the bot.
POST /servers/{id}/rolesCreate a role: name, color, permissions, self_assignable, position.
PATCH /servers/{id}/roles/{roleId}Edit a role. Only the fields you send change.
DELETE /servers/{id}/roles/{roleId}Delete a role.
PUT /servers/{id}/members/{userId}/roles/{roleId}Give a member a role.
DELETE /servers/{id}/members/{userId}/roles/{roleId}Take a role from a member.
GET /channels/{id}/permissionsThe channel's role overrides.
PUT /channels/{id}/permissions/{roleId}Set can_view, can_post, can_react and can_attach: true, false or null (inherit).
DELETE /channels/{id}/permissions/{roleId}Remove a role's override.
DELETE /channels/{id}/messages/{messageId}Delete a message (messages.delete). Your own always; anyone's with Manage Messages. Returns 204.

Channel permissions need Manage Channels and channels.manage or roles.manage. Setting @everyone to can_view: false and a Verified role to can_view: true hides a channel until someone is verified. Members from GET /servers/{id}/members list their roles.

Every role has a numeric display_id (a string, like "113456789012345678"), and messages have one too. Any {roleId} or {messageId} in a path takes either the id or the numeric display_id. Space owners can copy a role's number from Space Settings › Roles.

curl -X POST https://www.disband.dev/api/v1/servers/$SERVER/roles \
  -H "Authorization: Bot $DISBAND_BOT_TOKEN" -H "Content-Type: application/json" \
  -d '{ "name": "Verified", "color": "#3ba55c", "permissions": { "send_messages": true } }'

curl -X PUT https://www.disband.dev/api/v1/servers/$SERVER/members/$USER/roles/$VERIFIED \
  -H "Authorization: Bot $DISBAND_BOT_TOKEN"

curl -X PUT https://www.disband.dev/api/v1/channels/$CHANNEL/permissions/$EVERYONE \
  -H "Authorization: Bot $DISBAND_BOT_TOKEN" -H "Content-Type: application/json" \
  -d '{ "can_view": false }'

General Invite Links

When creating an invite, enable General invite to generate a link that anyone can use. The recipient signs in, picks a space they manage, and approves the bot. Great for distributing bots publicly.

Managing Bots

Go to Developers → Applications to:

  • Create and revoke bots
  • Customize name, avatar, bio, and scopes
  • Generate targeted or general invite links
  • Copy bot tokens (shown once at creation)

SDK Reference

Python

client.run() — start the bot (blocking)

client.connect() — connect (async)

client.close() — disconnect

client.send_message(channel_id, content)

client.list_messages(channel_id, limit, before)

client.list_channels(server_id)

client.list_members(server_id)

client.create_channel(server_id, name, type, category_id)

client.rename_channel(channel_id, name)

client.delete_channel(channel_id)

client.leave_server(server_id)

client.create_invite(server_id, scopes)

JavaScript / TypeScript

client.connect() — connect (async)

client.on(event, handler) — register event handler

message.reply(content) — reply to a message

message.edit(content) — edit a message

Slash Commands, Interactions and Buttons

Register commands once and they appear in the / list in every space your bot is in. When someone runs one (or presses one of your bot's buttons) you receive an interactionCreate event and answer it within 15 minutes. Answering doesn't need messages.write or messages.read. Every request uses Authorization: Bot YOUR_TOKEN against https://www.disband.dev/api/v1.

1. Register commands

PUT /commands replaces all of your commands with the list you send. POST /commands creates or updates one, GET /commands lists them and DELETE /commands?name=weather removes one. Up to 100 commands with 25 options each. Names are 1–32 lowercase letters, digits, _ or -. Option types are string, integer, number, boolean, user and channel. Required options come first, and string or number options can list up to 25 choices.

curl -X PUT https://www.disband.dev/api/v1/commands \
  -H "Authorization: Bot $DISBAND_BOT_TOKEN" -H "Content-Type: application/json" \
  -d '[
    { "name": "weather", "description": "Today\u2019s weather",
      "options": [
        { "name": "city", "description": "Which city", "type": "string", "required": true },
        { "name": "units", "description": "C or F", "type": "string",
          "choices": [{ "name": "Celsius", "value": "c" }, { "name": "Fahrenheit", "value": "f" }] }
      ] },
    { "name": "poll", "description": "Start a yes/no poll" }
  ]'

2. Receive the interaction

It arrives through the gateway (GET /gateway) like any other event. user and channel options arrive as IDs.

{ "type": "interactionCreate",
  "payload": {
    "id": "6f1c…", "type": "command",            // or "component" for a button
    "server_id": "…", "channel_id": "…",
    "user": { "id": "…", "username": "kai", "display_name": "Kai", "avatar_url": null },
    "command": { "name": "weather", "options": { "city": "London", "units": "c" } },
    "component": null,                            // { "custom_id", "message_id" } for a button
    "expires_at": "2026-11-01T12:15:00Z" } }

3. Answer it

POST /interactions/{id}/callback with content, embeds and components. The reply shows “Kai used /weather” above it. You can answer up to 5 times (follow-ups). For a button press, send "update": true to edit the message the button is on instead, for pages, counters or disabling a button once it's used.

const base = "https://www.disband.dev/api/v1";
const headers = { Authorization: `Bot ${process.env.DISBAND_BOT_TOKEN}`, "Content-Type": "application/json" };

for (;;) {
  const { events } = await fetch(`${base}/gateway`, { headers }).then((r) => r.json());
  for (const { type, payload: i } of events ?? []) {
    if (type !== "interactionCreate") continue;
    const reply = (body) =>
      fetch(`${base}/interactions/${i.id}/callback`, { method: "POST", headers, body: JSON.stringify(body) });

    if (i.command?.name === "poll") {
      await reply({
        content: "Do you like pizza?",
        components: [{ type: "action_row", components: [
          { type: "button", style: "success", label: "Yes", custom_id: "poll:yes" },
          { type: "button", style: "danger", label: "No", custom_id: "poll:no" },
          { type: "button", style: "link", label: "Docs", url: "https://www.disband.dev/developers/docs" },
        ] }],
      });
    } else if (i.component?.custom_id?.startsWith("poll:")) {
      await reply({ update: true, content: `${i.user.display_name} voted ${i.component.custom_id.slice(5)}.`, components: [] });
    }
  }
}

Buttons

Any message your bot sends (POST /channels/{id}/messages) or any answer can carry up to 5 action_rows of up to 5 buttons. Styles are primary, secondary, success, danger and link (Discord's numbers 1–5 work too). A link button needs an https url; every other button needs a unique custom_id of up to 100 characters. Labels are up to 80 characters, and a button can have an emoji or be disabled.

Events

EventDescription
readyBot is connected and ready.
messageCreateA new message was posted.
messageUpdateA message was edited.
messageDeleteA message was deleted.
memberJoinSomeone joined a space (members.read).
memberLeaveSomeone left or was removed from a space (members.read).
memberUpdateA member's roles changed; carries their roles now (members.read).
interactionCreateSomeone ran one of your slash commands or pressed one of your bot's buttons.
errorAn error occurred.

Rate Limits

Bot API requests are rate-limited per bot. Limits vary by endpoint type (read, write, gateway). When rate-limited, the API returns 429 with aRetry-After header. Both SDKs handle this automatically with exponential backoff.

Disband Bot SDKs: pip install disband-bot · npm install @disband/bot