← Back to Plugins
Voice

Twilio Sms

clawSean By clawSean 👁 23 views ▲ 0 votes

External OpenClaw Twilio SMS channel plugin

GitHub

Install

npm install
npm

README

# OpenClaw Twilio SMS

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.

## Install

ClawHub package publishing is the intended distribution path:

```bash
openclaw plugins install clawhub:clawsean/openclaw-twilio-sms
```

Until the ClawHub package is published, install from a local checkout:

```bash
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.

## Configure

Minimal config:

```json5
{
  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:

```text
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:

```json5
{
  channels: {
    "twilio-sms": {
      accountSid: "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      authToken: "TWILIO_AUTH_TOKEN",
      messagingServiceSid: "MGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    },
  },
}
```

Multiple accounts:

```json5
{
  channels: {
    "twilio-sms": {
      defaultAccount: "alerts",
      accounts: {
        alerts: {
          accountSid: "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
          authToken: "TWILIO_AUTH_TOKEN",
          fromNumber: "+15551230000",
        },
      },
    },
  },
}
```

## Send messages

Use the normal message delivery surface. Targets can be bare E.164 numbers when
the channel is explicit:

```bash
openclaw message send --channel twilio-sms --target +15551230001 --message "Hello"
```

Provider-prefixed targets are also accepted:

```bash
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.

## Access control

Direct messages default to pairing:

```json5
{
  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.

## Message behavior

- 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 webhook direction

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.

## Prior Art

See [EXISTING_WORK.md](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.
voice

Comments

Sign in to leave a comment

Loading comments...