openclaw-channel-voice-sip
v2026.5.67
Published
SIP telephony plugin for OpenClaw. Single-process pure TypeScript. STT + agent + TTS over real SIP+RTP.
Readme
openclaw-channel-voice-sip
Single-process SIP telephony plugin for OpenClaw. Speech-to-text, agent invocation, and text-to-speech over real SIP+RTP — no Twilio, no webhook tunnel, no separate daemon.
Status: TypeScript rewrite of the original two-process build. Shipping to Clawhub. Pure JS / pure TS, single Node process. Zero native deps.
Quickstart
openclaw plugins install openclaw-channel-voice-sipRegister in openclaw.yaml:
plugins:
entries:
voice-sip:
enabled: true
maxConcurrentCalls: 1
inboundGreeting: "Hello! How can I help you?"
outbound:
defaultMode: "conversation"
notifyHangupDelaySec: 1
ringTimeoutMs: 30000
maxDurationSeconds: 600
silenceTimeoutMs: 30000
inboundPolicy: "open"
store: "/var/log/voice-call.jsonl"SIP credentials via env:
SIP_HOST=sip.linphone.org
SIP_USERNAME=your-user
SIP_PASSWORD=your-pass
SIP_REALM=sip.linphone.org
SIP_DOMAIN=sip.linphone.org
SIP_EXTERNAL_IP=<your public IP>
SIP_LOCAL_BIND_PORT=6060
SIP_CONTACT_PORT=6060
RTP_PORT_MIN=40000
RTP_PORT_MAX=40100
SIP_PREFERRED_CODEC=opus,l16,pcmuRestart the gateway. Verify with openclaw plugins inspect voice-sip and openclaw voicecall list.
Features
- Inbound calls — answers, greets, full STT → agent → TTS loop
- Outbound notify — cron/HTTP trigger plays TTS and hangs up
- Outbound conversation — multi-turn dialogue, agent drives
voice_callagent tool — 6 actions- 10
voicecall.*RPC methods - 12
openclaw voicecallCLI subcommands - HTTP API on
127.0.0.1:9751(POST /call /hangup /speak /continue /dtmf,GET /status /calls /health) - Codecs: Opus (HD, WASM), L16 16 kHz, PCMU 8 kHz
- Per-dialed-line config overrides
- Inbound policy: open / allowlist / disabled
Defense layers (production-tested)
SIP/RTP/codec (18): 486 Busy concurrent INVITE, 488 scanner SDP, fork-loser skip, RTP socket close in finally, NAT Record-Route + multi-Via echo, _pending_ack before 200 OK, RFC 2617 digest auth, RTP port pool, per-dialog CSeq, inbound policy + 403 echo, SIP INFO DTMF with dialog asymmetry, lastRecvTs silence watchdog, stale call reaper, JSONL snapshot-before-write, Opus stateful codec session, 20 ms silence carrier, CodecDesc descriptor pattern.
Application (9): Whisper hallucination denylist, NO_REPLY filter, VAD 1.5 s energy-threshold trigger, TTS round-robin + half-open circuit breaker, retry + 3-beep error tone, markdown stripper + bullet/heading splitter, echo suppressor, JSON {"spoken":"..."} extraction + <thinking> strip, outbound drain-before-BYE.
CLI
openclaw voicecall call --to alice --message "Hello there"
openclaw voicecall list --json
openclaw voicecall status <call-id>
openclaw voicecall speak <call-id> -m "Just checking in"
openclaw voicecall continue <call-id> -m "What did you think?" --timeout 30000
openclaw voicecall dtmf <call-id> 1234
openclaw voicecall end <call-id>
openclaw voicecall tail -n 50HTTP API
# Outbound notify
curl -X POST http://127.0.0.1:9751/call \
-H 'Content-Type: application/json' \
-d '{"to":"alice","mode":"notify","message":"Daily reminder: meeting at 3"}'
# Status
curl http://127.0.0.1:9751/calls
curl 'http://127.0.0.1:9751/status?callId=out-...'
# Speak into active call
curl -X POST http://127.0.0.1:9751/speak \
-H 'Content-Type: application/json' \
-d '{"callId":"out-...","message":"Following up"}'Per-dialed-line config
plugins:
entries:
voice-sip:
numbers:
sales729:
inboundGreeting: "Sales line — how can I help?"
agentId: "sales-rep"
support729:
inboundGreeting: "Support — what's broken?"
agentId: "support-rep"
responseTimeoutMs: 90000Lookup key is the SIP user-info part of the dialed URI.
Troubleshooting
- REGISTER fails with 401 loop: check
SIP_REALMmatches the realm in the 401 challenge. - Inbound calls drop after 30 s: silence watchdog — caller muted or RTP path blocked.
- No audio on Opus calls: verify
opusscriptWASM init in plugin startup log. - All calls get 488: caller's INVITE has malformed SDP — inspect bridge log for the rejected offer.
- Outbound DTMF not detected by IVR: some IVRs only accept RFC 2833 RTP telephone-event, not SIP INFO. This plugin sends INFO; ask provider to enable INFO support.
Development
npm install
npm test # 226 tests
npm run typecheck
npm run build # → dist/{index,setup,cli-metadata}.jsLicense
MIT
