Skip to main content
The Channel Gateway is the inbound counterpart to Notifications. While notifications push alerts from Polpo to external channels, the gateway accepts messages into Polpo from those same channels — turning messaging apps into full control interfaces.
Notifications = Polpo talks to you (outbound). Gateway = you talk to Polpo (inbound).

Architecture

Channel Gateway architecture: messaging channels flow through adapters into ChannelGateway, which branches to PeerStore, slash commands, and the completions loop
The gateway is channel-agnostic. Each messaging platform has a thin adapter that translates platform-specific messages into a common InboundMessage format. The gateway handles everything else: identity, authorization, session management, command routing, and the agentic conversation loop.

Components

ComponentResponsibility
ChannelGatewayCore message router — DM policy, slash commands, chat completions
PeerStorePeer identity, allowlist, pairing, session mapping, presence
TelegramGatewayAdapterBridges Telegram polling to the gateway
WhatsAppGatewayAdapterBridges WhatsApp (Baileys) to the gateway
Channel adapters (future)Slack, Discord — same pattern

Enabling the Gateway

Add a gateway block to any notification channel in your config:
{
  "settings": {
    "notifications": {
      "channels": {
        "telegram": {
          "type": "telegram",
          "botToken": "${TELEGRAM_BOT_TOKEN}",
          "chatId": "-1001234567890",
          "gateway": {
            "enableInbound": true,
            "dmPolicy": "pairing"
          }
        }
      }
    }
  }
}
The same channel handles both outbound notifications and inbound messages. When gateway.enableInbound is true, the poller routes all incoming messages through the gateway instead of ignoring them.

Gateway Config

FieldTypeDefaultDescription
enableInboundbooleanfalseEnable inbound message processing
dmPolicystring"allowlist"Authorization policy (see below)
allowFromstring[][]Pre-authorized peer IDs. ["*"] allows everyone
sessionIdleMinutesnumber60Auto-reset session after idle period

Message Flow

When a message arrives:
  1. Peer upsert — the sender’s identity is recorded or updated in the PeerStore
  2. Presence update — the sender is marked as active
  3. DM policy check — is this peer authorized to message?
  4. Pending rejection check — is this a reply to a rejection prompt?
  5. Slash command check — does the message start with /?
  6. Chat completions — free-text is forwarded to the orchestrator’s agentic loop

Slash Commands

The gateway provides built-in commands for common operations:
CommandDescription
/helpShow available commands
/statusProject status — task counts, active agents, connected peers
/tasksList tasks with status
/missionsList missions
/agentsList agents with active/idle status
/approve [ID]List pending approvals, or approve a specific one
/reject ID [reason]Reject with feedback
/newReset conversation session
/pair CODEApprove a pairing request (authorized peers only)
Unknown commands (e.g. /unknown) fall through to the chat handler, so they’re processed as free text.

Free-Text Chat

Non-command messages enter the orchestrator’s completions loop — the same one used by the TUI, REST API, and Web UI. The gateway:
  1. Gets or creates a session for the peer
  2. Loads conversation history from the session store
  3. Builds a system prompt with project context and peer identity
  4. Runs a multi-turn agentic loop (up to 15 tool-call turns)
  5. Stores the response and sends it back
Each peer gets an isolated session. Sessions auto-expire after sessionIdleMinutes of inactivity.

Approval Integration

The gateway integrates with Approval Gates in two ways:

Inline buttons (Telegram)

When an approval notification is sent via Telegram, it includes Approve and Reject buttons. Pressing a button triggers handleApprovalCallback() in the gateway.

Slash commands

Peers can also manage approvals via /approve and /reject:
/approve                    — list all pending approvals
/approve req-abc123         — approve a specific request
/reject req-abc123 bad code — reject with inline feedback
/reject req-abc123          — reject, then send feedback in next message

Events

The gateway emits events via the orchestrator’s typed emitter:
EventPayloadDescription
gateway:started{ channels }Gateway activated
gateway:stopped{}Gateway deactivated
peer:paired{ peer, channel }New peer completed pairing
peer:message{ peerId, channel, text, sessionId }Peer sent a message
peer:blocked{ peerId, channel, reason }Message blocked by DM policy
peer:presence{ peerId, channel, status }Peer came online or went offline
These events flow through SSE to the Web UI and React SDK, and can trigger notification rules like any other Polpo event.

REST API

MethodPathDescription
GET/peersList known peers (optional ?channel=telegram filter)
GET/peers/presenceCurrently active peers
GET/peers/allowlistCurrent allowlist
POST/peers/allowlistAdd peer to allowlist {"peerId": "telegram:123"}
DELETE/peers/allowlist/:idRemove from allowlist
POST/peers/pairApprove a pairing code {"code": "ABC123"}
POST/peers/linkLink peer identities {"peerId": "...", "linkedTo": "..."}

Adding New Channels

The gateway is designed for easy extension. To add a new messaging platform:
  1. Create an adapter that implements the message translation (see TelegramGatewayAdapter for reference)
  2. Wire it to the platform’s message source (polling, webhook, WebSocket)
  3. Register it in the orchestrator startup
The adapter only needs to translate platform-specific messages into the common InboundMessage format — the gateway handles everything else.
The gateway is opt-in and backward-compatible. Existing notification-only setups continue to work unchanged. The gateway only activates when gateway.enableInbound is set to true.