Channels
A Channel is where the agent communicates with users. Agent One is channel-agnostic — the same logic runs regardless of whether you’re chatting on WhatsApp, Telegram, or the terminal.
Channel Interface
Section titled “Channel Interface”type Channel interface { ID() string Send(ctx context.Context, to string, msg Message) error Listen(ctx context.Context, events chan<- Event) error}Every channel can both send and receive messages. The Listen method pushes incoming messages as events into the shared queue (where they become Message signals).
Supported Channels
Section titled “Supported Channels”Local terminal input/output. Always available, used for development and testing.
channels: cli: enabled: trueGo-native WhatsApp Web integration via whatsmeow. No Node.js dependency — pure Go.
channels: whatsapp: enabled: true device_db: "./data/whatsapp.db" allowed_numbers: [] # empty = allow allPairing happens via QR code in the TUI setup wizard. The device session persists in device_db.
Telegram
Section titled “Telegram”Standard Telegram Bot API. Create a bot via BotFather and provide the token.
channels: telegram: enabled: true bot_token: "${TELEGRAM_BOT_TOKEN}"Supports webhook-first mode for production, with polling fallback for development.
ClickUp
Section titled “ClickUp”Listens to and responds via ClickUp comments and chat.
channels: clickup: enabled: true api_key: "${CLICKUP_API_KEY}" listen_spaces: ["my-workspace"]Channel Resolution
Section titled “Channel Resolution”The agent uses different channels depending on the signal type:
| Signal Type | Response Goes To |
|---|---|
| Message | The channel it came from |
| Cron | channels.default |
| Heartbeat | channels.default |
| Webhook | channels.default |
channels: default: whatsapp # Proactive signals go hereAdding a New Channel
Section titled “Adding a New Channel”Channels use the registry pattern. Adding a new channel requires:
- Implement the interface — create a package under
internal/channel/:
package slack
type Channel struct { /* config, client */ }
func (c *Channel) ID() string { return "slack" }
func (c *Channel) Send(ctx context.Context, to string, msg signal.Message) error { // Send message via Slack API}
func (c *Channel) Listen(ctx context.Context, events chan<- signal.Event) error { // Listen for incoming Slack messages, push to events}-
Register it — add to the channel registry during startup. No switch statements — the registry handles lookup.
-
Add config — add the channel’s config fields under
channels:in config.yaml.
The agent loop doesn’t know or care which channels exist. It dequeues events from the shared queue and sends responses via whichever channel the event came from (or channels.default for proactive signals).
Cross-Device Identity
Section titled “Cross-Device Identity”Agent One supports unified identity across channels. The channel_identities table maps (channel, external_id) to a single user_id. This means the same person chatting on WhatsApp and Telegram sees one continuous conversation with shared memory and persona.
Identity linking methods:
- OAuth auto-link — web chat sessions automatically link when authenticated
- Code-based — send
/link 123456from a new channel to associate it - Phone match — WhatsApp numbers can auto-match with existing profiles