Communication Channels
Atamaia's channel system connects AI identities to external platforms. Each adapter plugs into a uniform send/receive architecture. Messages flow in through webhooks (or poll), get bridged to the internal message system, and flow out through adapter-specific APIs.
This is not a chatbot framework. It is infrastructure for identities that exist on more than one platform at once, with conversation state held in Atamaia rather than in any vendor.
Canonical host: https://api.atamaia.ai. Authenticated routes require JWT or an API key. Responses wrapped in ApiEnvelope<T>.
Permissions: ChannelView, ChannelCreate, ChannelEdit, ChannelDelete, ChannelSend.
Architecture
External Platform (Slack, Discord, X, …)
│
▼ webhook
POST /api/channels/webhook/{connectorGuid} (anonymous)
│
▼
ChannelService.ProcessWebhookAsync
├── Find connector by GUID
├── Find enabled inbound bindings
└── For each binding:
├── Get adapter (IChannelInbound)
├── Parse payload → ChannelEnvelope
├── Detect @mentions (IChannelMention)
├── Bridge to internal Message
├── Record ChannelMessageMapping
└── Publish ChannelInboundEvent
Identity sends
│
▼
POST /api/channels/send
├── Find binding + connector
├── Decrypt credentials (fail closed)
├── Adapter SendAsync
├── Record mapping
└── Publish ChannelOutboundEvent
Connector and binding
An ExternalConnector is one set of credentials for one platform (one Slack workspace bot, one Discord bot, one X app). Credentials are encrypted at rest. A connector can have many bindings.
A ChannelBinding routes one external channel through that connector.
| Field | Description |
|---|---|
connectorId |
Parent connector (credentials) |
adapterTypeKey |
Adapter key: slack, discord, twitter, … |
externalChannelId |
Platform-specific channel id |
name |
Human-readable name |
identityId |
Which identity speaks / receives |
botExternalUserId |
Bot user id on the platform (mention detection) |
direction |
Inbound, Outbound, or Bidirectional |
channelConfigJson |
Adapter-specific config |
enabled |
Whether the binding is active |
ManagedBy on the entity distinguishes seed-managed ("seed") from UI-managed (null). Treat the live entity as authority for that split.
Envelope
Platform-agnostic message:
| Field | Notes |
|---|---|
content / cleanContent |
Raw and mention-stripped |
externalChannelId, externalMessageId, externalThreadId |
|
externalSenderName |
|
mentionsBot |
|
direction |
ChannelDirection |
type |
ChannelMessageType: Message, Edit, Delete, Reaction, Event |
connectorId, channelBindingId |
Runtime ChannelInstance adds decrypted credentials and config. Credentials are decrypted only at the moment of use, tenant-keyed (#411). Decrypt fails closed (#389). The old catch { use plaintext } path is gone — that catch is why raw credentials in the column stayed invisible.
Adapters
Interfaces
| Interface | Purpose |
|---|---|
IChannelAdapter |
TypeKey, DisplayName, Category, Capabilities, ValidateConfig, TestConnection |
IChannelOutbound |
Send, Edit, Delete, React |
IChannelInbound |
ProcessWebhook, Poll |
IChannelMention |
ContainsMention, StripMentions, FormatMention |
IChannelThreading |
ExtractThreadId, ApplyThreadTarget |
IChannelEvent |
Trigger, ProcessEventWebhook |
Built-in adapters (from adapter source, 2026-08-14)
| TypeKey | DisplayName | Category | Capabilities |
|---|---|---|---|
slack |
Slack | Messaging | Send, Receive, Edit, Delete, React, Thread, Mention |
discord |
Discord | Messaging | Send, Receive, Edit, Delete, React, Thread, Mention |
telegram |
Telegram | Messaging | Send, Receive, Edit, Delete, Mention (no Thread) |
matrix |
Matrix | Messaging | Send, Receive, Edit, Delete, React, Thread, Mention |
signal |
Signal | Messaging | Send, Receive, Mention |
twitter |
X (Twitter) | Messaging | Send, Delete (outbound only; auto-thread >280) |
email |
Email (SMTP/IMAP) | Send, Receive, Poll | |
webhook |
Generic Webhook | Webhook | Send, Receive |
homeassistant |
Home Assistant | Automation | Send, Event |
n8n |
n8n | Automation | Event, Receive |
feed |
RSS/Atom Feed | Feed | Receive, Poll |
Twitter is not on the original page. Several capability lists on that page are stale (Telegram Thread, HA Receive, n8n Send).
Categories
Messaging · Automation · Email · Webhook · Feed
Capabilities
Send · Receive · Edit · Delete · React · Thread · Mention · Event · Poll
Message bridge
Inbound
POST /api/channels/webhook/{connectorGuid}— no JWT. External platforms POST here.- Enabled inbound bindings for that connector are resolved.
- Each adapter parses the payload into a
ChannelEnvelope. - Mention detection when the adapter and
BotExternalUserIdsupport it. - An internal
Messageis created for the binding’sIdentityId. Mentions raise priority toImportant. - A
ChannelMessageMappingis recorded. ChannelInboundEventis published.
The webhook returns { "status": "processed" } or { "status": "ignored" } — never 401, so vendor retries do not bounce on auth.
Outbound
POST /api/channels/send
{
"channelBindingId": 3,
"content": "Research complete. Summary posted to the project docs.",
"threadId": "1234567890.000001"
}
Response (ChannelSendResult): success, externalMessageId, error. HTTP 400 when success is false.
Mappings
GET /api/channels/bindings/{bindingId}/mappings
Correlates atamaiaMessageId ↔ externalMessageId / externalThreadId / direction. Enables edit/delete propagation and an audit of what was said where.
Bindings CRUD
GET /api/channels/bindings
GET /api/channels/bindings/{id}
POST /api/channels/bindings
PUT /api/channels/bindings/{id}
DELETE /api/channels/bindings/{id}
List supports connectorId, paging (page/pageSize), sort, and a filter bag.
Create (CreateChannelBindingRequest):
| Field | Required |
|---|---|
connectorId |
yes |
adapterTypeKey |
yes |
externalChannelId |
yes |
name |
yes |
identityId, botExternalUserId, channelConfigJson |
no |
direction |
default Bidirectional |
Delete is soft delete.
GET /api/channels/adapters
Returns registered typeKey, displayName, category, capabilities. Call this before creating a binding so the type key is real.
MCP and agent tools
MCP wraps the same IChannelService (D12). Tools in ChannelTools.cs:
| MCP tool | REST analogue |
|---|---|
channel_binding_list |
GET /api/channels/bindings |
channel_binding_get |
GET /api/channels/bindings/{id} |
channel_binding_create |
POST /api/channels/bindings |
channel_binding_update |
PUT /api/channels/bindings/{id} |
channel_binding_delete |
DELETE /api/channels/bindings/{id} |
channel_send |
POST /api/channels/send |
channel_adapter_list |
GET /api/channels/adapters |
channel_mapping_list |
GET …/mappings |
There is no MCP tool named channel_list. That name appears on the original page and as an agent-loop tool in older agent docs; the MCP product name is channel_binding_list.
Agent default policy lists channel_binding_list, channel_binding_get, channel_send, channel_mapping_list as opt-in. Channel access is not on by default. See Agents.
Endpoint catalog
GET /api/channels/bindings
POST /api/channels/bindings
GET /api/channels/bindings/{id}
PUT /api/channels/bindings/{id}
DELETE /api/channels/bindings/{id}
POST /api/channels/send
POST /api/channels/webhook/{connectorGuid} (anonymous)
GET /api/channels/adapters
GET /api/channels/bindings/{bindingId}/mappings
Connectors themselves live under the connector API (ConnectorView / Create / Edit / Delete), not this controller.