Files
vercel__chat/packages/adapter-whatsapp
github-actions[bot] 3468cdfe0b chore(release): version packages (#767)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @chat-adapter/gchat@4.37.0

### Minor Changes

- c3b5a08: Bind Pub/Sub push verification to a specific identity with
the new pubsubServiceAccountEmail option, alongside the existing
audience check. Pushes are rejected unless the token email matches it.
Direct webhooks are unaffected.
- 7a19223: Bind Workspace Add-on webhook verification to a specific
identity with the new `workspaceAddOnServiceAccountEmail` option,
replacing a pattern match on the add-on service account email. Workspace
Add-on Chat apps must set it; standalone Chat apps are unaffected.

### Patch Changes

- Updated dependencies [2a2b2c5]
- Updated dependencies [4ac0455]
- Updated dependencies [0ec6a73]
- Updated dependencies [85e3d22]
  - chat@4.37.0
  - @chat-adapter/shared@4.37.0
## @chat-adapter/instagram@4.37.0

### Minor Changes

- 2a2b2c5: Add a native Instagram Direct Messages adapter with signed
webhooks, media, quick replies, story context, reactions, and typed Meta
API errors.

### Patch Changes

- Updated dependencies [2a2b2c5]
- Updated dependencies [4ac0455]
- Updated dependencies [0ec6a73]
- Updated dependencies [85e3d22]
  - chat@4.37.0
  - @chat-adapter/shared@4.37.0
## @chat-adapter/notion@4.37.0

### Minor Changes

- 0ec6a73: Add `@chat-adapter/notion` for Notion page and block comment
discussions: webhook HMAC verification, Post+Edit streaming,
conversation history, `message.subject` page metadata, plain-text
`@userName`/`@botUserId` mention detection, and File Uploads (up to 3
native attachments). Registers the adapter in the `chat/adapters`
catalog and `create-chat-sdk` CLI scaffold, and adds Notion emoji
platform support.

### Patch Changes

- Updated dependencies [2a2b2c5]
- Updated dependencies [4ac0455]
- Updated dependencies [0ec6a73]
- Updated dependencies [85e3d22]
  - chat@4.37.0
  - @chat-adapter/shared@4.37.0
## @chat-adapter/slack@4.37.0

### Minor Changes

- 4ac0455: Add message update and delete lifecycle callbacks, with Slack
message_changed and message_deleted dispatch support.

### Patch Changes

- 6f0d2f0: Resolve outgoing @name mentions on the Slack native streaming
path so streamed responses mention users consistently with the
post-and-edit fallback. Committed renderer text is resolved
incrementally, keeping fenced code literal and preserving the existing
ambiguity semantics.
- 4cc3445: Bound the length of bracketed URLs parsed from message text
in the link-unfurl fallback, avoiding a quadratic scan on adversarial
input. Valid links are unaffected.
- c311827: Preserve the Slack channel ID when converting labeled channel
tokens (`<#C123|general>` now becomes `#general (C123)`) so agents can
pass the ID to channel tools, and normalize the commonly hallucinated
`<label|url>` link order before Markdown conversion
- Updated dependencies [2a2b2c5]
- Updated dependencies [4ac0455]
- Updated dependencies [0ec6a73]
- Updated dependencies [85e3d22]
  - chat@4.37.0
  - @chat-adapter/shared@4.37.0
## @chat-adapter/whatsapp@4.37.0

### Minor Changes

- 6abf480: Add native WhatsApp LinkButton support

- A card whose only interactive element is a single `LinkButton` with a
non-empty label and an `http://` or `https://` URL is now sent as a
native `cta_url` interactive message, as long as the card has no header
image or image, table, chart, or inline link children and the post
carries no files or attachments.
- Link button URLs are now appended as `Label: url` lines to interactive
button message bodies and to media captions, instead of being dropped.
- Everything else is unchanged: non-matching cards keep the formatted
text fallback, and card + media posts keep the single captioned media
send.

- 16879fd: Fix the `WhatsAppInboundMessage.context` type to model all
documented webhook variants. The type previously declared `context?: {
from: string; id: string }`, but Meta's Cloud API sends mutually
exclusive context shapes: quoted replies carry `from`/`id`, forwarded
messages carry only `forwarded` or `frequently_forwarded` (no `id`), and
catalog product inquiries add `referred_product`. Code narrowed by the
old type could dereference `context.id` and crash at runtime on
forwarded messages. All context fields are now optional and the
forwarded/product-inquiry fields are included. Consumers that
dereference `context.from` or `context.id` without a guard will now see
a type error, surfacing what was already a latent crash on forwarded
messages.

### Patch Changes

- Updated dependencies [2a2b2c5]
- Updated dependencies [4ac0455]
- Updated dependencies [0ec6a73]
- Updated dependencies [85e3d22]
  - chat@4.37.0
  - @chat-adapter/shared@4.37.0
## chat@4.37.0

### Minor Changes

- 2a2b2c5: Add a native Instagram Direct Messages adapter with signed
webhooks, media, quick replies, story context, reactions, and typed Meta
API errors.
- 4ac0455: Add message update and delete lifecycle callbacks, with Slack
message_changed and message_deleted dispatch support.
- 0ec6a73: Add `@chat-adapter/notion` for Notion page and block comment
discussions: webhook HMAC verification, Post+Edit streaming,
conversation history, `message.subject` page metadata, plain-text
`@userName`/`@botUserId` mention detection, and File Uploads (up to 3
native attachments). Registers the adapter in the `chat/adapters`
catalog and `create-chat-sdk` CLI scaffold, and adds Notion emoji
platform support.
- 85e3d22: Close residual gaps in agent read-tool scoping.
`createChatTools`'s read guard now wraps modal, assistant-thread,
assistant-context, app-home, app-context, and member-joined dispatch so
tools built in those handlers inherit the active conversation, and it
logs a warning (instead of failing open silently) when a read runs with
no resolvable scope. Scoping stays channel-level by default, so a thread
scope still permits sibling threads in its channel. Pass the new
`strictScope: true` to confine a thread scope to that thread alone,
rejecting both sibling threads and the parent channel, which matters on
platforms where a channel is the widest read available (a GitHub channel
is an entire repo).

Note that reads inside those newly wrapped handlers were previously
unscoped. An agent built in an `onModalSubmit`, `onAppHomeOpened`, or
`onMemberJoinedChannel` handler that reads another channel will now be
rejected. Pass an explicit `scope`, or `scope: false` for intentionally
workspace-wide reads.
## create-chat-sdk@0.3.0

### Minor Changes

- 2a2b2c5: Add a native Instagram Direct Messages adapter with signed
webhooks, media, quick replies, story context, reactions, and typed Meta
API errors.
- 0ec6a73: Add `@chat-adapter/notion` for Notion page and block comment
discussions: webhook HMAC verification, Post+Edit streaming,
conversation history, `message.subject` page metadata, plain-text
`@userName`/`@botUserId` mention detection, and File Uploads (up to 3
native attachments). Registers the adapter in the `chat/adapters`
catalog and `create-chat-sdk` CLI scaffold, and adds Notion emoji
platform support.
## @chat-adapter/discord@4.37.0

### Patch Changes

- Updated dependencies [2a2b2c5]
- Updated dependencies [4ac0455]
- Updated dependencies [0ec6a73]
- Updated dependencies [85e3d22]
  - chat@4.37.0
  - @chat-adapter/shared@4.37.0
## @chat-adapter/github@4.37.0

### Patch Changes

- Updated dependencies [2a2b2c5]
- Updated dependencies [4ac0455]
- Updated dependencies [0ec6a73]
- Updated dependencies [85e3d22]
  - chat@4.37.0
  - @chat-adapter/shared@4.37.0
## @chat-adapter/linear@4.37.0

### Patch Changes

- Updated dependencies [2a2b2c5]
- Updated dependencies [4ac0455]
- Updated dependencies [0ec6a73]
- Updated dependencies [85e3d22]
  - chat@4.37.0
  - @chat-adapter/shared@4.37.0
## @chat-adapter/messenger@4.37.0

### Patch Changes

- Updated dependencies [2a2b2c5]
- Updated dependencies [4ac0455]
- Updated dependencies [0ec6a73]
- Updated dependencies [85e3d22]
  - chat@4.37.0
  - @chat-adapter/shared@4.37.0
## @chat-adapter/shared@4.37.0

### Patch Changes

- Updated dependencies [2a2b2c5]
- Updated dependencies [4ac0455]
- Updated dependencies [0ec6a73]
- Updated dependencies [85e3d22]
  - chat@4.37.0
## @chat-adapter/teams@4.37.0

### Patch Changes

- 4cc3445: Harden Teams HTML-to-text conversion to strip tags until the
output is stable, so nested or malformed markup can't leave a partial
tag behind. `stripHtmlTags` is now shared across the format and Graph
message converters.
- Updated dependencies [2a2b2c5]
- Updated dependencies [4ac0455]
- Updated dependencies [0ec6a73]
- Updated dependencies [85e3d22]
  - chat@4.37.0
  - @chat-adapter/shared@4.37.0
## @chat-adapter/telegram@4.37.0

### Patch Changes

- 629e655: Combine incoming Telegram media groups into one message with
ordered attachments and the shared caption.
- Updated dependencies [2a2b2c5]
- Updated dependencies [4ac0455]
- Updated dependencies [0ec6a73]
- Updated dependencies [85e3d22]
  - chat@4.37.0
  - @chat-adapter/shared@4.37.0
## @chat-adapter/twilio@4.37.0

### Patch Changes

- Updated dependencies [2a2b2c5]
- Updated dependencies [4ac0455]
- Updated dependencies [0ec6a73]
- Updated dependencies [85e3d22]
  - chat@4.37.0
  - @chat-adapter/shared@4.37.0
## @chat-adapter/web@4.37.0

### Patch Changes

- Updated dependencies [2a2b2c5]
- Updated dependencies [4ac0455]
- Updated dependencies [0ec6a73]
- Updated dependencies [85e3d22]
  - chat@4.37.0
  - @chat-adapter/shared@4.37.0
## @chat-adapter/x@4.37.0

### Patch Changes

- b674923: Restrict the X CRC challenge to the opaque token shape X
sends before signing it. The endpoint previously returned an HMAC over
any `crc_token`, which let a caller have an arbitrary webhook body
signed and replay that as `x-twitter-webhooks-signature` on a forged
POST. A webhook body is JSON and can no longer pass the token check, so
a CRC response can't double as a POST event signature.
- Updated dependencies [2a2b2c5]
- Updated dependencies [4ac0455]
- Updated dependencies [0ec6a73]
- Updated dependencies [85e3d22]
  - chat@4.37.0
  - @chat-adapter/shared@4.37.0
## @chat-adapter/state-ioredis@4.37.0

### Patch Changes

- Updated dependencies [2a2b2c5]
- Updated dependencies [4ac0455]
- Updated dependencies [0ec6a73]
- Updated dependencies [85e3d22]
  - chat@4.37.0
## @chat-adapter/state-memory@4.37.0

### Patch Changes

- Updated dependencies [2a2b2c5]
- Updated dependencies [4ac0455]
- Updated dependencies [0ec6a73]
- Updated dependencies [85e3d22]
  - chat@4.37.0
## @chat-adapter/state-pg@4.37.0

### Patch Changes

- Updated dependencies [2a2b2c5]
- Updated dependencies [4ac0455]
- Updated dependencies [0ec6a73]
- Updated dependencies [85e3d22]
  - chat@4.37.0
## @chat-adapter/state-redis@4.37.0

### Patch Changes

- Updated dependencies [2a2b2c5]
- Updated dependencies [4ac0455]
- Updated dependencies [0ec6a73]
- Updated dependencies [85e3d22]
  - chat@4.37.0
## @chat-adapter/tests@4.37.0

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-07 18:30:01 +01:00
..

WhatsApp Business Cloud adapter for Chat SDK

@chat-adapter/whatsapp

npm package: @chat-adapter/whatsapp

Agent Stack MIT License

WhatsApp Business Cloud adapter for Chat SDK, using the WhatsApp Business Cloud API.

Documentation: chat-sdk.dev/adapters/official/whatsapp · Guides: vercel.com/kb/chat-sdk

Installation

pnpm add @chat-adapter/whatsapp

Scaffold with the CLI

To scaffold a new WhatsApp bot with this adapter preselected:

npx create-chat-sdk@latest my-bot --adapter whatsapp memory

Visit the adapters directory to see other available official and vendor-official adapters.

Usage

import { Chat } from "chat";
import { createWhatsAppAdapter } from "@chat-adapter/whatsapp";

const bot = new Chat({
  userName: "mybot",
  adapters: {
    whatsapp: createWhatsAppAdapter(),
  },
});

bot.onNewMention(async (thread, message) => {
  await thread.post("Hello from WhatsApp!");
});

When using createWhatsAppAdapter() without arguments, credentials are auto-detected from environment variables.

WhatsApp Business setup

1. Create a Meta app

  1. Go to developers.facebook.com/apps
  2. Click Create App, select Business type
  3. Add the WhatsApp product to your app
  4. Go to WhatsApp > API Setup and note your Phone Number ID and Access Token

2. Configure webhooks

  1. Go to WhatsApp > Configuration in your Meta app
  2. Set Callback URL to https://your-domain.com/api/webhooks/whatsapp
  3. Set Verify Token to a secret string of your choice (this becomes WHATSAPP_VERIFY_TOKEN)
  4. Subscribe to the messages webhook field

3. Get credentials

From your Meta app dashboard, copy:

  • App Secret (under App Settings > Basic) as WHATSAPP_APP_SECRET
  • Access Token (under WhatsApp > API Setup) as WHATSAPP_ACCESS_TOKEN
  • Phone Number ID (under WhatsApp > API Setup) as WHATSAPP_PHONE_NUMBER_ID

For production, generate a permanent System User Token instead of the temporary access token.

Configuration

All options are auto-detected from environment variables when not provided. You can call createWhatsAppAdapter() with no arguments if the env vars are set.

Option Required Description
accessToken No* Meta access token. Auto-detected from WHATSAPP_ACCESS_TOKEN
appSecret No* App secret for webhook verification. Auto-detected from WHATSAPP_APP_SECRET
phoneNumberId No* Bot's phone number ID. Auto-detected from WHATSAPP_PHONE_NUMBER_ID
verifyToken No* Webhook verification secret. Auto-detected from WHATSAPP_VERIFY_TOKEN
apiVersion No Graph API version (defaults to v25.0)
userName No Bot username for self-message detection. Auto-detected from WHATSAPP_BOT_USERNAME (defaults to whatsapp-bot)
apiUrl No Override the Meta Graph API base URL. Auto-detected from WHATSAPP_API_URL
logger No Logger instance (defaults to ConsoleLogger("info"))

*Required at runtime — either via config or environment variable.

Environment variables

WHATSAPP_ACCESS_TOKEN=...          # Meta access token (permanent or system user token)
WHATSAPP_APP_SECRET=...            # App secret for X-Hub-Signature-256 verification
WHATSAPP_PHONE_NUMBER_ID=...       # Bot's phone number ID from Meta dashboard
WHATSAPP_VERIFY_TOKEN=...          # User-defined secret for webhook verification
WHATSAPP_BOT_USERNAME=...          # Optional, defaults to "whatsapp-bot"
WHATSAPP_API_URL=...               # Optional, override the Meta Graph API base URL

Webhook setup

WhatsApp uses two webhook mechanisms:

  1. Verification handshake (GET) — Meta sends a hub.verify_token challenge that must match your WHATSAPP_VERIFY_TOKEN.
  2. Event delivery (POST) — incoming messages, reactions, and interactive responses, verified via X-Hub-Signature-256.
// Next.js App Router example
import { bot } from "@/lib/bot";

export async function GET(request: Request) {
  return bot.webhooks.whatsapp(request);
}

export async function POST(request: Request) {
  return bot.webhooks.whatsapp(request);
}

Features

Messaging

Feature Supported
Post message Yes
Edit message No (WhatsApp limitation)
Delete message No (WhatsApp limitation)
Streaming Buffered (accumulates then sends)
Mark as read Yes
Auto-chunking Yes (splits at 4096 chars)
Template messages Yes (via sendTemplate)

Rich content

Feature Supported
Interactive buttons Yes (up to 3)
Link buttons Partial (single link button becomes a native CTA URL message)
Button title limit 20 characters
List messages Yes
Text fallback Yes (for >3 buttons)

Conversations

Feature Supported
Reactions Yes (add and remove)
Typing indicator Yes (requires a recent inbound message, marks it as read, and displays for up to 25 seconds)
DMs Yes
Open DM Yes

Typing indicators

WhatsApp supports typing indicators through thread.startTyping() or adapter.startTyping(threadId).

Use it when the bot is about to respond and may take a few seconds. The adapter uses the most recent inbound message ID from thread history, so startTyping() only works after the bot has received a message.

await thread.startTyping();

await thread.post({
  markdown: "Thanks, I am checking that now.",
});

WhatsApp-specific behavior:

  • If there is no inbound message context, startTyping() no-ops.
  • The typing indicator is dismissed when the bot sends its reply, or after the WhatsApp platform timeout.

Incoming message types

Type Supported
Text Yes
Images Yes (with captions)
Documents Yes (with captions)
Audio / Voice Yes
Video Yes (with captions)
Stickers Yes
Locations Yes (converted to map URL)
Interactive replies Yes (button and list)
Reactions Yes

Message history

Feature Supported
Fetch messages No (Cloud API limitation)
Fetch thread info Yes

Interactive messages

Card elements are automatically converted to WhatsApp interactive messages:

  • 3 or fewer buttons — rendered as WhatsApp reply buttons (max 20 chars per title)
  • More than 3 buttons — falls back to formatted text
  • Max body text — 1024 characters

When a card with reply buttons also contains link buttons, each link button is appended to the interactive message body as a Label: url line, since WhatsApp reply buttons cannot open URLs.

A card whose only interactive element is a single link button is sent as a native CTA URL message with a tappable link button. The card is promoted only when all of these hold:

  • The link button is the card's only action across every actions row, including rows nested in sections. Reply buttons, selects, radio selects, or a second populated actions row keep the text fallback.
  • The URL starts with http:// or https:// and the label is non-empty. Other schemes (mailto:, tel:, relative paths) keep the text fallback because the Cloud API rejects them.
  • The card has no header image and no image, table, chart, or inline link children. Text, fields, sections, and dividers are fine.
  • The post has no files or attachments. When media accompanies the card, the adapter keeps the single captioned media send, and the caption includes a Label: url line for each link button.

The button label is truncated to 20 characters, the header (card title) to 60, and the body to 1024. Cards that do not match these rules fall back to formatted text, where link buttons render as Label: url.

Template messages

Outside the 24-hour customer service window, WhatsApp only accepts pre-approved template messages. Use sendTemplate to start business-initiated conversations:

const threadId = await adapter.openDM("15551234567");

await adapter.sendTemplate(threadId, {
  name: "appointment_reminder",
  language: "en",
  components: [
    {
      type: "body",
      parameters: [{ type: "text", text: "Tomorrow at 2pm" }],
    },
  ],
});

Templates must be created and approved in WhatsApp Manager before they can be sent. Quick reply button taps on a template arrive as button responses and are dispatched to your onAction handlers.

Thread ID format

whatsapp:{phoneNumberId}:{userWaId}

Example: whatsapp:1234567890:15551234567

Troubleshooting

Webhook verification failing

  • Confirm WHATSAPP_VERIFY_TOKEN matches the value you entered in the Meta dashboard
  • Ensure your endpoint returns the hub.challenge value for GET requests

Messages not arriving

  • Check that you subscribed to the messages webhook field in Meta app settings
  • Verify WHATSAPP_APP_SECRET is correct — signature verification silently rejects invalid payloads
  • Ensure your phone number is registered and verified in the WhatsApp Business dashboard

"Invalid signature" errors

  • Double-check WHATSAPP_APP_SECRET matches the value under App Settings > Basic
  • The adapter uses HMAC-SHA256 to verify the X-Hub-Signature-256 header

Token expired

  • Temporary tokens from the API Setup page expire after 24 hours
  • For production, create a System User in Meta Business Suite and generate a permanent token

AI Coding Agents

If you use an AI coding agent such as OpenAI Codex, Claude Code, or Cursor, install the Chat SDK skill so it knows the SDK APIs, adapter patterns, and project conventions before writing code.

npx skills add vercel/chat

The skill references bundled documentation in node_modules/chat/docs, plus adapter guides and starter templates in the published package.

You can also install the Vercel Plugin for a broader agent toolkit — it includes the Chat SDK skill alongside specialist agents, agent slash commands, and more:

npx plugins add vercel/vercel-plugin

The plugin is optional; the skill alone is enough to build with Chat SDK.

For agent-readable documentation, see chat-sdk.dev/llms.txt (page index) or chat-sdk.dev/llms-full.txt (full text).

License

MIT