Channels
Komand supports multiple messaging channels through a unified adapter architecture. Each channel normalises messages to a common internal format, so agents work identically regardless of the source.
Supported Channels
Section titled “Supported Channels”| Channel | Transport | Status |
|---|---|---|
| WebChat | REST API + SignalR | Available |
| Slack | Events API + Socket Mode | Planned |
| Microsoft Teams | Bot Framework | Planned |
| Discord | Gateway + Interactions | Planned |
| Telegram | Webhook | Planned |
| Business API | Planned | |
| Signal | Signal CLI | Planned |
| SMS | Twilio / Vonage | Planned |
WebChat
Section titled “WebChat”The WebChat adapter is the built-in channel — a REST endpoint for synchronous messaging plus a SignalR hub for real-time streaming.
REST Endpoint
Section titled “REST Endpoint”curl -X POST http://localhost:5000/api/webchat/message \ -H "Content-Type: application/json" \ -d '{ "senderId": "user-1", "text": "What are your business hours?", "displayName": "Alice", "accountId": "default" }'Parameters:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
senderId | string | Yes | — | Unique user identifier |
text | string | Yes | — | Message content (max 10,000 chars) |
displayName | string | No | null | User’s display name |
accountId | string | No | "default" | Account identifier for multi-tenant setups |
Response:
{ "success": true, "data": { "sessionId": "WebChat:default:user-1", "text": "Our office hours are Monday to Friday, 9 AM to 5 PM AEST.", "timestamp": "2026-02-23T10:30:02Z" }}The session key is constructed as "WebChat:{accountId}:{senderId}".
SignalR Hub
Section titled “SignalR Hub”The web dashboard connects via SignalR for real-time bi-directional messaging:
| Method | Direction | Purpose |
|---|---|---|
SendMessage | Client → Server | Send a message to the agent |
ReceiveMessage | Server → Client | Receive the agent’s response |
AgentTyping | Server → Client | Agent is generating a response |
The connection uses exponential backoff for reconnection and gracefully handles disconnects.
Message Normalisation
Section titled “Message Normalisation”All adapters produce the same InboundMessage format regardless of the source channel:
{ "messageId": "msg-uuid", "channel": "Telegram", "channelAccountId": "bot-token-id", "senderId": "telegram-user-id", "senderDisplayName": "Alice", "text": "Hello!", "timestamp": "2026-02-23T10:30:00Z", "attachments": [], "metadata": {}}Responses use the corresponding OutboundMessage format:
{ "SessionId": "Telegram:bot-token-id:telegram-user-id", "Channel": "Telegram", "RecipientId": "telegram-user-id", "Text": "Hi Alice! How can I help you today?", "Timestamp": "2026-02-23T10:30:02Z", "Attachments": []}Attachments
Section titled “Attachments”Messages can include file attachments:
{ "fileName": "proposal.pdf", "contentType": "application/pdf", "url": "https://files.komand.ai/uploads/proposal.pdf"}Metadata
Section titled “Metadata”The metadata field carries channel-specific data without polluting the core message format:
| Channel | Example Metadata |
|---|---|
| Telegram | telegramChatId, telegramMessageId |
| Slack | slackThreadTs, slackTeamId |
| Discord | discordGuildId, discordChannelId |
whatsappPhoneNumber | |
| Teams | teamsConversationId, teamsTenantId |
Adding a Channel Adapter
Section titled “Adding a Channel Adapter”Channel adapters are implemented as ASP.NET Core minimal API endpoints in the Gateway. Each adapter follows the same pattern:
1. Receive the Webhook
Section titled “1. Receive the Webhook”Accept the channel-native webhook payload and validate the request signature:
POST /api/{channel}/webhook2. Normalise to InboundMessage
Section titled “2. Normalise to InboundMessage”Map the channel-specific fields to the common format:
var inbound = new InboundMessage{ MessageId = Guid.NewGuid().ToString(), Channel = Channel.Telegram, ChannelAccountId = botId, SenderId = telegramUserId, SenderDisplayName = telegramUserName, Text = update.Message.Text, Timestamp = DateTimeOffset.UtcNow, Attachments = [], Metadata = new Dictionary<string, string> { ["telegramChatId"] = update.Message.Chat.Id.ToString() }};3. Route to SessionGrain
Section titled “3. Route to SessionGrain”Construct the session key and route:
var sessionKey = $"{Channel.Telegram}:{botId}:{telegramUserId}";var session = grainFactory.GetGrain<ISessionGrain>(sessionKey);var response = await session.HandleMessageAsync(inbound);4. Send the Response
Section titled “4. Send the Response”Convert the OutboundMessage back to the channel’s native format and send it:
await telegramClient.SendTextMessageAsync( chatId: telegramChatId, text: response.Text);Multi-Channel Agents
Section titled “Multi-Channel Agents”A single agent can serve multiple channels simultaneously. The agent’s behaviour is consistent across channels — the same system prompt, skills, and memory apply everywhere.
[Telegram] "What's my next meeting?"[Slack] "What's my next meeting?"[WebChat] "What's my next meeting?"
→ Same agent, same skills, same answer (adjusted for context)Session state is channel-specific, so a user on Telegram and the same user on Slack have separate conversation threads. However, the agent’s memory is shared across all sessions — information stored during a Telegram conversation is available in Slack.
Channel-Specific Agent Routing
Section titled “Channel-Specific Agent Routing”You can restrict which channels an agent operates on via allowedChannels in the agent configuration, and bind sessions to specific agents per channel:
| Channel | Agent | Use Case |
|---|---|---|
| WebChat | sales-bot | Website visitor lead capture |
| Slack | internal-bot | Team operations and status |
| Telegram | support-bot | Customer support |
default | General inquiries |
See Sessions for how to bind agents to sessions.