meetbot / docs
Per-platform

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 tracksaudio.<userId>.ogg per user, written straight from Discord's voice gateway (Discord's negotiated codec IS Opus, so no transcoding). Multiple utterances by the same user rotate as audio.<userId>-2.ogg, audio.<userId>-3.ogg, … — same convention as Recall.ai's per-speaker artifacts.
  • Roster events in the manifest: participant.join / participant.leave for 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 displayName field 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 Channels
  • Connect
  • Speak
  • Use Voice Activity
  • Read 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 the DISCORD_BOT_TOKEN env var. The token is stripped from every API response — the metadata.discord echoed in GET /api/v1/jobs/:id and webhook payloads contains guildId, channelId, and textChannelId only, 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 Channels is 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.

On this page