Messaging Channels

Purpose and Scope

Messaging channels connect consumer messaging providers to a Flue application through verified inbound HTTP routes. In Flue terminology, a channel is not a full provider client. It verifies the provider request, parses the body into provider-native typed data, and calls the application handler that you write. That handler decides whether to dispatch an agent, invoke application code, acknowledge a webhook, or ignore the event. This page focuses on Facebook Messenger, WhatsApp Business Cloud, Telegram Bot API, and Twilio Programmable Messaging, where the shared pattern is trusted ingress plus application-owned outbound replies.

Sources: apps/docs/src/content/docs/guide/channels.md, packages/messenger/README.md, packages/telegram/README.md, packages/twilio/README.md, packages/whatsapp/README.md

The most important design boundary is ownership. Channel packages own the inbound boundary: signature or token verification, provider handshakes, body parsing, provider-native payload delivery, route integration, and canonical conversation identity where the package exposes it. Your application owns credentials for outbound APIs, provider SDK or Fetch clients, agent tools, authorization policy, deduplication, and business persistence. This is especially important for messaging systems because sending a message can have compliance, billing, consent, rate-limit, and escalation implications that belong in application policy rather than in a generic framework abstraction.

Sources: apps/docs/src/content/docs/guide/channels.md, packages/messenger/README.md, packages/twilio/README.md, packages/whatsapp/README.md

Relevant Source Files

  • apps/docs/src/content/docs/guide/channels.md — Defines the general channel model, the channel versus client ownership split, the file-based routing convention, the custom channel flow, and the review checklist for verification and target behavior.
  • packages/messenger/README.md — Documents the Facebook Messenger channel factory, required configuration, Page verification responsibilities, provider-native payload handling, and application-owned Graph API responsibilities.
  • packages/telegram/README.md — Documents the Telegram channel factory, webhook secret verification, provider-native Update pass-through, response handling, canonical conversation identity, and bot-client ownership.
  • packages/twilio/README.md — Documents the Twilio channel factory, configured public URL signature validation, destination checks, TwiML acknowledgement, provider-native form body behavior, and outbound ownership.
  • packages/whatsapp/README.md — Documents the WhatsApp channel factory, Meta verification and signature behavior, unmodified payload forwarding, conversation identity helpers, and application-owned filtering and outbound clients.

Add and Shape a Messaging Channel

Start from the channel guide workflow: use the first-party blueprint when one exists, or use the generic channel blueprint with the provider webhook documentation when it does not. The generated module normally lives under the source root in a channels directory and exports a named channel binding. Messaging blueprints also create or expect ordinary application code for outbound calls, such as a Graph API client, a grammY API object, or a Fetch-based sender. Keep those exports separate: the channel is Flue ingress, while the client and tools are your application’s controlled way to respond.

Sources: apps/docs/src/content/docs/guide/channels.md

flue add channel messenger
flue add channel telegram

The route namespace follows the file-based channel convention. An immediate file beneath the channels directory exports one named channel binding, and the filename defines the namespace below the channels route. Provider packages may choose provider-specific endpoint names inside that namespace, such as a webhook or events path. In practice, that means the project structure is part of the public integration contract: move or rename the channel file only when you also intend to move the provider webhook URL and update the provider dashboard. Treat webhook URLs, route names, and environment variables as deployment configuration, not incidental implementation details.

Sources: apps/docs/src/content/docs/guide/channels.md

Provider Patterns

Facebook Messenger uses a Page webhook model. The Messenger package factory accepts an app secret, verify token, and Page identity, then calls your webhook handler with a verified, potentially batched, provider-native Page payload. The package README states that it owns verification, exact-body signatures, fixed Page identity, provider-native webhook payload delivery, and canonical conversation identity. Your application owns the Page access token, the outbound Graph client, dispatch policy, tools, and deduplication. A typical handler filters echo messages and unsupported event shapes before continuing or dispatching the agent instance for the conversation.

Sources: packages/messenger/README.md

Telegram uses webhook secret verification around provider-native Updates. The Telegram package factory accepts a webhook secret token and passes a verified Update to your handler. The official ecosystem flow pairs the channel with grammY for outbound Bot API access, while the package README keeps the same ownership boundary: Flue verifies and parses the inbound Update, and the application owns the bot token, outbound Bot API client, tools, dispatch policy, and update-id deduplication. For agent routing, choose a stable conversation key from the chat, business chat, channel post, topic, or callback context before dispatching work.

Sources: packages/telegram/README.md

WhatsApp Business Cloud is similar to Messenger because it uses Meta webhook verification and signed deliveries, but its application-level filtering is different. The WhatsApp package owns GET verification, exact-body signature validation, and unmodified forwarding of Meta’s webhook payload. It also provides canonical phone, Business-Scoped User ID, and group conversation identity helpers. The package explicitly leaves payload interpretation, business account or phone-number filtering, access tokens, outbound clients, tools, dispatch policy, and deduplication to the application. That means a robust app should inspect entries and changes, reject irrelevant accounts, and only dispatch messages it is prepared to handle.

Sources: packages/whatsapp/README.md

Twilio Programmable Messaging uses a configured public webhook URL as part of signature validation. The Twilio package factory includes the account SID, auth token, webhook URL, and a destination descriptor such as a phone number address. The package owns signature validation over that public URL, fixed account and destination checks, provider-native verified form bodies, TwiML acknowledgement, and canonical conversation identity. It intentionally does not rename, narrow, or coerce Twilio field names. Application handlers should therefore expect Twilio’s wire shape, decide how to handle SMS versus MMS, and apply their own deduplication and outbound messaging policy.

Sources: packages/twilio/README.md

Implementation Reference

ProviderFactoryInbound verification owned by packageApplication-owned outbound sideDeduplication hint
MessengercreateMessengerChannelApp secret, verify token, exact-body signatures, fixed Page identityPage access token, Graph API client, reply toolMessage or delivery identity from Page payload
WhatsAppcreateWhatsAppChannelGET verification and exact-body Meta signature validationAccess token, Business Cloud API client, toolsMeta delivery and message identifiers
TelegramcreateTelegramChannelWebhook secret token validation and Update parsingBot token, grammY or Bot API client, message toolupdate_id
TwiliocreateTwilioChannelPublic URL signature validation, account and destination checksFetch or SDK client, send-message toolsMessage SID or application persistence key
import { createTelegramChannel } from '@flue/telegram';
import { dispatch } from '@flue/runtime';
import assistant from '../agents/assistant.ts';
 
export const channel = createTelegramChannel({
  secretToken: process.env.TELEGRAM_WEBHOOK_SECRET_TOKEN!,
  async webhook({ update }) {
    const message = update.message ?? update.channel_post ?? update.business_message;
    if (!message) return;
 
    await dispatch(assistant, {
      id: channel.conversationKey(conversationFromMessage(message)),
      input: { type: 'telegram.message', updateId: update.update_id, message },
    });
  },
});

Use the example shape as a pattern rather than a universal adapter. A good messaging handler first accepts only events relevant to the application, then converts the provider-specific conversation into the agent instance identity, then dispatches a small input that preserves the provider-native message for later policy decisions. Put outbound sends behind explicit tools so the agent can request a reply without receiving unrestricted access to the full provider account. That separation lets you review authorization, rate limits, human handoff rules, and content restrictions independently from the verified webhook plumbing.

Sources: apps/docs/src/content/docs/guide/channels.md, packages/telegram/README.md

Operational Checklist and Next Steps

Before enabling a messaging provider in production, test valid signatures, invalid signatures, handshake requests, ignored payloads, duplicate deliveries, and the configured Node or Cloudflare target. Confirm the public webhook URL exactly matches provider configuration, especially for Twilio where the URL participates in verification. Confirm that generated tools reply to the same canonical conversation that received the event. Finally, decide where delivery deduplication and long-term message state live, because the channel packages intentionally do not become business databases or outbound clients. Next, read the channel guide for routing, then the provider ecosystem page for deployment-specific setup.

Sources: apps/docs/src/content/docs/guide/channels.md, packages/twilio/README.md