Skip to main content
The gateway (unify/gateway/) is a single FastAPI application assembled by create_app() in app.py and launched with python -m unify.gateway serve (the CLI in __main__.py also provides doctor, urls, setup, smoke, and wizard subcommands). It runs as a separate process from the ConversationManager.

Channels vs adapters

The two sub-packages split along an ingress/egress line — but precisely:
  • channels/ hosts the outbound and admin APIs: sending messages, placing calls, provisioning numbers, managing watches and installs. Nearly all routes require the admin bearer token (admin_auth_dependency in common/auth.py). A few unauthenticated routes live here too where the provider calls back mid-flow (Twilio’s TwiML and conference-status callbacks).
  • adapters/ hosts inbound normalization: provider webhooks and internal Console routes, each validated (Twilio signatures, Slack signing secrets, OAuth state), resolved to a target assistant, and reduced to a {thread, event} envelope.
Two deliberate exceptions: Discord inbound uses a persistent WebSocket (GatewayConnection in channels/discord/gateway.py, supervised by bot_manager.py) rather than a webhook — DMs always route, guild messages only on @mention. And social verification (channels/social/views.py) is a synchronous Twilio send that returns the code to the caller — it never publishes an envelope.

Channel modules

Each channel is a sub-package under channels/ with a views.py exposing its router(s):

Adapter modules

The shared inbound pipeline lives in adapters/common.py: resolve the assistant (get_assistant), resolve the channel route where pools are involved (resolve_phone_route, resolve_whatsapp_route — this is how shared numbers map (pool number, sender) to the right assistant), activate the runtime, then publish_runtime_event().

Credentials

unify/gateway/credentials/ holds operator infrastructure credentials — Twilio account, Slack signing secret, OAuth client IDs, LiveKit keys — behind the CredentialStore protocol (base.py), with EnvCredentialStore (env.py) as the OSS implementation reading environment variables. The package docstring draws the line explicitly: this is distinct from unify.secret_manager, which holds assistant-owned user secrets (per-assistant OAuth tokens like GOOGLE_ACCESS_TOKEN live in Orchestra assistant secrets, not here).

Adding a channel: the seams

The gateway is designed so a new channel touches a predictable set of files:
  1. Outbound — a new sub-package under channels/ with an admin-authenticated router; mount it via the prefix table in create_app().
  2. Inbound — a webhook handler under adapters/ that validates, resolves the assistant, and publishes an envelope with a new thread key (register it in KNOWN_THREADS in envelopes.py).
  3. Runtime — a typed event class in events.py, an entry in CommsManager.events_map, and an @EventHandler.register handler — covered in the conversation runtime.
  4. Send path — a method on CommsPrimitives plus its brain-tool wrapper.