@srinathh/openclaw-channel-twilio-whatsapp
v2.0.10
Published
Twilio WhatsApp channel plugin for OpenClaw
Readme
OpenClaw Twilio WhatsApp Channel
A channel plugin for OpenClaw that connects your AI agent to WhatsApp via the Twilio Business API.
Why Twilio over Baileys?
OpenClaw ships with a built-in WhatsApp channel based on Baileys, which reverse-engineers the WhatsApp Web protocol. Baileys is convenient — no business verification, no monthly fees — but the trade-offs are real:
| Concern | Baileys | Twilio (this plugin) | |---|---|---| | Protocol stability | Breaks when WhatsApp changes their internal protocol | Official, versioned API | | Account safety | Risk of bans for "automated" behavior | Compliant Business API | | Delivery receipts | Best-effort | First-class status callbacks | | Group messaging | Yes | No (1:1 DMs only) | | Cost | Free | Per-message fees | | Setup | QR-code pairing | Sender registration + webhook |
Pick this plugin when you need stability and compliance — for personal automations or when you need group chat, the bundled Baileys channel is simpler.
Features
- Inbound webhooks via OpenClaw's gateway (no separate HTTP server)
- Twilio signature validation on every inbound request
- Allowlist enforcement so only approved phone numbers can talk to your agent
- Inbound media download with redirect-following Basic Auth
- Outbound media staging — local files are served back to Twilio via UUID-randomized URLs
- Message chunking at Twilio's 1600-char limit
- WhatsApp formatting hints injected into the agent prompt (
*bold*,_italic_, etc.) - Fire-and-forget webhook responses — TwiML returned immediately, processing happens async (avoids Twilio's 15s timeout)
Installation
This plugin is loaded by OpenClaw at runtime via npm. Add it to your OpenClawInstance CRD:
apiVersion: openclaw.rocks/v1alpha1
kind: OpenClawInstance
metadata:
name: openclaw
spec:
plugins:
- "@srinathh/openclaw-channel-twilio-whatsapp@latest"Or in a non-Kubernetes deployment, install it into your OpenClaw runtime's node_modules:
npm install @srinathh/openclaw-channel-twilio-whatsapp@latestThen add it to the plugins.load.paths array in openclaw.json (see Configuration).
Configuration
openclaw.json
{
"channels": {
"twilio-whatsapp": {
"enabled": true,
"dmPolicy": "allowlist",
"allowFrom": ["+14155551234", "+14155555678"],
"fromNumber": "+14155550000",
"webhookUrl": "https://your-public-host.example.com"
}
},
"plugins": {
"enabled": true,
"allow": ["@srinathh/openclaw-channel-twilio-whatsapp"],
"load": {
"paths": ["~/.openclaw/node_modules/@srinathh/openclaw-channel-twilio-whatsapp"]
},
"entries": {
"@srinathh/openclaw-channel-twilio-whatsapp": { "enabled": true }
}
}
}| Field | Required | Description |
|---|---|---|
| enabled | yes | Activate the channel |
| dmPolicy | yes | "allowlist" (only allowFrom numbers) or "open" (anyone) |
| allowFrom | when allowlist | Phone numbers in E.164 format (e.g. +14155551234) |
| fromNumber | yes | Your Twilio WhatsApp sender in E.164 |
| webhookUrl | yes | Public base URL where Twilio can reach OpenClaw — used both for signature validation and media serving |
All phone numbers use E.164 format without the whatsapp: prefix — the plugin prepends it internally when calling Twilio.
Environment variables (secrets)
| Variable | Required | Description |
|---|---|---|
| TWILIO_ACCOUNT_SID | yes | Your Twilio account SID (starts with AC) |
| TWILIO_AUTH_TOKEN | yes | Your Twilio auth token |
Twilio setup
1. Get a WhatsApp sender
For development, use the Twilio Sandbox for WhatsApp. For production, register a WhatsApp sender.
2. Configure the inbound webhook
In the Twilio Console for your WhatsApp sender, set:
- When a message comes in:
https://<your-host>/webhook/twilio-whatsapp - Method:
HTTP POST
The path is fixed by this plugin. The host must match webhookUrl in your OpenClaw config exactly — Twilio's signature validation requires the URL to match.
3. Verify the health endpoint
curl https://<your-host>/webhook/twilio-whatsapp/health
# {"status":"ok","channel":"twilio-whatsapp"}4. Send a test message
WhatsApp the number you registered in fromNumber from a phone in your allowFrom list. The agent should reply.
Architecture
┌─────────────────┐ POST /webhook/twilio-whatsapp
│ Twilio API │ ──────────────────────────────────► ┌──────────────────┐
│ │ ◄────────── 200 TwiML <Response/> ── │ OpenClaw gateway │
└─────────────────┘ │ (this plugin) │
▲ └────────┬─────────┘
│ client.messages.create({...}) │ dispatchInboundDirectDmWithRuntime
│ ▼
┌───────┴─────────┐ ┌──────────────┐
│ Outbound: │ ◄──────────── deliver(payload) ────── │ Agent runtime│
│ sendText / │ └──────────────┘
│ sendMedia │
└─────────────────┘HTTP routes registered
| Path | Auth | Purpose |
|---|---|---|
| POST /webhook/twilio-whatsapp | plugin (signature-validated) | Inbound from Twilio |
| GET /webhook/twilio-whatsapp/media/* | plugin | Serves outbound media for Twilio to fetch |
| GET /webhook/twilio-whatsapp/health | plugin | Liveness check |
Media handling
Inbound media (Twilio → agent):
- Downloaded with redirect-following Basic Auth
- Saved to
~/.openclaw/media/twilio-whatsapp/inbound/<MessageSid>-<i><ext> - Path included in the
MediaPath/MediaPathsenvelope fields
Outbound media (agent → Twilio):
- Local files are copied to
~/.openclaw/media/twilio-whatsapp/outbound/<uuid><ext> - Served via the media endpoint with parent-directory check (no traversal)
- Twilio fetches the URL and forwards to WhatsApp
Inbound flow
- Twilio POSTs
application/x-www-form-urlencodedbody withBody,From,MessageSid,NumMedia, etc. - Plugin validates
X-Twilio-SignatureagainstwebhookUrl + path— rejects with403on mismatch - Plugin checks
FromagainstallowFrom(withwhatsapp:prefix stripped) — rejects with403if not allowed - Plugin immediately responds with empty TwiML (
<Response/>) so Twilio doesn't time out - Plugin downloads any inbound media (async, after responding)
- Plugin calls
dispatchInboundDirectDmWithRuntimewith the message envelope - Agent processes the message and sends a reply via
sendText/sendMedia
Development
git clone https://github.com/srinathh/openclaw-channel-twilio-whatsapp.git
cd openclaw-channel-twilio-whatsapp
npm install
npm run buildProject layout
src/
├── index.ts # defineChannelPluginEntry — plugin entry point
├── channel.ts # createChatChannelPlugin — main plugin definition
├── webhook.ts # Twilio webhook handler (signature validation + dispatch)
├── media.ts # download / stage / serve media
├── runtime.ts # createPluginRuntimeStore — runtime accessor for dispatch
├── util.ts # phone formatting + form body parsing
└── openclaw-sdk.d.ts # ambient type declarations for openclaw/plugin-sdk/*Testing locally
You'll need an OpenClaw instance running with this plugin installed. The simplest setup:
# 1. In one terminal: build and link
npm run build
npm link
# 2. In your OpenClaw instance directory
npm link @srinathh/openclaw-channel-twilio-whatsapp
# 3. Add to your openclaw.json plugins config (see Configuration)
# 4. Use a tunnel (cloudflared, ngrok) to expose the gateway
# 5. Point Twilio's webhook at the tunnel URLCompatibility
- OpenClaw: requires
openclaw >= 2026.3.28(uses theplugin-sdk/*subpath imports) - OpenClaw operator (k8s): requires v0.30.0+ for the plugin peerDependency symlink
- Node.js: 20+
Known limitations
- DMs only — no group chat (Twilio's WhatsApp Business API doesn't support groups)
- No reactions / typing indicators — Twilio doesn't expose these
- No threaded replies — WhatsApp threading not exposed by Twilio
- Single account — multiple Twilio accounts aren't supported in this version
License
Apache-2.0 — see LICENSE.
Contributing
Issues and PRs welcome at github.com/srinathh/openclaw-channel-twilio-whatsapp.
