extensions/voice-call/README.md
Official Voice Call plugin for OpenClaw.
Providers:
Docs: https://docs.openclaw.ai/plugins/voice-call
Plugin system: https://docs.openclaw.ai/plugin
openclaw plugins install @openclaw/voice-call
Restart the Gateway afterwards.
PLUGIN_HOME=~/.openclaw/extensions
mkdir -p "$PLUGIN_HOME"
cp -R <local-plugin-checkout> "$PLUGIN_HOME/voice-call"
cd "$PLUGIN_HOME/voice-call" && pnpm install
Put under plugins.entries.voice-call.config:
{
provider: "twilio", // or "telnyx" | "plivo" | "mock"
fromNumber: "+15550001234",
toNumber: "+15550005678",
sessionScope: "per-phone", // or "per-call"
twilio: {
accountSid: "ACxxxxxxxx",
authToken: "your_token",
},
telnyx: {
apiKey: "KEYxxxx",
connectionId: "CONNxxxx",
// Telnyx webhook public key from the Telnyx Mission Control Portal
// (Base64 string; can also be set via TELNYX_PUBLIC_KEY).
publicKey: "...",
},
plivo: {
authId: "MAxxxxxxxxxxxxxxxxxxxx",
authToken: "your_token",
},
// Webhook server
serve: {
port: 3334,
path: "/voice/webhook",
},
// Public exposure (pick one):
// publicUrl: "https://example.ngrok.app/voice/webhook",
// tunnel: { provider: "ngrok" },
// tailscale: { mode: "funnel", path: "/voice/webhook" }
outbound: {
defaultMode: "notify", // or "conversation"
},
// Optional response agent workspace. Defaults to "main".
agentId: "main",
streaming: {
enabled: true,
// optional; if omitted, Voice Call picks the first registered
// realtime-transcription provider by autoSelectOrder
provider: "<realtime-transcription-provider-id>",
streamPath: "/voice/stream",
providers: {
"<realtime-transcription-provider-id>": {
// provider-owned options
},
},
preStartTimeoutMs: 5000,
maxPendingConnections: 32,
maxPendingConnectionsPerIp: 4,
maxConnections: 128,
},
}
Notes:
mock is a local dev provider (no network calls).telnyx.publicKey (or TELNYX_PUBLIC_KEY) unless skipSignatureVerification is true.provider: "log", twilio.from, or legacy streaming.* OpenAI keys, run openclaw doctor --fix to rewrite them.https://docs.openclaw.ai/plugins/voice-callresponseModel is optional. When unset, voice responses use the runtime default model.sessionScope defaults to per-phone, preserving caller memory across calls. Use per-call for reception, booking, IVR, and bridge flows where each carrier call should start fresh.See the plugin docs for recommended ranges and production examples:
https://docs.openclaw.ai/plugins/voice-call#stale-call-reaper
Voice Call uses the core messages.tts configuration for
streaming speech on calls. Override examples and provider caveats live here:
https://docs.openclaw.ai/plugins/voice-call#tts-for-calls
openclaw voicecall call --to "+15555550123" --message "Hello from OpenClaw"
openclaw voicecall continue --call-id <id> --message "Any questions?"
openclaw voicecall speak --call-id <id> --message "One moment"
openclaw voicecall end --call-id <id>
openclaw voicecall status --json
openclaw voicecall status --call-id <id>
openclaw voicecall tail
openclaw voicecall expose --mode funnel
Tool name: voice_call
Actions:
initiate_call (message, to?, mode?)continue_call (callId, message)speak_to_user (callId, message)end_call (callId)get_status (callId)voicecall.initiate (to?, message, mode?)voicecall.continue (callId, message)voicecall.speak (callId, message)voicecall.end (callId)voicecall.status (callId)responseModel / responseSystemPrompt control AI auto-responses.{"spoken":"..."}) and filter reasoning/meta output before playback.<Say>; stream-TTS failures fail the playback request.streaming.provider / realtime.provider and put provider-owned options under providers.<id>.