← All @molecule/* packages · App templates
@molecule/api-channel-whatsappProvider bond · channel · API (Node) · v1.0.1 · Apache-2.0
WhatsApp channel bond — implements @molecule/api-channel via the WhatsApp Cloud API for sendMessage (text/template/interactive), x-hub-signature-256 HMAC-SHA256 webhook verification, and inbound message parsing.
npm install @molecule/api-channel-whatsappnpm · Source on GitHub · Implements @molecule/api-channel
@molecule/api-channel-whatsapp is a provider bond on the API (Node) side: it implements the channel core interface (@molecule/api-channel) with a concrete vendor or library behind it.
Your code calls the core; you wire this provider once at startup. Swapping vendors later is one line in that wiring, not a rewrite.
import { setProvider } from '@molecule/api-channel'
import { provider } from '@molecule/api-channel-whatsapp'
setProvider('whatsapp', provider)Works with: @molecule/api-secrets
Secrets: CHANNEL_WHATSAPP_ACCESS_TOKEN, CHANNEL_WHATSAPP_PHONE_NUMBER_ID, CHANNEL_WHATSAPP_APP_SECRET
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
WhatsApp channel provider for molecule.dev.
Implements the framework-agnostic {@link ChannelProvider} interface
over the WhatsApp Cloud API
(https://graph.facebook.com/v22.0/<phone-id>/messages). Bond under
the named-multi-provider 'channel' category at app startup:
import { setProvider } from '@molecule/api-channel'
import { provider } from '@molecule/api-channel-whatsapp'
setProvider('whatsapp', provider)
provider
npm install @molecule/api-channel-whatsapp @molecule/api-channel @molecule/api-secrets
ProcessEnvEnvironment variables consumed by the WhatsApp channel provider.
interface ProcessEnv {
/** Cloud API access token. */
CHANNEL_WHATSAPP_ACCESS_TOKEN: string
/** Numeric phone-number id of the WhatsApp business sender. */
CHANNEL_WHATSAPP_PHONE_NUMBER_ID: string
/** Meta App secret used to verify `X-Hub-Signature-256`. */
CHANNEL_WHATSAPP_APP_SECRET: string
}
WhatsAppButtonReplySubset of the inbound button reply object (template button click).
interface WhatsAppButtonReply {
/** Opaque payload originally set on the template button. */
payload?: string
/** Visible label of the button that was clicked. */
text?: string
}
WhatsAppConfigConfiguration for the WhatsApp channel provider.
The access token is the credential that authorizes Cloud API calls —
it is deliberately accepted only via this config (or the
CHANNEL_WHATSAPP_ACCESS_TOKEN env var) and is NEVER included in
error messages, log lines, or normalized payloads.
interface WhatsAppConfig {
/**
* WhatsApp Cloud API access token (typically a long-lived `EAA…`
* system-user token). Defaults to the
* `CHANNEL_WHATSAPP_ACCESS_TOKEN` env var.
*
* Treat as a secret — providers redact this value in any user-facing
* output.
*/
accessToken?: string
/**
* The numeric phone-number id assigned to the business in the Meta
* developer dashboard (NOT the E.164 phone number). Used to build the
* Cloud API endpoint. Defaults to the
* `CHANNEL_WHATSAPP_PHONE_NUMBER_ID` env var.
*/
phoneNumberId?: string
/**
* Meta App secret used to verify inbound `X-Hub-Signature-256`
* webhook signatures. Defaults to the `CHANNEL_WHATSAPP_APP_SECRET`
* env var.
*
* If unset, {@link WhatsAppChannelProvider.verifyWebhookSignature}
* returns `false`.
*/
appSecret?: string
/**
* Cloud API base URL. Override only for tests / staging endpoints.
* Defaults to `https://graph.facebook.com`.
*/
apiBaseUrl?: string
/**
* Cloud API version segment (e.g. `'v22.0'`). Defaults to `'v22.0'`.
*/
apiVersion?: string
/**
* Per-request timeout in milliseconds. Defaults to 10000.
*/
timeoutMs?: number
}
WhatsAppContactSubset of the WhatsApp inbound contact object surfaced via
{@link InboundMessage.payload}.
interface WhatsAppContact {
/** Display name set by the user. */
profile?: { name?: string }
/** Sender's WhatsApp id (typically the E.164 phone number). */
wa_id: string
}
WhatsAppInboundMessageSubset of the inbound messages[] entry. Only fields the provider
actually inspects are typed; others are passed through opaquely on
{@link InboundMessage.payload}.
interface WhatsAppInboundMessage {
/** WhatsApp message id (`wamid.…`). */
id: string
/** Sender's WhatsApp id (E.164 phone number). */
from: string
/** Unix timestamp (seconds, as a string) reported by WhatsApp. */
timestamp?: string
/**
* Discriminator: `'text'`, `'image'`, `'audio'`, `'video'`,
* `'document'`, `'sticker'`, `'location'`, `'button'`,
* `'interactive'`, `'reaction'`, `'contacts'`, `'unsupported'`, …
*/
type?: string
/** Text body for `type === 'text'`. */
text?: { body?: string }
/** Inline media payloads. */
image?: WhatsAppMediaRef
audio?: WhatsAppMediaRef
video?: WhatsAppMediaRef
document?: WhatsAppMediaRef
sticker?: WhatsAppMediaRef
/** Geo payload for `type === 'location'`. */
location?: WhatsAppLocation
/** Template-button click for `type === 'button'`. */
button?: WhatsAppButtonReply
/** Interactive-button / list-reply for `type === 'interactive'`. */
interactive?: WhatsAppInteractiveReply
/** Reply context — present when this message replies to another. */
context?: { id?: string; from?: string }
}
WhatsAppInteractiveReplySubset of the inbound interactive reply object (button-list /
list-reply click on an interactive message).
interface WhatsAppInteractiveReply {
/**
* Whether this was a `'button_reply'` or `'list_reply'`.
*/
type?: string
button_reply?: { id?: string; title?: string }
list_reply?: { id?: string; title?: string; description?: string }
}
WhatsAppLocationSubset of the WhatsApp inbound location object.
interface WhatsAppLocation {
/** Geographic latitude in decimal degrees. */
latitude: number
/** Geographic longitude in decimal degrees. */
longitude: number
/** Optional human-readable place name. */
name?: string
/** Optional human-readable street address. */
address?: string
}
WhatsAppMediaRefSubset of the WhatsApp inbound media descriptor, shared by
image / audio / video / document / sticker envelopes.
interface WhatsAppMediaRef {
/** Cloud API media id (download via `/v22.0/<id>`). */
id?: string
/** Reported MIME type. */
mime_type?: string
/** Original filename (documents only). */
filename?: string
/** Optional caption (image / video / document). */
caption?: string
}
WhatsAppOutboundExtensionsOptional WhatsApp-specific extensions carried via
OutboundMessage.payload. Not part of the core
{@link OutboundMessage} contract — providers SHOULD ignore unknown
fields.
interface WhatsAppOutboundExtensions {
/**
* When set, the message is sent as a WhatsApp template instead of a
* free-form text / interactive message.
*/
template?: WhatsAppTemplateRef
}
WhatsAppSendResponseSubset of the WhatsApp Cloud API outbound messages response that
the provider inspects to build a {@link SendResult}.
interface WhatsAppSendResponse {
/** Echoed phone-number id of the sending business. */
messaging_product?: string
/**
* Per-recipient envelope. Cloud API always returns at least one
* entry on success.
*/
messages?: Array<{
/** WhatsApp message id (e.g. `'wamid.HBgL…'`). */
id: string
}>
/**
* Cloud API error object on failure responses. Surfaced into the
* thrown error message after token redaction.
*/
error?: {
message?: string
type?: string
code?: number
}
}
WhatsAppTemplateRefIdentifies a WhatsApp template message to send. Templates are the only message form allowed outside the 24-hour customer-service window. The template (and its variables) must be pre-approved in the Meta WhatsApp Manager.
interface WhatsAppTemplateRef {
/** Approved template name (e.g. `'order_confirmation'`). */
name: string
/** Template language code (e.g. `'en_US'`, `'es'`). */
language: string
/**
* Optional positional body parameters substituted into the template
* `{{1}}`, `{{2}}` placeholders. WhatsApp expects strings.
*/
bodyParameters?: string[]
}
WhatsAppWebhookPayloadTop-level shape of an inbound WhatsApp Cloud API webhook payload.
interface WhatsAppWebhookPayload {
/** Always `'whatsapp_business_account'` for Cloud API events. */
object?: string
/** Top-level entries (one per WABA). */
entry?: Array<{
/** WhatsApp Business Account id. */
id?: string
/** Per-product changes (`'messages'`, `'message_template_status_update'`, …). */
changes?: Array<{
field?: string
value?: WhatsAppWebhookValue
}>
}>
}
WhatsAppWebhookValueSubset of the entry[].changes[].value payload in a Cloud API
webhook event.
interface WhatsAppWebhookValue {
/** Always `'whatsapp'` for Cloud API events. */
messaging_product?: string
/** Sender / receiver metadata (display phone, phone-number id). */
metadata?: { phone_number_id?: string; display_phone_number?: string }
/** Inbound contacts that triggered this event. */
contacts?: WhatsAppContact[]
/** Inbound messages that triggered this event. */
messages?: WhatsAppInboundMessage[]
}
WhatsAppChannelProviderWhatsApp channel provider — implements the framework-agnostic {@link ChannelProvider} contract on top of the WhatsApp Cloud API.
createProvider(config)Convenience factory for the named-multi-provider bond pattern.
function createProvider(config?: WhatsAppConfig): WhatsAppChannelProvider
config — Optional WhatsApp config.Returns: A new {@link WhatsAppChannelProvider} instance.
channelWhatsappSecretDefinitionsSecret definitions required by the WhatsApp channel bond.
const channelWhatsappSecretDefinitions: SecretDefinition[]
providerLazily-instantiated singleton instance for app-startup wiring. Reads configuration from environment variables on first use.
const provider: ChannelProvider
Implements @molecule/api-channel interface.
Peer dependencies:
@molecule/api-channel ^1.0.1@molecule/api-secrets ^1.0.1CHANNEL_WHATSAPP_ACCESS_TOKEN (required) — WhatsApp access token
CHANNEL_WHATSAPP_PHONE_NUMBER_ID (required) — WhatsApp phone number ID
CHANNEL_WHATSAPP_APP_SECRET (required) — Meta app secret (WhatsApp)
@molecule/api-channel@molecule/api-secretsOutbound messages outside the WhatsApp 24-hour customer-service window
MUST be sent as approved templates. The provider exposes
OutboundMessage.kind = 'rich' two ways: via interactive button
objects (in-window) or via WhatsApp templates (out-of-window) when
payload.template is supplied.
Webhook subscription needs a GET echo the bond does not provide. When
registering the webhook URL in the Meta console, Meta sends
GET ?hub.mode=subscribe&hub.verify_token=<your token>&hub.challenge=<n>;
your route must validate the verify token and respond 200 with the raw
hub.challenge. Only POST deliveries flow through
verifyWebhookSignature() / parseInbound().
Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual screens/flows, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip:
read_activity tool (filter type 'channel'); never mock the flow or
modify production code to expose the message.undefined
placeholders, no secrets).