Discord Bot Integration
Drive your own Discord bot from a VM, with the bot token held off-VM
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=-`.