---
description: Reference for verified WhatsApp Business Cloud ingress from @flue/whatsapp.
title: WhatsApp Channel API | Flue
image: https://flueframework.com/docs/og4.jpg
---

# WhatsApp Channel API

AI-generated, awaiting review [ View as Markdown](https://flueframework.com/docs/api/whatsapp-channel/index.md) 

Import from `@flue/whatsapp`.

## `createWhatsAppChannel()`

```
function createWhatsAppChannel<E extends Env = Env>(
  options: WhatsAppChannelOptions<E>,
): WhatsAppChannel<E>;
```

Creates `GET /webhook` for endpoint verification and `POST /webhook` for signed deliveries.

## `WhatsAppChannelOptions`

```
interface WhatsAppChannelOptions<E extends Env = Env> {
  appSecret: string;
  verifyToken: string;
  bodyLimit?: number;
  webhook(input: WhatsAppWebhookHandlerInput<E>): WhatsAppHandlerResult;
}
```

| Field       | Description                                                     |
| ----------- | --------------------------------------------------------------- |
| appSecret   | Meta app secret for exact-body HMAC-SHA256 verification.        |
| verifyToken | User-chosen token for GET challenge verification.               |
| bodyLimit   | Maximum POST body. Default: 3 \* 1024 \* 1024 bytes.            |
| webhook     | Callback for one verified delivery with every change preserved. |

The channel verifies the exact request bytes and then forwards Meta’s provider-native payload unmodified. It does not filter by business account or phone number; restricting to your configured phone number (`metadata.phone_number_id`) or business account (`entry[].id`) is application policy.

## `WhatsAppWebhookHandlerInput`

```
interface WhatsAppWebhookHandlerInput<E extends Env = Env> {
  c: Context<E>;
  payload: WhatsAppWebhookPayload;
}
```

`c` is the Hono request context. `payload` is the verified, provider-native WhatsApp Cloud API webhook object, forwarded unmodified.

```
type WhatsAppHandlerResult = WhatsAppHandlerValue | Promise<WhatsAppHandlerValue>;
// WhatsAppHandlerValue = undefined | JsonValue | Response
```

Returning nothing produces an empty `200`. A JSON-compatible value becomes the response body. An ordinary Hono or Fetch `Response` passes through. A thrown handler is not swallowed; it reaches Hono’s error handler.

## `WhatsAppChannel`

```
interface WhatsAppChannel<E extends Env = Env> {
  readonly routes: readonly ChannelRoute<E>[];
  conversationKey(ref: WhatsAppConversationRef): string;
  parseConversationKey(id: string): WhatsAppConversationRef;
}
```

A file named `channels/whatsapp.ts` serves GET and POST`/channels/whatsapp/webhook` relative to the `flue()` mount.

The channel does not persist or deduplicate deliveries. Conversation keys are canonical identifiers, not authorization capabilities.

## Payload

```
type WhatsAppWebhookPayload = WebhookPayload;
```

`payload` is Meta’s provider-native `whatsapp_business_account` webhook object, forwarded unmodified after exact-body signature verification. Its field names, nesting, and discriminants match Meta’s documented wire shape. One POST may batch multiple entries, changes, messages, and statuses; walk`payload.entry[].changes[]` in the order Meta sent them, narrowing on`change.field`, then on `message.type` or `status`.

The channel re-exports provider-shaped webhook types from the third-party, community-maintained[@whatsapp-cloudapi/types](https://www.npmjs.com/package/@whatsapp-cloudapi/types)package:

```
import type {
  WebhookChange,
  WebhookContact,
  WebhookConversation,
  WebhookEntry,
  WebhookError,
  WebhookMessage,
  WebhookMessageContact,
  WebhookMetadata,
  WebhookPayload,
  WebhookPricing,
  WebhookReferral,
  WebhookStatus,
  WebhookValue,
} from '@flue/whatsapp';
```

Within a `messages` change, `change.value.messages[].type` discriminates text, image, audio, video, document, sticker, location, contacts, interactive replies, legacy buttons, reactions, order, system, and unsupported messages. Authenticated future event and message shapes are still forwarded at runtime, but TypeScript consumers may need a cast or application type guard until the type package models them. The `change.value.statuses[].status` discriminant preserves `sent`, `delivered`, `read`, `played`, and `failed`. Do not dispatch or persist raw payloads wholesale.

## Identity

```
type WhatsAppConversationRef =
  | {
      type: 'individual';
      businessAccountId: string;
      phoneNumberId: string;
      destination:
        | {
            type: 'phone-number';
            phoneNumber: string;
          }
        | {
            type: 'user-id';
            userId: string;
          };
    }
  | {
      type: 'group';
      businessAccountId: string;
      phoneNumberId: string;
      groupId: string;
    };
```

Individual destinations distinguish phone numbers from Meta Business-Scoped User IDs so equal strings in different identity namespaces cannot collide. For stable inbound individual identity, derive the destination from the BSUID (`message.from_user_id`) even when `message.from` is also present. Groups remain keyed by provider `group_id`.

`conversationKey(ref)` serializes a canonical namespaced identifier suitable for a Flue agent-instance id; it is not an authorization capability.`parseConversationKey(id)` parses only keys produced by `conversationKey()` and throws `InvalidWhatsAppConversationKeyError` otherwise. The round trip is lossless across phone, BSUID, and group destinations.

## Errors

* `InvalidWhatsAppConversationKeyError`
* `InvalidWhatsAppInputError`, with structured `field`

See [WhatsApp setup](https://flueframework.com/docs/ecosystem/channels/whatsapp/) for Meta configuration and project-owned client composition.

## Docs Navigation

Current page: [WhatsApp Channel API](https://flueframework.com/docs/api/whatsapp-channel/)

### Sections

* [Guide](https://flueframework.com/docs/getting-started/quickstart/)
* [Reference](https://flueframework.com/docs/api/agent-api/)
* [CLI](https://flueframework.com/docs/cli/overview/)
* [SDK](https://flueframework.com/docs/sdk/overview/)
* [Ecosystem](https://flueframework.com/docs/ecosystem/)

### Runtime

* [ Configuration ](https://flueframework.com/docs/reference/configuration/)
* [ Errors Reference ](https://flueframework.com/docs/api/errors-reference/)
* [ Agent API ](https://flueframework.com/docs/api/agent-api/)
* [ Provider API ](https://flueframework.com/docs/api/provider-api/)
* [ Routing API ](https://flueframework.com/docs/api/routing-api/)
* [ GitHub Channel ](https://flueframework.com/docs/api/github-channel/)
* [ Stripe Channel ](https://flueframework.com/docs/api/stripe-channel/)
* [ Notion Channel ](https://flueframework.com/docs/api/notion-channel/)
* [ Resend Channel ](https://flueframework.com/docs/api/resend-channel/)
* [ Shopify Channel ](https://flueframework.com/docs/api/shopify-channel/)
* [ Intercom Channel ](https://flueframework.com/docs/api/intercom-channel/)
* [ Zendesk Channel ](https://flueframework.com/docs/api/zendesk-channel/)
* [ Salesforce Marketing Cloud Channel ](https://flueframework.com/docs/api/salesforce-marketing-cloud-channel/)
* [ Slack Channel ](https://flueframework.com/docs/api/slack-channel/)
* [ Discord Channel ](https://flueframework.com/docs/api/discord-channel/)
* [ Teams Channel ](https://flueframework.com/docs/api/teams-channel/)
* [ Google Chat Channel ](https://flueframework.com/docs/api/google-chat-channel/)
* [ Linear Channel ](https://flueframework.com/docs/api/linear-channel/)
* [ Telegram Channel ](https://flueframework.com/docs/api/telegram-channel/)
* [ WhatsApp Channel ](https://flueframework.com/docs/api/whatsapp-channel/)
* [ Twilio Channel ](https://flueframework.com/docs/api/twilio-channel/)
* [ Messenger Channel ](https://flueframework.com/docs/api/messenger-channel/)
* [ Streaming Protocol ](https://flueframework.com/docs/api/streaming-protocol/)
* [ Events Reference ](https://flueframework.com/docs/api/events-reference/)

### Advanced

* [ Sandbox Adapter API ](https://flueframework.com/docs/api/sandbox-api/)
* [ Data Persistence API ](https://flueframework.com/docs/api/data-persistence-api/)