Plugin guides
Plugin per chiamate vocali
Chiamate vocali per OpenClaw tramite un plugin: notifiche in uscita, conversazioni a più turni, voce in tempo reale full-duplex, trascrizione in streaming e chiamate in entrata con criteri basati su elenchi di autorizzazione.
Provider: mock (sviluppo, nessuna rete), plivo (Voice API + trasferimento XML +
riconoscimento vocale GetInput), telnyx (Call Control v2), twilio (Programmable Voice +
Media Streams).
Avvio rapido
Install the plugin
From npm
openclaw plugins install @openclaw/voice-callFrom a local folder (dev)
PLUGIN_SRC=./path/to/local/voice-call-pluginopenclaw plugins install "$PLUGIN_SRC"cd "$PLUGIN_SRC" && pnpm installUsa il pacchetto senza versione per seguire il tag della versione corrente. Fissa una versione esatta solo quando è necessaria un'installazione riproducibile. In seguito, riavvia il Gateway affinché il plugin venga caricato.
Configure provider and webhook
Imposta la configurazione in plugins.entries.voice-call.config (vedi
Configurazione di seguito). Sono richiesti almeno: provider, le
credenziali del provider, fromNumber e un URL Webhook accessibile pubblicamente.
Verify setup
openclaw voicecall setupopenclaw voicecall setup --jsonVerifica l'abilitazione del plugin, le credenziali del provider, l'esposizione del Webhook e
che sia attiva una sola modalità audio (streaming o realtime).
Smoke test
openclaw voicecall smokeopenclaw voicecall smoke --to "+15555550123"Per impostazione predefinita, entrambi eseguono una simulazione. Aggiungi --yes per effettuare una breve
chiamata di notifica in uscita:
openclaw voicecall smoke --to "+15555550123" --yesConfigurazione
Se enabled: true ma al provider selezionato mancano le credenziali, all'avvio il Gateway
registra un avviso di configurazione incompleta con le chiavi mancanti e non
avvia il runtime. I comandi, le chiamate RPC e gli strumenti dell'agente restituiscono comunque
l'esatta configurazione mancante quando vengono utilizzati.
{ plugins: { entries: { "voice-call": { enabled: true, config: { provider: "twilio", // or "telnyx" | "plivo" | "mock" fromNumber: "+15550001234", // or TWILIO_FROM_NUMBER for Twilio toNumber: "+15550005678", sessionScope: "per-phone", // per-phone | per-call numbers: { "+15550009999": { inboundGreeting: "Silver Fox Cards, how can I help?", responseSystemPrompt: "You are a concise baseball card specialist.", tts: { providers: { openai: { speakerVoice: "alloy" }, }, }, }, }, twilio: { accountSid: "ACxxxxxxxx", authToken: "...", // region: "ie1", // optional: us1 | ie1 | au1; defaults to us1 }, telnyx: { apiKey: "...", connectionId: "...", // Telnyx webhook public key from the Mission Control Portal // (Base64; can also be set via TELNYX_PUBLIC_KEY). publicKey: "...", }, plivo: { authId: "MAxxxxxxxxxxxxxxxxxxxx", authToken: "...", }, // Webhook server serve: { port: 3334, path: "/voice/webhook", }, // Webhook security (recommended for tunnels/proxies) webhookSecurity: { allowedHosts: ["voice.example.com"], trustedProxyIPs: ["100.64.0.1"], }, // Public exposure (pick one) // publicUrl: "https://example.ngrok.app/voice/webhook", // tunnel: { provider: "ngrok" }, // tailscale: { mode: "funnel", path: "/voice/webhook" }, outbound: { defaultMode: "notify", // notify | conversation }, streaming: { enabled: true /* see Streaming transcription */ }, realtime: { enabled: false /* see Realtime voice conversations */ }, }, }, }, },}Riferimento della configurazione
Chiavi di primo livello in plugins.entries.voice-call.config non mostrate in precedenza:
| Chiave | Valore predefinito | Note |
|---|---|---|
enabled |
false |
Interruttore generale di attivazione/disattivazione. |
inboundPolicy |
"disabled" |
disabled | allowlist | pairing | open. Vedi Chiamate in entrata. |
allowFrom |
[] |
Elenco di autorizzazione E.164 per inboundPolicy: "allowlist". |
maxDurationSeconds |
300 |
Limite massimo rigido della durata di ogni chiamata, applicato indipendentemente dallo stato di risposta. |
staleCallReaperSeconds |
120 |
Vedi Pulizia delle chiamate obsolete. 0 la disabilita. |
silenceTimeoutMs |
800 |
Rilevamento del silenzio di fine parlato per il flusso classico (non in tempo reale). |
transcriptTimeoutMs |
180000 |
Attesa massima della trascrizione del chiamante prima di rinunciare a un turno. |
ringTimeoutMs |
30000 |
Timeout dello squillo per le chiamate in uscita. |
maxConcurrentCalls |
1 |
Le chiamate in uscita oltre questo limite vengono rifiutate. |
outbound.notifyHangupDelaySec |
3 |
Secondi di attesa dopo il TTS prima della chiusura automatica in modalità di notifica. |
skipSignatureVerification |
false |
Solo per test locali; non abilitarlo mai in produzione. |
store |
non impostato | Sostituisce il percorso predefinito del registro chiamate ~/.openclaw/voice-calls. |
agentId |
"main" |
Agente utilizzato per la generazione delle risposte e l'archiviazione delle sessioni. |
responseModel |
non impostato | Sostituisce il modello predefinito per le risposte classiche (non in tempo reale). |
responseSystemPrompt |
generato | Prompt di sistema personalizzato per le risposte classiche. |
responseTimeoutMs |
30000 |
Timeout per la generazione delle risposte classiche (ms). |
Twilio utilizza per impostazione predefinita il proprio endpoint REST US1. Per elaborare le chiamate in una
regione non statunitense supportata, imposta twilio.region su ie1 o au1 e utilizza le credenziali di
quella regione. Consulta la
guida di Twilio all'API REST nelle regioni non statunitensi.
Provider exposure and security notes
- Twilio, Telnyx e Plivo richiedono tutti un URL Webhook accessibile pubblicamente.
mockè un provider per lo sviluppo locale (nessuna chiamata di rete).- Telnyx richiede
telnyx.publicKey(oTELNYX_PUBLIC_KEY), a meno cheskipSignatureVerificationnon sia true. skipSignatureVerificationè destinato esclusivamente ai test locali.- Nel piano gratuito di ngrok, imposta
publicUrlsull'URL ngrok esatto; la verifica della firma viene sempre applicata. tunnel.allowNgrokFreeTierLoopbackBypass: trueconsente Webhook Twilio con firme non valide solo quandotunnel.provider="ngrok"eserve.bindè local loopback (agente locale ngrok). Solo per lo sviluppo locale.- Gli URL del piano gratuito di ngrok possono cambiare o aggiungere schermate intermedie; se
publicUrlcambia, le firme Twilio non sono valide. In produzione, preferisci un dominio stabile o un funnel Tailscale.
Streaming connection caps
streaming.preStartTimeoutMs(valore predefinito5000) chiude i socket che non inviano mai un framestartvalido.streaming.maxPendingConnections(valore predefinito32) limita il numero totale di socket non autenticati in attesa dell'avvio.streaming.maxPendingConnectionsPerIp(valore predefinito4) limita i socket non autenticati in attesa dell'avvio per ciascun IP di origine.streaming.maxConnections(valore predefinito128) limita tutti i socket aperti dei flussi multimediali (in attesa + attivi).
Legacy config migrations
L'analisi della configurazione normalizza automaticamente queste chiavi precedenti e registra un
avviso che indica il percorso sostitutivo; il livello di compatibilità verrà rimosso in una versione
futura (2026.6.0), quindi esegui openclaw doctor --fix per riscrivere la configurazione salvata
nella forma canonica:
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.includeSystemPromptviene rimosso (il contesto in tempo reale ora utilizza il prompt dell'agente generato)
Ambito della sessione
Per impostazione predefinita, Voice Call utilizza sessionScope: "per-phone", così le chiamate ripetute dello
stesso chiamante conservano la memoria della conversazione. Imposta sessionScope: "per-call" quando
ogni chiamata dell'operatore deve iniziare con un contesto nuovo, ad esempio per reception,
prenotazioni, IVR o flussi bridge di Google Meet in cui lo stesso numero di telefono può
rappresentare riunioni diverse.
Voice Call archivia le chiavi di sessione generate nello spazio dei nomi dell'agente configurato
(agent:<agentId>:voice:*). Le chiavi di integrazione esplicite non elaborate vengono risolte nello
stesso spazio dei nomi: una chiave canonica agent:<configuredAgentId>:* mantiene tale
proprietario e rispetta gli alias session.mainKey/dell'ambito globale del core; un input
agent:* esterno o non valido viene delimitato come chiave opaca sotto l'agente
configurato; global e unknown rimangono sentinelle globali.
Conversazioni vocali in tempo reale
realtime seleziona un provider vocale in tempo reale full-duplex per l'audio delle chiamate dal vivo.
È separato da streaming, che inoltra l'audio soltanto ai provider di
trascrizione in tempo reale.
Comportamento attuale del runtime:
realtime.enabledè supportato per Twilio e Telnyx.realtime.providerè facoltativo. Se non è impostato, Voice Call usa il primo provider vocale in tempo reale registrato.- Provider vocali in tempo reale inclusi: Google Gemini Live (
google) e OpenAI (openai), registrati dai rispettivi Plugin del provider. - La configurazione non elaborata gestita dal provider si trova in
realtime.providers.<providerId>. - Per impostazione predefinita, Voice Call espone lo strumento in tempo reale condiviso
openclaw_agent_consult. Il modello in tempo reale può chiamarlo quando l'interlocutore richiede un ragionamento più approfondito, informazioni aggiornate o i normali strumenti di OpenClaw. realtime.consultPolicyaggiunge facoltativamente indicazioni sui casi in cui il modello in tempo reale dovrebbe chiamareopenclaw_agent_consult.realtime.agentContext.enabledè disattivato per impostazione predefinita. Quando è abilitato, Voice Call inserisce nelle istruzioni del provider in tempo reale, durante la configurazione della sessione, un'identità delimitata dell'agente e una selezione delimitata di file dell'area di lavoro.realtime.fastContext.enabledè disattivato per impostazione predefinita. Quando è abilitato, Voice Call cerca innanzitutto la domanda della consultazione nel contesto indicizzato della memoria e della sessione e restituisce questi estratti al modello in tempo reale entrorealtime.fastContext.timeoutMs; ricorre all'agente di consultazione completo solo serealtime.fastContext.fallbackToConsultètrue.- Se
realtime.providerfa riferimento a un provider non registrato, oppure non è registrato alcun provider vocale in tempo reale, Voice Call registra un avviso e ignora i contenuti multimediali in tempo reale anziché causare il malfunzionamento dell'intero Plugin. inboundPolicynon deve essere"disabled"quandorealtime.enabledètrue;validateProviderConfigrifiuta questa combinazione.- Quando disponibile, le chiavi della sessione di consultazione riutilizzano la sessione di chiamata archiviata; in caso contrario, usano il valore
sessionScopeconfigurato (per-phoneper impostazione predefinita oppureper-callper chiamate isolate).
Criteri degli strumenti
realtime.toolPolicy controlla l'esecuzione della consultazione:
| Criterio | Comportamento |
|---|---|
safe-read-only |
Espone lo strumento di consultazione e limita l'agente normale a read, web_search, web_fetch, x_search, memory_search e memory_get. |
owner |
Espone lo strumento di consultazione e consente all'agente normale di utilizzare i normali criteri degli strumenti dell'agente. |
none |
Non espone lo strumento di consultazione. Gli strumenti realtime.tools personalizzati vengono comunque trasmessi al provider in tempo reale. |
realtime.consultPolicy controlla esclusivamente le istruzioni del modello in tempo reale:
| Criterio | Indicazioni |
|---|---|
auto |
Mantiene il prompt predefinito e lascia che il provider decida quando chiamare lo strumento di consultazione. |
substantive |
Risponde direttamente ai semplici scambi conversazionali e consulta prima di usare fatti, memoria, strumenti o contesto. |
always |
Effettua una consultazione prima di ogni risposta sostanziale. |
Contesto vocale dell'agente
Abilita realtime.agentContext quando il ponte vocale deve esprimersi come
l'agente OpenClaw configurato senza sostenere un ciclo completo di consultazione
dell'agente nelle interazioni ordinarie. La capsula di contesto viene aggiunta
una sola volta alla creazione della sessione in tempo reale, quindi non introduce
latenza per ogni interazione. Le chiamate a openclaw_agent_consult eseguono
comunque l'agente OpenClaw completo e devono essere utilizzate per operazioni
con strumenti, informazioni aggiornate, ricerche nella memoria o stato
dell'area di lavoro.
{ plugins: { entries: { "voice-call": { config: { agentId: "main", realtime: { enabled: true, provider: "google", toolPolicy: "safe-read-only", consultPolicy: "substantive", agentContext: { enabled: true, maxChars: 6000, includeIdentity: true, includeWorkspaceFiles: true, files: ["SOUL.md", "IDENTITY.md", "USER.md"], }, }, }, }, }, },}Esempi di provider in tempo reale
Google Gemini Live
Valori predefiniti: chiave API da realtime.providers.google.apiKey,
GEMINI_API_KEY o GOOGLE_API_KEY; modello
gemini-3.1-flash-live-preview; voce Kore. sessionResumption e
contextWindowCompression sono abilitati per impostazione predefinita
per chiamate più lunghe e riconnettibili. Usa silenceDurationMs,
startSensitivity ed endSensitivity per regolare un'alternanza dei
turni più rapida sull'audio telefonico.
{ plugins: { entries: { "voice-call": { config: { provider: "twilio", inboundPolicy: "allowlist", allowFrom: ["+15550005678"], realtime: { enabled: true, provider: "google", instructions: "Speak briefly. Call openclaw_agent_consult before using deeper tools.", toolPolicy: "safe-read-only", consultPolicy: "substantive", consultThinkingLevel: "low", consultFastMode: true, agentContext: { enabled: true }, providers: { google: { apiKey: "${GEMINI_API_KEY}", model: "gemini-3.1-flash-live-preview", speakerVoice: "Kore", silenceDurationMs: 500, startSensitivity: "high", }, }, }, }, }, }, },}OpenAI
{ plugins: { entries: { "voice-call": { config: { realtime: { enabled: true, provider: "openai", providers: { openai: { apiKey: "${OPENAI_API_KEY}" }, }, }, }, }, }, },}Consulta Provider Google e provider OpenAI per le opzioni vocali in tempo reale specifiche del provider.
Trascrizione in streaming
streaming seleziona un provider di trascrizione in tempo reale per l'audio
delle chiamate dal vivo.
Comportamento attuale in fase di esecuzione:
streaming.providerè facoltativo. Se non è impostato, Voice Call usa il primo provider di trascrizione in tempo reale registrato.- Provider di trascrizione in tempo reale inclusi: Deepgram (
deepgram), ElevenLabs (elevenlabs), Mistral (mistral), OpenAI (openai) e xAI (xai), registrati dai rispettivi Plugin del provider. - La configurazione non elaborata gestita dal provider si trova in
streaming.providers.<providerId>. - Dopo che Twilio invia un messaggio
startdi streaming accettato, Voice Call registra immediatamente lo streaming, accoda i contenuti multimediali in ingresso tramite il provider di trascrizione mentre questo stabilisce la connessione e avvia il saluto iniziale solo quando la trascrizione in tempo reale è pronta. - Se
streaming.providerfa riferimento a un provider non registrato, oppure non ne è registrato alcuno, Voice Call registra un avviso e ignora lo streaming multimediale anziché causare il malfunzionamento dell'intero Plugin.
Esempi di provider di streaming
OpenAI
Valori predefiniti: chiave API streaming.providers.openai.apiKey o
OPENAI_API_KEY; modello gpt-4o-transcribe; silenceDurationMs: 800;
vadThreshold: 0.5.
{ plugins: { entries: { "voice-call": { config: { streaming: { enabled: true, provider: "openai", streamPath: "/voice/stream", providers: { openai: { apiKey: "sk-...", // optional if OPENAI_API_KEY is set model: "gpt-4o-transcribe", silenceDurationMs: 800, vadThreshold: 0.5, }, }, }, }, }, }, },}xAI
Valori predefiniti: chiave API streaming.providers.xai.apiKey o
XAI_API_KEY (se nessuna delle due è impostata, usa un profilo di
autenticazione OAuth xAI); endpoint wss://api.x.ai/v1/stt; codifica
mulaw; frequenza di campionamento 8000; endpointingMs: 800;
interimResults: true.
{ plugins: { entries: { "voice-call": { config: { streaming: { enabled: true, provider: "xai", streamPath: "/voice/stream", providers: { xai: { apiKey: "${XAI_API_KEY}", // optional if XAI_API_KEY is set endpointingMs: 800, language: "en", }, }, }, }, }, }, },}TTS per le chiamate
Voice Call usa la configurazione principale messages.tts per la sintesi
vocale in streaming durante le chiamate. Puoi sovrascriverla nella
configurazione del Plugin utilizzando la stessa struttura: viene unita
ricorsivamente a messages.tts.
{ tts: { provider: "elevenlabs", providers: { elevenlabs: { speakerVoiceId: "pMsXgVXv3BLzUgSXRplE", modelId: "eleven_multilingual_v2", }, }, },}Note sul comportamento:
- Le chiavi legacy
tts.<provider>nella configurazione del Plugin (openai,elevenlabs,microsoft,edge) vengono corrette daopenclaw doctor --fix; la configurazione salvata deve usaretts.providers.<provider>. - Il TTS principale viene usato quando lo streaming multimediale di Twilio è abilitato; altrimenti, le chiamate ricorrono alle voci native del provider.
- Se uno streaming multimediale Twilio è già attivo, Voice Call non ricorre a
OPENCLAW_DOCS_MARKER:calloutOpen:U2F5di TwiML. Se il TTS telefonico non è disponibile in tale stato, la richiesta di riproduzione non riesce anziché combinare due percorsi di riproduzione. - Quando il TTS telefonico ricorre a un provider secondario, Voice Call registra un avviso contenente la catena dei provider (
from,to,attempts) per agevolare il debug. - Quando l'interruzione dell'interlocutore o la chiusura dello streaming Twilio svuota la coda TTS in sospeso, le richieste di riproduzione accodate vengono completate anziché lasciare in attesa chi aspetta il completamento della riproduzione.
Esempi di TTS
Core TTS only
{messages: {tts: {provider: "openai",providers: { openai: { speakerVoice: "alloy" },},},},}Override to ElevenLabs (calls only)
{plugins: {entries: {"voice-call": { config: { tts: { provider: "elevenlabs", providers: { elevenlabs: { apiKey: "elevenlabs_key", speakerVoiceId: "pMsXgVXv3BLzUgSXRplE", modelId: "eleven_multilingual_v2", }, }, }, },},},},}OpenAI model override (deep-merge)
{plugins: {entries: {"voice-call": { config: { tts: { providers: { openai: { model: "gpt-4o-mini-tts", speakerVoice: "marin", }, }, }, },},},},}Chiamate in ingresso
Per impostazione predefinita, il criterio per le chiamate in ingresso è
disabled. Per abilitare le chiamate in ingresso, imposta:
{inboundPolicy: "allowlist",allowFrom: ["+15550001234"],inboundGreeting: "Hello! How can I help?",}Le risposte automatiche usano il sistema dell'agente. Configurale con responseModel,
responseSystemPrompt e responseTimeoutMs.
Instradamento per numero
Usa numbers quando un singolo plugin Voice Call riceve chiamate per più numeri di
telefono e ciascun numero deve comportarsi come una linea diversa. Ad esempio,
un numero può usare un assistente personale informale, mentre un altro usa un'identità
aziendale, un agente di risposta diverso e una voce TTS diversa.
Le route vengono selezionate in base al numero To chiamato fornito dal provider. Le chiavi devono
essere numeri E.164. Quando arriva una chiamata, Voice Call risolve una sola volta la
route corrispondente, la memorizza nel record della chiamata e riutilizza tale
configurazione effettiva per il saluto, il percorso classico di risposta automatica, il percorso
di consultazione in tempo reale e la riproduzione TTS. Se nessuna route corrisponde, viene usata
la configurazione globale di Voice Call. Le chiamate in uscita non usano numbers; specifica
esplicitamente destinazione, messaggio e sessione in uscita quando avvii la chiamata.
Le sostituzioni specifiche della route attualmente supportano:
inboundGreetingttsagentIdresponseModelresponseSystemPromptresponseTimeoutMs
Il valore tts della route viene unito ricorsivamente alla configurazione globale tts di Voice Call, quindi
in genere puoi sostituire solo la voce del provider:
{inboundGreeting: "Hello from the main line.",responseSystemPrompt: "You are the default voice assistant.",tts: { provider: "openai", providers: { openai: { speakerVoice: "coral" }, },},numbers: { "+15550001111": { inboundGreeting: "Silver Fox Cards, how can I help?", responseSystemPrompt: "You are a concise baseball card specialist.", tts: { providers: { openai: { speakerVoice: "alloy" }, }, }, },},}Contratto dell'output vocale
Per le risposte automatiche, Voice Call aggiunge al prompt di sistema un rigido contratto
per l'output vocale che richiede una risposta JSON {"spoken":"..."}. Voice Call
estrae in modo difensivo il testo da pronunciare:
- Ignora i payload contrassegnati come contenuto di ragionamento o di errore.
- Analizza JSON diretto, JSON delimitato o chiavi
"spoken"incorporate. - In alternativa usa testo normale e rimuove i probabili paragrafi introduttivi di pianificazione o metacontenuto.
Ciò mantiene la riproduzione vocale concentrata sul testo destinato al chiamante ed evita di diffondere nell'audio il testo di pianificazione.
Comportamento all'avvio della conversazione
Per le chiamate conversation in uscita, la gestione del primo messaggio è legata allo stato
di riproduzione in tempo reale:
- La pulizia della coda in caso di interruzione vocale e la risposta automatica vengono soppresse solo mentre il saluto iniziale viene effettivamente pronunciato.
- Se la riproduzione iniziale non riesce, la chiamata torna allo stato
listeninge il messaggio iniziale rimane in coda per un nuovo tentativo. - La riproduzione iniziale per lo streaming Twilio inizia alla connessione del flusso senza ulteriori ritardi.
- L'interruzione vocale interrompe la riproduzione attiva e rimuove le voci TTS Twilio in coda ma non ancora in riproduzione. Le voci rimosse vengono risolte come ignorate, consentendo alla logica delle risposte successive di proseguire senza attendere un audio che non verrà mai riprodotto.
- Le conversazioni vocali in tempo reale usano il turno iniziale del flusso in tempo reale. Voice Call non invia un aggiornamento TwiML
OPENCLAW_DOCS_MARKER:calloutOpen:U2F5legacy per quel messaggio iniziale, quindi le sessioni<Connect><Stream>in uscita rimangono collegate.
Periodo di tolleranza per la disconnessione del flusso Twilio
Quando un flusso multimediale Twilio si disconnette, Voice Call attende 2000 ms prima di terminare automaticamente la chiamata:
- Se il flusso si riconnette durante tale intervallo, la terminazione automatica viene annullata.
- Se dopo il periodo di tolleranza non viene registrato nuovamente alcun flusso, la chiamata viene terminata per evitare chiamate attive bloccate.
Eliminazione delle chiamate obsolete
Usa staleCallReaperSeconds (valore predefinito 120) per terminare le chiamate che non ricevono mai
risposta e non raggiungono mai uno stato di conversazione attiva, ad esempio le chiamate in modalità
di notifica per le quali il provider non invia mai un Webhook terminale. Impostalo su 0 per
disabilitarlo.
Il processo di eliminazione viene eseguito ogni 30 secondi e termina solo le chiamate prive di
timestamp answeredAt e che non si trovano già in uno stato terminale o attivo
(speaking/listening), quindi le conversazioni a cui è stata data risposta non vengono mai eliminate
da questo timer; maxDurationSeconds (valore predefinito 300) è il limite separato che
termina le chiamate con risposta che durano troppo a lungo.
Per i flussi in stile notifica in cui gli operatori possono impiegare molto tempo a inviare i Webhook
di squillo/risposta, aumenta staleCallReaperSeconds oltre il valore predefinito affinché le chiamate
lente ma normali non vengano eliminate prematuramente; 120-300 secondi è un intervallo ragionevole
per la produzione.
{plugins: {entries: { "voice-call": { config: { maxDurationSeconds: 300, staleCallReaperSeconds: 120, }, },},},}Sicurezza dei Webhook
Quando davanti al Gateway è presente un proxy o un tunnel, il plugin ricostruisce l'URL pubblico per la verifica della firma. Queste opzioni stabiliscono quali intestazioni inoltrate sono considerate attendibili:
webhookSecurity.allowedHostsstring[]Host consentiti provenienti dalle intestazioni di inoltro.
webhookSecurity.trustForwardingHeadersbooleanConsidera attendibili le intestazioni inoltrate senza un elenco consentito.
webhookSecurity.trustedProxyIPsstring[]Considera attendibili le intestazioni inoltrate solo quando l'IP remoto della richiesta corrisponde all'elenco.
Protezioni aggiuntive:
- La protezione dalla ripetizione dei Webhook è abilitata per Twilio, Telnyx e Plivo. Le richieste Webhook valide ripetute vengono confermate, ma i relativi effetti collaterali vengono ignorati.
- I turni di conversazione Twilio includono un token specifico per turno nei callback
<Gather>, quindi i callback vocali obsoleti o ripetuti non possono soddisfare un turno di trascrizione in sospeso più recente. - Le richieste Webhook non autenticate vengono rifiutate prima della lettura del corpo quando mancano le intestazioni di firma richieste dal provider.
- Il Webhook voice-call usa il profilo condiviso di lettura del corpo precedente all'autenticazione (corpo massimo di 64 KB, timeout di lettura di 5 secondi) e un limite per chiave delle richieste in corso (8 richieste simultanee per chiave per impostazione predefinita) prima della verifica della firma.
Esempio con un host pubblico stabile:
{plugins: {entries: { "voice-call": { config: { publicUrl: "https://voice.example.com/voice/webhook", webhookSecurity: { allowedHosts: ["voice.example.com"], }, }, },},},}CLI
openclaw voicecall call --to "+15555550123" --message "Hello from OpenClaw"openclaw voicecall start --to "+15555550123" # alias for callopenclaw voicecall continue --call-id <id> --message "Any questions?"openclaw voicecall speak --call-id <id> --message "One moment"openclaw voicecall dtmf --call-id <id> --digits "ww123456#"openclaw voicecall end --call-id <id>openclaw voicecall status --call-id <id>openclaw voicecall tailopenclaw voicecall latency # summarize turn latency from logsopenclaw voicecall expose --mode funnelQuando il Gateway è già in esecuzione, i comandi operativi voicecall
vengono delegati al runtime voice-call gestito dal Gateway, così la CLI non associa un
secondo server Webhook. Se non è possibile raggiungere alcun Gateway, i comandi passano
a un runtime CLI autonomo.
latency legge calls.jsonl dal percorso di archiviazione predefinito di voice-call. Usa
--file <path> per specificare un log diverso e --last <n> per limitare
l'analisi agli ultimi N record (valore predefinito 200). L'output include minimo/massimo/media,
p50 e p95 per la latenza dei turni e i tempi di attesa dell'ascolto.
Strumento dell'agente
Nome dello strumento: voice_call.
| Azione | Argomenti |
|---|---|
initiate_call |
message, to?, mode?, dtmfSequence? |
continue_call |
callId, message |
speak_to_user |
callId, message |
send_dtmf |
callId, digits |
end_call |
callId |
get_status |
callId |
Il plugin voice-call include una Skills corrispondente per l'agente.
RPC del Gateway
| Metodo | Argomenti | Note |
|---|---|---|
voicecall.initiate |
to?, message, mode?, sessionKey?, requesterSessionKey? |
Usa come alternativa la configurazione toNumber quando to viene omesso. |
voicecall.start |
to, message?, mode?, dtmfSequence?, sessionKey? |
Equivale a initiate, ma accetta anche dtmfSequence prima della connessione. |
voicecall.continue |
callId, message |
Blocca fino alla risoluzione del turno; restituisce la trascrizione. |
voicecall.continue.start |
callId, message |
Variante asincrona: restituisce immediatamente un operationId. |
voicecall.continue.result |
operationId |
Interroga un'operazione voicecall.continue.start in sospeso per ottenerne il risultato. |
voicecall.speak |
callId, message |
Parla senza attendere; usa il bridge in tempo reale quando realtime.enabled. |
voicecall.dtmf |
callId, digits |
|
voicecall.end |
callId |
|
voicecall.status |
callId? |
Ometti callId per elencare tutte le chiamate attive. |
dtmfSequence è valido solo con mode: "conversation"; le chiamate in modalità notifica
devono usare voicecall.dtmf dopo la creazione della chiamata se necessitano di cifre
successive alla connessione.
Risoluzione dei problemi
La configurazione dell'esposizione del Webhook non riesce
Esegui la configurazione dallo stesso ambiente in cui viene eseguito il Gateway:
openclaw voicecall setupopenclaw voicecall setup --jsonPer twilio, telnyx e plivo, webhook-exposure deve essere verde. Anche un
publicUrl configurato non supera il controllo quando punta a uno spazio di rete locale o privato,
perché l'operatore non può richiamare tali indirizzi.
Non usare 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 o altri intervalli NAT
di livello operatore come publicUrl.
Le chiamate in uscita Twilio in modalità notifica inviano il TwiML OPENCLAW_DOCS_MARKER:calloutOpen:U2F5 iniziale direttamente
nella richiesta di creazione della chiamata, quindi il primo messaggio vocale non dipende dal
recupero del TwiML del Webhook da parte di Twilio. Un Webhook pubblico è comunque necessario per i callback
di stato, le chiamate di conversazione, il DTMF precedente alla connessione, i flussi in tempo reale e
il controllo della chiamata dopo la connessione.
Usa un solo percorso di esposizione pubblica:
{plugins: {entries: {"voice-call": { config: { publicUrl: "https://voice.example.com/voice/webhook", // or tunnel: { provider: "ngrok" }, // or tailscale: { mode: "funnel", path: "/voice/webhook" }, },},},},}Dopo aver modificato la configurazione, riavvia o ricarica il Gateway, quindi esegui:
openclaw voicecall setupopenclaw voicecall smokevoicecall smoke è un'esecuzione di prova, a meno che non specifichi --yes.
Le credenziali del provider non sono valide
Controlla il provider selezionato e i campi delle credenziali obbligatori:
- Twilio:
twilio.accountSid,twilio.authTokenefromNumber, oppureTWILIO_ACCOUNT_SID,TWILIO_AUTH_TOKENeTWILIO_FROM_NUMBER. - Telnyx:
telnyx.apiKey,telnyx.connectionId,telnyx.publicKeyefromNumber, oppureTELNYX_API_KEY,TELNYX_CONNECTION_IDeTELNYX_PUBLIC_KEY. - Plivo:
plivo.authId,plivo.authTokenefromNumber, oppurePLIVO_AUTH_IDePLIVO_AUTH_TOKEN.
Le credenziali devono essere presenti sull'host del Gateway. La modifica di un profilo della shell locale non influisce su un Gateway già in esecuzione finché non viene riavviato o non ricarica il proprio ambiente.
Le chiamate si avviano, ma i webhook del provider non arrivano
Verifica che la console del provider punti all'URL pubblico esatto del webhook:
https://voice.example.com/voice/webhookQuindi controlla lo stato di runtime:
openclaw voicecall status --call-id <id>openclaw voicecall tailopenclaw logs --followCause comuni:
publicUrlpunta a un percorso diverso daserve.path.- L'URL del tunnel è cambiato dopo l'avvio del Gateway.
- Un proxy inoltra la richiesta, ma rimuove o riscrive le intestazioni host/protocollo.
- Il firewall o il DNS instrada il nome host pubblico verso una destinazione diversa dal Gateway.
- Il Gateway è stato riavviato senza il plugin Voice Call abilitato.
Quando un reverse proxy o un tunnel si trova davanti al Gateway, imposta
webhookSecurity.allowedHosts sul nome host pubblico oppure utilizza
webhookSecurity.trustedProxyIPs per un indirizzo proxy noto. Utilizza
webhookSecurity.trustForwardingHeaders solo quando il confine del proxy è
sotto il tuo controllo.
La verifica della firma non riesce
Le firme del provider vengono verificate rispetto all'URL pubblico che OpenClaw ricostruisce dalla richiesta in arrivo. Se la verifica delle firme non riesce:
- Verifica che l'URL del webhook del provider corrisponda esattamente a
publicUrl, inclusi schema, host e percorso. - Per gli URL del piano gratuito di ngrok, aggiorna
publicUrlquando cambia il nome host del tunnel. - Assicurati che il proxy conservi le intestazioni host e protocollo originali oppure configura
webhookSecurity.allowedHosts. - Non abilitare
skipSignatureVerificational di fuori dei test locali.
Le partecipazioni a Google Meet tramite Twilio non riescono
Google Meet utilizza questo plugin per partecipare tramite chiamata telefonica Twilio. Verifica innanzitutto Voice Call:
openclaw voicecall setupopenclaw voicecall smoke --to "+15555550123"Quindi verifica esplicitamente il trasporto di Google Meet:
openclaw googlemeet setup --transport twilioSe Voice Call funziona correttamente, ma il partecipante non accede mai a Meet, controlla il numero
di accesso telefonico di Meet, il PIN e --dtmf-sequence. La chiamata telefonica può funzionare
correttamente anche se la riunione rifiuta o ignora una sequenza DTMF errata.
Google Meet avvia la tratta telefonica Twilio tramite voicecall.start con una
sequenza DTMF precedente alla connessione. Le sequenze derivate dal PIN includono
voiceCall.dtmfDelayMs del plugin Google Meet (valore predefinito: 12000 ms) come cifre
di attesa Twilio iniziali, poiché i messaggi vocali per l'accesso telefonico a Meet possono arrivare in ritardo. Voice Call quindi
reindirizza nuovamente alla gestione in tempo reale prima che venga richiesto il saluto introduttivo.
Utilizza openclaw logs --follow per la traccia in tempo reale delle fasi. Una corretta partecipazione
a Meet tramite Twilio registra questo ordine:
- Google Meet delega a Voice Call la partecipazione tramite Twilio.
- Voice Call memorizza il TwiML DTMF precedente alla connessione.
- Il TwiML iniziale di Twilio viene elaborato e fornito prima della gestione in tempo reale.
- Voice Call fornisce il TwiML in tempo reale per la chiamata Twilio.
- Google Meet richiede il messaggio vocale introduttivo con
voicecall.speakdopo il ritardo successivo al DTMF.
openclaw voicecall tail mostra comunque i record persistenti delle chiamate; è utile per
lo stato delle chiamate e le trascrizioni, ma non tutte le transizioni dei webhook o in tempo reale
vengono visualizzate in questo comando.
La chiamata in tempo reale non ha audio vocale
Verifica che sia abilitata una sola modalità audio: realtime.enabled e
streaming.enabled non possono essere entrambi impostati su true.
Per le chiamate Twilio/Telnyx in tempo reale, verifica inoltre che:
- Sia caricato e registrato un plugin per un provider in tempo reale.
realtime.providernon sia impostato oppure indichi un provider registrato.- La chiave API del provider sia disponibile per il processo del Gateway.
openclaw logs --followmostri che il TwiML in tempo reale è stato fornito, che il bridge in tempo reale è stato avviato e che il saluto iniziale è stato accodato.