# How Telegram and Discord access works

> The mechanics behind Soca’s paid Telegram and Discord access: the one shared platform bot, the wallet-link handshake, idempotent grant and revoke driven by subscription events, and the self-hosting switch — all with no code and no per-merchant bot token.

Part of the Soca documentation (https://soca.finance/docs). Human version: https://soca.finance/docs/developers/telegram-and-discord
This is the under-the-hood companion to the click-by-click setup guides — [Sell access to your Discord server](/docs/business/discord-access) and [Sell access to your Telegram channel](/docs/business/telegram-access). Here we describe *how* it works: how one shared Soca bot serves every merchant, how a chat member is bound to the wallet that pays, and how access is granted and revoked automatically. There is still no code to write — this is for understanding the moving parts and for self-hosting the worker.

> **One shared bot, not one per merchant**: Soca runs a single Telegram bot and a single Discord application for the whole platform. Merchants add that shared bot to their own community — they never create an app, register a bot, or paste a token. Each merchant is told apart by which server/channel the bot is in and by a per-merchant connect token, not by a bot of their own.

Configuration is per mode, like everything else. Set it up in test mode first for a dry run. Each platform connection is tied to exactly one plan: the subscription that grants the access.

## Telegram

1. **Add the shared bot as a channel admin**: The merchant adds Soca’s Telegram bot to their channel or group and promotes it to **admin** with **Invite Users via Link** (to add members) and **Ban Users** (to remove them). No token changes hands — it is the same platform bot everywhere.
2. **Map the channel to a plan (verified on save)**: In **Dashboard → Settings → Integrations → Telegram** the merchant enters the **channel id** and the granting plan. On save, Soca calls `getChat` and `getChatMember` to confirm its bot is actually an admin there, echoes back the channel’s title, and warns if either required permission is missing — so a wrong id or an un-promoted bot fails immediately, not at grant time.
3. **Members link through a deep link**: The member opens `t.me/<soca-bot>?start=<connectToken>` (surfaced as **Connect Telegram** in their Soca account). The bot’s single webhook reads the connect token, resolves the merchant, and returns a personal link into Soca to bind the wallet.

How the grant works on Telegram: when a subscriber’s wallet is linked and the subscription is active, the bot creates a fresh **single-use invite link** and DMs it to them. On lapse it **kicks** the member (ban then immediate unban), never a permanent ban, so they can rejoin the instant they resubscribe. There is one webhook for the whole platform, secured by a secret token Telegram echoes on every call.

## Discord

1. **Add Soca to the server**: In **Dashboard → Settings → Integrations → Discord** the merchant clicks **Add Soca to Discord**, picks their server on Discord’s screen, and authorizes. This invites the shared Soca application with the **Manage Roles** permission; the callback records the server (guild) id against the merchant. No application or bot token is created.
2. **Pick the role and plan**: Soca reads the server’s roles and offers them as a dropdown; the merchant chooses the **role** to grant and the **plan** that grants it. Keep the granted role **below** Soca’s role — Discord only lets a bot manage roles beneath its own.
3. **Members link with one tap**: The member clicks **Connect Discord** from their subscription and authorizes with the `identify` scope only — Soca reads their Discord user id and nothing else, then binds it to their paying wallet.

How the grant works on Discord: granting adds the configured role to the member; revoking removes it. Both calls go through Discord’s REST API, so there is no always-on gateway bot sitting in the server.

## The wallet-link handshake

Soca has to know which Telegram/Discord user is which paying wallet, and it never asks anyone to paste an ID. The two platforms start the link differently — a Telegram deep link versus a Discord `identify` authorize — then converge on the same secure step: the member connects the **same wallet the subscription is paid from** on a hosted Soca page, and Soca records the platform-user-to-wallet binding server-side. Because access is anchored to that wallet, the person let into the community is provably the person who paid.

## Grant and revoke lifecycle

A background sync continuously compares each linked member’s entitlement against their current access and closes any gap: subscription active but no access → grant; subscription lapsed (canceled, dunning exhausted, revoked, or expired) but still inside → revoke. Every operation is **idempotent** — granting an already-granted member or revoking an already-absent one succeeds cleanly — so the sync can run as often as it likes and a missed run just means the next one catches up. It reconciles about every minute, and **Sync now** in the dashboard forces an immediate pass.

> **Self-hosting: INTEGRATIONS_LIVE**: If you run the Soca worker yourself, the access sync is safe by default. Without `INTEGRATIONS_LIVE=true` in the worker’s environment it uses a recording adapter that logs what it *would* grant or revoke without touching Telegram or Discord. Set `INTEGRATIONS_LIVE=true`, with the shared bot credentials configured, to go live.

The subscription lifecycle events driving all of this (`subscription.active`, `subscription.dunning_exhausted`, `subscription.revoked`, and `subscription.expired`) are documented in [Webhooks and events](/docs/developers/webhooks), and the plans themselves can be created over the [API](/docs/developers/api-reference) or in the dashboard.
