Blog
Technical Deep Dives

The connector adapter contract: channels and limits

How Simmetric Chat routes external channels (Telegram, Discord, Slack, WhatsApp) through one adapter contract into the same grounded RAG pipeline - with rate and session limits protecting the workspace.

Simmetric Team7 min read10/10/2026
The connector adapter contract: channels and limits

The channels where your customers already are

An AI workspace is most useful in the place the conversation already happens. For many organizations that place is not a web UI you deploy; it is the messaging channels people open without thinking - WhatsApp groups, Telegram chats, Discord servers, Slack workspaces. Simmetric Chat treats those channels as first-class surfaces of the same knowledge base: a connector binds a channel to a workspace and to one document archive, and every question arriving on that channel is answered by the same retrieval pipeline the internal chat uses.

The word in the title - the adapter contract - names the design rule that makes this safe. Each platform speaks a different protocol with different credential semantics, so each runs behind a dedicated adapter; everything the adapter shares is a fixed vocabulary of fields and limits. The result reads like a security policy: one connector per channel, one binding per connector, one corpus per binding.

This article walks through the contract from the operator's side: what you configure when creating a connector, which v0.28-era surfaces changed (the edit arm), how the limits protect the workspace from external traffic, and what happens when the corpus has no citable answer.

Enterprise edition only: connectors_enabled is false in the Community license, the adapter registry is empty there and the admin panel shows the upgrade prompt instead of the create form. Facts per FACT-CENSUS.md F-CONN-1.

What you configure at creation

The create form lives at Settings → Conversations → Connectors and asks for two kinds of fields. The binding fields name the integration: a connector name that identifies the instance, the platform (one of telegram, discord, slack, whatsapp), the channel's secret token, and the workspace it serves. The behavior fields shape the conversation: the knowledge archive binding that determines the corpus, the welcome message the channel sends to an incoming conversation, the fallback message and its fallbackLocale used when search produces no citable sources, and the two limits - a rate limit and a daily session limit, where 0 means unlimited.

Three of those fields are immutable after creation, and the immutability is not an implementation detail - it is the trust model. The platform and the secret token define who can talk to the corpus; the workspace defines what they can reach. If either changes, the identity of the integration changes, and the contract answers that with a new connector rather than a mutation. The v0.28.1 schema carries those rules literally: welcome and fallback messages cap at 4000 characters, the platform and token are create-only, and the PUT endpoint never touches them.

Credentials stay server-side by design. The app calls the channel's APIs with the credentials the administrator supplied; the secret token is never reflected in the UI after creation, and the connector's routing lives inside your own infrastructure - the channel talks to your server, your server talks to its own knowledge base.

The adapter layer

Each platform runs a dedicated adapter, and each adapter's registration matches what the platform actually requires:

  • Telegram - a bot token from @BotFather, with both polling and webhook modes supported (the card carries dedicated mode buttons).
  • Discord - a bot token from the Developer Portal plus message intents; the adapter reads the channels the bot is authorized for.
  • Slack - a bot token and the signing secret of the Slack app; message events arrive through the configured webhook.
  • WhatsApp - the provider's Business API secret token; the adapter calls the provider's API.

The adapter layer is where per-platform differences die. Webhook verification, intent scoping, polling loops, message-shape normalization - each lives behind its platform's adapter, and the rest of the product sees one uniform connector record. The adapter registry itself is gated: in Community it is empty by construction, so a Community instance fails closed before any external traffic reaches the workspace.

Editing: the v0.28.1 mutability surface

Before v0.28.1, adjusting a connector meant deleting and recreating it. The v0.28.1 change adds an edit arm: the connector card's overflow menu carries an Edit item that opens a pre-filled dialog over exactly the mutable surface - via PUT /api/connectors/:id:

  • Mutable - the name, the archive binding, the welcome and fallback messages, the fallback locale, and both limits (empty = null; 0 = unlimited).
  • Immutable - the platform, the bot token, the workspace.

The isEnabled toggle stays on the card (it suspends routing without deleting the configuration), and the mutation is audited like every other admin action: the endpoint logs the chat_connector.updated event, at parity with the DELETE arm's chat_connector.deleted. The edit arm matters for the operator story in one specific way: rebinding a channel to a freshly rebuilt archive, or tightening a limit after an incident, no longer costs the connector's identity.

The reply path and the fallback

Every question arriving from a channel goes through the same pipeline as an internal chat: hybrid search over the bound archive, source citation, and synthesis - then the cited answer is sent back on the originating channel. The archive binding is the contract's quiet core: it is what makes an external conversation grounded.

When the search produces no valid citable elements, the contract's answer is the fallback message - a configured reply in the configured fallbackLocale. An external channel never receives an ungrounded generation when the fallback is in place; the visitor reads the fallback instead of a confident guess with nothing behind it.

Limits as the workspace's airlock

The rate limit and the daily session limit are the contract's protection against external traffic:

  • 0 = unlimited (the router's sentinel value).
  • N = the per-connector maximum for requests, and for sessions per day.

Together with isEnabled, they let an operator expose the knowledge base to a public channel while keeping the blast radius bounded: a runaway bot, a scraped channel, or an unexpected audience consumes at most the configured share of the workspace. Disable, retighten, or rebind - each is now a single edit (the v0.28.1 arm), no recreation.

Reading on

The guide carries the full operator walkthrough - the create form field by field, the per-platform adapter table, and the audit behavior - at /en/docs/connectors:

Provenance: Simmetric Chat packages connector.schema.ts (field shapes + limits), routes/connectors.ts (the router + limiter), CHANGELOG.md v0.28.1, license.ts (connectors_enabled) at the census's pinned docs state v0.28.1 (SHA aca5b8ff).

Related articles