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
libopussystem library —libopusis 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.
bot + applications.commands scopes and four permissions —
View Channels, Send Messages, Attach Files, Connect.
1 · Create the Discord application
Open the Developer Portal
Go to discord.com/developers/applications and sign in.
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
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.
DISCORD_TOKEN and restart the container.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.
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:
Enable Developer Mode
Discord app → User Settings (the gear, bottom-left) → Advanced → turn Developer Mode ON.
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:
| Permission | Why Scriber needs it |
|---|---|
View Channels | See the voice & text channels (required to join a voice channel). |
Connect | Join the voice channel to receive audio. Speak is deliberately not requested — the bot only listens. |
Send Messages | Post the recording notice and the minutes in the text channel. |
Attach Files | Post 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.
Or build it in the portal
Two equivalent paths — use one consistently:
- OAuth2 → URL Generator: tick the
botandapplications.commandsscopes, 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.
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.
Open the voice channel's permissions
Hover the private voice channel → gear (Edit Channel) → Permissions.
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.
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.
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
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.
data/models/; later meetings reuse the cache.
8 · Your first recording
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.
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.
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.