Skip to content

Channels

OpenParallax supports multi-channel messaging, allowing you to interact with your agent through various platforms beyond the CLI and web UI. Each channel connects to the same engine and goes through the same pipeline — Shield evaluation, audit logging, and memory persistence apply identically regardless of the channel.

Architecture

Every channel adapter implements the same pattern:

  1. Receive a message from the external platform
  2. Normalize it into a standard format (sender, session ID, message content)
  3. Call engine.ProcessMessageForWeb(ctx, sender, sessionID, messageID, content, mode)
  4. Receive pipeline events via the EventSender interface
  5. Format events back into the platform's message format and send to the user

Each channel maintains independent sessions. A Telegram conversation and a Discord conversation are separate sessions with separate histories.

Supported Channels

WhatsApp (Cloud API)

Connect your agent to WhatsApp via the Meta Cloud API.

Configuration:

yaml
channels:
  whatsapp:
    enabled: true
    phone_number_id: "1234567890"
    access_token_env: WHATSAPP_ACCESS_TOKEN
    verify_token: "your-verify-token"
    webhook_port: 8443
    allowed_numbers: ["+1234567890"]
FieldDescription
phone_number_idWhatsApp Business phone number ID from Meta Developer Portal
access_token_envEnvironment variable containing the permanent access token
verify_tokenToken for webhook verification (you choose this value)
webhook_portTCP port the adapter binds for incoming webhook requests
allowed_numbersList of phone numbers permitted to message the bot. Empty list allows all numbers

Setup steps:

  1. Create a Meta Developer account and a WhatsApp Business app
  2. In the WhatsApp product settings, get your Phone Number ID and generate a permanent access token
  3. Set the access token as an environment variable: export WHATSAPP_ACCESS_TOKEN="..."
  4. Configure the webhook URL in Meta Developer Portal: https://your-domain:3100/webhooks/whatsapp
  5. Enter the verify token you chose in step 3
  6. Subscribe to the messages webhook field
  7. Enable the channel in config.yaml and restart

The agent needs to be reachable from the internet for WhatsApp webhooks. Use a reverse proxy (nginx, Caddy) or a tunnel (ngrok, Cloudflare Tunnel) to expose the webhook endpoint.


Telegram (Bot API)

Connect via a Telegram bot.

Configuration:

yaml
channels:
  telegram:
    enabled: true
    token_env: TELEGRAM_BOT_TOKEN
    allowed_users: [123456789]
    allowed_groups: []
    private_only: true
    polling_interval: 1
FieldDescription
token_envEnvironment variable containing the bot token from @BotFather
allowed_usersList of Telegram user IDs (numeric) that can interact with the bot. Empty list allows all users.
allowed_groupsList of Telegram group chat IDs (numeric) where the bot is permitted to respond. Empty list allows all groups.
private_onlyWhen true, the bot only responds to private (direct) messages and ignores group chats. Defaults to true for security.
polling_intervalLong-polling interval in seconds. Defaults to 1.

The Telegram adapter uses long polling — no public URL or webhook is needed.

Setup steps:

  1. Create a bot via @BotFather on Telegram
  2. Copy the bot token and set it: export TELEGRAM_BOT_TOKEN="..."
  3. Find your Telegram user ID (send a message to @userinfobot)
  4. Add your user ID to allowed_users to restrict access
  5. Optionally set a webhook URL for push-based delivery, or leave empty for long polling
  6. Enable the channel and restart

Long polling mode does not require a public URL, making it easier to set up for personal use.


Discord (Bot)

Connect via a Discord bot.

Configuration:

yaml
channels:
  discord:
    enabled: true
    token_env: DISCORD_BOT_TOKEN
    allowed_guilds: ["9876543210"]
    allowed_channels: []
    allowed_users: []
    respond_to_mentions: true
FieldDescription
token_envEnvironment variable containing the Discord bot token
allowed_guildsList of Discord server (guild) IDs where the bot is permitted to operate. Required — the bot refuses to respond in unlisted guilds.
allowed_channelsOptional channel ID allowlist within those guilds. Empty list allows all channels.
allowed_usersOptional user ID allowlist. Empty list allows all members of the allowed guilds.
respond_to_mentionsWhen true, the bot replies to @mentions in addition to direct messages.

Setup steps:

  1. Create an application in the Discord Developer Portal
  2. Create a bot user and copy the token: export DISCORD_BOT_TOKEN="..."
  3. Enable the MESSAGE CONTENT privileged gateway intent
  4. Generate an invite URL with bot and applications.commands scopes
  5. Invite the bot to your server
  6. Add the guild ID to guild_ids for server-specific slash commands
  7. Enable the channel and restart

The bot responds to direct messages and mentions in channels. Each Discord user gets their own session.


Slack (App)

Planned — Not Yet Implemented

The Slack adapter is on the roadmap but not yet implemented. The configuration schema is defined, but enabling it has no effect at runtime.

Will connect via a Slack app using Socket Mode.

Configuration:

yaml
channels:
  slack:
    enabled: true
    bot_token_env: SLACK_BOT_TOKEN
    app_token_env: SLACK_APP_TOKEN
FieldDescription
bot_token_envBot User OAuth Token (xoxb-...)
app_token_envApp-Level Token (xapp-...) for Socket Mode

Setup steps:

  1. Create a new app at api.slack.com
  2. Enable Socket Mode in the app settings
  3. Generate an App-Level Token with connections:write scope
  4. Under OAuth & Permissions, add bot token scopes: chat:write, app_mentions:read, im:history, im:read, im:write
  5. Install the app to your workspace
  6. Set environment variables: SLACK_BOT_TOKEN, SLACK_APP_TOKEN
  7. Enable the channel and restart

The bot responds to direct messages and @mentions. Socket Mode means no public URL is required.


Signal (signal-cli)

Connect via Signal using signal-cli as the transport layer.

Configuration:

yaml
channels:
  signal:
    enabled: true
    cli_path: "/usr/local/bin/signal-cli"
    account: "+1234567890"
    allowed_numbers: ["+1987654321"]
FieldDescription
cli_pathPath to the signal-cli binary
accountThe phone number registered with Signal for the bot
allowed_numbersList of phone numbers allowed to message the bot. Empty list allows all.

Setup steps:

  1. Install signal-cli
  2. Register or link a phone number: signal-cli -u +1234567890 register and verify
  3. Set the path and phone number in config.yaml
  4. Add trusted phone numbers to allowed_numbers
  5. Enable the channel and restart

Signal provides end-to-end encrypted messaging. All messages between you and the agent are encrypted in transit.


Microsoft Teams (Graph API)

Planned — Not Yet Implemented

The Microsoft Teams adapter is on the roadmap but not yet implemented. The configuration schema is defined, but enabling it has no effect at runtime.

Will connect via Microsoft Teams using the Microsoft Graph API and Bot Framework.

Configuration:

yaml
channels:
  teams:
    enabled: true
    app_id_env: TEAMS_APP_ID
    password_env: TEAMS_APP_PASSWORD
FieldDescription
app_id_envEnvironment variable for the Azure AD application (client) ID
password_envEnvironment variable containing the client secret

Setup steps:

  1. Register an application in Azure AD
  2. Add Microsoft Graph API permissions: ChatMessage.Read, ChatMessage.Send
  3. Create a client secret and set it: export TEAMS_APP_PASSWORD="..."
  4. Create a Bot Channel Registration in Azure
  5. Configure the messaging endpoint URL
  6. Add the bot to your Teams workspace
  7. Enable the channel and restart

iMessage (macOS only)

Connect via iMessage using the AppleScript bridge to Messages.app.

macOS Only

iMessage integration requires macOS with Messages.app configured. It is not available on Linux or Windows.

Configuration:

yaml
channels:
  imessage:
    enabled: true
    apple_id: "you@icloud.com"
FieldDescription
apple_idThe Apple ID email address configured in Messages.app

Setup steps:

  1. Sign in to Messages.app with your Apple ID
  2. Grant Full Disk Access to the OpenParallax process in System Settings > Privacy & Security > Full Disk Access
  3. Set your Apple ID email in config.yaml
  4. Enable the channel and restart

The adapter uses AppleScript to poll for new messages and send responses through Messages.app. A GUI session is required -- headless servers are not supported. Text messages only; attachments are not processed in the initial implementation.

See the iMessage adapter documentation for full details.


Message Normalization

Regardless of the source channel, messages are normalized into a common format before entering the pipeline:

  • Sender — channel-specific user identifier
  • Session ID — unique per user per channel
  • Message content — text content extracted from platform-specific formatting
  • Mode — normal or OTR

Platform-specific features (reactions, threads, attachments) are handled by each channel adapter as appropriate. Text content is always extracted and passed through.

Dynamic Channel Management

Channels can be connected and disconnected at runtime without restarting the engine.

attach connects a channel to a running agent:

bash
openparallax attach telegram
openparallax attach discord myagent

detach disconnects a channel from a running agent:

bash
openparallax detach telegram
openparallax detach discord myagent

The channel must be configured in config.yaml with valid credentials. attach starts the adapter and begins processing messages; detach gracefully shuts it down and stops accepting new messages.

Security Across Channels

All channels go through the identical Shield pipeline. There is no bypass for any channel:

  • Every tool call is evaluated by Shield
  • Every action is logged in the audit trail
  • Every channel respects the active policy file
  • OTR mode works in all channels (the agent checks session mode, not channel type)

The allowed_users / allowed_numbers fields on Telegram, Signal, and other channels provide an additional access control layer that restricts who can interact with the bot.

Security defaults by channel:

  • Discord — requires a guild allowlist (allowed_guilds). The bot refuses to respond in unlisted guilds.
  • Telegram — defaults to private-chat-only (private_only: true). Group chat responses require explicitly setting private_only: false and configuring allowed_groups.

Tier 3 human approval:

When Shield escalates an action to Tier 3, approval requests are broadcast to all connected channels:

  • Web UI — inline approval card with Approve/Deny buttons and countdown timer.
  • Telegram — inline keyboard buttons. Tap to approve or deny.
  • Discord — message component buttons. Click to approve or deny.
  • WhatsApp, Signal, iMessage — notification only. Approve or deny on the web UI, Telegram, or Discord.

Multiple Channels Simultaneously

You can enable multiple channels at the same time. The engine handles all channel adapters concurrently:

yaml
channels:
  telegram:
    enabled: true
    token_env: TELEGRAM_BOT_TOKEN
    allowed_users: ["123456789"]
  discord:
    enabled: true
    token_env: DISCORD_BOT_TOKEN
  slack:
    enabled: true
    bot_token_env: SLACK_BOT_TOKEN
    app_token_env: SLACK_APP_TOKEN
    signing_secret_env: SLACK_SIGNING_SECRET

Each channel maintains its own sessions. The web UI and CLI are always available alongside any configured messaging channels.

Next Steps

  • Configuration — full channel configuration reference
  • Security — how Shield protects across channels
  • Sessions — how cross-channel sessions work