Problem Statement
The WhatsApp adapter (@chat-adapter/whatsapp) can only send messages inside the 24-hour customer service window. The WhatsApp Business Cloud API requires pre-approved Message Templates (type: "template") for any business-initiated conversation — i.e. messaging a user who hasn't written to you in the last 24 hours.
Today the adapter has no way to send a template message:
postMessage() / stream() only emit type: "text", type: "interactive", and type: "reaction" payloads.
openDM() constructs the thread ID, but its own JSDoc notes you "can only message users who have messaged you first (within the 24-hour window) or via approved template messages" — without offering a way to actually do the latter.
- There is no raw-payload escape hatch, so the only workaround is calling the Graph API directly, bypassing the SDK entirely.
This makes common WhatsApp use cases impossible through the SDK: notifications, reminders, order updates, re-engagement — anything where the bot speaks first.
Related docs nit: the feature matrix on the WhatsApp adapter page lists "Card format: WhatsApp templates" and "Fields: Template variables", but the implementation renders cards as interactive messages, not templates — which is also what the page body says. The frontmatter labels probably should say "Interactive messages" until/unless real template support lands.
Proposed Solution
Add first-class template support to the WhatsApp adapter, e.g. a sendTemplate() method on the adapter (and ideally surfaced on Thread):
await thread.sendTemplate({
name: "order_shipped",
language: "en_US",
components: [
{
type: "body",
parameters: [
{ type: "text", text: "Shkumbin" },
{ type: "text", text: "#12345" },
],
},
],
});
Under the hood this posts to the same /{phoneNumberId}/messages endpoint with:
{
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": "<userWaId>",
"type": "template",
"template": { "name": "...", "language": { "code": "..." }, "components": [...] }
}
Scope notes:
- Template quick-reply button responses already flow back in via
handleButtonResponse(), so the inbound half exists — this is purely about the outbound side.
- Since templates are platform-specific by nature (sent by name + variable components, not free-form markdown), this probably belongs as an adapter-level method rather than something forced through the
PostableMessage/mdast pipeline.
- Header media (image/document/video components) and quick-reply/CTA button components would be nice to support from day one, since most approved templates use them.
Alternatives Considered
- Calling the Graph API directly alongside the SDK — works, but you lose the SDK's thread model, and the reply lands in a thread the SDK didn't initiate cleanly.
- A generic
postRaw(threadId, payload) escape hatch on the adapter — more flexible (covers other unsupported types too), but less discoverable and unvalidated.
- Other adapters have a similar concept (Messenger's message tags / Generic+Button templates), so a shared abstraction might emerge later, but WhatsApp-specific support seems like the right first step.
Priority
Important — without it, the SDK only covers reactive WhatsApp bots, not business-initiated messaging.
Problem Statement
The WhatsApp adapter (
@chat-adapter/whatsapp) can only send messages inside the 24-hour customer service window. The WhatsApp Business Cloud API requires pre-approved Message Templates (type: "template") for any business-initiated conversation — i.e. messaging a user who hasn't written to you in the last 24 hours.Today the adapter has no way to send a template message:
postMessage()/stream()only emittype: "text",type: "interactive", andtype: "reaction"payloads.openDM()constructs the thread ID, but its own JSDoc notes you "can only message users who have messaged you first (within the 24-hour window) or via approved template messages" — without offering a way to actually do the latter.This makes common WhatsApp use cases impossible through the SDK: notifications, reminders, order updates, re-engagement — anything where the bot speaks first.
Related docs nit: the feature matrix on the WhatsApp adapter page lists "Card format: WhatsApp templates" and "Fields: Template variables", but the implementation renders cards as interactive messages, not templates — which is also what the page body says. The frontmatter labels probably should say "Interactive messages" until/unless real template support lands.
Proposed Solution
Add first-class template support to the WhatsApp adapter, e.g. a
sendTemplate()method on the adapter (and ideally surfaced onThread):Under the hood this posts to the same
/{phoneNumberId}/messagesendpoint with:{ "messaging_product": "whatsapp", "recipient_type": "individual", "to": "<userWaId>", "type": "template", "template": { "name": "...", "language": { "code": "..." }, "components": [...] } }Scope notes:
handleButtonResponse(), so the inbound half exists — this is purely about the outbound side.PostableMessage/mdast pipeline.Alternatives Considered
postRaw(threadId, payload)escape hatch on the adapter — more flexible (covers other unsupported types too), but less discoverable and unvalidated.Priority
Important — without it, the SDK only covers reactive WhatsApp bots, not business-initiated messaging.