feat(messaging): introduce comprehensive setup docs for Telegram, WhatsApp, Slack, and Discord messaging channels

This commit is contained in:
Anish Sarkar 2026-06-02 00:25:49 +05:30
parent 20994671bc
commit ebddf4506a
14 changed files with 530 additions and 54 deletions

View file

@ -70,7 +70,7 @@ EMBEDDING_MODEL=sentence-transformers/all-MiniLM-L6-v2
# ------------------------------------------------------------------------------
# ONLY set these if you are serving SurfSense on a real domain via a reverse
# proxy (e.g. Caddy, Nginx, Cloudflare Tunnel).
# For standard localhost deployments, leave all of these commented out
# For standard localhost deployments, leave all of these commented out.
# they are automatically derived from the port settings above.
#
# NEXT_FRONTEND_URL=https://app.yourdomain.com
@ -92,7 +92,7 @@ EMBEDDING_MODEL=sentence-transformers/all-MiniLM-L6-v2
# Only change this if you manage publications manually.
# ZERO_APP_PUBLICATIONS=zero_publication
# Sync worker tuning zero-cache defaults ZERO_NUM_SYNC_WORKERS to the number
# Sync worker tuning. zero-cache defaults ZERO_NUM_SYNC_WORKERS to the number
# of CPU cores, which can exceed the connection pool limits on high-core machines.
# Each sync worker needs at least 1 connection from both the UPSTREAM and CVR
# pools, so these constraints must hold:
@ -137,7 +137,7 @@ EMBEDDING_MODEL=sentence-transformers/all-MiniLM-L6-v2
# SSL mode for database connections: disable, require, verify-ca, verify-full
# DB_SSLMODE=disable
# Full DATABASE_URL override — when set, takes precedence over the individual
# Full DATABASE_URL override. When set, this takes precedence over the individual
# DB_USER / DB_PASSWORD / DB_NAME / DB_HOST / DB_PORT settings above.
# Use this for managed databases (AWS RDS, GCP Cloud SQL, Supabase, etc.)
# DATABASE_URL=postgresql+asyncpg://user:password@your-rds-host:5432/surfsense?sslmode=require
@ -152,7 +152,7 @@ EMBEDDING_MODEL=sentence-transformers/all-MiniLM-L6-v2
# REDIS_URL=redis://redis:6379/0
# ------------------------------------------------------------------------------
# Stripe (pay-as-you-go page packs disabled by default)
# Stripe (pay-as-you-go page packs, disabled by default)
# ------------------------------------------------------------------------------
# Set TRUE to allow users to buy additional page packs via Stripe Checkout
@ -171,7 +171,7 @@ STRIPE_PAGE_BUYING_ENABLED=FALSE
# STRIPE_TOKEN_BUYING_ENABLED=FALSE
# STRIPE_PREMIUM_TOKEN_PRICE_ID=price_...
# STRIPE_CREDIT_MICROS_PER_UNIT=1000000
# DEPRECATED STRIPE_TOKENS_PER_UNIT=1000000
# DEPRECATED: STRIPE_TOKENS_PER_UNIT=1000000
# ------------------------------------------------------------------------------
# TTS & STT (Text-to-Speech / Speech-to-Text)
@ -266,18 +266,18 @@ STT_SERVICE=local/base
# COMPOSIO_REDIRECT_URI=http://localhost:8000/api/v1/auth/composio/connector/callback
# ------------------------------------------------------------------------------
# Messaging Gateways (optional)
# Messaging Channels (optional)
# ------------------------------------------------------------------------------
# Configure only the gateways you want to use.
# Configure only the external chat channels you want to use.
# -- Telegram Gateway --
# -- Telegram --
# TELEGRAM_SHARED_BOT_TOKEN=
# TELEGRAM_SHARED_BOT_USERNAME=
# TELEGRAM_WEBHOOK_SECRET=
# GATEWAY_BASE_URL=http://localhost:8929
# GATEWAY_TELEGRAM_INTAKE_MODE=webhook
# -- WhatsApp Gateway --
# -- WhatsApp --
# GATEWAY_WHATSAPP_INTAKE_MODE=disabled
# WHATSAPP_SHARED_BUSINESS_TOKEN=
# WHATSAPP_SHARED_PHONE_NUMBER_ID=
@ -288,14 +288,14 @@ STT_SERVICE=local/base
# WHATSAPP_WEBHOOK_APP_SECRET=
# WHATSAPP_BRIDGE_URL=http://whatsapp-bridge:9929
# -- Slack Gateway --
# -- Slack --
# Uses SLACK_CLIENT_ID and SLACK_CLIENT_SECRET from the Slack connector section.
#
# GATEWAY_SLACK_ENABLED=FALSE
# GATEWAY_SLACK_SIGNING_SECRET=
# GATEWAY_SLACK_REDIRECT_URI=http://localhost:8929/api/v1/gateway/slack/callback
# -- Discord Gateway --
# -- Discord --
# Uses DISCORD_CLIENT_ID, DISCORD_CLIENT_SECRET, and DISCORD_BOT_TOKEN from the
# Discord connector section.
#
@ -303,7 +303,7 @@ STT_SERVICE=local/base
# GATEWAY_DISCORD_REDIRECT_URI=http://localhost:8929/api/v1/gateway/discord/callback
# ------------------------------------------------------------------------------
# SearXNG (bundled web search — works out of the box, no config needed)
# SearXNG (bundled web search, works out of the box with no config needed)
# ------------------------------------------------------------------------------
# SearXNG provides web search to all search spaces automatically.
# To access the SearXNG UI directly: http://localhost:8888
@ -313,7 +313,7 @@ STT_SERVICE=local/base
# SEARXNG_SECRET=surfsense-searxng-secret
# ------------------------------------------------------------------------------
# Daytona Sandbox (optional cloud code execution for the deep agent)
# Daytona Sandbox (optional cloud code execution for the deep agent)
# ------------------------------------------------------------------------------
# Set DAYTONA_SANDBOX_ENABLED=TRUE and provide credentials to give the agent
# an isolated code execution environment via the Daytona cloud API.
@ -404,7 +404,7 @@ SURFSENSE_ENABLE_DOOM_LOOP=true
# Premium turns are debited at the actual per-call provider cost reported
# by LiteLLM. Only applies to models with billing_tier=premium.
# PREMIUM_CREDIT_MICROS_LIMIT=5000000
# DEPRECATED PREMIUM_TOKEN_LIMIT=5000000
# DEPRECATED: PREMIUM_TOKEN_LIMIT=5000000
# Safety ceiling on per-call premium reservation, in micro-USD ($1.00 default).
# QUOTA_MAX_RESERVE_MICROS=1000000
@ -416,10 +416,10 @@ SURFSENSE_ENABLE_DOOM_LOOP=true
# QUOTA_DEFAULT_PODCAST_RESERVE_MICROS=200000
# Per-video-presentation reservation for the video Celery task ($1.00 default).
# Override path bypasses QUOTA_MAX_RESERVE_MICROS clamp — raise with care.
# Override path bypasses QUOTA_MAX_RESERVE_MICROS clamp. Raise with care.
# QUOTA_DEFAULT_VIDEO_PRESENTATION_RESERVE_MICROS=1000000
# No-login (anonymous) mode — public users can chat without an account
# No-login (anonymous) mode. Public users can chat without an account
# Set TRUE to enable /free pages and anonymous chat API
NOLOGIN_MODE_ENABLED=FALSE
# ANON_TOKEN_LIMIT=1000000

View file

@ -124,6 +124,19 @@ Uncomment the connectors you want to use. Redirect URIs follow the pattern `http
| Microsoft (Teams & OneDrive) | `MICROSOFT_CLIENT_ID`, `MICROSOFT_CLIENT_SECRET`, `TEAMS_REDIRECT_URI`, `ONEDRIVE_REDIRECT_URI` |
| Dropbox | `DROPBOX_APP_KEY`, `DROPBOX_APP_SECRET`, `DROPBOX_REDIRECT_URI` |
### Messaging Channels
Configure these in the same `docker/.env` file when you want users to chat with
SurfSense from external apps. See [Messaging Channels](/docs/messaging-channels)
for full setup.
| Channel | Variables |
|---------|-----------|
| Telegram | `TELEGRAM_SHARED_BOT_TOKEN`, `TELEGRAM_SHARED_BOT_USERNAME`, `TELEGRAM_WEBHOOK_SECRET`, `GATEWAY_BASE_URL`, `GATEWAY_TELEGRAM_INTAKE_MODE` |
| WhatsApp | `GATEWAY_WHATSAPP_INTAKE_MODE`, `WHATSAPP_SHARED_BUSINESS_TOKEN`, `WHATSAPP_SHARED_PHONE_NUMBER_ID`, `WHATSAPP_SHARED_DISPLAY_PHONE_NUMBER`, `WHATSAPP_SHARED_WABA_ID`, `WHATSAPP_WEBHOOK_VERIFY_TOKEN`, `WHATSAPP_WEBHOOK_APP_SECRET` |
| Slack | `SLACK_CLIENT_ID`, `SLACK_CLIENT_SECRET`, `GATEWAY_SLACK_ENABLED`, `GATEWAY_SLACK_SIGNING_SECRET`, `GATEWAY_SLACK_REDIRECT_URI` |
| Discord | `DISCORD_CLIENT_ID`, `DISCORD_CLIENT_SECRET`, `DISCORD_BOT_TOKEN`, `GATEWAY_DISCORD_ENABLED`, `GATEWAY_DISCORD_REDIRECT_URI` |
### Observability (optional)
| Variable | Description |
@ -187,9 +200,9 @@ Postgres. Before this design, a silent migration failure would leave
The backend exposes two endpoints:
- `GET /health` lightweight liveness probe (always returns 200 if the
- `GET /health`: lightweight liveness probe (always returns 200 if the
process is up).
- `GET /ready` readiness probe that confirms `zero_publication` exists.
- `GET /ready`: readiness probe that confirms `zero_publication` exists.
Returns 503 if not. The compose `backend.healthcheck` uses `/ready` so the
container only reports `healthy` once the schema is actually usable by
zero-cache.
@ -247,7 +260,7 @@ docker compose exec db psql -U surfsense -d surfsense \
```
The default migration timeout is 900 seconds. Slow disks (Windows / WSL2)
may need more — set `MIGRATION_TIMEOUT` in `.env` to increase it.
may need more. Set `MIGRATION_TIMEOUT` in `.env` to increase it.
### Zero-cache stuck on `Unknown or invalid publications`
@ -258,7 +271,7 @@ Error: Unknown or invalid publications. Specified: [zero_publication]. Found: []
```
This means `zero-cache` started before `zero_publication` was created. With
the current compose files this should be impossible — the `migrations`
the current compose files this should be impossible. The `migrations`
service blocks `zero-cache` from starting. If you see it, your stack
predates the fix or you brought up `zero-cache` manually with `docker
compose up zero-cache` before the migrations service ran.

View file

@ -5,7 +5,7 @@ icon: BookOpen
---
import { Card, Cards } from 'fumadocs-ui/components/card';
import { ClipboardCheck, Download, Container, Wrench, Cable, BookOpen, FlaskConical, Heart } from 'lucide-react';
import { ClipboardCheck, Download, Container, Wrench, Cable, BookOpen, FlaskConical, Heart, MessageCircle } from 'lucide-react';
Welcome to **SurfSense's Documentation!** Here, you'll find everything you need to get the most out of SurfSense. Dive in to explore how SurfSense can be your AI-powered research companion.
@ -40,6 +40,12 @@ Welcome to **SurfSense's Documentation!** Here, you'll find everything you need
description="Integrate with third-party services"
href="/docs/connectors"
/>
<Card
icon={<MessageCircle />}
title="Messaging Channels"
description="Chat with SurfSense from Telegram, WhatsApp, Slack, and Discord"
href="/docs/messaging-channels"
/>
<Card
icon={<BookOpen />}
title="How-To Guides"

View file

@ -39,38 +39,14 @@ Complete all the [setup steps](/docs), including:
The backend is the core of SurfSense. Follow these steps to set it up:
### Optional: Telegram External Chat Surface
### Optional: Messaging Channels
SurfSense can expose the same canonical chat agent through Telegram. The `external_chat_*` tables store adapter identity, delivery configuration, and durable inbox rows. The actual chat thread and messages remain in `new_chat_threads` and `new_chat_messages`, with first-party web/desktop chats marked as `source="surfsense"` and external surfaces marked by platform, such as `source="telegram"`. All chat-message sources are eligible for Zero replication so a future SurfSense UI layer can render external chat surfaces. The web app initially shows pairing, health, revoke, and resume controls under **User Settings > Messaging Channels**.
SurfSense can expose the same backend agent through Telegram, WhatsApp, Slack,
and Discord. For manual installs, configure the relevant channel variables in
`surfsense_backend/.env`.
Add these variables to `surfsense_backend/.env` when enabling the Telegram surface:
```bash
TELEGRAM_SHARED_BOT_TOKEN=123456:bot-token-from-botfather
TELEGRAM_SHARED_BOT_USERNAME=your_bot_username
TELEGRAM_WEBHOOK_SECRET=generate-a-long-random-secret
GATEWAY_BASE_URL=https://api.example.com
GATEWAY_TELEGRAM_INTAKE_MODE=webhook
```
`GATEWAY_TELEGRAM_INTAKE_MODE` must be `webhook`, `longpoll`, or `disabled`. Use `webhook` for production/SaaS deployments, `longpoll` only for single-replica self-host installs that cannot expose a public HTTPS webhook, and `disabled` to skip Telegram intake. `TELEGRAM_WEBHOOK_SECRET` must use only `A-Z`, `a-z`, `0-9`, `_`, and `-` characters. `REDIS_APP_URL` is reused for external chat rate limits and per-thread locks. The webhook URL shape is:
```text
${GATEWAY_BASE_URL}/api/v1/gateway/webhooks/telegram/{account_id}
```
After deploying the backend, register the webhook:
```bash
cd surfsense_backend
uv run python scripts/register_webhook.py
```
Keep the FastAPI backend, Celery worker, and Celery beat running. Telegram webhooks write inbound updates into `external_chat_inbound_events`. The FastAPI process owns external chat inbox processing and runs the same SurfSense agent used by web UI chats, then replies back to Telegram. Celery remains maintenance-only for external chat reconciliation, health checks, and retention sweeps. There is no separate gateway service, `SERVICE_ROLE=gateway` process, or Celery agent-processing path.
For self-hosted BYO Telegram bots without a public HTTPS URL, set `GATEWAY_TELEGRAM_INTAKE_MODE=longpoll`. The FastAPI process starts one lifespan long-poll supervisor per non-system Telegram account and writes updates into the same durable inbox. The FastAPI inbox worker then processes those rows in-process through the canonical `new_chat_*` surface. This fallback is intended for single-replica self-hosted installs. For SaaS-style multi-replica deployments, prefer public webhooks and keep `GATEWAY_TELEGRAM_INTAKE_MODE=webhook` so API replicas skip BYO polling entirely. Telegram does not allow `get_updates()` while a webhook is active, so delete any existing webhook for a BYO bot before relying on long polling.
When upgrading from an older gateway-runner deployment, apply the rewritten migration 144 external chat schema, deploy the new backend, worker, and beat images, then stop the old `gateway` service. Wait about 30 seconds for any old Telegram `getUpdates` long-poll request to release its advisory lock before starting the new API process. Register each webhook again with the account-id URL above and the per-account `webhook_secret`. If you roll back before using the migration in production, restore the old image and downgrade the schema first.
See [Messaging Channels](/docs/messaging-channels) for the channel-specific
setup guides.
### 1. Environment Configuration
@ -490,7 +466,7 @@ If everything is set up correctly, you should see output indicating the server i
## Zero-Cache Setup
**zero-cache** is the Rocicorp Zero server that sits between PostgreSQL and the browser. It streams real-time updates (notifications, document indexing status, chat comments, collaboration indicators) to all connected clients via WebSocket. The frontend connects to it on startup — without zero-cache running, you will not see live updates and many parts of the UI will sit on stale data.
**zero-cache** is the Rocicorp Zero server that sits between PostgreSQL and the browser. It streams real-time updates (notifications, document indexing status, chat comments, collaboration indicators) to all connected clients via WebSocket. The frontend connects to it on startup. Without zero-cache running, you will not see live updates and many parts of the UI will sit on stale data.
For an overview of how Zero works and the list of synced tables, see the [Real-Time Sync with Zero](/docs/how-to/zero-sync) guide.
@ -572,7 +548,7 @@ cd ../docker
docker compose -f docker-compose.deps-only.yml up -d
```
The deps-only stack exposes zero-cache on port `4848` (default) — keep `NEXT_PUBLIC_ZERO_CACHE_URL=http://localhost:4848` in your `surfsense_web/.env`.
The deps-only stack exposes zero-cache on port `4848` by default. Keep `NEXT_PUBLIC_ZERO_CACHE_URL=http://localhost:4848` in your `surfsense_web/.env`.
## Frontend Setup
@ -708,7 +684,7 @@ To verify your installation:
1. Open your browser and navigate to `http://localhost:3000`
2. Sign in with your Google account (or local credentials if `AUTH_TYPE=LOCAL`)
3. Create a search space and try uploading a document
4. Watch the upload status update live without refreshing — this confirms zero-cache is wired up correctly
4. Watch the upload status update live without refreshing. This confirms zero-cache is wired up correctly
5. Test the chat functionality with your uploaded content
## Troubleshooting

View file

@ -0,0 +1,76 @@
---
title: Discord
description: Enable the SurfSense bot for in-Discord agent chat
---
# Discord Messaging Channel
The Discord messaging channel lets users mention the SurfSense bot in Discord
and chat with the SurfSense backend agent from a Discord channel.
This is separate from the Discord connector. The messaging channel handles bot
mentions and replies; the connector gives the agent Discord channel/message read
tools.
## Discord Application Settings
Create or reuse a Discord application in the
[Discord Developer Portal](https://discord.com/developers/applications).
In **OAuth2 > Redirects**, add both callback URLs if the same application powers
the connector and messaging channel:
```bash
https://your-backend.example.com/api/v1/auth/discord/connector/callback
https://your-backend.example.com/api/v1/gateway/discord/callback
```
For local OAuth testing, replace the host with your local or public tunnel URL,
and make sure `DISCORD_REDIRECT_URI` and `GATEWAY_DISCORD_REDIRECT_URI` match
the Discord dashboard exactly.
## Bot Permissions And Intents
In **Bot > Privileged Gateway Intents**, enable:
- **Message Content Intent** so SurfSense can read text after a bot mention.
When installing the bot, grant:
- View Channels
- Send Messages
- Send Messages in Threads
- Read Message History
## Environment Variables
For Docker installs, add these to `docker/.env`. For manual installs, add them to
`surfsense_backend/.env`.
```bash
DISCORD_CLIENT_ID=your_discord_client_id
DISCORD_CLIENT_SECRET=your_discord_client_secret
DISCORD_BOT_TOKEN=your_discord_bot_token
GATEWAY_DISCORD_ENABLED=TRUE
GATEWAY_DISCORD_REDIRECT_URI=https://your-backend.example.com/api/v1/gateway/discord/callback
```
The messaging channel uses the same Discord app credentials as the Discord
connector. `DISCORD_REDIRECT_URI` remains the connector callback;
`GATEWAY_DISCORD_REDIRECT_URI` is the separate messaging channel install
callback.
## Runtime Behavior
1. Discord sends a `MESSAGE_CREATE` event over its WebSocket API.
2. SurfSense stores the event in the durable gateway inbox.
3. SurfSense resolves the Discord user binding to a SurfSense user and search space.
4. SurfSense runs the backend agent with that user's permissions.
5. The agent reply is posted back to the same Discord channel.
## Deployment Note
Only one running backend process should connect to Discord with a
given bot token. For multi-replica deployments, enable
`GATEWAY_DISCORD_ENABLED=TRUE` in a single backend process and leave it disabled
in other API replicas.

View file

@ -0,0 +1,60 @@
---
title: Docker Setup
description: Configure messaging channels for Docker and one-line installs
---
# Docker Setup
For Docker and one-line installs, configure messaging channels in the generated
`docker/.env` file. You do not need to edit `surfsense_backend/.env.example`.
The Compose stack passes `docker/.env` into the backend, worker, and beat
containers. Database, Redis, SearXNG, and internal Docker networking are already
wired by Compose.
## Public URLs
For localhost-only testing, the defaults are enough for the SurfSense UI, but
public webhooks from Telegram, WhatsApp, and Slack require a public HTTPS backend
URL. Use your deployed backend URL or a tunnel such as Cloudflare Tunnel or
ngrok.
When using a custom domain or tunnel, set:
```bash
BACKEND_URL=https://api.example.com
GATEWAY_BASE_URL=https://api.example.com
NEXT_FRONTEND_URL=https://app.example.com
NEXT_PUBLIC_FASTAPI_BACKEND_URL=https://api.example.com
```
## Environment Variables
Uncomment only the channel you are enabling in `docker/.env`.
| Channel | Main variables |
| --- | --- |
| Telegram | `TELEGRAM_SHARED_BOT_TOKEN`, `TELEGRAM_SHARED_BOT_USERNAME`, `TELEGRAM_WEBHOOK_SECRET`, `GATEWAY_BASE_URL`, `GATEWAY_TELEGRAM_INTAKE_MODE` |
| WhatsApp Cloud API | `GATEWAY_WHATSAPP_INTAKE_MODE`, `WHATSAPP_SHARED_BUSINESS_TOKEN`, `WHATSAPP_SHARED_PHONE_NUMBER_ID`, `WHATSAPP_SHARED_DISPLAY_PHONE_NUMBER`, `WHATSAPP_SHARED_WABA_ID`, `WHATSAPP_WEBHOOK_VERIFY_TOKEN`, `WHATSAPP_WEBHOOK_APP_SECRET` |
| WhatsApp Baileys | `GATEWAY_WHATSAPP_INTAKE_MODE`, `WHATSAPP_BRIDGE_URL`, `WHATSAPP_MODE` |
| Slack | `SLACK_CLIENT_ID`, `SLACK_CLIENT_SECRET`, `GATEWAY_SLACK_ENABLED`, `GATEWAY_SLACK_SIGNING_SECRET`, `GATEWAY_SLACK_REDIRECT_URI` |
| Discord | `DISCORD_CLIENT_ID`, `DISCORD_CLIENT_SECRET`, `DISCORD_BOT_TOKEN`, `GATEWAY_DISCORD_ENABLED`, `GATEWAY_DISCORD_REDIRECT_URI` |
After editing `docker/.env`, restart the stack:
```bash
docker compose up -d
```
For WhatsApp Baileys, start the Compose profile:
```bash
docker compose --profile whatsapp up -d
```
Then follow the per-channel setup pages:
- [Telegram](/docs/messaging-channels/telegram)
- [WhatsApp](/docs/messaging-channels/whatsapp)
- [Slack](/docs/messaging-channels/slack)
- [Discord](/docs/messaging-channels/discord)

View file

@ -0,0 +1,42 @@
---
title: Messaging Channels
description: Chat with SurfSense from Telegram, WhatsApp, Slack, and Discord
---
import { Card, Cards } from 'fumadocs-ui/components/card';
Choose the external chat app you want to connect to SurfSense. Each guide shows
the required app setup, environment variables, and pairing flow.
<Cards>
<Card
title="Telegram"
description="Enable SurfSense chat from Telegram"
href="/docs/messaging-channels/telegram"
/>
<Card
title="WhatsApp"
description="Enable SurfSense chat from WhatsApp"
href="/docs/messaging-channels/whatsapp"
/>
<Card
title="Slack"
description="Enable the SurfSense bot for in-Slack agent chat"
href="/docs/messaging-channels/slack"
/>
<Card
title="Discord"
description="Enable the SurfSense bot for in-Discord agent chat"
href="/docs/messaging-channels/discord"
/>
<Card
title="Docker Setup"
description="Configure messaging channels for Docker and one-line installs"
href="/docs/messaging-channels/docker"
/>
<Card
title="Troubleshooting"
description="Common pairing, webhook, and bot reply issues"
href="/docs/messaging-channels/troubleshooting"
/>
</Cards>

View file

@ -0,0 +1,13 @@
{
"title": "Messaging Channels",
"icon": "MessageCircle",
"pages": [
"telegram",
"whatsapp",
"slack",
"discord",
"docker",
"troubleshooting"
],
"defaultOpen": false
}

View file

@ -0,0 +1,84 @@
---
title: Slack
description: Enable the SurfSense bot for in-Slack agent chat
---
# Slack Messaging Channel
The Slack messaging channel lets users mention the SurfSense bot in Slack and
chat with the SurfSense backend agent from a Slack thread.
This is separate from the Slack connector. The messaging channel handles bot
mentions and replies; the connector gives the agent Slack search/read tools.
## Required Slack App Scopes
Add these **Bot Token Scopes** in Slack OAuth & Permissions:
| Scope | Purpose |
| --- | --- |
| `app_mentions:read` | Receive bot mention events |
| `chat:write` | Reply in Slack threads |
| `channels:read` | Read public channel metadata |
| `groups:read` | Read private channel metadata where the bot is present |
| `im:write` | Send onboarding or direct replies |
| `users:read` | Resolve Slack users |
| `team:read` | Resolve workspace metadata |
Optional scopes:
- `im:history` if you support direct message chat with the bot.
- `commands` if you add slash commands.
Avoid `channels:history` and `groups:history` for the messaging channel unless
you specifically need gateway-side context reads. Slack workspace search should
stay with the Slack connector.
## Event Subscriptions
Enable Slack Events API and subscribe to:
- `app_mention`
Set the request URL to:
```bash
https://your-backend.example.com/api/v1/gateway/webhooks/slack
```
Slack must be able to reach this URL. Do not use `localhost` for event
subscriptions.
## OAuth Redirect URLs
If the same Slack app powers both the connector and messaging channel, add both
redirect URLs in **OAuth & Permissions**:
```bash
https://your-backend.example.com/api/v1/auth/slack/connector/callback
https://your-backend.example.com/api/v1/gateway/slack/callback
```
## Environment Variables
For Docker installs, add these to `docker/.env`. For manual installs, add them to
`surfsense_backend/.env`.
```bash
SLACK_CLIENT_ID=your_slack_client_id
SLACK_CLIENT_SECRET=your_slack_client_secret
GATEWAY_SLACK_ENABLED=TRUE
GATEWAY_SLACK_SIGNING_SECRET=your_slack_signing_secret
GATEWAY_SLACK_REDIRECT_URI=https://your-backend.example.com/api/v1/gateway/slack/callback
```
After changing Slack scopes, redirect URLs, or event subscriptions, reinstall
the Slack app to your workspace so Slack grants the updated permissions.
## Runtime Behavior
1. Slack sends an `app_mention` event to SurfSense.
2. SurfSense verifies the Slack signature and stores the event in the gateway inbox.
3. SurfSense resolves the Slack user binding to a SurfSense user and search space.
4. SurfSense runs the backend agent with that user's permissions.
5. The agent reply is posted back in the same Slack thread.

View file

@ -0,0 +1,62 @@
---
title: Telegram
description: Enable SurfSense chat from Telegram
---
# Telegram Messaging Channel
Telegram lets users chat with the SurfSense agent from a Telegram bot. Users pair
their Telegram chat from **User Settings > Messaging Channels**.
## Environment Variables
For Docker installs, add these to `docker/.env`. For manual installs, add them to
`surfsense_backend/.env`.
```bash
TELEGRAM_SHARED_BOT_TOKEN=123456:bot-token-from-botfather
TELEGRAM_SHARED_BOT_USERNAME=your_bot_username
TELEGRAM_WEBHOOK_SECRET=generate-a-long-random-secret
GATEWAY_BASE_URL=https://api.example.com
GATEWAY_TELEGRAM_INTAKE_MODE=webhook
```
`TELEGRAM_WEBHOOK_SECRET` must be 1-256 characters and contain only `A-Z`, `a-z`,
`0-9`, `_`, or `-`.
## Intake Modes
| Mode | Use when |
| --- | --- |
| `webhook` | Production or any deployment with a public HTTPS backend URL |
| `longpoll` | Single-replica self-host installs that cannot expose a public HTTPS webhook |
| `disabled` | You do not want Telegram intake enabled |
For SaaS-style or multi-replica deployments, use `webhook`. Long polling should
only run in a single backend process.
## Webhook URL
Telegram webhooks use this shape:
```text
${GATEWAY_BASE_URL}/api/v1/gateway/webhooks/telegram/{account_id}
```
After deploying the backend, register the webhook:
```bash
cd surfsense_backend
uv run python scripts/register_webhook.py
```
If switching a bot from long polling to webhooks, delete any existing Telegram
webhook or pending `getUpdates` session before relying on the new mode.
## Pairing Flow
1. The user opens **User Settings > Messaging Channels**.
2. The user starts Telegram pairing.
3. SurfSense provides a pairing code or bot link.
4. The user sends the pairing command to the Telegram bot.
5. SurfSense binds that Telegram chat to the selected search space.

View file

@ -0,0 +1,66 @@
---
title: Troubleshooting
description: Common messaging channel pairing, webhook, and bot reply issues
---
# Messaging Channels Troubleshooting
## The Bot Does Not Reply
Check that:
- The channel is enabled in the backend environment.
- The backend restarted after the environment change.
- The external platform can reach your public HTTPS backend URL.
- The user paired the channel from **User Settings > Messaging Channels**.
- Redis is running, because gateway inbox processing uses backend coordination
and rate-limit state.
## Telegram
Check that:
- `TELEGRAM_SHARED_BOT_TOKEN` and `TELEGRAM_SHARED_BOT_USERNAME` are correct.
- `GATEWAY_TELEGRAM_INTAKE_MODE` is one of `webhook`, `longpoll`, or `disabled`.
- `TELEGRAM_WEBHOOK_SECRET` contains only `A-Z`, `a-z`, `0-9`, `_`, or `-`.
- Webhook mode uses a public HTTPS `GATEWAY_BASE_URL`.
- Long polling runs in only one backend process.
## WhatsApp
For Meta Cloud API, check that:
- `GATEWAY_WHATSAPP_INTAKE_MODE=cloud`.
- The Meta webhook URL is `${GATEWAY_BASE_URL}/api/v1/gateway/webhooks/whatsapp`.
- The Meta verify token matches `WHATSAPP_WEBHOOK_VERIFY_TOKEN`.
- `WHATSAPP_SHARED_DISPLAY_PHONE_NUMBER` contains the public WhatsApp number
users should message.
For Baileys, check that:
- `GATEWAY_WHATSAPP_INTAKE_MODE=baileys`.
- The `whatsapp` Compose profile is running.
- The bridge is paired and healthy.
- You are messaging the account's Message Yourself chat.
## Slack
Check that:
- `GATEWAY_SLACK_ENABLED=TRUE`.
- The Slack signing secret matches `GATEWAY_SLACK_SIGNING_SECRET`.
- Slack Events API is enabled and subscribed to `app_mention`.
- The Slack event request URL is public HTTPS and points to
`/api/v1/gateway/webhooks/slack`.
- The Slack app was reinstalled after scope or redirect URL changes.
## Discord
Check that:
- `GATEWAY_DISCORD_ENABLED=TRUE`.
- The bot token is valid.
- Message Content Intent is enabled.
- The bot can view and send messages in the channel.
- Exactly one backend process is running the Discord listener.
- The Discord user is paired to a SurfSense user and search space.

View file

@ -0,0 +1,75 @@
---
title: WhatsApp
description: Enable SurfSense chat from WhatsApp
---
# WhatsApp Messaging Channel
WhatsApp supports two intake modes:
- `cloud` uses the official Meta WhatsApp Cloud API with a SurfSense-owned system
WhatsApp Business Account.
- `baileys` uses the unofficial Baileys WebSocket bridge for self-hosted,
one-tenant Message Yourself installs.
Use `cloud` for production and shared deployments.
## Meta Cloud API
Create a Meta app, provision a WhatsApp Business Account and phone number, and
create a long-lived system user token with WhatsApp permissions.
Point the Meta app webhook to:
```text
${GATEWAY_BASE_URL}/api/v1/gateway/webhooks/whatsapp
```
Set the webhook verify token in Meta to the same value as
`WHATSAPP_WEBHOOK_VERIFY_TOKEN`.
For Docker installs, add these to `docker/.env`. For manual installs, add them to
`surfsense_backend/.env`.
```bash
GATEWAY_WHATSAPP_INTAKE_MODE=cloud
WHATSAPP_SHARED_BUSINESS_TOKEN=your-system-user-token
WHATSAPP_SHARED_PHONE_NUMBER_ID=your-meta-phone-number-id
WHATSAPP_SHARED_DISPLAY_PHONE_NUMBER=15551234567
WHATSAPP_SHARED_WABA_ID=your-waba-id
WHATSAPP_GRAPH_API_VERSION=v25.0
WHATSAPP_WEBHOOK_VERIFY_TOKEN=generate-a-long-random-secret
WHATSAPP_WEBHOOK_APP_SECRET=your-meta-app-secret
```
Users open **User Settings > Messaging Channels**, click **Pair WhatsApp**, and
open the returned `wa.me` link. WhatsApp pre-fills `/start CODE`; the user must
press send to bind the chat.
## Baileys Self-Hosted Mode
Baileys is unofficial. Use it only for single-tenant self-hosted installs where
the operator accepts the risk of a personal WhatsApp session bridge.
```bash
GATEWAY_WHATSAPP_INTAKE_MODE=baileys
WHATSAPP_BRIDGE_URL=http://whatsapp-bridge:9929
WHATSAPP_MODE=self-chat
docker compose --profile whatsapp up -d
```
After pairing, use WhatsApp's Message Yourself chat. The bridge only forwards
messages from your own self-chat and ignores groups, other chats, and other
people.
The `whatsapp-bridge` container stores Baileys auth state in the
`surfsense-whatsapp-sessions` Docker volume. That volume contains account
takeover material. Treat it like a secret.
To intentionally reset pairing:
```bash
docker compose --profile whatsapp down
docker volume rm surfsense-whatsapp-sessions
docker compose --profile whatsapp up -d
```

View file

@ -9,6 +9,7 @@
"installation",
"manual-installation",
"docker-installation",
"messaging-channels",
"connectors",
"how-to",
"---Developers---",

View file

@ -7,6 +7,7 @@ import {
Download,
FlaskConical,
Heart,
MessageCircle,
Radar,
Unplug,
Wrench,
@ -27,6 +28,7 @@ const DOCS_ICONS: Record<string, React.ComponentType> = {
Download,
FlaskConical,
Heart,
MessageCircle,
Radar,
Unplug,
Wrench,