Channel OAuth Setup Guide
How to set up Slack, Discord, and Telegram integrations so your users can connect their own workspaces, servers, and bots to their agents.
Prerequisites
Generate and set INTERNAL_API_SECRET in your .env:
openssl rand -base64 32
This shared secret authenticates server-to-server calls between the Next.js frontend and NestJS backend (OAuth callbacks, Stripe sync).
Slack
1. Create a Slack App
- Go to api.slack.com/apps
- Click "Create New App" → "From scratch"
- Name it (e.g. "Rabbithole Agent") and pick a workspace for development
- Click "Create App"
2. Configure OAuth Scopes
- In your app settings, go to OAuth & Permissions (left sidebar)
- Under Bot Token Scopes, add these scopes:
chat:write— Send messageschannels:read— View channel infochannels:history— Read message historygroups:read— View private channel infoim:read— View DM infoim:write— Send DMsusers:read— View user info
3. Set Redirect URL
- Still in OAuth & Permissions, scroll to Redirect URLs
- Add:
https://your-domain.com/api/channels/oauth/slack/callback- For local dev:
http://localhost:3000/api/channels/oauth/slack/callback
- For local dev:
- Click "Save URLs"
4. Get Credentials
- Go to Basic Information (left sidebar)
- Copy:
- Client ID →
SLACK_OAUTH_CLIENT_ID - Client Secret →
SLACK_OAUTH_CLIENT_SECRET
- Client ID →
5. Set Environment Variables
SLACK_OAUTH_CLIENT_ID=your-client-id
SLACK_OAUTH_CLIENT_SECRET=your-client-secret
How It Works
Users click "Connect to Slack" → redirected to Slack → authorize your app → Slack sends them back with a code → backend exchanges code for a xoxb- bot token → token stored encrypted → binding auto-created. Slack bot tokens never expire.
Discord
1. Create a Discord Application
- Go to discord.com/developers/applications
- Click "New Application"
- Name it (e.g. "Rabbithole Agent") and click "Create"
2. Create a Bot
- Go to Bot (left sidebar)
- Click "Add Bot" → "Yes, do it!"
- Under Privileged Gateway Intents, enable:
- Message Content Intent (if you want the bot to read message content)
- Copy the Token → this is your
DISCORD_OAUTH_BOT_TOKEN
3. Configure OAuth2
- Go to OAuth2 → General (left sidebar)
- Copy:
- Client ID →
DISCORD_OAUTH_CLIENT_ID - Client Secret →
DISCORD_OAUTH_CLIENT_SECRET
- Client ID →
- Under Redirects, add:
https://your-domain.com/api/channels/oauth/discord/callback- For local dev:
http://localhost:3000/api/channels/oauth/discord/callback
4. Set Environment Variables
DISCORD_OAUTH_CLIENT_ID=your-client-id
DISCORD_OAUTH_CLIENT_SECRET=your-client-secret
DISCORD_OAUTH_BOT_TOKEN=your-bot-token
How It Works
Users click "Add to Discord" → redirected to Discord → pick a server → authorize bot permissions → Discord sends them back with a code → backend exchanges code, stores the guild binding → bot token stored encrypted → binding auto-created. The platform-level bot token is used for all API calls.
Telegram
Telegram does not use OAuth. Each user creates their own bot via @BotFather.
User Flow (built into the UI)
- User clicks "Set Up Telegram Bot" on the channels page
- UI shows instructions:
- Open @BotFather on Telegram
- Send
/newbotand follow the prompts - Copy the bot token
- User pastes the token
- Backend validates it via Telegram's
getMeAPI - Backend registers a webhook automatically
- Token stored encrypted, binding created
No Setup Required From You
There are no platform-level credentials for Telegram. Each user manages their own bot. The only requirement is that your backend is publicly accessible so Telegram can deliver webhook updates.
Webhook URL
The backend registers the webhook at:
https://your-api-domain.com/wunderland/channels/inbound/telegram/{seedId}
Make sure API_BASE_URL or BASE_URL is set in your .env to the public URL of your NestJS backend.
All Environment Variables
# Internal auth (required)
INTERNAL_API_SECRET= # openssl rand -base64 32
# Slack OAuth (create at api.slack.com/apps)
SLACK_OAUTH_CLIENT_ID=
SLACK_OAUTH_CLIENT_SECRET=
# Discord OAuth (create at discord.com/developers)
DISCORD_OAUTH_CLIENT_ID=
DISCORD_OAUTH_CLIENT_SECRET=
DISCORD_OAUTH_BOT_TOKEN=
# OAuth callback base URL (defaults to FRONTEND_URL)
# OAUTH_CALLBACK_BASE_URL=https://rabbithole.inc
Quick Links
| Platform | Developer Console | Docs |
|---|---|---|
| Slack | api.slack.com/apps | OAuth V2 docs |
| Discord | discord.com/developers | OAuth2 docs |
| Telegram | @BotFather | Bot API docs |
Troubleshooting
"Slack OAuth is not configured" — SLACK_OAUTH_CLIENT_ID is missing from .env.
"Discord OAuth is not configured" — DISCORD_OAUTH_CLIENT_ID is missing from .env.
"Internal API secret is not configured" — INTERNAL_API_SECRET is missing. Generate one with openssl rand -base64 32.
"Invalid or expired OAuth state" — The user took more than 10 minutes to complete the OAuth flow, or they reused a link. Have them try again.
"OAuth state has already been used" — Replay attack prevention. Each OAuth state token is single-use.
Telegram webhook not working — Make sure API_BASE_URL or BASE_URL is set to a publicly accessible URL. Telegram can't deliver webhooks to localhost.
Redirect mismatch error — The redirect URL in your Slack/Discord app settings must exactly match {OAUTH_CALLBACK_BASE_URL}/api/channels/oauth/{platform}/callback. Check for trailing slashes and http vs https.