Twilio SMS connects OpenClaw to carrier SMS through Twilio Programmable Messaging.
This is an external OpenClaw channel plugin. It is intentionally packaged for the ClawHub/community plugin path rather than as a bundled OpenClaw core PR.
Outbound text sends use the shared OpenClaw message delivery path and durable receipts. Inbound SMS webhooks verify Twilio signatures, reject replays, use bounded request bodies, and default to pairing-first sender authorization before model turns run.
This repository is the external/ClawHub path for Twilio SMS. It is also prior art and proof support for OpenClaw's native SMS direction:
- Public RFC: openclaw/openclaw#85857
- Native SMS PR: openclaw/openclaw#88476
The goal is not to compete with a native OpenClaw sms channel. If native SMS
lands, this plugin remains useful as an installable external option, a
security/proof reference, and a compatibility path for deployments that want a
provider-specific twilio-sms channel.
Areas this plugin can help validate upstream:
- pairing-first sender admission
- Twilio signature verification and replay handling
- bounded webhook bodies and fail-closed auth behavior
- SMS-safe Markdown flattening and chunking
- durable receipts carrying Twilio Message SIDs
- redacted live Twilio inbound/outbound proof from a real A2P setup
ClawHub package publishing is the intended distribution path:
openclaw plugins install clawhub:clawsean/openclaw-twilio-smsUntil the ClawHub package is published, install from a local checkout:
git clone https://github.com/clawSean/openclaw-twilio-sms.git
cd openclaw-twilio-sms
npm install
npm run build
openclaw plugins install "$(pwd)"Gateway/plugin reloads are required after installing or changing plugin code. Do not rely on a config hot reload for source-code changes.
Minimal config:
{
channels: {
"twilio-sms": {
enabled: true,
accountSid: "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
authToken: "TWILIO_AUTH_TOKEN",
fromNumber: "+15551230000",
publicUrl: "https://sms.example.com/twilio-sms/webhook",
dmPolicy: "pairing",
},
},
}Environment variables for the default account:
- TWILIO_ACCOUNT_SID
- TWILIO_AUTH_TOKEN
- TWILIO_SMS_FROM
- TWILIO_MESSAGING_SERVICE_SID
Configure the Twilio Messaging webhook URL to:
https://sms.example.com/twilio-sms/webhook
Set channels.twilio-sms.publicUrl to the same public URL when OpenClaw is behind a reverse proxy. Signature verification uses publicUrl as the trusted origin and the incoming request path/query as the signed webhook URL. Without publicUrl, the plugin uses the direct Host header and does not trust forwarded host headers.
Proxy safety: set publicUrl in production whenever Twilio reaches
OpenClaw through a proxy, tunnel, or load balancer. Leaving it unset is intended
for direct/local deployments where the incoming Host header is already the URL
Twilio signed.
You can use a Messaging Service instead of a sender number:
{
channels: {
"twilio-sms": {
accountSid: "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
authToken: "TWILIO_AUTH_TOKEN",
messagingServiceSid: "MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
},
},
}Multiple accounts:
{
channels: {
"twilio-sms": {
defaultAccount: "alerts",
accounts: {
alerts: {
accountSid: "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
authToken: "TWILIO_AUTH_TOKEN",
fromNumber: "+15551230000",
},
},
},
},
}Use the normal message delivery surface. Targets can be bare E.164 numbers when the channel is explicit:
openclaw message send --channel twilio-sms --target +15551230001 --message "Hello"Provider-prefixed targets are also accepted:
openclaw message send --target twilio:+15551230001 --message "Hello"twilio: is the provider prefix. The generic sms: prefix is not used by this plugin because other channels use it as an internal service selector.
Direct messages default to pairing:
{
channels: {
"twilio-sms": {
dmPolicy: "pairing",
allowFrom: ["+15551230001"],
},
},
}Policies:
- pairing: unknown senders must be approved before they can reach an agent.
- allowlist: only allowFrom numbers can reach the agent.
- open: every sender is allowed and requires allowFrom: ["*"].
- disabled: inbound direct messages are blocked.
SMS is cost-bearing and phone numbers are personal data. Prefer pairing or allowlist unless you intentionally want public inbound SMS.
- SMS is text-only in the MVP.
- Groups, reactions, edits, polls, typing indicators, and threads are not supported.
- Outbound Markdown is flattened to plain text before sending.
- Long messages are chunked before delivery. The default chunk limit is 1530 characters, and channels.twilio-sms.textChunkLimit can reduce it.
- Durable receipts include Twilio Message SIDs.
Inbound Twilio webhooks are handled by the gateway route at the configured webhook path, defaulting to /twilio-sms/webhook.
The inbound path:
- validates X-Twilio-Signature against the configured Account SID/Auth Token
- rejects replayed MessageSid/SmsSid deliveries without dispatching twice
- acknowledges valid webhooks before the agent turn runs
- gates unknown direct senders through pairing by default
- dispatches accepted inbound SMS through the shared channel turn kernel
- sends final assistant replies back through durable Twilio SMS delivery
Keep dmPolicy as pairing or allowlist unless you explicitly want public inbound SMS. The channel remains text-only; MMS/media is deferred.
Replay protection currently uses an in-process cache. That is enough for a single Gateway process, but clustered or multi-replica deployments should put Twilio webhooks behind sticky routing or add a shared replay store before exposing high-volume traffic.
- Replay protection is process-local. Use a single Gateway process, sticky routing, or a shared replay store before exposing clustered/high-volume SMS traffic.
- The MVP is SMS text-only. MMS/media, groups, reactions, edits, polls, typing indicators, and threads are intentionally out of scope.
- A redacted live proof bundle is still required before treating the package as production-ready or asking reviewers to bless the ClawHub listing.
See PROOF_PLAN.md for the redacted live-proof checklist.
See EXISTING_WORK.md for the ClawHub/GitHub redundancy review. The short version: there are several public Twilio SMS attempts, but no ClawHub-listed first-class OpenClaw channel plugin was found. This package is shaped to avoid repeating the prior core PR path and to address known review notes around fail-closed webhook auth, replay protection, rate/in-flight control, and plain-SMS formatting.
This work also connects to the later native-SMS effort in openclaw/openclaw#88476. The preferred outcome is a polished first-class OpenClaw SMS experience, with this repository preserving the external plugin path and implementation trail.