Channels & Omnichannel

The omnichannel layer (core/omnichannel) lets a tenant's agents converse with end-users over ten messaging and voice channel types. All channels share one inbound pipeline — pairing, session resolution, agent routing, reply delivery — so adding a channel is an adapter, not a new subsystem (ChannelType javadoc documents this "add a case" design).

flowchart LR subgraph Channels WA["WhatsApp"] & TG["Telegram"] & EM["Email"] & VO["Voice"] & OT["Messenger · Instagram · Slack · Discord · X · Teams"] end Channels --> WH["Channel webhook / poller / gateway"] WH --> DD["Inbound dedup
(InboundMessageDedupService)"] DD --> SESS["Session resolve + pairing gate
(ChannelSessionService)"] SESS --> ROUTE["Default-agent routing
(ChannelDefaultAgent)"] ROUTE --> AGT["Agent chat turn"] AGT --> OUT["Channel reply (adapter send)"] AGT -.handoff.-> OP["Operator inbox
(human take-over)"]
Every channel funnels into the same session → route → agent → reply pipeline.

FR-CH-01 · Channel type catalogue

ChannelTypeTransport / onboardingNotes
WHATSAPP Two official onboarding paths: Meta Cloud API (including Embedded Signup) and the Twilio path (TwilioWhatsAppChannelAdapter). The flagship channel; subject to the whatsapp_allowed plan gate.
MESSENGER Meta webhook + page credentials. First channel added after WhatsApp; proved the channel-agnostic pipeline.
INSTAGRAM Meta webhook + credentials. Same machinery as Messenger.
SLACK Slack app credentials; events webhook. Workspace-scoped sessions.
DISCORD Via the dedicated gateway service (Discord requires a persistent gateway connection, so it lives outside core/app). See Service Topology.
X X/Twitter API credentials. Mentions/DM ingress.
TELEGRAM Bot token; webhook (TelegramWebhookController) + TelegramApiClient sends. Supports workflow triggers and agent events on the same webhook.
EMAIL IMAP inbound polling (EmailInboundPollingJob) + SMTP outbound. Tenant supplies mailbox credentials.
TEAMS Tenant's own Microsoft Bot Framework (Azure Bot) registration; JWT-signed activities at /webhook/teams/{appId} (migration V240). Replies via Bot Framework connector; per-conversation service URLs stored in teams_conversation_route.
VOICE Two paths: LiveKit browser calls (in-app voice) and Twilio voice relay (TwilioVoiceWebhookController + VoiceRelayWebSocketHandler). Voice replies are spoken live over the WebSocket bridge — they bypass the async approval queue by design (a live call cannot wait mid-conversation). external_id is the caller's number so memory persists call to call.

FR-CH-02 · Contacts, groups & routing

Inbound identities resolve to contacts; contacts can be grouped and routed to different default agents per channel or per account (ChannelDefaultAgent). A channel session (ChannelSession) keys on external_id — the sender's channel-native identity — so a returning WhatsApp sender or voice caller resumes their conversation and memory.

FR-CH-03 · Pairing inbox

First-time senders land in the pending-pairings inbox (ChannelPairingController) — a pairing gate that holds a new contact until a tenant member approves the pairing. This is the anti-abuse boundary between "a stranger messaged the number" and "an agent consumes LLM budget replying to them".

FR-CH-04 · Operator human-handoff inbox

OperatorInboxService provides human handoff: a conversation can be flagged to a human operator, who replies through the same channel adapter while the agent stands down. Handoff works across all channel types uniformly.

FR-CH-05 · Universal disconnect

Any channel connection can be severed from one disconnect surface — credentials are revoked, webhooks/pollers stand down, and sessions on that account stop routing. One operation regardless of adapter.

FR-CH-06 · Health checks & credentials

RequirementDescriptionStatus
FR-CH-06.1Per-channel health checks report connection state (ChannelStatusController) — credentials valid, webhook live, poller running.live
FR-CH-06.2Credentials are per-channel and per-account, stored encrypted (see Secrets Management); each channel family has its own credential service (e.g. MetaWhatsAppCredentialService, TelegramCredentialService).live
FR-CH-06.3Inbound deduplication (InboundMessageDedupService) drops retried/duplicated webhook deliveries so one user message never produces two agent turns.live

FR-CH-07 · Plan gates

Plan gate: whatsapp_allowed

WhatsApp connectivity is plan-gated by the whatsapp_allowed flag in control_plane.plan_limits (FREE access was tuned by migration V225__free_plan_whatsapp.sql — check current rows for exact tier behaviour). Other channels are not individually gated. Plan gates apply only when billing is on (see Functional Overview).

Cross-references