@laburen/openclaw-plugin-whatsapp-api
v0.5.0
Published
WhatsApp API channel plugin for OpenClaw
Readme
WhatsApp API Plugin for OpenClaw
WhatsApp Cloud API channel for OpenClaw: your router receives Meta webhooks and forwards them to OpenClaw; replies go straight to the Meta Graph API.
Install
openclaw plugins install @laburen/openclaw-plugin-whatsapp-apiSetup
- In Meta for Developers, create or open an app with WhatsApp product enabled and note your Phone number ID and a long-lived System User or Temporary access token with
whatsapp_business_messaging(and webhook permissions as required by your setup). - Point Meta’s webhook callback URL to your public HTTPS endpoint (router / reverse proxy). That service should forward the raw
POSTbody to OpenClaw at the path you set aswebhookPath(default below). - Set the same verify token / shared secret flow your deployment uses: configure
inboundSharedSecretin OpenClaw and send it from your router as the headerx-openclaw-shared-secret(recommended).
Via OpenClaw Config
Minimal example — set secrets and IDs (use env-backed config in production; never commit tokens):
openclaw config set channels.whatsapp-api.enabled true
openclaw config set channels.whatsapp-api.inboundSharedSecret "your-internal-secret"
openclaw config set channels.whatsapp-api.outboundPhoneNumberId "123456789012345"
openclaw config set channels.whatsapp-api.outboundAccessToken "EAAG..."Allow the plugin and enable its entry, then restart the gateway:
openclaw config set plugins.allow '["whatsapp-api"]'
openclaw config set plugins.entries.whatsapp-api.enabled true
openclaw gateway restartHow It Works
Registers channel id
whatsapp-apiand exposes an inboundPOSTroute (default/webhook/whatsapp-api).Parses WhatsApp Cloud API webhook payloads (
entry[].changes[].value.messages[]) and sends inbound text into the OpenClaw reply pipeline.Outbound replies are sent directly to Meta:
https://graph.facebook.com/{apiVersion}/{phoneNumberId}/messageswith
Authorization: Bearer <outboundAccessToken>.Transient failures (
429,5xx, timeouts) are retried according tomaxRetries,retryBackoffMs, andrequestTimeoutMs.
WhatsApp 24-hour context window (reminder)
WhatsApp Cloud API lets you send session messages (normal replies) only within about 24 hours of the user’s last inbound message. After that window, outbound traffic is limited to approved templates until the user writes again.
This plugin can nudge you before the window closes:
- Snapshot on inbound: For each user message handled by the channel, a hook writes
<OpenClaw state dir>/whatsapp-api/last-inbound-message.jsonwithfrom,content,timestamp, channel/account/conversation ids when present, andnotified: false. That reset means a new user message clears any previous “we already warned” state. - Cron from the host: On service start, the plugin runs
setup.sh(shipped undersrc/scripts/in the npm package). The script registers an OpenClaw cron job that periodically invokescheck.shvia the exec tool (see the job’s description insetup.sh). check.shbehavior: It reads the JSON file, skips if missing ornotifiedis alreadytrue, and compares elapsed time sincetimestampto a threshold (default 23 hours). If the threshold is met, it runsopenclaw message sendto the user’s phone (parsed fromfrom) with a configurable warning text, then setsnotified: truein the same file so the warning is not sent again until the next inbound message.
Useful environment variables for the scripts: OPENCLAW_DIR (defaults to /home/<user>/.openclaw), THRESHOLD_MINUTES, WARNING_MESSAGE, and EVERY for the cron interval when installing (default 30m). To remove the job: bash /path/to/node_modules/@laburen/openclaw-plugin-whatsapp-api/src/scripts/setup.sh --uninstall (adjust path to your install).
Inbound webhook (from your router to OpenClaw)
| Item | Detail |
|------|--------|
| Method | POST |
| Path | Value of webhookPath |
| Header (recommended) | x-openclaw-shared-secret: <inboundSharedSecret> |
| Body | Raw WhatsApp Cloud webhook JSON forwarded by your router |
Responses: 403 if the shared secret does not match, 400 if the payload is invalid, 200 when accepted (processing continues asynchronously after ACK).
Configuration
{
"plugins": {
"allow": ["whatsapp-api"],
"entries": {
"whatsapp-api": {
"enabled": true
}
}
},
"channels": {
"whatsapp-api": {
"enabled": true,
"webhookPath": "/webhook/whatsapp-api",
"inboundSharedSecret": "replace-with-internal-secret",
"outboundPhoneNumberId": "123456789012345",
"outboundAccessToken": "EAAG...",
"outboundApiVersion": "v22.0",
"requestTimeoutMs": 10000,
"maxRetries": 2,
"retryBackoffMs": 500,
"dedupeTtlMs": 300000,
"maxBodyBytes": 524288,
"accounts": {
"default": {
"enabled": true
}
}
}
}
}| Option | Description | Default |
|--------|-------------|---------|
| plugins.entries.whatsapp-api.enabled | Turn the plugin on or off | — |
| channels.whatsapp-api.enabled | Turn the channel on or off | — |
| webhookPath | HTTP path OpenClaw listens on for inbound webhooks | /webhook/whatsapp-api |
| inboundSharedSecret | Secret your router must send (e.g. header x-openclaw-shared-secret) | — |
| outboundPhoneNumberId | WhatsApp Phone number ID from Meta | — |
| outboundAccessToken | Bearer token for Graph API sends | — |
| outboundApiVersion | Graph API version segment in the URL | e.g. v22.0 |
| requestTimeoutMs | HTTP timeout for outbound calls (ms) | 10000 |
| maxRetries | Retries on transient errors | 2 |
| retryBackoffMs | Base backoff between retries (ms) | 500 |
| dedupeTtlMs | Deduplication window for inbound events (ms) | 300000 |
| maxBodyBytes | Max accepted webhook body size (bytes) | 524288 |
| accounts | Per-account overrides (multi-tenant); keys are account ids | { "default": { "enabled": true } } |
Per-account fields can override phone id, token, retries, etc. under channels["whatsapp-api"].accounts.<accountId>.
Notes
- Outbound mode is direct-to-Meta only (no third-party relay in this plugin).
- Keep tokens out of logs and rotate them on a schedule.
Links
- WhatsApp Cloud API — Overview
- Graph API — WhatsApp messages
- OpenClaw (ecosystem reference)
