Give your OpenClaw / self-hosted AI agent inbound phone calls and SMS.
A self-hostable TypeScript service that bridges Twilio inbound voice calls and SMS to an OpenClaw gateway — routing every utterance or message through a full agent turn with native tool access.
Why this exists
OpenClaw's 176k-member Discord has a persistent demand: "I want my agent to answer my phone." There are now two architecturally distinct ways to do that, and clawcall is the second one.
The native plugin approach (@openclaw/voice-call realtime mode, PR
#71272 / e2f13959d4): the
realtime LLM (OpenAI or Gemini Live) handles the audio session end-to-end and
can call a special openclaw_agent_consult tool when it needs to reach the
gateway. Sub-second conversational latency; agent tools available via the consult
hop.
clawcall's approach: skip the realtime LLM entirely. Every caller utterance
goes straight to the OpenClaw gateway via chat.send, running the agent's
complete tool-calling loop on the gateway turn — the same path as any other
message to your agent. clawcall then synthesises the text reply to speech and
delivers it to the caller.
The trade-offs are real and worth stating plainly:
| clawcall | native realtime plugin | |
|---|---|---|
| Latency | Higher (STT → full agent turn → TTS) | Lower (realtime LLM + async consult hop) |
| Agent tool access | Every turn, natively | Via openclaw_agent_consult tool call |
| STT/TTS provider | Pluggable (Deepgram / ElevenLabs / keyless dev mode) | OpenAI / ElevenLabs |
| Model control | Your gateway's configured model | OpenAI or Gemini Live |
| SMS | Yes (POST /twilio/sms, keyless) |
No |
| Self-host / keyless dev | Yes | No |
Choose clawcall when you want full agent-turn fidelity on every utterance,
control over your STT and TTS providers, or a simpler mental model: the phone
call is just another chat.send. Choose the native plugin when you need the
lowest possible conversational latency and are comfortable with the realtime
LLM + consult-hop model.
Architecture
PSTN / VoIP
│
▼
┌─────────────────┐
│ Twilio Voice │
│ (inbound call) │
└───────┬─────────┘
│ POST /twilio/voice
│ (TwiML: <Connect><Stream>)
▼
┌─────────────────────────────────────────────┐
│ clawcall server │
│ ┌──────────────────────────────────────┐ │
│ │ CallSession │ │
│ │ │ │
│ │ Twilio WS ──► STT ──► GatewayClient │ │
│ │ (mulaw in) (Deepgram) (chat.send) │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ OpenClaw Gateway │ │
│ │ (agent turn — │ │
│ │ tools run here) │ │
│ │ │ │ │
│ │ Twilio WS ◄── TTS ◄─────────┘ │ │
│ │ (mulaw out) (ElevenLabs) │ │
│ └──────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
│
▼
OpenClaw Gateway
ws://127.0.0.1:18789
(tools: calendar, memory,
send-message, web search…)
Data flow per utterance:
- Twilio streams μ-law 8 kHz audio over WebSocket to
/twilio/stream - Deepgram streaming STT transcribes it in real time
- On a final transcript,
GatewayClient.agentTurn()sends achat.sendmessage to the OpenClaw gateway over the protocol-v4 WebSocket - The gateway runs the agent's full tool-calling loop; text streams back as
deltaTextevents - ElevenLabs streaming TTS synthesises each chunk as it arrives
- μ-law audio is sent back to Twilio, which plays it to the caller
- Barge-in: if Deepgram detects speech while audio is playing, TTS is interrupted immediately and a new agent turn begins
Quickstart
1. Prerequisites
- Node.js 20+
- A running OpenClaw gateway
- A Twilio account with a voice-capable phone number
- (Optional) Deepgram and ElevenLabs API keys
2. Clone and install
git clone https://github.com/CODEANDTRUST/clawcall.git
cd clawcall
npm install3. Configure
cp .env.example .env # Edit .env — minimum required fields: # PUBLIC_URL, TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, # OPENCLAW_GATEWAY_TOKEN, ALLOW_FROM
See Provider setup below for API key details.
4. Expose your server
During development, use ngrok to get a public HTTPS URL:
ngrok http 3000
# Copy the https://... URL into PUBLIC_URL in your .env5. Point your Twilio number at clawcall
In the Twilio Console, set the Voice webhook for your number to:
https://your-server.example.com/twilio/voice
HTTP method: POST
6. Start the server
npm run build
npm start
# or for development with hot reload:
npm run dev7. Call it
Call your Twilio number. The agent will answer.
Provider setup
Twilio (required)
- Create an account at twilio.com
- Buy a voice-capable phone number
- Copy your Account SID and Auth Token from the Console dashboard
- Set
TWILIO_ACCOUNT_SIDandTWILIO_AUTH_TOKENin.env
OpenClaw gateway (required)
clawcall connects to your locally-running OpenClaw gateway over WebSocket.
Set OPENCLAW_GATEWAY_URL (default: ws://127.0.0.1:18789) and
OPENCLAW_GATEWAY_TOKEN in .env.
Deepgram STT (recommended)
- Sign up at deepgram.com (free tier available)
- Create an API key
- Set
STT_PROVIDER=deepgramandDEEPGRAM_API_KEY=...in.env
Keyless dev mode: set STT_PROVIDER=null. The server logs audio chunk
counts and emits a canned transcript after 2 seconds of audio — enough to
exercise the full pipeline without any API key.
ElevenLabs TTS (recommended)
- Sign up at elevenlabs.io (free tier available)
- Create an API key
- Set
TTS_PROVIDER=elevenlabs,ELEVENLABS_API_KEY=..., and optionallyELEVENLABS_VOICE_ID=...in.env
Keyless dev mode: set TTS_PROVIDER=twilio-say. Twilio synthesises speech
using its built-in <Say> verb — no ElevenLabs account needed. Latency is
higher (no streaming) but works with zero external TTS API keys.
Inbound allowlist
By default (INBOUND_POLICY=allowlist), only numbers listed in ALLOW_FROM
can reach your agent. This is the recommended production configuration.
# Allow specific numbers (comma-separated E.164): ALLOW_FROM=+18432965626,+18005551234 # Open to all callers (dev/testing only): INBOUND_POLICY=all
Important: The allowlist authenticates the caller's reported number via the Twilio webhook signature, which proves the request came from Twilio. It does not cryptographically verify PSTN caller-ID ownership — VoIP callers can spoof their From number. Use this as a first line of defence, not as the only security layer for sensitive agents.
Per-number persona
Map individual Twilio "To" numbers to a custom greeting and ElevenLabs voice:
PERSONA_MAP='[{"from":"+18005551234","greeting":"Welcome to Acme Corp, how can I help?","voiceId":"YOUR_VOICE_ID"}]'Inbound SMS
clawcall handles inbound SMS out of the box. The same inbound allowlist policy and OpenClaw gateway connection used for voice apply to SMS — no extra config needed beyond pointing Twilio at the webhook.
How it works
- A text arrives at your Twilio number
- Twilio POSTs to
POST /twilio/sms(Twilio request signature verified) - The sender is checked against the allowlist (same
INBOUND_POLICY/ALLOW_FROM) - The message body is routed through
GatewayClient.agentTurn()— exactly like a voice utterance, so gateway tools (calendar, memory, web search…) are all available - The agent's text reply is returned as TwiML
<Message>— Twilio delivers it as a reply SMS
Keyless: SMS requires no STT or TTS keys. It works with STT_PROVIDER=null
and TTS_PROVIDER=twilio-say (or any provider combination).
Session continuity: SMS and voice from the same number share the same
conversation session key (clawcall:<E.164>), so context carries across
channels when OPENCLAW_SESSION_SCOPE=per-caller.
Setup
Enable SMS_ENABLED=true (the default) and point your Twilio number's
Messaging webhook at:
https://your-server.example.com/twilio/sms
In the Twilio Console:
- Go to Phone Numbers → Manage → Active numbers
- Click your SMS-capable number
- Under Messaging → A message comes in, set:
- Webhook:
https://your-server.example.com/twilio/sms - HTTP method:
POST
- Webhook:
- Save
Config
| Variable | Default | Description |
|---|---|---|
SMS_ENABLED |
true |
Set to false to disable the /twilio/sms route entirely |
All other SMS behaviour is controlled by existing variables (INBOUND_POLICY,
ALLOW_FROM, OPENCLAW_SESSION_SCOPE).
Long / async replies
For agents that may take longer than Twilio's 15-second webhook timeout, reply
with an empty <Response/> immediately and send the reply via the Twilio REST
API once the agent turn completes:
// Instead of TwiML, fire-and-forget the gateway turn and send via REST: const twilioClient = twilio(accountSid, authToken); const reply = await gateway.agentTurn(sessionKey, messageBody, () => {}); await twilioClient.messages.create({ from: toNumber, to: senderNumber, body: reply });
This pattern is documented in the POST /twilio/sms route comments in
src/server.ts.
Security notes
Prompt injection over voice: A caller can speak arbitrary text that becomes the agent's input. Configure your OpenClaw agent's system prompt to treat the voice channel as untrusted user input. Do not give the agent access to tools that execute code or send messages without confirmation unless you trust all callers in your allowlist.
Cost: Each call incurs Twilio per-minute charges, Deepgram streaming
transcription costs, and ElevenLabs character costs. Set INBOUND_POLICY=allowlist
to prevent unexpected charges from unknown callers.
Comparison
| clawcall | @openclaw/voice-call |
clawphone | deepclaw | |
|---|---|---|---|---|
| Agent tools mid-call | Yes (every turn, gateway) | Via consult tool (#71272) | No (CLI spawn) | No (side LLM) |
| Streaming STT | Deepgram | OpenAI/ElevenLabs | Twilio built-in | Deepgram |
| Streaming TTS | ElevenLabs | ElevenLabs | Twilio built-in | ElevenLabs |
| SMS | Yes (keyless) | No | No | No |
| Keyless dev mode | Yes | No | Yes | No |
| Inbound allowlist | Yes | Yes | No | No |
| Self-hostable | Yes | Plugin only | Yes | Yes |
| Barge-in | Yes | Partial | No | Yes |
For a full comparison including managed platforms (Vapi, Retell, Bland), latency trade-offs, SMS, and license details, see COMPARISON.md.
Examples
The examples/quickstart/ directory has a documented
.env.example for keyless dev mode and a step-by-step guide to pointing a
Twilio number at the server and placing a test call. A smoke script
(examples/quickstart/smoke.js) boots the server and hits /health to confirm
it runs — no API keys required:
npm run build node examples/quickstart/smoke.js # [smoke] GET /health → 200 {"status":"ok","activeCalls":0} # [smoke] PASS
Contributing
PRs welcome. See CONTRIBUTING.md for the full workflow — how to run tests, build, and PR norms.
npm test # run the test suite (36 tests, no API keys needed) npm run build # typecheck + compile npm run lint # ESLint
Built and maintained by Code and Trust
Code and Trust builds AI agent infrastructure for businesses.
Companion guide: Give your OpenClaw agent a phone number
MIT License — see LICENSE.
























