Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
f85f2b7
feat: add WhatsApp Business Cloud API adapter
ghellach Feb 25, 2026
28e3365
feat: add media download, attachments, and location support to WhatsA…
ghellach Feb 25, 2026
2669f10
fix: address PR review feedback for WhatsApp adapter
ghellach Feb 25, 2026
b1d781f
Migrate improvements from #179
haydenbleasel Mar 6, 2026
b11625b
fix(whatsapp): add error handling for inbound message processing
haydenbleasel Mar 6, 2026
2300a84
fix(whatsapp): prevent markdown regex from matching across newlines
haydenbleasel Mar 6, 2026
46375cf
fix(whatsapp): use WhatsAppInteractiveMessage type instead of object
haydenbleasel Mar 6, 2026
34ee565
fix(whatsapp): hoist emoji mapping to module-level constant
haydenbleasel Mar 6, 2026
1d022cd
fix(whatsapp): remove duplicate JSDoc comment in types
haydenbleasel Mar 6, 2026
8fccba7
fix(example): add startTyping to WhatsApp recording methods
haydenbleasel Mar 6, 2026
6fcc74f
fix(whatsapp): fix formatting and add package to readme test allowlist
haydenbleasel Mar 6, 2026
4eb3157
fix(whatsapp): use defaultEmojiResolver instead of custom emoji map
haydenbleasel Mar 6, 2026
e020253
fix(whatsapp): make Graph API version configurable
haydenbleasel Mar 7, 2026
f8b4e98
fix(whatsapp): validate lat/lng before constructing Google Maps URL
haydenbleasel Mar 7, 2026
8a66a74
fix(whatsapp): throw on editMessage instead of silently sending new m…
haydenbleasel Mar 7, 2026
8f00a87
fix(whatsapp): document regex asymmetry between toWhatsApp and fromWh…
haydenbleasel Mar 7, 2026
c132f61
fix(whatsapp): document callback data passthrough behavior
haydenbleasel Mar 7, 2026
08eeaff
fix(whatsapp): add editMessage and deleteMessage to recording methods
haydenbleasel Mar 7, 2026
18a6983
fix(whatsapp): preserve escaped formatting chars in toWhatsAppFormat
haydenbleasel Mar 7, 2026
35e5d62
fix(whatsapp): split long messages instead of truncating
haydenbleasel Mar 7, 2026
31514f8
fix(whatsapp): align editMessage/deleteMessage behavior and docs
haydenbleasel Mar 7, 2026
f6e7a93
docs(whatsapp): add adapter documentation page
haydenbleasel Mar 7, 2026
645d022
fix: add whatsapp adapter debug logging and try/catch
cramforce Mar 8, 2026
cb1aac2
fix(whatsapp): convert emoji placeholders in outgoing messages
cramforce Mar 8, 2026
28ff40b
Fix button rendering and streaming (needs to buffer)
cramforce Mar 8, 2026
20574fd
fix(example): handle editMessage failure on WhatsApp
cramforce Mar 8, 2026
37133a3
feat(chat): add onDirectMessage handler, stop treating DMs as mentions
haydenbleasel Mar 8, 2026
e7165f2
feat(chat): always route DMs to onDirectMessage regardless of subscri…
cramforce Mar 8, 2026
661fe29
feat(chat): pass channel as third argument to DirectMessageHandler
cramforce Mar 8, 2026
f758e5d
fix(example): reply to channel instead of thread in DM handler
cramforce Mar 8, 2026
e4c9c03
fix(example): use thread instead of channel for DM operations
cramforce Mar 8, 2026
445df61
Revert "fix(example): use thread instead of channel for DM operations"
cramforce Mar 8, 2026
56e436d
fix(adapters): return valid thread IDs from channelIdFromThreadId
cramforce Mar 8, 2026
20d3716
fix(chat): normalize fullStream in Channel.post() to extract text deltas
cramforce Mar 8, 2026
5b2a3c8
fix(example): use thread.allMessages for DM history instead of adapte…
cramforce Mar 8, 2026
9f029dc
feat(chat): add message history support to Channel for DM platforms
cramforce Mar 8, 2026
f81e336
fix(whatsapp): improve markdown rendering and remove broken typing in…
cramforce Mar 8, 2026
c4b0209
fix(example): reverse channel.messages to chronological order for AI
cramforce Mar 8, 2026
c551d30
fix(chat): auto-sort messages chronologically in toAiMessages
cramforce Mar 8, 2026
5de587a
fix(chat): pass accumulated stream text as markdown in Channel.post()
cramforce Mar 8, 2026
c393ac5
fix(whatsapp): use stringifier options for emphasis and bullets
cramforce Mar 8, 2026
47c69fc
fix(whatsapp): flatten bold inside headings to avoid triple asterisks
cramforce Mar 8, 2026
3195b45
test(whatsapp): add full toBe assertion for complex markdown conversion
cramforce Mar 8, 2026
6169001
test: add WhatsApp replay tests from production recordings
cramforce Mar 8, 2026
fdd763b
Merge main into feat/adapter-whatsapp
ghellach Mar 8, 2026
71d543b
fix(whatsapp): fix type narrowing in replay test after merge
ghellach Mar 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/add-whatsapp-adapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@chat-adapter/whatsapp": minor
---

Add WhatsApp adapter using Meta's WhatsApp Business Cloud API
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ This is a **pnpm monorepo** using **Turborepo** for build orchestration. All pac
- **`packages/adapter-teams`** - Microsoft Teams adapter using `botbuilder`
- **`packages/state-memory`** - In-memory state adapter (for development/testing)
- **`packages/state-redis`** - Redis state adapter (for production)
- **`packages/adapter-whatsapp`** - WhatsApp adapter using Meta Cloud API
- **`packages/integration-tests`** - Integration tests against real platform APIs
- **`examples/nextjs-chat`** - Example Next.js app showing how to use the SDK

Expand Down Expand Up @@ -211,6 +212,7 @@ Key env vars used (see `turbo.json` for full list):
- `SLACK_BOT_TOKEN`, `SLACK_SIGNING_SECRET` - Slack credentials
- `TEAMS_APP_ID`, `TEAMS_APP_PASSWORD`, `TEAMS_APP_TENANT_ID` - Teams credentials
- `GOOGLE_CHAT_CREDENTIALS` or `GOOGLE_CHAT_USE_ADC` - Google Chat auth
- `WHATSAPP_ACCESS_TOKEN`, `WHATSAPP_APP_SECRET`, `WHATSAPP_PHONE_NUMBER_ID`, `WHATSAPP_VERIFY_TOKEN` - WhatsApp credentials
- `REDIS_URL` - Redis connection for state adapter
- `BOT_USERNAME` - Default bot username

Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![npm downloads](https://img.shields.io/npm/dm/chat)](https://www.npmjs.com/package/chat)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)

A unified TypeScript SDK for building chat bots across Slack, Microsoft Teams, Google Chat, Discord, Telegram, GitHub, and Linear. Write your bot logic once, deploy everywhere.
A unified TypeScript SDK for building chat bots across Slack, Microsoft Teams, Google Chat, Discord, Telegram, GitHub, Linear, and WhatsApp. Write your bot logic once, deploy everywhere.

## Installation

Expand Down Expand Up @@ -56,6 +56,7 @@ See the [Getting Started guide](https://chat-sdk.dev/docs/getting-started) for a
| Telegram | `@chat-adapter/telegram` | Yes | Yes | Partial | No | Post+Edit | Yes |
| GitHub | `@chat-adapter/github` | Yes | Yes | No | No | No | No |
| Linear | `@chat-adapter/linear` | Yes | Yes | No | No | No | No |
| WhatsApp | `@chat-adapter/whatsapp` | N/A | Yes | Partial | No | No | Yes |

## Features

Expand All @@ -82,6 +83,7 @@ See the [Getting Started guide](https://chat-sdk.dev/docs/getting-started) for a
| `@chat-adapter/telegram` | [Telegram adapter](https://chat-sdk.dev/docs/adapters/telegram) |
| `@chat-adapter/github` | [GitHub adapter](https://chat-sdk.dev/docs/adapters/github) |
| `@chat-adapter/linear` | [Linear adapter](https://chat-sdk.dev/docs/adapters/linear) |
| `@chat-adapter/whatsapp` | [WhatsApp adapter](https://chat-sdk.dev/docs/adapters/whatsapp) |
| `@chat-adapter/state-redis` | [Redis state adapter](https://chat-sdk.dev/docs/state/redis) (production) |
| `@chat-adapter/state-ioredis` | [ioredis state adapter](https://chat-sdk.dev/docs/state/ioredis) (alternative) |
| `@chat-adapter/state-pg` | [PostgreSQL state adapter](https://chat-sdk.dev/docs/state/postgres) (production) |
Expand Down
73 changes: 37 additions & 36 deletions apps/docs/content/docs/adapters/index.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Overview
description: Platform-specific adapters for Slack, Teams, Google Chat, Discord, Telegram, GitHub, and Linear.
description: Platform-specific adapters for Slack, Teams, Google Chat, Discord, Telegram, GitHub, Linear, and WhatsApp.
type: overview
prerequisites:
- /docs/getting-started
Expand All @@ -12,50 +12,50 @@ Adapters handle webhook verification, message parsing, and API calls for each pl

### Messaging

| Feature | [Slack](/docs/adapters/slack) | [Teams](/docs/adapters/teams) | [Google Chat](/docs/adapters/gchat) | [Discord](/docs/adapters/discord) | [Telegram](/docs/adapters/telegram) | [GitHub](/docs/adapters/github) | [Linear](/docs/adapters/linear) |
|---------|-------|-------|-------------|---------|---------|--------|--------|
| Post message | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Edit message | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Delete message | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| File uploads | ✅ | ✅ | ❌ | ✅ | ⚠️ Single file | ❌ | ❌ |
| Streaming | ✅ Native | ⚠️ Post+Edit | ⚠️ Post+Edit | ⚠️ Post+Edit | ⚠️ Post+Edit | ❌ | ❌ |
| Feature | [Slack](/docs/adapters/slack) | [Teams](/docs/adapters/teams) | [Google Chat](/docs/adapters/gchat) | [Discord](/docs/adapters/discord) | [Telegram](/docs/adapters/telegram) | [GitHub](/docs/adapters/github) | [Linear](/docs/adapters/linear) | [WhatsApp](/docs/adapters/whatsapp) |
|---------|-------|-------|-------------|---------|---------|--------|--------|----------|
| Post message | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Edit message | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| Delete message | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| File uploads | ✅ | ✅ | ❌ | ✅ | ⚠️ Single file | ❌ | ❌ | ⚠️ Receive |
| Streaming | ✅ Native | ⚠️ Post+Edit | ⚠️ Post+Edit | ⚠️ Post+Edit | ⚠️ Post+Edit | ❌ | ❌ | ❌ |

### Rich content

| Feature | Slack | Teams | Google Chat | Discord | Telegram | GitHub | Linear |
|---------|-------|-------|-------------|---------|----------|--------|--------|
| Card format | Block Kit | Adaptive Cards | Google Chat Cards | Embeds | Markdown + inline keyboard buttons | GFM Markdown | Markdown |
| Buttons | ✅ | ✅ | ✅ | ✅ | ⚠️ Inline keyboard callbacks | ❌ | ❌ |
| Link buttons | ✅ | ✅ | ✅ | ✅ | ⚠️ Inline keyboard URLs | ❌ | ❌ |
| Select menus | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
| Tables | ✅ Block Kit | ✅ GFM | ⚠️ ASCII | ✅ GFM | ⚠️ ASCII | ✅ GFM | ✅ GFM |
| Fields | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Images in cards | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ |
| Modals | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Feature | Slack | Teams | Google Chat | Discord | Telegram | GitHub | Linear | WhatsApp |
|---------|-------|-------|-------------|---------|----------|--------|--------|----------|
| Card format | Block Kit | Adaptive Cards | Google Chat Cards | Embeds | Markdown + inline keyboard buttons | GFM Markdown | Markdown | Interactive / Text |
| Buttons | ✅ | ✅ | ✅ | ✅ | ⚠️ Inline keyboard callbacks | ❌ | ❌ | ⚠️ Max 3 |
| Link buttons | ✅ | ✅ | ✅ | ✅ | ⚠️ Inline keyboard URLs | ❌ | ❌ | ❌ |
| Select menus | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Tables | ✅ Block Kit | ✅ GFM | ⚠️ ASCII | ✅ GFM | ⚠️ ASCII | ✅ GFM | ✅ GFM | ❌ |
| Fields | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Images in cards | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ |
| Modals | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |

### Conversations

| Feature | Slack | Teams | Google Chat | Discord | Telegram | GitHub | Linear |
|---------|-------|-------|-------------|---------|----------|--------|--------|
| Slash commands | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
| Mentions | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Add reactions | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Remove reactions | ✅ | ❌ | ✅ | ✅ | ✅ | ⚠️ | ⚠️ |
| Typing indicator | ❌ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ |
| DMs | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
| Ephemeral messages | ✅ Native | ❌ | ✅ Native | ❌ | ❌ | ❌ | ❌ |
| Feature | Slack | Teams | Google Chat | Discord | Telegram | GitHub | Linear | WhatsApp |
|---------|-------|-------|-------------|---------|----------|--------|--------|----------|
| Slash commands | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
| Mentions | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | N/A |
| Add reactions | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Remove reactions | ✅ | ❌ | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | ✅ |
| Typing indicator | ❌ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ |
| DMs | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ |
| Ephemeral messages | ✅ Native | ❌ | ✅ Native | ❌ | ❌ | ❌ | ❌ | ❌ |

### Message history

| Feature | Slack | Teams | Google Chat | Discord | Telegram | GitHub | Linear |
|---------|-------|-------|-------------|---------|----------|--------|--------|
| Fetch messages | ✅ | ✅ | ✅ | ✅ | ⚠️ Cached | ✅ | ✅ |
| Fetch single message | ✅ | ❌ | ❌ | ❌ | ⚠️ Cached | ❌ | ❌ |
| Fetch thread info | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Fetch channel messages | ✅ | ✅ | ✅ | ✅ | ⚠️ Cached | ✅ | ❌ |
| List threads | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ |
| Fetch channel info | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| Post channel message | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
| Feature | Slack | Teams | Google Chat | Discord | Telegram | GitHub | Linear | WhatsApp |
|---------|-------|-------|-------------|---------|----------|--------|--------|----------|
| Fetch messages | ✅ | ✅ | ✅ | ✅ | ⚠️ Cached | ✅ | ✅ | ❌ |
| Fetch single message | ✅ | ❌ | ❌ | ❌ | ⚠️ Cached | ❌ | ❌ | ❌ |
| Fetch thread info | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Fetch channel messages | ✅ | ✅ | ✅ | ✅ | ⚠️ Cached | ✅ | ❌ | ❌ |
| List threads | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ |
| Fetch channel info | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
| Post channel message | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |

<Callout type="info">
⚠️ indicates partial support — the feature works with limitations. See individual adapter pages for details.
Expand All @@ -72,6 +72,7 @@ Adapters handle webhook verification, message parsing, and API calls for each pl
| [Telegram](/docs/adapters/telegram) | `@chat-adapter/telegram` |
| [GitHub](/docs/adapters/github) | `@chat-adapter/github` |
| [Linear](/docs/adapters/linear) | `@chat-adapter/linear` |
| [WhatsApp](/docs/adapters/whatsapp) | `@chat-adapter/whatsapp` |

## Community adapters

Expand Down
3 changes: 2 additions & 1 deletion apps/docs/content/docs/adapters/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"discord",
"telegram",
"github",
"linear"
"linear",
"whatsapp"
]
}
222 changes: 222 additions & 0 deletions apps/docs/content/docs/adapters/whatsapp.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
---
title: WhatsApp
description: Configure the WhatsApp adapter for the WhatsApp Business Cloud API.
type: integration
prerequisites:
- /docs/getting-started
---

## Installation

```sh title="Terminal"
pnpm add @chat-adapter/whatsapp
```

## Usage

The adapter auto-detects `WHATSAPP_ACCESS_TOKEN`, `WHATSAPP_APP_SECRET`, `WHATSAPP_PHONE_NUMBER_ID`, and `WHATSAPP_VERIFY_TOKEN` from environment variables:

```typescript title="lib/bot.ts" lineNumbers
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(`You said: ${message.text}`);
});
```

Since all WhatsApp conversations are 1:1 DMs, every incoming message is treated as a mention.

## Webhook route

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`.

Both are handled by the same `handleWebhook` method:

```typescript title="app/api/webhooks/whatsapp/route.ts" lineNumbers
import { bot } from "@/lib/bot";

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

export async function POST(request: Request): Promise<Response> {
return bot.webhooks.whatsapp(request);
}
```

## Meta app setup

### 1. Create a Meta app

1. Go to [developers.facebook.com/apps](https://developers.facebook.com/apps)
2. Click **Create App** and select **Business** type
3. Give it a name and click **Create App**

### 2. Add WhatsApp product

1. In the app dashboard, find **WhatsApp** and click **Set Up**
2. This creates a test phone number and sandbox environment

### 3. Get credentials

From the app dashboard:

| Credential | Where to find it |
|---|---|
| **Access Token** | WhatsApp > API Setup > Temporary access token (or create a System User token for production) |
| **Phone Number ID** | WhatsApp > API Setup > Phone number ID |
| **App Secret** | Settings > Basic > App Secret |
| **Verify Token** | You define this — any secret string you choose |

### 4. Configure webhooks

1. Go to **WhatsApp** > **Configuration** in your app dashboard
2. Click **Edit** next to Webhook URL
3. Set **Callback URL** to `https://your-domain.com/api/webhooks/whatsapp`
4. Set **Verify token** to the same value as your `WHATSAPP_VERIFY_TOKEN`
5. Click **Verify and Save**
6. Subscribe to the **messages** webhook field

### 5. Production access

For production use:

1. Add a real phone number under **WhatsApp** > **API Setup**
2. Create a **System User** in Meta Business Suite for a permanent access token
3. Complete Meta's **App Review** process for the `whatsapp_business_messaging` permission

## Interactive messages

Card elements are automatically converted to WhatsApp interactive messages:

- **3 or fewer buttons** — rendered as WhatsApp reply buttons (title max 20 characters)
- **More than 3 buttons** — falls back to formatted text

```typescript title="lib/bot.ts" lineNumbers
import { Card, Actions, Button, Body, BodyText } from "chat";

bot.onNewMention(async (thread) => {
await thread.post(
<Card>
<Body>
<BodyText>How can I help?</BodyText>
</Body>
<Actions>
<Button id="help" value="help">Get Help</Button>
<Button id="status" value="status">Check Status</Button>
</Actions>
</Card>
);
});
```

## Media attachments

Incoming media messages (images, documents, audio, video, voice, stickers) are exposed as attachments with a lazy `fetchData()` function. Media is downloaded in two steps via the Graph API — first fetching the media URL, then downloading the binary data.

```typescript title="lib/bot.ts" lineNumbers
bot.onNewMention(async (thread, message) => {
for (const attachment of message.attachments) {
if (attachment.fetchData) {
const data = await attachment.fetchData();
// Process the media buffer...
}
}
});
```

## 24-hour messaging window

WhatsApp enforces a [24-hour customer service window](https://developers.facebook.com/docs/whatsapp/cloud-api/guides/send-messages#customer-service-windows). You can only send free-form messages to a user within 24 hours of their last message. After that, you must use approved **message templates**.

## Configuration

All options are auto-detected from environment variables when not provided.

| Option | Required | Description |
|--------|----------|-------------|
| `accessToken` | No* | Meta access token. Auto-detected from `WHATSAPP_ACCESS_TOKEN` |
| `appSecret` | No* | App secret for webhook signature verification. Auto-detected from `WHATSAPP_APP_SECRET` |
| `phoneNumberId` | No* | Bot's phone number ID. Auto-detected from `WHATSAPP_PHONE_NUMBER_ID` |
| `verifyToken` | No* | Secret for webhook verification handshake. Auto-detected from `WHATSAPP_VERIFY_TOKEN` |
| `apiVersion` | No | Graph API version (defaults to `v21.0`) |
| `userName` | No | Bot username. Auto-detected from `WHATSAPP_BOT_USERNAME` or defaults to `whatsapp-bot` |
| `logger` | No | Logger instance (defaults to `ConsoleLogger("info")`) |

*All four credentials are required — either via config or environment variables.

## Environment variables

```bash title=".env.local"
WHATSAPP_ACCESS_TOKEN=EAAx... # Meta access token
WHATSAPP_APP_SECRET=abc123... # App secret for signature verification
WHATSAPP_PHONE_NUMBER_ID=1234567890 # Phone number ID from Meta dashboard
WHATSAPP_VERIFY_TOKEN=my-secret # Your chosen webhook verify token
```

## Features

| Feature | Supported |
|---------|-----------|
| Mentions | N/A (all messages are DMs) |
| Reactions (add/remove) | Yes |
| Cards | Interactive messages (max 3 buttons) / text fallback |
| Modals | No |
| Streaming | No |
| DMs | Yes (all conversations) |
| Ephemeral messages | No |
| File uploads | Receive only |
| Typing indicator | Yes |
| Message history | No |
| Edit message | No (throws) |
| Delete message | No (throws) |

## Thread ID format

```
whatsapp:{phoneNumberId}:{userWaId}
```

Example: `whatsapp:1234567890:15551234567`

## Notes

- All WhatsApp conversations are 1:1 DMs between the business phone number and the user, so every message sets `isMention: true`.
- `editMessage()` and `deleteMessage()` throw errors — WhatsApp does not support these operations.
- `fetchMessages()` returns empty results — WhatsApp does not provide a message history API.
- Messages exceeding 4096 characters are automatically split at paragraph or line boundaries.
- Webhook signatures are verified using HMAC-SHA256 with `timingSafeEqual` for timing-attack resistance.

## Troubleshooting

### Webhook verification failing

- Verify `WHATSAPP_VERIFY_TOKEN` matches what you set in the Meta dashboard
- Ensure both GET and POST routes are configured for the webhook URL

### "Invalid signature" error

- Check that `WHATSAPP_APP_SECRET` is correct (find it under Settings > Basic in your Meta app)
- Ensure the raw request body is not parsed before verification

### Bot not responding

- Confirm the **messages** webhook field is subscribed in Meta dashboard
- Check that you're within the 24-hour messaging window (or using template messages)
- Verify the phone number ID matches your configured `WHATSAPP_PHONE_NUMBER_ID`

### Media downloads failing

- Ensure your access token has the required permissions
- Check that the media hasn't expired (WhatsApp media URLs are temporary)
Loading
Loading