Configuration
Ifenabled: true but the selected provider is missing credentials, Gateway
startup logs a setup-incomplete warning with the missing keys and skips
starting the runtime. Commands, RPC calls, and agent tools still return the
exact missing configuration when used.
Voice-call credentials accept SecretRefs.
plugins.entries.voice-call.config.twilio.authToken, plugins.entries.voice-call.config.realtime.providers.*.apiKey, plugins.entries.voice-call.config.streaming.providers.*.apiKey, and plugins.entries.voice-call.config.tts.providers.*.apiKey resolve through the standard SecretRef surface; see SecretRef credential surface.Choose the call owner
With one configured agent, Voice Call uses that agent automatically. With multiple agents, setplugins.entries.voice-call.config.agentId to the intended
response and session owner. main is an ordinary agent ID, not a fallback for
a multi-agent fleet. Per-number routes may choose different agents for inbound
calls, but do not replace the plugin’s startup owner.
If startup reports that Voice Call has no explicit owner, list your agents with
openclaw agents list, set the existing agentId field, and rerun
openclaw voicecall setup. Restart the Gateway after updating its configuration.
Existing legacy default-agent selection is preserved; new multi-agent setups
should use an explicit owner. See Agent configuration.
Config reference
Top-level keys underplugins.entries.voice-call.config not shown above:
Twilio defaults to its US1 REST endpoint. To process calls in a supported
non-US Region, set
twilio.region to ie1 or au1 and use credentials from
that Region. See
Twilio’s non-US REST API guide.
Provider exposure and security notes
Provider exposure and security notes
- Twilio, Telnyx, and Plivo all require a publicly reachable webhook URL.
mockis a local dev provider (no network calls).- Telnyx requires
telnyx.publicKey(orTELNYX_PUBLIC_KEY) unlessskipSignatureVerificationis true. skipSignatureVerificationis for local testing only.- On ngrok free tier, set
publicUrlto the exact ngrok URL; signature verification is always enforced. tunnel.allowNgrokFreeTierLoopbackBypass: trueallows Twilio webhooks with invalid signatures only whentunnel.provider="ngrok"andserve.bindis loopback (ngrok local agent). Local dev only.- Ngrok free-tier URLs can change or add interstitial behavior; if
publicUrldrifts, Twilio signatures fail. Production: prefer a stable domain or a Tailscale funnel. - Tailscale Serve and Funnel automatically expose the realtime or streaming WebSocket path when that audio mode is enabled.
tailscale.portselects the external HTTPS port for bothtailscale.modeand unifiedtunnel.provider: "tailscale-serve" | "tailscale-funnel". It defaults to443; use8443when another HTTPS server owns port 443. Funnel accepts only443,8443, or10000, while Serve accepts any valid TCP port. Non-default ports appear in the webhook and realtime stream URLs.
Streaming connection caps
Streaming connection caps
streaming.preStartTimeoutMs(default5000) closes sockets that never send a validstartframe.streaming.maxPendingConnections(default32) caps total unauthenticated pre-start sockets.streaming.maxPendingConnectionsPerIp(default4) caps unauthenticated pre-start sockets per source IP.streaming.maxConnections(default128) caps all open media stream sockets (pending + active).
Legacy config migrations
Legacy config migrations
Run
openclaw doctor --fix to rewrite these legacy keys to the canonical
shape. The Voice Call plugin owns the migration; runtime config parsing
accepts only the current keys. When both old and current settings exist,
Doctor keeps the current setting, removes the legacy key, and reports which
destination it retained. Legacy values fill only missing current fields:provider: "log"→provider: "mock"twilio.from→fromNumberstreaming.sttProvider→streaming.providerstreaming.openaiApiKey→streaming.providers.openai.apiKeystreaming.sttModel→streaming.providers.openai.modelstreaming.silenceDurationMs→streaming.providers.openai.silenceDurationMsstreaming.vadThreshold→streaming.providers.openai.vadThresholdrealtime.agentContext.includeSystemPromptis removed (realtime context now uses the generated agent prompt)
Session scope
By default, Voice Call usessessionScope: "per-phone" so repeat calls from
the same caller keep conversation memory. Set sessionScope: "per-call" when
each carrier call should start with fresh context, for example reception,
booking, IVR, or Google Meet bridge flows where the same phone number may
represent different meetings.
Set sessionScope: "main" to route every call into the configured agent’s main
session, agent:<agentId>:main, or global when core session.scope is
"global". Custom core session.mainKey values are ignored. Raw call turns
then share history with the agent’s primary session, so use this only when
that shared context is intentional.
For per-phone and per-call, Voice Call stores generated session keys under
the configured agent namespace (agent:<agentId>:voice:*). Raw explicit
integration keys resolve into the same namespace: a canonical
agent:<configuredAgentId>:* key keeps that owner and honors core
main-session/global-scope aliasing; foreign or malformed agent:* input
is scoped as an opaque key under the configured agent; global and unknown
remain global sentinels.