channel docs

channel / overview

Every channel. One thread.

Channel is the communication domain of Ekairos: one canonical message model across web, email, whatsapp, slack, teams, discord and telegram, persisted on InstantDB and linked to the agent's context. Your product renders one timeline; the customer answers wherever they live.

The problem it removes

Every product that talks to people ends up rebuilding the same machinery: a webhook per platform, a message table per provider, glue code to show "the conversation" in one place, and a fragile mapping between what the agent said and what was actually delivered. That machinery is undifferentiated — and it keeps breaking as platforms change.

Channel turns all of it into domain state. Inbound and outbound messages from every platform land as channel_messages records on the agent thread's context. Subscriptions, delivery locks and queues persist in the same domain. There is no message API to build and no sync job to babysit: UI reads InstantDB reactively, and the agent reacts to inbound messages like it reacts to anything else.

How it fits the Ekairos domains

domain composition
agent_threads ──one── event_contexts ──many── event_items
                                   │                  │
                                   └─many─ channel_messages ─one┘
                                          (web · email · whatsapp · slack · ...)

The agent domain owns the thread — a thin indirection over a context. The events domain owns the durable conversation state. Channel adds the communication surface: every message that crosses a platform boundary is recorded canonically and linked to that same context, so useThread returns the whole multichannel conversation with one reactive query.

What you write vs what you get

An application owns exactly two pieces of custom code:

the entire integration surface
1. A webhook mount    →  app/api/channels/[platform]/route.ts
2. A send endpoint    →  app/api/channels/send/route.ts

Everything else is provided: platform delivery, canonical persistence, runtime state, reactive queries, and plug & play UI components that work the moment the schema is pushed — because they query channel_messages directly. That is the magic: the components depend on a schema, not on your backend.

Under the hood

Platform delivery is built on Vercel's Chat SDK — wrapped and contained as an internal of the channel package, including its adapters. You configure platforms, never the underlying SDK; its runtime state (subscriptions, locks, queues) is persisted on InstantDB through channel domain entities. Containment is the point: upstream updates are routine dependency bumps that never touch your code.

Start here

database…