# Discord Bot Integration


The Discord Bot integration lets a VM act as a real Discord bot: post
messages, read channel history, react, manage roles — anything in the
Discord Bot REST API — while the bot's token stays off the VM entirely. You
bring your own Discord application; exe.dev holds its bot token server-side
and injects it on the way to Discord.

This is the do-everything sibling of the send-only
[Discord Integration](integrations-discord). Use that one if all you need is
"post a message to a channel."

## How it works

Your VM talks to the integration hostname exactly as it would talk to
`discord.com`, version prefix and all:

```
curl -X POST https://mybot.int.exe.xyz/api/v10/channels/1234567890/messages --json '{"content":"hi"}'
```

exe.dev forwards the request to the Discord Bot API with your bot token
attached as `Authorization: Bot …`. The path is forwarded verbatim —
including the `/api/v10` prefix, so you choose the API version — and `GET`,
`POST`, `PUT`, `PATCH`, and `DELETE` all work. There is no endpoint
allowlist: what the bot may actually do is bounded by your own bot's
permissions in the servers you invited it to.

## Step 1: create your Discord bot

Go to the [Discord Developer
Portal](https://discord.com/developers/applications) → **New Application**,
name it, then:

1. **Get the bot token.** Open the **Bot** tab and click **Reset Token**;
   copy the token it reveals. This is the credential the integration needs —
   *not* the Client Secret on the OAuth2 tab, which is a different string
   that won't work here.
2. **(Optional) enable intents.** Still on the Bot tab: if your bot will
   read other users' message *content* via the API, switch on the
   **Message Content Intent** under Privileged Gateway Intents.
3. **Invite the bot to your server.** Under **OAuth2 → URL Generator**,
   check the `bot` scope, pick the permissions your bot needs (e.g.
   Send Messages, Read Message History, Add Reactions), open the generated
   URL in your browser, and choose your server.

## Step 2: create the integration

On the [Integrations page](/integrations), click the **Discord Bot** tile
and paste the token — the Test button verifies it against Discord and fills
in the bot's username. Or via SSH, passing `-` so the token is
[never on the command line](integrations#providing-secrets-to-integrations-commands):

```
exe.dev ▶ integrations add discord --name mybot --bot-token=-
Secret:
exe.dev ▶ integrations attach mybot vm:my-vm
```

Scripted (`ssh exe.dev integrations add ...`), stdin carries the token:

```
$ printf '%s\n' "$DISCORD_BOT_TOKEN" | \
    ssh exe.dev integrations add discord --name mybot --bot-token=-
```

## Step 3: use it from the VM

The examples below use the integration hostname `mybot.int.exe.xyz` —
substitute the exact URL printed when you created the integration (it varies
by integration name and environment).

Sanity-check the token routing (returns your bot's user object):

```
curl https://mybot.int.exe.xyz/api/v10/users/@me
```

Post as the bot:

```
curl -X POST https://mybot.int.exe.xyz/api/v10/channels/1234567890/messages \
  --json '{"content":"hello from my VM"}'
```

Read recent channel history:

```
curl 'https://mybot.int.exe.xyz/api/v10/channels/1234567890/messages?limit=10'
```

React to a message:

```
curl -X PUT 'https://mybot.int.exe.xyz/api/v10/channels/1234567890/messages/9876543210/reactions/%F0%9F%91%8D/@me'
```

At no point did the bot token exist on the VM — the VM only ever holds the
integration hostname.

## Receiving events

Unlike the [Slack Bot Integration](integrations-slack-bot), the Discord Bot
integration cannot *receive* events (new messages, mentions) as a push.
Discord has no analogue of Slack's Socket Mode: its Gateway WebSocket
requires the bot token inside the connection handshake itself, which would
put the token on your VM — exactly what this integration exists to prevent.

What works instead is REST polling: fetch
`/api/v10/channels/<id>/messages` on a timer and act on what's new. For many
agent workloads (watch a support channel, respond to commands) a poll every
few seconds through the integration is entirely sufficient.

## Notes

- Discord rate-limits the Bot API per route. exe.dev tracks Discord's
  `X-RateLimit-*` response headers and, once a route's budget is exhausted,
  rejects further calls to that route locally (429 + `Retry-After`) until
  the window resets; other routes are unaffected. A *global* back-off from
  Discord pauses all forwarding until it passes.
- On top of Discord's limits, exe.dev applies its own per-user limit
  (sustained 10 requests/second, bursting to 20).
- Request bodies are capped at 256 KiB, so large file uploads won't fit;
  responses (message history, member lists) stream through without a size
  cap.
- exe.dev sets the `User-Agent` Discord requires on Bot API calls, so plain
  `curl` works without extra headers.
- Discord SDKs like `discord.py` and `discord.js` hardcode `discord.com`
  and open Gateway WebSockets, so they can't be pointed at the integration
  hostname — use plain HTTP against the REST API instead.
- Rotating the token (after a **Reset Token** in the Developer Portal):
  `integrations edit mybot --bot-token=-`.
