> ## Documentation Index
> Fetch the complete documentation index at: https://openclaw.ai2me.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Voice call troubleshooting

Fixes for setup, webhook exposure, credentials, signature verification, Google Meet dial-in, and silent realtime calls. Part of the [Voice call plugin](/plugins/voice-call) guide.

## Troubleshooting

### Call placement fails to save its initial record

Voice Call reserves pending capacity while saving the initial record, then publishes
the active call and contacts the carrier only after the write succeeds. If the write
fails, placement reports the storage error without dialing and releases the reservation.
Restore access to the state directory, then retry; a failed placement does not consume
`maxConcurrentCalls` capacity.

Webhook acknowledgments and transcript-driven replies wait for call-record persistence.
Token-bound realtime streams wait for pending call updates before matching the carrier ID.
Failed event writes remain retryable, and shutdown drains admitted call work after
closing webhook and stream producers.

If another realtime stream becomes active during admission, a failure to create
the new bridge leaves that active call connected.

### Setup fails webhook exposure

Run setup from the same environment that runs the Gateway:

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
openclaw voicecall setup
openclaw voicecall setup --json
```

For `twilio`, `telnyx`, and `plivo`, `webhook-exposure` must be green. A
configured `publicUrl` still fails when it points at local or private
network space, because the carrier cannot call back into those addresses.
Do not use `localhost`, `127.0.0.1`, `0.0.0.0`, `10.x`, `172.16.x`-`172.31.x`,
`192.168.x`, `169.254.x`, `fc00::/7`, `fd00::/8`, or other carrier-grade-NAT
ranges as `publicUrl`.

Twilio notify-mode outbound calls send their initial `<Say>` TwiML directly
in the create-call request, so the first spoken message does not depend on
Twilio fetching webhook TwiML. A public webhook is still required for status
callbacks, conversation calls, pre-connect DTMF, realtime streams, and
post-connect call control.

Use one public exposure path:

```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
{
  plugins: {
    entries: {
      "voice-call": {
        config: {
          publicUrl: "https://voice.example.com/voice/webhook",
          // or
          tunnel: { provider: "ngrok" },
          // or
          tailscale: { mode: "funnel", port: 8443, path: "/voice/webhook" },
        },
      },
    },
  },
}
```

Config changes apply automatically with the default hybrid reload mode (see
[Hot reload](/gateway/configuration/hot-reload)). After application, run:

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
openclaw voicecall setup
openclaw voicecall smoke
```

`voicecall smoke` is a dry run unless you pass `--yes`.

### Provider credentials fail

Check the selected provider and the required credential fields:

* Twilio: `twilio.accountSid`, `twilio.authToken`, and `fromNumber`, or
  `TWILIO_ACCOUNT_SID`, `TWILIO_AUTH_TOKEN`, and `TWILIO_FROM_NUMBER`.
* Telnyx: `telnyx.apiKey`, `telnyx.connectionId`, `telnyx.publicKey`, and
  `fromNumber`, or `TELNYX_API_KEY`, `TELNYX_CONNECTION_ID`, and
  `TELNYX_PUBLIC_KEY`.
* Plivo: `plivo.authId`, `plivo.authToken`, and `fromNumber`, or
  `PLIVO_AUTH_ID` and `PLIVO_AUTH_TOKEN`.

Credentials must exist on the Gateway host. Editing a local shell profile
does not change the running Gateway's environment. Update its service environment
and restart the Gateway when changing environment-based credentials.

### Calls start but provider webhooks do not arrive

Confirm the provider console points at the exact public webhook URL:

```text theme={"theme":{"light":"min-light","dark":"min-dark"}}
https://voice.example.com/voice/webhook
```

For a Twilio inbound number, configure both number-level callbacks in the
Twilio Console:

* **Voice webhook:** `https://voice.example.com/voice/webhook` using `POST`.
* **Status Callback:** `https://voice.example.com/voice/webhook?type=status` using `POST`.

Media Streams `stop`/WebSocket close handling is the primary auto-end path and
does not depend on the HTTP status callback. Twilio's optional `<Stream
statusCallback>` is a separate stream-diagnostic signal and is not required for
teardown. `openclaw voicecall setup` validates local configuration and webhook
exposure; it cannot inspect or change Twilio Console settings.

Then inspect runtime state:

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
openclaw voicecall status --call-id <id>
openclaw voicecall tail
openclaw logs --follow
```

Common causes:

* `publicUrl` does not match the public webhook URL configured with the provider. A reverse proxy may map that public path to a different `serve.path`, but `publicUrl` must remain the provider-facing URL.
* The tunnel URL changed after the Gateway started.
* A proxy forwards the request but strips or rewrites host/proto headers.
* Firewall or DNS routes the public hostname somewhere other than the Gateway.
* The Gateway was restarted without the Voice Call plugin enabled.

When a reverse proxy or tunnel is in front of the Gateway, set
`webhookSecurity.allowedHosts` to the public hostname, or use
`webhookSecurity.trustedProxyIPs` for a known proxy address. Use
`webhookSecurity.trustForwardingHeaders` only when the proxy boundary is
under your control.

### Signature verification fails

Twilio and Plivo URL signatures use `publicUrl` when it is configured: its
scheme, host, and path are preserved, while the request query is applied.
Without `publicUrl`, OpenClaw reconstructs the URL from the request. Telnyx
signatures do not include the request URL. If signatures fail:

* Confirm the provider webhook URL exactly matches `publicUrl`, including scheme, host, and path.
* For ngrok free-tier URLs, update `publicUrl` when the tunnel hostname changes.
* Ensure the proxy preserves the original host and proto headers, or configure `webhookSecurity.allowedHosts`.
* Do not enable `skipSignatureVerification` outside local testing.

### Google Meet Twilio joins fail

Google Meet uses this plugin for Twilio dial-in joins. First verify Voice
Call:

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
openclaw voicecall setup
openclaw voicecall smoke --to "+15555550123"
```

Then verify the Google Meet transport explicitly:

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
openclaw googlemeet setup --transport twilio
```

If Voice Call is green but the Meet participant never joins, check the Meet
dial-in number, PIN, and `--dtmf-sequence`. The phone call can be healthy
while the meeting rejects or ignores an incorrect DTMF sequence.

Google Meet starts the Twilio phone leg through `voicecall.start` with a
pre-connect DTMF sequence. PIN-derived sequences include the Google Meet
plugin's `voiceCall.dtmfDelayMs` (default **12000 ms**) as leading Twilio
wait digits, because Meet dial-in prompts can arrive late. Voice Call then
redirects back to realtime handling before the intro greeting is requested.

Use `openclaw logs --follow` for the live phase trace. A healthy Twilio Meet
join logs this order:

* Google Meet delegates the Twilio join to Voice Call.
* Voice Call stores pre-connect DTMF TwiML.
* Twilio initial TwiML is consumed and served before realtime handling.
* Voice Call serves realtime TwiML for the Twilio call.
* Google Meet requests intro speech with `voicecall.speak` after the post-DTMF delay.

`openclaw voicecall tail` still shows persisted call records; useful for
call state and transcripts, but not every webhook/realtime transition
appears there.

### Realtime call has no speech

Confirm only one audio mode is enabled: `realtime.enabled` and
`streaming.enabled` cannot both be true.

For realtime Twilio/Telnyx calls, also verify:

* A realtime provider plugin is loaded and registered.
* `realtime.provider` is unset or names a registered provider.
* The provider API key is available to the Gateway process.
* `openclaw logs --follow` shows realtime TwiML served, the realtime bridge started, and the initial greeting queued.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.