Scriber setup guide

Everything to take Scriber from zero to recording: create the Discord app, get its token and IDs, invite it with the right permissions, admit it to a private voice channel, then configure and run the container.

Roughly 10–15 minutes. You'll need a Discord account and a server where you have the Manage Server permission.

Before you start

You will need:

  • Docker or Podman (recommended), or Python 3.12+ with the libopus system library — libopus is required for Discord voice, config alone won't make voice work.
  • A Discord server you manage (you need Manage Server to add a bot).
  • An API key for Anthropic or OpenAI — or a self-hosted OpenAI-compatible server (Ollama, vLLM, LM Studio, LocalAI) if you want summaries to never leave your machine.
What Scriber needs from Discord, in one line: a bot token, and the bot invited with the bot + applications.commands scopes and four permissions — View Channels, Send Messages, Attach Files, Connect.

1 · Create the Discord application

1

Open the Developer Portal

Go to discord.com/developers/applications and sign in.

2

New Application

Click New Application, give it a name (e.g. Scriber), accept the Terms, and click Create. This creates the application that your bot lives inside.

2 · Configure the bot & get the token

1

Reveal the bot token

Open the Bot tab in the left sidebar. Click Reset Token and confirm — Discord no longer shows an existing token, so you must reset to reveal one. Click Copy and keep it somewhere safe; you'll paste it into .env as DISCORD_TOKEN.

Keep it secret. Anyone with this token controls your bot. Each reset invalidates the previous token — if you reset again, update DISCORD_TOKEN and restart the container.
2

Authorization Flow toggles

Still on the Bot tab, in the Authorization Flow section:

  • Public Bot — turn OFF for a private self-hosted bot, so only you (and app team members) can add it.
  • Requires OAuth2 Code Grant — leave OFF. If it's on, simple invite links fail with an "integration requires code grant" error.
3

Privileged Gateway Intents — leave them off

Under Privileged Gateway Intents, leave Server Members Intent and Message Content Intent OFF. Scriber needs neither. Voice recording uses the standard (non-privileged) Voice States intent, which is enabled in Scriber's own code — there's no portal toggle for it. Leaving the privileged intents off also avoids Discord's 100-server verification requirements entirely.

3 · Find the IDs you need

Application ID (a.k.a. Client ID)

On the General Information page, copy Application ID. (The identical value appears on the OAuth2 tab as Client ID.) You'll use it to build the invite link.

Server ID and Channel IDs (optional but recommended)

To make slash commands appear instantly in one server (instead of waiting up to an hour for a global sync), set DISCORD_GUILD_ID. To get it, enable Developer Mode in the Discord app:

1

Enable Developer Mode

Discord app → User Settings (the gear, bottom-left) → Advanced → turn Developer Mode ON.

2

Copy the IDs

Right-click your server icon → Copy Server ID (this is the guild ID). Right-click a channel → Copy Channel ID if you ever need it.

4 · Invite the bot to your server

The bot must be added with two scopes — bot (to join and record) and applications.commands (so /scriber registers). The four permissions Scriber advertises add up to the integer 1084416:

View Channels · 1024 Send Messages · 2048 Attach Files · 32768 Connect · 1048576
PermissionWhy Scriber needs it
View ChannelsSee the voice & text channels (required to join a voice channel).
ConnectJoin the voice channel to receive audio. Speak is deliberately not requested — the bot only listens.
Send MessagesPost the recording notice and the minutes in the text channel.
Attach FilesPost long transcripts/summaries as an attached .md file.

Easiest: use the ready-made invite URL

Replace YOUR_APPLICATION_ID with the ID from step 3 and open the URL in a browser:

https://discord.com/oauth2/authorize?client_id=YOUR_APPLICATION_ID&scope=bot%20applications.commands&permissions=1084416

Pick a server where you have Manage Server, click Authorize, and solve the captcha. The bot joins.

Tip: Scriber logs this exact invite URL on startup, and the dashboard shows an "Invite the bot to your server" button with the correct scopes if the bot isn't set up yet.

Or build it in the portal

Two equivalent paths — use one consistently:

  • OAuth2 → URL Generator: tick the bot and applications.commands scopes, then in the permissions grid tick View Channel, Send Messages, Attach Files, Connect. Copy the generated URL.
  • Installation tab (newer UI): enable Guild Install, set the Install Link to Discord Provided Link, and under Default Install Settings for Guild Install add the same scopes and permissions.
Don't use "User Install" alone. A user-installed app only gets the applications.commands scope and cannot join voice. Make sure Guild Install (with the bot scope) is enabled.

5 · Give the bot access to a private voice channel

A private channel denies View Channel to @everyone; only roles or members with an explicit allow can see or join it. Server-wide permissions are not enough — you must add a channel-level override for the bot.

1

Open the voice channel's permissions

Hover the private voice channel → gear (Edit Channel) → Permissions.

2

Add the bot and allow View Channel + Connect

Under Advanced permissions, click + and add the bot's integration-managed role (auto-created with the bot's name) or the bot member directly. Set View Channel = ✔ (allow) and Connect = ✔ (allow). Leave Speak off.

3

Grant the text channel too

In the text channel where you'll run /scriber start, make sure the bot has View Channel, Send Messages, and Attach Files. If that text channel is also private, add an explicit allow override for the bot there as well. Attach Files is required — long minutes are posted as an attached file.

Why channel overrides matter: channel-level permissions beat server-level roles. A channel that denies Connect blocks the bot even if its server role grants Connect; conversely a private channel needs an explicit allow for the bot. Adding the bot member directly is the most reliable override. (Granting Administrator would also work but is over-privileged — stick to the four scoped permissions.)

6 · Configure Scriber

Copy the example environment file and edit it. The only things you must set are DISCORD_TOKEN and one summary-provider block.

cp .env.example .env
$EDITOR .env

Discord

DISCORD_TOKEN=your-bot-token-from-step-2
# Optional: instant slash-command sync in one server (see step 3)
DISCORD_GUILD_ID=your-server-id

Summary providers (ordered failover list)

Each provider is a numbered block (_1, _2, …), tried in order until one succeeds. A single block is fine. Secrets can later be edited from the dashboard Settings page.

# Provider 1: Anthropic (Claude) — tried first
SUMMARY_PROVIDER_1=anthropic
SUMMARY_API_KEY_1=sk-ant-xxxxxxxx
SUMMARY_MODEL_1=claude-opus-5
SUMMARY_BASE_URL_1=https://api.anthropic.com

# Provider 2 (optional): OpenAI — tried if provider 1 fails
SUMMARY_PROVIDER_2=openai
SUMMARY_API_KEY_2=sk-xxxxxxxx
SUMMARY_MODEL_2=gpt-4o
SUMMARY_BASE_URL_2=https://api.openai.com/v1
Fully local? Set a provider to openai-compatible pointed at Ollama/vLLM/LM Studio/LocalAI (e.g. http://host.docker.internal:11434/v1) and no meeting data leaves your machine at all.

Admin dashboard & the rest

Set the dashboard login and change the default password. Whisper, web bind, and storage keys all have sensible defaults — see the full reference in the README.

ADMIN_USERNAME=admin
ADMIN_PASSWORD=change-me   # change before exposing the port!
WHISPER_MODEL=base       # tiny / base / small / medium / large-v3
WHISPER_LANGUAGE=en       # default; override per meeting with lang:

7 · Run it

With Docker Compose (a compose.yaml is included):

docker compose up -d          # or: podman-compose up -d

Or with a plain container run:

docker build -t scriber .
docker run -d --name scriber \
  --env-file .env \
  -v ./data:/data \
  -v ./.env:/app/.env \
  -p 8080:8080 \
  scriber

Open http://localhost:8080 and sign in with your ADMIN_USERNAME / ADMIN_PASSWORD. The dashboard comes up even if the bot has a problem, and shows exactly what to fix.

First start is slow. The Whisper model downloads on first use into data/models/; later meetings reuse the cache.

8 · Your first recording

1

Join a voice channel

Be in a voice channel of the server, then run /scriber start in a text channel. Add lang:fr (or auto) to override the language for this meeting.

2

Scriber posts a recording notice

It names the voice channel, the transcription engine in use (and whether speech audio goes to a cloud STT service), and the exact AI target the transcript will be sent to — so anyone can leave before being recorded. If the bot lacks View Channel/Connect on that channel, it tells you precisely which permission to add instead of hanging.

3

Finish

Run /scriber stop to transcribe, summarize, and post the Markdown minutes (attached as a file when long). Or /scriber cancel to discard everything. Scriber also stops on its own when left alone or after 2 minutes of silence.

9 · Troubleshooting

/scriber doesn't appear

The bot was likely invited without applications.commands, so command registration returns 403 Missing Access. Re-invite with both scopes (step 4) — no need to kick it first — then re-run the sync from the dashboard (Resync commands) or restart. Guild commands appear instantly; a global sync can take up to an hour, so set DISCORD_GUILD_ID for testing.

The bot won't join the voice channel

Check it has View Channel and Connect on that specific channel (step 5) — a channel override can deny Connect even when the server role allows it. Confirm you're in a voice channel of the same server. Scriber's start command reports the missing permission by name.

Empty transcripts

Make sure libopus is installed (it's built into the Docker image). On servers that require voice end-to-end encryption (DAVE), Scriber decrypts it automatically; if it can't, recordings may come out empty — check the container logs.

Summary failed

The transcript is never lost — Scriber attaches the raw transcript to the error message and keeps it in the dashboard. Verify your SUMMARY_API_KEY, SUMMARY_MODEL, and SUMMARY_BASE_URL on the Settings page. Add a second provider for automatic failover.

nginx returns 502

Keep WEB_HOST=0.0.0.0 in .env (a loopback bind inside the container makes a published port unreachable) and keep the container side of -p at 8080. Full reverse-proxy notes are in the README.