Skip to main content
Everything in the Communication section is implemented in the open-source unifyai/unify repository. This developer sub-section explains how it actually works — the packages, classes, and contracts — for anyone reading, extending, or self-hosting the code. The communication stack is two cooperating pieces:
  1. The gatewayunify/gateway/ — a FastAPI application that is the HTTP edge between external providers (Twilio, Google, Microsoft, Slack, Discord, the Console) and the assistant runtime. It owns webhook parsing, route resolution, OAuth callbacks, and outbound provider calls.
  2. The conversation runtimeunify/conversation_manager/ — the ConversationManager process that consumes normalized events, runs the reasoning loop (the “slow brain”), and sends outbound messages back through the gateway via unify/comms/primitives.py.
unify.gateway architecture: providers feed adapters, which normalize to envelopes delivered through an EnvelopeSink to the ConversationManager; the ConversationManager sends outbound via channels

The wire contract: envelopes

Every inbound event that crosses the gateway → runtime boundary is wrapped in a {thread, publish_timestamp, event} envelope, defined in unify/gateway/envelopes.py (the full catalogue of thread keys lives in KNOWN_THREADS there). The thread is a routing key like msg, whatsapp, email, unify_meet, or call_answered; the event is a channel-specific payload. This single contract is what lets the same runtime code serve every channel.

One codebase, two deployments

A design goal stated in the gateway README: local self-hosted and hosted SaaS deployments run the same gateway code — only the backend implementations differ. The seams are small protocol interfaces carried on a GatewayContext (unify/gateway/context.py):
ProtocolLocal defaultHosted implementation
EnvelopeSinkHttpEnvelopeSink — POSTs envelopes to the runtime’s local ingressPub/Sub publish to a per-assistant topic
RuntimeActivatorLocalRuntimeActivator (no-op; the runtime is already running)HostedRuntimeActivator — wakes an idle assistant job
StorageLocalDiskStorage for attachmentsCloud storage
CredentialStoreEnvCredentialStore — reads environment variablesCloud secret backend
The app factory is create_app() in unify/gateway/app.py. The OSS path runs the stock app (python -m unify.gateway serve); the hosted platform constructs its own app via create_app(extra_routers=..., extra_setup_hooks=...) and mounts private pieces on top of the same factory. Envelope sinks live in unify/gateway/envelope_sink.py, and ingress transports (the runtime’s receiving side) in unify/gateway/ingress.py and siblings (ingress_pubsub.py, ingress_inmemory.py).

The full round trip

Following one SMS end to end:
  1. Twilio → adapter. Twilio POSTs to twilio_sms_webhook in unify/gateway/adapters/twilio.py. The signature is validated, the target assistant is resolved (via Orchestra’s admin API), and the runtime is woken if idle.
  2. Adapter → envelope. publish_runtime_event() in unify/gateway/adapters/common.py wraps the payload in an envelope with thread="msg" and hands it to the EnvelopeSink.
  3. Envelope → typed event. In the runtime, CommsManager (unify/conversation_manager/comms_manager.py) maps the thread key through its events_map to a typed event — SMSReceived from unify/conversation_manager/events.py — resolving the sender to a contact along the way.
  4. Event → handler → brain. The event is published on the in-process broker; ConversationManager.wait_for_events() picks it up, the registered handler logs it to transcripts and updates live state, and a slow-brain turn is scheduled. Details in The conversation runtime.
  5. Brain → outbound. If the brain decides to reply, its send_sms tool delegates to CommsPrimitives.send_sms, which enforces the contact’s should_respond gate and POSTs to the gateway’s /phone/send-text channel endpoint — which calls Twilio. A matching SMSSent event is published so the reply lands in transcripts too.

Package map

PackageRoleKey entry points
unify/gateway/adapters/Inbound webhook normalizationtwilio.py, google.py, microsoft.py, slack.py, internal.py
unify/gateway/channels/Outbound + admin channel APIsone sub-package per channel
unify/gateway/credentials/Operator infrastructure credentialsCredentialStore, EnvCredentialStore
unify/conversation_manager/The live runtimeConversationManager, events.py, domains/
unify/comms/Outbound send implementationCommsPrimitives
unify/transcript_manager/Durable conversation recordTranscriptManager

Reading order

The gateway

Channels vs adapters, per-channel modules, credentials, and how to add a channel.

The conversation runtime

Events, mediums, the slow-brain loop, outbound sends, and transcripts.

Voice calls

The dual-brain call architecture — LiveKit, fillers, barge-in, and hang-up semantics.