Business SaaS Channels

Purpose and Scope

Business SaaS channels are Flue channel modules for webhook-driven products such as Intercom, Linear, Notion, and Zendesk. They solve the same integration problem as chat or developer-tool channels: receive a provider HTTP delivery, verify that it really came from the provider, preserve the provider-native payload shape, and hand the authenticated event to application code. In Flue terms, a channel is not the whole provider integration. It is the inbound boundary where external events enter the Flue application, after which your handler can dispatch an agent, call workflow or application code, or return a provider-specific response.

Sources: apps/docs/src/content/docs/guide/channels.md, packages/intercom/README.md, packages/linear/README.md, packages/notion/README.md, packages/zendesk/README.md

The first-party channels guide is explicit about this boundary: channels focus on inbound HTTP and should not become universal clients for the provider. Outbound API calls remain ordinary application code, usually using the provider’s established SDK. That distinction is especially important for business SaaS systems because installation flows, OAuth, account scoping, ticket or conversation policy, deduplication, and token rotation are usually domain-specific. Flue verifies ingress and routes it into your program; your application decides which verified events become durable agent sessions or business operations.

Relevant Source Files

  • apps/docs/src/content/docs/guide/channels.md — Defines the channel model, the flue add channel workflow, the ownership split between channel packages and application code, and file-based routing under channels/.
  • packages/intercom/README.md — Documents createIntercomChannel, Intercom webhook validation routes, supported notification handling, and what the package intentionally does not own.
  • packages/linear/README.md — Documents createLinearChannel, Linear webhook verification, delivery metadata, provider-native payload forwarding, and conversation-key behavior.
  • packages/notion/README.md — Documents createNotionChannel, setup-token handling, exact-body HMAC verification, Notion event typing, and application-owned outbound concerns.
  • packages/zendesk/README.md — Documents createZendeskChannel, Zendesk signature verification, callback shape, response behavior, delivery metadata, retry guidance, and ticket key helpers.

Channel Ownership Model

A Flue channel module is discovered from a channels/<provider>.ts file and normally exports a named channel binding. The filename defines the route namespace, while the package decides its provider-specific subroute, such as /webhook. The general channels guide describes channel packages as owning request authentication, signature verification, handshakes, body parsing, typed provider payloads, and the discovered route boundary. The application owns the provider SDK client, outbound credentials, OAuth and installation state, agent tools, authorization policy, durable deduplication, and business persistence.

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

That ownership model keeps provider APIs from being duplicated inside Flue. For example, a Linear integration can accept a signed comment-created webhook and then use an application-owned @linear/sdk client to post a reply. An Intercom integration can dispatch an agent for a workspace-scoped conversation and keep the Intercom client factory in project code. The channel package authenticates and normalizes ingress enough for safe handling; it does not decide which tickets, pages, conversations, or issues the agent may read or mutate.

Provider Patterns

Intercom uses createIntercomChannel with a clientSecret and a webhook callback. The package publishes HEAD /webhook for endpoint validation and POST /webhook for signed notifications. Its README shows callbacks switching on topics such as conversation.user.created and conversation.user.replied, while preserving future topics for application policy. The ecosystem quickstart further shows dispatching an assistant with a conversation key built from workspaceId and conversationId, so one Intercom conversation can continue a durable Flue agent session without giving the model raw workspace credentials.

Sources: packages/intercom/README.md

Linear uses createLinearChannel with webhookSecret and an async webhook({ payload, deliveryId }) callback. The package verifies exact request bytes with HMAC-SHA256, rejects timestamps outside Linear’s recommended one-minute window, can restrict organization and webhook IDs, and requires a UUID-v4 Linear-Delivery header. The forwarded payload keeps Linear’s own type, action, and field names, including authenticated deliveries newer than the installed modeled union. Conversation keys cover issues, nested issue-comment threads, and agent sessions, but they are identifiers rather than authorization capabilities.

Sources: packages/linear/README.md

Notion uses createNotionChannel with a verificationToken and a fixed POST /webhook route. Its setup flow is unusual because the initial endpoint setup token is unsigned; the package handles that separately through a temporary verification(...) hook. After storing the token as NOTION_WEBHOOK_VERIFICATION_TOKEN, recurring events are verified against exact request bytes with HMAC-SHA256 before application code receives event. The package forwards the official SDK’s provider-native webhook payload union and keeps newer verified event types reachable from a default branch.

Sources: packages/notion/README.md

Zendesk uses createZendeskChannel with signingSecret, accountId, and a webhook({ payload }) callback. The package verifies Zendesk’s base64 HMAC-SHA256 over the signature timestamp concatenated directly with the exact request bytes before parsing. The callback receives { c, payload, delivery }, where payload is the signed provider-native event envelope and delivery is unsigned routing metadata from headers. The README emphasizes that deduplication should persist the signed payload.id, while delivery.invocationId is useful only for correlating provider delivery attempts.

Sources: packages/zendesk/README.md

Implementation Reference

ProviderFactoryTypical fileRoute behaviorCallback dataApplication-owned concerns
IntercomcreateIntercomChannelchannels/intercom.tsHEAD /webhook validation and POST /webhook notifications{ notification } with topics such as conversation.user.repliedSDK client, access token, region, conversation retrieval, deduplication, outbound Intercom calls
LinearcreateLinearChannelchannels/linear.tsPOST /channels/linear/webhook relative to flue() mount{ payload, deliveryId } using Linear webhook payloads@linear/sdk client, message tool, organization policy, delivery deduplication
NotioncreateNotionChannelchannels/notion.tsFixed POST /webhook; separate initial setup-token handling{ event } using Notion provider-native event typesOAuth, subscriptions, credentials, ordering, persistence, outbound Notion API calls
ZendeskcreateZendeskChannelchannels/zendesk.tsFixed POST /webhook; Response passthrough supported{ c, payload, delivery }Webhook creation, triggers, automations, ticket policy, OAuth, deduplication, outbound Zendesk API calls

Use flue add channel <provider> when a first-party blueprint exists. The general guide describes flue add as the way to give a coding agent the integration blueprint, inspect the project, and create a module beneath the source-root channels/ directory. For providers without a first-party package, use the generic channel blueprint with the provider webhook documentation, then review verification, unconsumed-body handling, protocol handshakes, valid and invalid signatures, and both Node and Cloudflare target behavior before deploying.

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

Execution Flow

A typical SaaS webhook flow begins outside Flue, when the provider sends an HTTP request to the discovered /channels/<name>/... route. The channel package reads the exact request body in the provider-required form, checks signatures, timestamps, tokens, account restrictions, or setup handshakes, and parses the delivery only after authentication succeeds. It then calls your application handler with provider-native data. The handler should do the smallest safe amount of synchronous work needed to admit durable processing, such as calling dispatch(...) for an agent session keyed by a conversation, issue, page, or ticket.

After admission, the long-running work belongs to Flue agents, workflows, tools, or application services rather than the webhook request itself. This is most visible in Zendesk, where the README advises acknowledging promptly because Zendesk allows a limited request window and redeliveries or omissions can occur. The same design applies broadly: persist provider delivery IDs or signed event IDs when idempotency matters, authorize outbound actions through application-owned tools, and use the provider SDK for reads or writes after the channel has authenticated the inbound event.

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

Next Steps

Start with the general channels guide to understand discovery, route namespaces, and ownership. Then add the provider-specific package or blueprint for the SaaS system you are integrating. For Intercom and Linear, the official ecosystem guides show generated agent-dispatch examples that bind conversations or issues to durable Flue agent sessions. For Notion and Zendesk, pay close attention to setup-token or response semantics, because those provider protocols affect how you test the first delivery. In all cases, treat outbound API access, OAuth, authorization, and deduplication as project code that surrounds the verified channel boundary.