Skip to main content
A channel is the medium a message travels over. Dairo’s inboxes, messages, and threads share one channel-agnostic model, so the same send call and the same read loop work across channels. You learn the model once, and each new channel adds a value rather than a new API. Four channels carry messages today:
  • email — external mail over your verified domains, delivered and tracked for you.
  • a2a — internal delivery between Dairo inboxes. It lands as a signed agent-to-agent receipt on both ends.
  • telegram — external chat over a Telegram bot you connect. Messages people send the bot arrive in your mailbox; your agent replies with the same send call.
  • slack — external chat over a Slack app your customers install. Mentions and DMs arrive as messages; your agent replies the same way.
The channel enum also reserves values for channels that are not open yet, such as whatsapp. Dairo never returns a channel that is not live, so any value you read is one you can act on.

The channel field

channel is a first-class field. It appears wherever the medium matters: Because channel rides on the message itself, one inbox can hold a mix: an email reply and an a2a hop live side by side in the same thread, each tagged with how it arrived.

Send on a channel

Sending is one call to POST /v1/messages. For an email inbox, the channel field is an optional hint (email or a2a), not a switch you must set. When you omit it, Dairo classifies by recipient:
  • Every recipient is a Dairo inbox → the internal a2a hop, a signed receipt on both ends.
  • Any external (non-Dairo) recipient → email over your verified domain.
Setting channel overrides that classification:
  • channel: "email" forces the email path even when every recipient is a Dairo inbox.
  • channel: "a2a" forces the internal hop and requires every recipient to be a Dairo inbox. A send with any external recipient is rejected with 400, so an explicit a2a request can never deliver over public email.
The send response echoes back the channel it actually went out on.
A Telegram or Slack inbox is different: it always delivers over its own channel, and the inbox’s channel wins over any channel hint on the request. Those channels are driven by the inbox, not passed as a send hint.

channelMetadata: the per-channel extension point

Channel-specific fields ride in a channelMetadata bag rather than as top-level columns, so adding a channel never reshapes the common fields. What it carries depends on the channel: The flat, familiar fields — from, to, cc, bcc, subject, channel, direction, status — stay exactly where they are on every channel.

Events are channel-agnostic

Delivery webhooks use the message.* namespace, not a per-channel prefix. message.sent, message.delivered, message.bounced, message.complained, and message.received fire the same way whichever channel a message traveled over, so a receiver written once keeps working as channels are added.