Files
vercel__chat/apps
Shkumbin Hasani 2338a66544 feat(whatsapp): add sendTemplate for pre-approved template messages (#588)
Closes #585

## Summary

Adds `sendTemplate()` to the WhatsApp adapter for sending pre-approved
[Message
Templates](https://developers.facebook.com/docs/whatsapp/cloud-api/guides/send-message-templates)
— the only message type the Cloud API accepts outside the 24-hour
customer service window, and therefore required for business-initiated
conversations (notifications, reminders, re-engagement).

The inbound half already existed (`handleButtonResponse` dispatches
template quick-reply taps to `onAction` handlers); this completes the
outbound side. Notably, the package's `AGENTS.md` already documented
`sendTemplate` and `WhatsAppTemplateMessage` as part of the public
surface — this PR implements exactly that documented API.

## Changes

- **`sendTemplate(threadId, template)`** on `WhatsAppAdapter` — posts a
`type: "template"` payload through the existing `graphApiRequest` path
and returns a `RawMessage`, mirroring
`sendInteractiveMessage`/`sendSingleTextMessage`
- **Types**: `WhatsAppTemplateMessage`, `WhatsAppTemplateComponent`,
`WhatsAppTemplateParameter`, `WhatsAppTemplateButtonParameter` in
`types.ts`, modeled on the Cloud API template object (header/body/button
components; text, currency, date_time, and media parameters),
re-exported from the package entry point
- Templates are kept out of the `PostableMessage`/mdast pipeline —
they're sent by name + variable components, not free-form markdown, and
the adapter intentionally does not auto-substitute templates for
outbound text posts (per AGENTS.md)
- `openDM()` JSDoc now links to `sendTemplate` for the
business-initiated path

## Docs

- New "Template messages" section in the adapter README and
`apps/docs/content/adapters/official/whatsapp.mdx` with a usage example
- Added "Template messages" row to the README feature table
- Corrected the feature-matrix frontmatter labels: `cardFormat`
"WhatsApp templates" → "Interactive messages" and `fields` "Template
variables" → "Formatted text" — cards render as Cloud API interactive
messages (as the page body already states), and the old labels would now
wrongly imply cards go through the new template API

## Usage

```typescript
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" }],
    },
  ],
});
```

## Testing

- 5 new tests in `index.test.ts` following the existing `fetch`-spy
pattern: payload shape (name/language/`to`), component pass-through
(body + URL button), empty-components omission, missing-message-ID
error, and invalid thread ID rejection
- `pnpm validate` (knip, check, typecheck, test, build) passes — 117/117
adapter tests green

Includes a `minor` changeset for `@chat-adapter/whatsapp`.

---------

Signed-off-by: dancer <josh@afterima.ge>
Co-authored-by: dancer <josh@afterima.ge>
2026-07-13 21:50:06 +10:00
..