Channels

Purpose and Scope

Channels are Flue’s ingress layer for provider-originated HTTP events. A channel receives a request from a system such as Slack, GitHub, Discord, Google Chat, Stripe, or another webhook provider, verifies that the request is authentic, parses the provider payload into provider-native data, and then calls application code. The important distinction is that Flue does not ask you to turn a provider into a generic chat transport. Instead, the channel gives your application a trusted point where an outside event can enter the harness and become agent work, workflow work, or an ordinary application response.

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

The guide frames channels narrowly on purpose. They are inbound HTTP integrations, not all-purpose clients for provider APIs. Outbound calls remain the responsibility of the provider’s established SDK and the application code that chooses how to use it. This separation matters in agent systems because the channel package should be responsible for verification, parsing, protocol handshakes, and typed ingress, while your application remains responsible for business policy, authorization, tool design, and outbound side effects. That keeps the trusted boundary small and makes it easier to review what an agent is allowed to do after an event is admitted.

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

Core Primitives

A channel module normally contains two different kinds of exports. The named channel export is the Flue integration that owns request verification and route handling. A named client export, when present, is ordinary project code initialized with the provider SDK. The distinction is visible in the guide’s Slack example: the Flue side is created with a first-party channel factory, while the outbound Slack Web API client is initialized separately with the application’s bot token. Keeping these bindings adjacent in one module is convenient, but they represent different responsibilities and should be reviewed with different security expectations.

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

Handlers are the bridge from provider events into the rest of the Flue application. A handler receives typed provider-native data and may choose to dispatch work to an agent, call application code directly, or return a provider-specific HTTP response. The handler should preserve the provider’s vocabulary rather than flattening everything into a generic message shape too early. For example, Slack events, Discord interactions, GitHub webhook deliveries, and Google Chat interaction payloads each carry identities, timestamps, retry semantics, and response rules that are meaningful to the provider. Channel handlers should admit work using those native facts.

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

Relevant Source Files

  • apps/docs/src/content/docs/guide/channels.md — First-party Channels guide that defines the concept, add flow, custom channel guidance, ownership boundary, and file-based routing convention.

Add a First-Party Channel

For a provider with a first-party integration, the recommended starting point is the Flue CLI blueprint flow. The guide shows using the add command with a channel name and printing the blueprint output for a coding agent to apply. That flow is designed for existing projects: the blueprint inspects the source root and creates a module under the project’s channels directory. In practice, this means the generated code is not only an install recipe. It also follows Flue’s discovery conventions, places the route where the runtime can find it, and gives the handler a provider-specific starting shape.

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

flue add channel slack --print | codex

A typical generated module is intentionally small at the ingress boundary. It imports a channel factory from the provider package, imports the provider’s outbound SDK, exports the SDK client, and exports the Flue channel. In the Slack example, the channel is configured with a signing secret, and the events callback checks the payload type before handling the native event. That pattern is the core workflow: verify at the channel layer, keep the typed provider payload intact, make a narrow admission decision, then call application behavior such as dispatching an agent or invoking a helper.

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

import { createSlackChannel } from '@flue/slack';
import { WebClient } from '@slack/web-api';
 
export const client = new WebClient(process.env.SLACK_BOT_TOKEN);
 
export const channel = createSlackChannel({
  signingSecret: process.env.SLACK_SIGNING_SECRET!,
 
  async events({ payload }) {
    if (payload.type !== 'event_callback') return;
  },
});

Custom Channels and Verification

When Flue does not provide a first-party package for a provider, the guide recommends using the generic channel blueprint with the provider’s webhook documentation. That workflow tells the coding agent what protocol to implement, but the generated module still needs human review. The key requirements are to verify the request against the unconsumed body, preserve provider-native event data, and add the provider’s established SDK for outbound calls rather than inventing a broad client inside the channel. This gives unsupported providers the same basic shape as supported providers without pretending that every webhook protocol is identical.

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

flue add channel https://provider.example/webhooks --print | codex

Custom channels deserve explicit negative testing. The guide calls out valid and invalid signatures, protocol handshakes, responses, and the configured target. Those cases are not incidental. Many providers sign the exact request bytes, so middleware that consumes or reserializes the body before verification can break authentication or create a bypass. Some providers also require challenge responses or fast acknowledgements before application work is complete. A robust custom channel should prove that unauthenticated traffic fails closed, authentic traffic reaches the handler, and provider-specific response requirements are satisfied on both Node and Cloudflare targets when those targets are used.

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

Ownership Boundary

The ownership table in the guide is the most important design rule for channel authors. Channel packages own request authentication, signature verification, provider handshakes, automatic protocol responses, body limits, parsing, typed payloads, and the discovered routes beneath the channel namespace. The application owns outbound credentials, SDK clients, OAuth or installation state, token storage, token rotation, agent tools, authorization policy, delivery deduplication, and business persistence. This boundary prevents channel packages from becoming incomplete reimplementations of huge provider APIs and lets application teams decide what operations are safe for agents.

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

That boundary also affects how you expose capabilities to agents. If an incoming provider event should cause an agent to respond, the handler should dispatch durable work with a stable conversation or resource identity. The outbound ability to post, comment, or update should usually be represented as a narrow application-owned tool bound to the trusted destination derived from the inbound event. The channel can confirm that the event is genuine, but it does not automatically authorize every future action. Direct agent routes, user-selected instance identifiers, and provider installation scopes still need independent authorization checks in application code.

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

ConcernOwner
Request authentication and signature verificationChannel package
Provider handshakes and automatic protocol responsesChannel package
Body limits, parsing, and typed provider payloadsChannel package
Discovered routes beneath /channels/<name>/...Flue
Provider SDK client and outbound credentialsApplication
OAuth, installation, token storage, and token rotationApplication
Agent tools and authorization policyApplication
Delivery deduplication and business persistenceApplication

File-Based Routing and Handler Flow

Flue discovers channel modules by filename. Each immediate file beneath the channels directory exports one named channel binding, and the filename defines the route namespace. The guide’s examples map a GitHub module to a GitHub webhook route and a Slack module to a Slack events route. This convention keeps inbound provider URLs predictable and colocates route behavior with verification configuration. It also means that moving or renaming the file changes the public route, so webhook provider settings, local tunnels, deployment URLs, and documentation should be updated together when the module name changes.

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

src/channels/github.ts -> /channels/github/webhook
src/channels/slack.ts  -> /channels/slack/events

The handler flow should be short and explicit. First, let the channel package authenticate and parse the request. Next, inspect the provider-native payload enough to decide whether the event is supported and where it belongs. Then admit durable work or return the provider response required by the protocol. Long-running work should usually be moved behind dispatch rather than performed inline in the webhook response path. This is especially important for providers that expect quick acknowledgement, retry failed requests, or provide delivery identifiers that your application may want to deduplicate in durable storage.

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

Operational Checklist and Next Steps

Before deploying a channel, confirm the environment variables required by both sides of the module. Verification secrets and public keys belong to the channel configuration. Bot tokens, API keys, service-account credentials, and OAuth-derived access tokens belong to the application client or token store. Confirm that the provider is configured with the exact route exposed by the file convention, including the correct deployment host and path. If local development uses a public tunnel, test the real provider callback rather than only synthetic requests, because some providers have handshake or signature details that are hard to reproduce manually.

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

After the channel admits events, review how the rest of the harness uses them. If the handler dispatches an agent, choose a stable instance identity derived from provider facts such as workspace, repository, issue, channel, thread, or conversation. If the agent can call outbound tools, bind those tools to the trusted destination rather than accepting arbitrary destinations from a prompt or direct route. For provider-specific setup, read the ecosystem channel pages for Slack, Discord, GitHub, Google Chat, Teams, messaging providers, and business SaaS providers. For the application side of the handoff, continue with Agents, Tools, Durable Execution, and Routing.