Discord
How to dispatch a meetbot bot into a Discord voice channel — Discord application setup, OAuth invite, and per-platform quirks.
Discord is structurally different from Meet, Teams, and Zoom. The other platforms have no meeting-bot API, so meetbot drives a real Chrome browser through the join UI. Discord has a first-class Bots API + voice gateway, so meetbot connects directly as a Discord bot — no browser, no DOM scraping, no anti-bot fingerprint dance.
What you get
- Per-speaker Opus tracks —
audio.<userId>.oggper user, written straight from Discord's voice gateway (Discord's negotiated codec IS Opus, so no transcoding). Multiple utterances by the same user rotate asaudio.<userId>-2.ogg,audio.<userId>-3.ogg, … — same convention as Recall.ai's per-speaker artifacts. - Roster events in the manifest:
participant.join/participant.leavefor every voice-channel transition. - Chat capture (optional): point us at the text channel paired
with the voice channel and we capture every message into
chat.jsonl. - Auto-leave on the same four thresholds as every other platform — silence, solo, max-duration, entry-delay (no humans joined within N seconds).
What you don't get
- No live captions. Discord doesn't ship server-side speech-to-text. The customer can BYO transcription against the per-speaker Opus tracks (Whisper, Deepgram, AssemblyAI — anything that takes Opus or transcoded WAV).
- No per-call display name. The bot uses whatever name you
registered for the Discord application; meetbot's
displayNamefield is silently ignored for Discord. To rename the bot, rename the application in the Discord Developer Portal. - No video. Discord voice channels are audio-only by default (stage channels and screenshare add video, but the voice-receive pipeline only exposes Opus).
Customer setup (one-time)
You'll need a Discord application + bot token. Five steps, takes ~5 minutes:
1. Create a Discord application
Visit
https://discord.com/developers/applications
and click New Application. Pick a name (this is what shows up in
the voice channel — pick something like Acme Notetaker). Save.
2. Add a Bot
In the app's settings, navigate to Bot in the left sidebar. Click Reset Token (or Add Bot if it's a fresh app). Discord shows the bot token ONCE — copy it now and paste it into your meetbot dispatch config. If you lose it, reset and copy again.
While you're on this page, scroll down and enable the "Message Content Intent" under Privileged Gateway Intents (only required if you want chat capture).
3. Generate an OAuth2 invite URL
Navigate to OAuth2 → URL Generator in the sidebar. Under
Scopes, check bot. A second permissions panel appears below;
check:
View ChannelsConnectSpeakUse Voice ActivityRead Message History(only needed for chat capture)
The page shows the integer permission bitmask at the bottom — should
be 36702208 if you ticked exactly the boxes above.
Copy the Generated URL at the bottom of the page.
4. Add the bot to your Discord server
Open the URL from step 3 in a browser. Discord asks which server to add the bot to. Pick the target server. You must be a server admin or the server owner to do this.
5. Look up the guild + voice-channel ids
In the Discord client, open User Settings → Advanced and toggle Developer Mode ON. Now right-click any server icon → Copy ID to get the guild id; right-click any voice channel → Copy ID to get the channel id. Both are 17-19 digit "snowflake" numbers.
If you want chat capture, also right-click the text channel paired with the voice channel and copy that id.
Dispatch via the SDK
import { MeetBot } from "@meetbot/sdk";
const meetbot = new MeetBot({ apiKey: process.env.MEETBOT_API_KEY });
const job = await meetbot.dispatch({
externalId: "standup-2026-05-09",
platform: "discord",
// The synthetic discord:// scheme keeps every platform URL-keyed
// in the API. The bot token + ids on the `discord` block below
// do the actual work.
meetingUrl: "discord://YOUR_GUILD_ID/YOUR_VOICE_CHANNEL_ID",
discord: {
botToken: process.env.DISCORD_BOT_TOKEN!,
guildId: "YOUR_GUILD_ID",
channelId: "YOUR_VOICE_CHANNEL_ID",
// Optional — omit to skip chat.jsonl capture.
textChannelId: "YOUR_TEXT_CHANNEL_ID",
},
webhooks: {
onFinalize: "https://api.example.com/meetbot-webhook",
},
autoLeave: {
// Default Discord auto-leave: bail if nobody joined the voice
// channel within 5 minutes of the bot connecting.
afterEntryDelaySeconds: 300,
// 4-hour cap.
afterMaxSeconds: 14_400,
},
});Python is identical:
from meetbot import MeetBot, DiscordConfig
meetbot = MeetBot(api_key=os.environ["MEETBOT_API_KEY"])
job = meetbot.dispatch(
external_id="standup-2026-05-09",
platform="discord",
meeting_url="discord://YOUR_GUILD_ID/YOUR_VOICE_CHANNEL_ID",
discord=DiscordConfig(
bot_token=os.environ["DISCORD_BOT_TOKEN"],
guild_id="YOUR_GUILD_ID",
channel_id="YOUR_VOICE_CHANNEL_ID",
text_channel_id="YOUR_TEXT_CHANNEL_ID",
),
)Manifest shape
A completed Discord job's manifest looks like this:
{
"platform": "discord",
"exitReason": "alone_in_room",
"speakers": [
{ "id": "287541432954880001", "displayName": "alice" },
{ "id": "354722510892302337", "displayName": "bob" }
],
"tracks": [
{
"trackId": "287541432954880001",
"kind": "per-speaker",
"format": "opus",
"speakerId": "287541432954880001",
"uri": "s3://bucket/runs/.../audio.287541432954880001.ogg",
"bytes": 123456,
"encoding": { "mimeType": "audio/ogg; codecs=opus" }
}
],
"chatUri": "s3://bucket/runs/.../chat.jsonl"
}Security notes
- The Discord bot token is treated as a secret. We accept it on
dispatch, store it in the job's
metadata.discord.botToken, and forward it to the bot container as theDISCORD_BOT_TOKENenv var. The token is stripped from every API response — themetadata.discordechoed inGET /api/v1/jobs/:idand webhook payloads containsguildId,channelId, andtextChannelIdonly, never the token. - We recommend rotating the token if any meetbot job using it ever lands in a state where the customer can't account for the container's logs (e.g. you fired a dispatch at the wrong account). Rotation is a single click in the Discord Developer Portal.
- Only grant the bot the permissions listed in step 3. The OAuth2
generator's "Administrator" tickbox is convenient but vastly
over-scoped —
Connect+Speak+View Channelsis everything the meetbot adapter needs.
Pricing
Discord meetings count against your meetbot quota the same way
Meet/Teams/Zoom do — $0.30/hour of bot-time after the 10
free-hours-per-month tier. The failure_code taxonomy is
identical: bad bot token surfaces as auth_failure, no humans
joined within entry-delay surfaces as lobby_timeout, and so on.