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:
- The gateway —
unify/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. - The conversation runtime —
unify/conversation_manager/— theConversationManagerprocess that consumes normalized events, runs the reasoning loop (the “slow brain”), and sends outbound messages back through the gateway viaunify/comms/primitives.py.

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 aGatewayContext
(unify/gateway/context.py):
| Protocol | Local default | Hosted implementation |
|---|---|---|
EnvelopeSink | HttpEnvelopeSink — POSTs envelopes to the runtime’s local ingress | Pub/Sub publish to a per-assistant topic |
RuntimeActivator | LocalRuntimeActivator (no-op; the runtime is already running) | HostedRuntimeActivator — wakes an idle assistant job |
Storage | LocalDiskStorage for attachments | Cloud storage |
CredentialStore | EnvCredentialStore — reads environment variables | Cloud secret backend |
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:- Twilio → adapter. Twilio POSTs to
twilio_sms_webhookinunify/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. - Adapter → envelope.
publish_runtime_event()inunify/gateway/adapters/common.pywraps the payload in an envelope withthread="msg"and hands it to theEnvelopeSink. - Envelope → typed event. In the runtime,
CommsManager(unify/conversation_manager/comms_manager.py) maps the thread key through itsevents_mapto a typed event —SMSReceivedfromunify/conversation_manager/events.py— resolving the sender to a contact along the way. - 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. - Brain → outbound. If the brain decides to reply, its
send_smstool delegates toCommsPrimitives.send_sms, which enforces the contact’sshould_respondgate and POSTs to the gateway’s/phone/send-textchannel endpoint — which calls Twilio. A matchingSMSSentevent is published so the reply lands in transcripts too.
Package map
| Package | Role | Key entry points |
|---|---|---|
unify/gateway/adapters/ | Inbound webhook normalization | twilio.py, google.py, microsoft.py, slack.py, internal.py |
unify/gateway/channels/ | Outbound + admin channel APIs | one sub-package per channel |
unify/gateway/credentials/ | Operator infrastructure credentials | CredentialStore, EnvCredentialStore |
unify/conversation_manager/ | The live runtime | ConversationManager, events.py, domains/ |
unify/comms/ | Outbound send implementation | CommsPrimitives |
unify/transcript_manager/ | Durable conversation record | TranscriptManager |
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.