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. 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 → 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, 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, pass - to read the token from stdin:

exe.dev ▶ integrations add discord --name mybot --bot-token=-
Bot token:
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, 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=-.