feat: add WhatsApp Business Cloud API adapter#102
Open
ghellach wants to merge 24 commits intovercel:mainfrom
Open
feat: add WhatsApp Business Cloud API adapter#102ghellach wants to merge 24 commits intovercel:mainfrom
ghellach wants to merge 24 commits intovercel:mainfrom
Conversation
Add @chat-adapter/whatsapp with support for sending/receiving messages, reactions, interactive reply buttons, typing indicators, and webhook verification via the Meta Graph API. Includes full test suite, documentation updates, and workspace/turbo configuration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Contributor
|
@ghellach is attempting to deploy a commit to the Vercel Team on Vercel. A member of the Team first needs to authorize it. |
…pp adapter - Add downloadMedia() public method for fetching images, documents, audio, video, and stickers via the Graph API (two-step: URL then binary) - Populate message attachments with lazy fetchData() for all media types - Add location support with Google Maps URL and structured text - Add audio, video, sticker, and location fields to WhatsAppInboundMessage - Set isMention: true on all messages (WhatsApp DMs are always direct) - Update parseMessage to include attachments and isMention - Add 10 new tests covering all media types, locations, and isMention - Update docs feature matrix to reflect media receive support Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a new @chat-adapter/whatsapp package that integrates the Chat SDK with the WhatsApp Business Cloud API (Meta Graph API), plus the necessary monorepo wiring (tests, env vars, docs).
Changes:
- Introduces
packages/adapter-whatsappwith adapter implementation, card rendering, markdown conversion, and a dedicated test suite. - Wires the new package into the workspace tooling (Vitest workspace, Turbo env passthrough, lockfile).
- Updates docs/README/CLAUDE.md to include WhatsApp as a supported platform and document its package.
Reviewed changes
Copilot reviewed 17 out of 18 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| vitest.workspace.ts | Adds the WhatsApp adapter package to the Vitest workspace. |
| turbo.json | Adds WhatsApp-related environment variables to Turbo’s global env passthrough list. |
| pnpm-lock.yaml | Adds the new package importer and updates dependency snapshots accordingly. |
| packages/adapter-whatsapp/vitest.config.ts | Defines Vitest config/coverage settings for the new adapter package. |
| packages/adapter-whatsapp/tsup.config.ts | Adds build configuration for bundling the adapter. |
| packages/adapter-whatsapp/tsconfig.json | Adds TypeScript configuration for the new package. |
| packages/adapter-whatsapp/src/types.ts | Introduces WhatsApp webhook/media/type definitions used by the adapter. |
| packages/adapter-whatsapp/src/markdown.ts | Implements WhatsApp-specific markdown conversion via AST conversion. |
| packages/adapter-whatsapp/src/markdown.test.ts | Tests markdown conversion behavior. |
| packages/adapter-whatsapp/src/index.ts | Implements the WhatsApp adapter: webhook handling, send/reaction/typing/read, thread ID encode/decode, media download, message parsing. |
| packages/adapter-whatsapp/src/index.test.ts | Tests thread ID logic, parsing behavior, attachments, and webhook verification challenge. |
| packages/adapter-whatsapp/src/cards.ts | Converts Card elements into WhatsApp interactive payloads or text fallback. |
| packages/adapter-whatsapp/src/cards.test.ts | Tests card conversion and fallback logic. |
| packages/adapter-whatsapp/package.json | Adds the new package manifest, scripts, and dependencies. |
| apps/docs/content/docs/index.mdx | Adds WhatsApp to the platform list and package table in docs landing page. |
| apps/docs/content/docs/adapters/index.mdx | Adds WhatsApp to the adapters overview matrices and adapter list. |
| README.md | Adds WhatsApp to the supported platforms and adapter list. |
| CLAUDE.md | Adds WhatsApp adapter package and env vars to repo documentation. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Validate Graph API response before accessing messages[0].id in sendTextMessage and sendInteractiveMessage - Escape backticks and backslashes in escapeWhatsApp() - Apply escapeWhatsApp() to renderText() content in all style branches - Use webhook phoneNumberId in buildMessage() instead of this.phoneNumberId - Encode proper threadId in parseMessage() instead of empty string - Strict decodeThreadId() validation (exactly 2 segments after prefix) - Add tests for extra segments in decodeThreadId and threadId in parseMessage Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2 tasks
Bring over several enhancements from chitru's WhatsApp adapter PR (vercel#179): - Voice message support (separate from audio) - Legacy button response handling (template quick replies) - Callback data encoding/decoding for interactive reply round-trips - Message truncation at WhatsApp's 4096 char limit - Example app integration (adapters, webhook route, package.json) - GET webhook forwarding for WhatsApp verification challenges - Package README and changeset - Tests for all new functionality (68 total) Co-Authored-By: Chitru Shrestha <chitra.shrestha@akuru.com.au> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
# Conflicts: # README.md # apps/docs/content/docs/adapters/index.mdx # apps/docs/content/docs/index.mdx # examples/nextjs-chat/src/lib/adapters.ts # pnpm-lock.yaml # vitest.workspace.ts
Member
|
Nice one @ghellach - jumping in to assist 👍 |
Wrap handleInboundMessage calls in try/catch to log errors if synchronous processing fails (e.g., thread ID encoding). The async processing already has its own error handling in Chat.processMessage. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Use [^\n*] and [^\n~] in fromWhatsAppFormat regex to prevent bold/strike spans from merging across line boundaries. Adds a regression test. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the untyped `object` parameter in sendInteractiveMessage with the proper WhatsAppInteractiveMessage type for full type safety. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move the emoji name-to-unicode mapping out of resolveEmoji() so it is not re-allocated on every call. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The adapter supports typing indicators but the method was missing from the recording proxy list. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix line-length formatting in markdown.ts regex and add @chat-adapter/whatsapp to the valid packages list in readme tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the hand-rolled EMOJI_MAP with the shared defaultEmojiResolver from the chat SDK. WhatsApp uses unicode emoji like GChat, so toGChat() provides the correct mapping with broader coverage. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add apiVersion option to WhatsAppAdapterConfig (defaults to v21.0) so users can upgrade without waiting for a package release. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Coerce and validate latitude/longitude with Number.isFinite() to prevent unexpected URL construction from malformed webhook payloads. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…essage Callers expecting an edit would get duplicate messages with the silent fallback. Throwing makes the unsupported operation explicit. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…atsApp Explain why toWhatsAppFormat doesn't need newline guards like fromWhatsAppFormat does — the standard markdown parser output never produces spans crossing line boundaries. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add comments explaining that non-prefixed and malformed callback data is intentionally passed through for legacy/external button IDs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Include all adapter methods in the recording list for complete debugging traces, even for unsupported operations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Escaped asterisks and tildes in standard markdown (e.g. \* and \~) are now preserved through the conversion pipeline so WhatsApp renders them as literal characters instead of misinterpreting them as formatting. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace silent truncation at 4096 chars with message splitting that breaks on paragraph (\n\n) then line (\n) boundaries, sending multiple messages so no content is lost. Adds 8 tests for the splitting logic. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix README: editMessage/deleteMessage both throw, not fallback/no-op - Fix editMessage JSDoc to reflect it throws - Make deleteMessage throw instead of silently warning (consistent with editMessage) - Bump @types/node to ^25.3.2 to match monorepo - Add sample-messages.md with webhook payload examples Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add whatsapp.mdx covering installation, usage, Meta app setup, webhook config, interactive messages, media attachments, 24-hour messaging window, configuration, features, and troubleshooting. Also add WhatsApp to the adapters navigation in meta.json. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
@chat-adapter/whatsapppackage implementing the WhatsApp Business Cloud API adapter using the Meta Graph API (v21.0, configurable viaapiVersionoption)downloadMedia()with lazyfetchData()on attachmentsisMention: true) for correct SDK routingDetails
Capabilities
Media support
Inbound media messages (images, documents, audio, video, voice, stickers) are exposed as
Attachmentobjects on theMessagewith:type—"image","file","audio","video"mimeType— from WhatsApp webhook payloadfetchData()— lazy download via two-step Graph API (get URL, then fetch binary)Location messages include a Google Maps URL and structured text with name/address/coordinates. Latitude and longitude are validated with
Number.isFinite()before URL construction.Thread ID format
whatsapp:{phoneNumberId}:{userWaId}Environment variables
WHATSAPP_ACCESS_TOKEN— Meta Graph API access tokenWHATSAPP_APP_SECRET— App secret for webhook signature verificationWHATSAPP_PHONE_NUMBER_ID— Phone number ID for sending messagesWHATSAPP_VERIFY_TOKEN— Token for webhook URL verificationTest plan
*bold*/~strike~↔ standard markdown, escaped chars)pnpm typecheckpasses across all packagespnpm check(lint/format) passespnpm knip(unused exports/deps) passespnpm testpasses (78 WhatsApp adapter tests + all existing tests)