Plugin guides
Plugin d’appels vocaux
Appels vocaux pour OpenClaw via un Plugin : notifications sortantes, conversations multi-tours, voix en temps réel en duplex intégral, transcription en streaming et appels entrants avec politiques de liste d’autorisation.
Fournisseurs : mock (développement, sans réseau), plivo (API Voice + transfert XML +
reconnaissance vocale GetInput), telnyx (Call Control v2), twilio (Programmable Voice +
Media Streams).
Démarrage rapide
Installer le Plugin
Depuis npm
openclaw plugins install @openclaw/voice-callDepuis un dossier local (développement)
PLUGIN_SRC=./path/to/local/voice-call-pluginopenclaw plugins install "$PLUGIN_SRC"cd "$PLUGIN_SRC" && pnpm installUtilisez le paquet sans version pour suivre l’étiquette de version actuelle. Épinglez une version exacte uniquement lorsqu’une installation reproductible est nécessaire. Redémarrez ensuite le Gateway afin que le Plugin soit chargé.
Configurer le fournisseur et le Webhook
Définissez la configuration sous plugins.entries.voice-call.config (voir
Configuration ci-dessous). Au minimum : provider, les
identifiants du fournisseur, fromNumber et une URL de Webhook accessible publiquement.
Vérifier la configuration
openclaw voicecall setupopenclaw voicecall setup --jsonVérifie l’activation du Plugin, les identifiants du fournisseur, l’exposition du Webhook et
qu’un seul mode audio (streaming ou realtime) est actif.
Effectuer un test de bon fonctionnement
openclaw voicecall smokeopenclaw voicecall smoke --to "+15555550123"Les deux commandes effectuent par défaut une simulation. Ajoutez --yes pour passer un bref
appel de notification sortant :
openclaw voicecall smoke --to "+15555550123" --yesConfiguration
Si enabled: true, mais que les identifiants du fournisseur sélectionné sont manquants, le démarrage
du Gateway consigne un avertissement indiquant que la configuration est incomplète, avec les clés manquantes, et ignore
le démarrage de l’environnement d’exécution. Lors de leur utilisation, les commandes, les appels RPC et les outils de l’agent renvoient
toujours précisément la configuration manquante.
{ plugins: { entries: { "voice-call": { enabled: true, config: { provider: "twilio", // ou "telnyx" | "plivo" | "mock" fromNumber: "+15550001234", // ou TWILIO_FROM_NUMBER pour Twilio toNumber: "+15550005678", sessionScope: "per-phone", // per-phone | per-call numbers: { "+15550009999": { inboundGreeting: "Silver Fox Cards, comment puis-je vous aider ?", responseSystemPrompt: "Vous êtes un spécialiste concis des cartes de baseball.", tts: { providers: { openai: { speakerVoice: "alloy" }, }, }, }, }, twilio: { accountSid: "ACxxxxxxxx", authToken: "...", // region: "ie1", // facultatif : us1 | ie1 | au1 ; valeur par défaut : us1 }, telnyx: { apiKey: "...", connectionId: "...", // Clé publique du Webhook Telnyx provenant du Mission Control Portal // (Base64 ; peut également être définie via TELNYX_PUBLIC_KEY). publicKey: "...", }, plivo: { authId: "MAxxxxxxxxxxxxxxxxxxxx", authToken: "...", }, // Serveur Webhook serve: { port: 3334, path: "/voice/webhook", }, // Sécurité du Webhook (recommandée pour les tunnels/proxys) webhookSecurity: { allowedHosts: ["voice.example.com"], trustedProxyIPs: ["100.64.0.1"], }, // Exposition publique (choisissez-en une) // publicUrl: "https://example.ngrok.app/voice/webhook", // tunnel: { provider: "ngrok" }, // tailscale: { mode: "funnel", path: "/voice/webhook" }, outbound: { defaultMode: "notify", // notify | conversation }, streaming: { enabled: true /* voir Transcription en streaming */ }, realtime: { enabled: false /* voir Conversations vocales en temps réel */ }, }, }, }, },}Référence de configuration
Clés de premier niveau sous plugins.entries.voice-call.config non présentées ci-dessus :
| Clé | Valeur par défaut | Remarques |
|---|---|---|
enabled |
false |
Interrupteur principal d’activation/désactivation. |
inboundPolicy |
"disabled" |
disabled | allowlist | pairing | open. Voir Appels entrants. |
allowFrom |
[] |
Liste d’autorisation E.164 pour inboundPolicy: "allowlist". |
maxDurationSeconds |
300 |
Limite stricte de durée par appel, appliquée quel que soit l’état de réponse. |
staleCallReaperSeconds |
120 |
Voir Nettoyeur d’appels obsolètes. 0 le désactive. |
silenceTimeoutMs |
800 |
Détection du silence de fin de parole pour le flux classique (hors temps réel). |
transcriptTimeoutMs |
180000 |
Durée d’attente maximale d’une transcription de l’appelant avant d’abandonner un tour. |
ringTimeoutMs |
30000 |
Délai d’expiration de la sonnerie pour les appels sortants. |
maxConcurrentCalls |
1 |
Les appels sortants dépassant cette limite sont rejetés. |
outbound.notifyHangupDelaySec |
3 |
Secondes d’attente après la synthèse vocale avant le raccrochage automatique en mode notification. |
skipSignatureVerification |
false |
Uniquement pour les tests locaux ; ne jamais activer en production. |
store |
non défini | Remplace le chemin par défaut du journal d’appels ~/.openclaw/voice-calls. |
agentId |
"main" |
Agent utilisé pour la génération des réponses et le stockage des sessions. |
responseModel |
non défini | Remplace le modèle par défaut pour les réponses classiques (hors temps réel). |
responseSystemPrompt |
généré | Invite système personnalisée pour les réponses classiques. |
responseTimeoutMs |
30000 |
Délai d’expiration de la génération des réponses classiques (ms). |
Twilio utilise par défaut son point de terminaison REST US1. Pour traiter les appels dans une Région
hors États-Unis prise en charge, définissez twilio.region sur ie1 ou au1 et utilisez les identifiants de
cette Région. Consultez
le guide de Twilio sur l’API REST dans une Région hors États-Unis.
Remarques sur l’exposition et la sécurité des fournisseurs
- Twilio, Telnyx et Plivo nécessitent tous une URL de Webhook accessible publiquement.
mockest un fournisseur de développement local (aucun appel réseau).- Telnyx nécessite
telnyx.publicKey(ouTELNYX_PUBLIC_KEY), sauf siskipSignatureVerificationvaut true. skipSignatureVerificationest uniquement destiné aux tests locaux.- Avec l’offre gratuite de ngrok, définissez
publicUrlsur l’URL ngrok exacte ; la vérification des signatures est toujours appliquée. tunnel.allowNgrokFreeTierLoopbackBypass: trueautorise les Webhooks Twilio dont les signatures sont non valides uniquement lorsquetunnel.provider="ngrok"et queserve.bindest local loopback (agent local ngrok). Uniquement pour le développement local.- Les URL de l’offre gratuite de ngrok peuvent changer ou ajouter une page intermédiaire ; si
publicUrldérive, les signatures Twilio échouent. En production : privilégiez un domaine stable ou un funnel Tailscale.
Limites des connexions en streaming
streaming.preStartTimeoutMs(valeur par défaut :5000) ferme les sockets qui n’envoient jamais de tramestartvalide.streaming.maxPendingConnections(valeur par défaut :32) limite le nombre total de sockets non authentifiés avant démarrage.streaming.maxPendingConnectionsPerIp(valeur par défaut :4) limite les sockets non authentifiés avant démarrage par adresse IP source.streaming.maxConnections(valeur par défaut :128) limite tous les sockets ouverts de flux multimédia (en attente + actifs).
Migrations de l’ancienne configuration
L’analyse de la configuration normalise automatiquement ces anciennes clés et consigne un
avertissement indiquant le chemin de remplacement ; la couche de compatibilité sera supprimée dans une prochaine
version (2026.6.0). Exécutez donc openclaw doctor --fix pour réécrire la configuration enregistrée
selon la structure canonique :
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.includeSystemPromptest supprimé (le contexte en temps réel utilise désormais l’invite générée de l’agent)
Portée de la session
Par défaut, Voice Call utilise sessionScope: "per-phone" afin que les appels répétés du
même appelant conservent la mémoire de la conversation. Définissez sessionScope: "per-call" lorsque
chaque appel de l’opérateur doit commencer avec un nouveau contexte, par exemple pour des flux d’accueil,
de réservation, de SVI ou de pont Google Meet dans lesquels le même numéro de téléphone peut
représenter différentes réunions.
Voice Call stocke les clés de session générées dans l’espace de noms de l’agent configuré
(agent:<agentId>:voice:*). Les clés d’intégration brutes explicites sont résolues dans le
même espace de noms : une clé canonique agent:<configuredAgentId>:* conserve ce
propriétaire et respecte les alias session.mainKey/de portée globale du cœur ; une entrée agent:*
étrangère ou mal formée est délimitée comme clé opaque sous l’agent configuré ;
global et unknown restent des sentinelles globales.
Conversations vocales en temps réel
realtime sélectionne un fournisseur vocal en temps réel et en duplex intégral pour l’audio des appels en direct.
Il est distinct de streaming, qui transmet uniquement l’audio aux fournisseurs de
transcription en temps réel.
Comportement actuel de l’environnement d’exécution :
realtime.enabledest pris en charge pour Twilio et Telnyx.realtime.providerest facultatif. S’il n’est pas défini, Voice Call utilise le premier fournisseur vocal en temps réel enregistré.- Fournisseurs vocaux en temps réel intégrés : Google Gemini Live (
google) et OpenAI (openai), enregistrés par leurs Plugins de fournisseur. - La configuration brute propre au fournisseur se trouve sous
realtime.providers.<providerId>. - Voice Call expose par défaut l’outil en temps réel partagé
openclaw_agent_consult. Le modèle en temps réel peut l’appeler lorsque l’appelant demande un raisonnement plus approfondi, des informations actuelles ou les outils OpenClaw habituels. realtime.consultPolicyajoute éventuellement des instructions indiquant quand le modèle en temps réel doit appeleropenclaw_agent_consult.realtime.agentContext.enabledest désactivé par défaut. Lorsqu’il est activé, Voice Call injecte une identité d’agent limitée et une capsule composée de fichiers sélectionnés de l’espace de travail dans les instructions du fournisseur en temps réel lors de l’établissement de la session.realtime.fastContext.enabledest désactivé par défaut. Lorsqu’il est activé, Voice Call recherche d’abord dans le contexte indexé de la mémoire et de la session les éléments correspondant à la question de consultation, puis renvoie ces extraits au modèle en temps réel dans le délairealtime.fastContext.timeoutMs, avant de se rabattre sur l’agent de consultation complet uniquement sirealtime.fastContext.fallbackToConsultvauttrue.- Si
realtime.providerdésigne un fournisseur non enregistré, ou si aucun fournisseur vocal en temps réel n’est enregistré, Voice Call consigne un avertissement et ignore les médias en temps réel au lieu de faire échouer l’ensemble du Plugin. inboundPolicyne doit pas être défini sur"disabled"lorsquerealtime.enabledvauttrue;validateProviderConfigrejette cette combinaison.- Les clés de session de consultation réutilisent la session d’appel enregistrée lorsqu’elle est disponible, puis se rabattent sur la valeur
sessionScopeconfigurée (per-phonepar défaut, ouper-callpour des appels isolés).
Politique des outils
realtime.toolPolicy contrôle l’exécution de la consultation :
| Politique | Comportement |
|---|---|
safe-read-only |
Expose l’outil de consultation et limite l’agent standard à read, web_search, web_fetch, x_search, memory_search et memory_get. |
owner |
Expose l’outil de consultation et permet à l’agent standard d’utiliser la politique habituelle des outils de l’agent. |
none |
N’expose pas l’outil de consultation. Les outils realtime.tools personnalisés sont néanmoins transmis au fournisseur en temps réel. |
realtime.consultPolicy contrôle uniquement les instructions du modèle en temps réel :
| Politique | Instructions |
|---|---|
auto |
Conserve le prompt par défaut et laisse le fournisseur décider quand appeler l’outil de consultation. |
substantive |
Répond directement aux simples éléments de liaison conversationnelle et consulte avant d’utiliser des faits, la mémoire, des outils ou du contexte. |
always |
Consulte avant chaque réponse substantielle. |
Contexte vocal de l’agent
Activez realtime.agentContext lorsque la passerelle vocale doit s’exprimer comme
l’agent OpenClaw configuré sans subir un aller-retour complet de consultation de
l’agent pour les échanges ordinaires. La capsule de contexte est ajoutée une seule
fois lors de la création de la session en temps réel ; elle n’ajoute donc aucune
latence à chaque échange. Les appels à openclaw_agent_consult exécutent toujours
l’agent OpenClaw complet et doivent être utilisés pour les tâches nécessitant des
outils, des informations actuelles, des recherches en mémoire ou l’état de l’espace
de travail.
{ 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"], }, }, }, }, }, },}Exemples de fournisseurs en temps réel
Google Gemini Live
Valeurs par défaut : clé d’API provenant de realtime.providers.google.apiKey, GEMINI_API_KEY
ou GOOGLE_API_KEY ; modèle gemini-3.1-flash-live-preview ;
voix Kore. sessionResumption et contextWindowCompression sont activés par défaut
pour les appels plus longs et pouvant être reconnectés. Utilisez silenceDurationMs,
startSensitivity et endSensitivity pour accélérer l’alternance des prises de parole
avec l’audio téléphonique.
{ plugins: { entries: { "voice-call": { config: { provider: "twilio", inboundPolicy: "allowlist", allowFrom: ["+15550005678"], realtime: { enabled: true, provider: "google", instructions: "Parlez brièvement. Appelez openclaw_agent_consult avant d’utiliser des outils plus avancés.", 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}" }, }, }, }, }, }, },}Consultez Fournisseur Google et Fournisseur OpenAI pour connaître les options vocales en temps réel propres à chaque fournisseur.
Transcription en continu
streaming sélectionne un fournisseur de transcription en temps réel pour l’audio des appels en direct.
Comportement actuel à l’exécution :
streaming.providerest facultatif. S’il n’est pas défini, Voice Call utilise le premier fournisseur de transcription en temps réel enregistré.- Fournisseurs de transcription en temps réel intégrés : Deepgram (
deepgram), ElevenLabs (elevenlabs), Mistral (mistral), OpenAI (openai) et xAI (xai), enregistrés par leurs Plugins de fournisseur. - La configuration brute propre au fournisseur se trouve sous
streaming.providers.<providerId>. - Après l’envoi par Twilio d’un message
startde flux accepté, Voice Call enregistre immédiatement le flux, met les médias entrants en file d’attente auprès du fournisseur de transcription pendant la connexion de celui-ci et ne lance le message d’accueil initial qu’une fois la transcription en temps réel prête. - Si
streaming.providerdésigne un fournisseur non enregistré, ou si aucun n’est enregistré, Voice Call consigne un avertissement et ignore la diffusion des médias au lieu de faire échouer l’ensemble du Plugin.
Exemples de fournisseurs de transcription en continu
OpenAI
Valeurs par défaut : clé d’API streaming.providers.openai.apiKey ou
OPENAI_API_KEY ; modèle 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-...", // facultatif si OPENAI_API_KEY est défini model: "gpt-4o-transcribe", silenceDurationMs: 800, vadThreshold: 0.5, }, }, }, }, }, }, },}xAI
Valeurs par défaut : clé d’API streaming.providers.xai.apiKey ou XAI_API_KEY (se rabat
sur un profil d’authentification OAuth xAI si aucune des deux n’est définie) ; point de terminaison
wss://api.x.ai/v1/stt ; encodage mulaw ; fréquence d’échantillonnage 8000 ;
endpointingMs: 800 ; interimResults: true.
{ plugins: { entries: { "voice-call": { config: { streaming: { enabled: true, provider: "xai", streamPath: "/voice/stream", providers: { xai: { apiKey: "${XAI_API_KEY}", // facultatif si XAI_API_KEY est défini endpointingMs: 800, language: "en", }, }, }, }, }, }, },}TTS pour les appels
Voice Call utilise la configuration centrale messages.tts pour la synthèse vocale en continu
pendant les appels. Vous pouvez la remplacer dans la configuration du Plugin avec la même structure —
elle est fusionnée en profondeur avec messages.tts.
{ tts: { provider: "elevenlabs", providers: { elevenlabs: { speakerVoiceId: "pMsXgVXv3BLzUgSXRplE", modelId: "eleven_multilingual_v2", }, }, },}Remarques sur le comportement :
- Les anciennes clés
tts.<provider>dans la configuration du Plugin (openai,elevenlabs,microsoft,edge) sont corrigées paropenclaw doctor --fix; la configuration enregistrée doit utilisertts.providers.<provider>. - Le TTS central est utilisé lorsque la diffusion multimédia Twilio est activée ; sinon, les appels se rabattent sur les voix natives du fournisseur.
- Si un flux multimédia Twilio est déjà actif, Voice Call ne se rabat pas sur
OPENCLAW_DOCS_MARKER:calloutOpen:U2F5de TwiML. Si le TTS téléphonique n’est pas disponible dans cet état, la demande de lecture échoue au lieu de mélanger deux chemins de lecture. - Lorsque le TTS téléphonique se rabat sur un fournisseur secondaire, Voice Call consigne un avertissement avec la chaîne de fournisseurs (
from,to,attempts) à des fins de débogage. - Lorsque l’interruption Twilio ou la fermeture du flux efface la file d’attente TTS, les demandes de lecture en attente se terminent au lieu de laisser bloqués les appelants qui attendent la fin de la lecture.
Exemples de TTS
TTS central uniquement
{messages: {tts: {provider: "openai",providers: { openai: { speakerVoice: "alloy" },},},},}Remplacement par ElevenLabs (appels uniquement)
{plugins: {entries: {"voice-call": { config: { tts: { provider: "elevenlabs", providers: { elevenlabs: { apiKey: "elevenlabs_key", speakerVoiceId: "pMsXgVXv3BLzUgSXRplE", modelId: "eleven_multilingual_v2", }, }, }, },},},},}Remplacement du modèle OpenAI (fusion en profondeur)
{plugins: {entries: {"voice-call": { config: { tts: { providers: { openai: { model: "gpt-4o-mini-tts", speakerVoice: "marin", }, }, }, },},},},}Appels entrants
La politique des appels entrants est définie sur disabled par défaut. Pour activer les appels entrants, définissez :
{inboundPolicy: "allowlist",allowFrom: ["+15550001234"],inboundGreeting: "Bonjour ! Comment puis-je vous aider ?",}Les réponses automatiques utilisent le système d’agent. Ajustez-les avec responseModel,
responseSystemPrompt et responseTimeoutMs.
Routage par numéro
Utilisez numbers lorsqu’un même Plugin Voice Call reçoit des appels destinés à plusieurs
numéros de téléphone et que chaque numéro doit se comporter comme une ligne différente. Par exemple,
un numéro peut utiliser un assistant personnel au ton informel, tandis qu’un autre utilise un personnage
professionnel, un agent de réponse différent et une autre voix TTS.
Les routes sont sélectionnées à partir du numéro To composé, fourni par le fournisseur. Les clés doivent
être des numéros au format E.164. Lorsqu’un appel arrive, Voice Call résout une seule fois la
route correspondante, stocke cette route dans l’enregistrement de l’appel et réutilise cette
configuration effective pour le message d’accueil, le chemin de réponse automatique classique, le chemin
de consultation en temps réel et la lecture TTS. Si aucune route ne correspond, la configuration globale
de Voice Call est utilisée. Les appels sortants n’utilisent pas numbers ; transmettez explicitement
la destination sortante, le message et la session lors du lancement de l’appel.
Les substitutions de route prennent actuellement en charge :
inboundGreetingttsagentIdresponseModelresponseSystemPromptresponseTimeoutMs
La valeur de route tts est fusionnée récursivement avec la configuration globale tts de Voice Call ; vous
pouvez donc généralement ne remplacer que la voix du fournisseur :
{inboundGreeting: "Bonjour, vous êtes sur la ligne principale.",responseSystemPrompt: "Vous êtes l’assistant vocal par défaut.",tts: { provider: "openai", providers: { openai: { speakerVoice: "coral" }, },},numbers: { "+15550001111": { inboundGreeting: "Silver Fox Cards, comment puis-je vous aider ?", responseSystemPrompt: "Vous êtes un spécialiste concis des cartes de baseball.", tts: { providers: { openai: { speakerVoice: "alloy" }, }, }, },},}Contrat de sortie vocale
Pour les réponses automatiques, Voice Call ajoute au prompt système un contrat strict de sortie
vocale exigeant une réponse JSON {"spoken":"..."}. Voice Call
extrait le texte à prononcer de manière défensive :
- Ignore les charges utiles marquées comme contenu de raisonnement ou d’erreur.
- Analyse le JSON direct, le JSON dans un bloc délimité ou les clés
"spoken"intégrées. - Se rabat sur le texte brut et supprime les paragraphes d’introduction susceptibles de contenir de la planification ou des métadonnées.
Cela permet de concentrer la lecture vocale sur le texte destiné à l’appelant et évite de divulguer du texte de planification dans l’audio.
Comportement au démarrage de la conversation
Pour les appels conversation sortants, le traitement du premier message dépend de l’état
de lecture en direct :
- L’effacement de la file lors d’une interruption vocale et la réponse automatique sont désactivés uniquement tant que le message d’accueil initial est effectivement en cours de lecture.
- Si la lecture initiale échoue, l’appel revient à l’état
listeninget le message initial reste en file d’attente pour une nouvelle tentative. - Pour la diffusion en continu Twilio, la lecture initiale commence à la connexion du flux, sans délai supplémentaire.
- Une interruption vocale interrompt la lecture active et efface les entrées TTS Twilio en file d’attente qui n’ont pas encore commencé. Les entrées effacées sont résolues comme ignorées, afin que la logique de réponse suivante puisse continuer sans attendre un contenu audio qui ne sera jamais lu.
- Les conversations vocales en temps réel utilisent le premier tour propre au flux en temps réel. Voice Call ne publie pas de mise à jour TwiML
OPENCLAW_DOCS_MARKER:calloutOpen:U2F5héritée pour ce message initial, afin que les sessions<Connect><Stream>sortantes restent connectées.
Délai de grâce après déconnexion d’un flux Twilio
Lorsqu’un flux multimédia Twilio se déconnecte, Voice Call attend 2000 ms avant de mettre automatiquement fin à l’appel :
- Si le flux se reconnecte pendant cet intervalle, la fin automatique est annulée.
- Si aucun flux ne se réenregistre après le délai de grâce, l’appel prend fin afin d’éviter que des appels actifs restent bloqués.
Nettoyeur d’appels obsolètes
Utilisez staleCallReaperSeconds (valeur par défaut : 120) pour mettre fin aux appels qui ne reçoivent
jamais de réponse et n’atteignent jamais un état de conversation en direct, par exemple les appels en mode
notification pour lesquels le fournisseur n’envoie jamais de Webhook terminal. Définissez cette option sur 0 pour
la désactiver.
Le nettoyeur s’exécute toutes les 30 secondes et met uniquement fin aux appels qui n’ont pas
d’horodatage answeredAt et qui ne sont pas déjà dans un état terminal ou en direct
(speaking/listening). Les conversations auxquelles une réponse a été apportée ne sont donc jamais supprimées
par ce minuteur ; maxDurationSeconds (valeur par défaut : 300) constitue la limite distincte qui
met fin aux appels répondus dont la durée est excessive.
Pour les flux de type notification où les opérateurs peuvent transmettre lentement les Webhooks
de sonnerie ou de réponse, augmentez staleCallReaperSeconds au-delà de la valeur par défaut afin que les appels
lents mais normaux ne soient pas supprimés prématurément ; une plage de 120 à 300 secondes est raisonnable
en production.
{plugins: {entries: { "voice-call": { config: { maxDurationSeconds: 300, staleCallReaperSeconds: 120, }, },},},}Sécurité des Webhooks
Lorsqu’un proxy ou un tunnel se trouve devant le Gateway, le Plugin reconstruit l’URL publique pour la vérification de la signature. Ces options déterminent quels en-têtes transférés sont approuvés :
webhookSecurity.allowedHostsstring[]Hôtes autorisés provenant des en-têtes de transfert.
webhookSecurity.trustForwardingHeadersbooleanFaire confiance aux en-têtes transférés sans liste d’autorisation.
webhookSecurity.trustedProxyIPsstring[]Faire confiance aux en-têtes transférés uniquement lorsque l’adresse IP distante de la requête correspond à la liste.
Protections supplémentaires :
- La protection contre la relecture des Webhooks est activée pour Twilio, Telnyx et Plivo. Les requêtes de Webhook valides rejouées sont acquittées, mais leurs effets de bord sont ignorés.
- Les tours de conversation Twilio incluent un jeton propre à chaque tour dans les rappels
<Gather>, afin que les rappels vocaux obsolètes ou rejoués ne puissent pas satisfaire un tour de transcription en attente plus récent. - Les requêtes de Webhook non authentifiées sont rejetées avant la lecture du corps lorsque les en-têtes de signature requis par le fournisseur sont absents.
- Le Webhook voice-call utilise le profil partagé de lecture du corps avant authentification (corps limité à 64 Ko, délai de lecture de 5 secondes), ainsi qu’une limite par clé du nombre de requêtes en cours (8 requêtes simultanées par clé par défaut), avant la vérification de la signature.
Exemple avec un hôte public stable :
{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 funnelLorsque le Gateway est déjà en cours d’exécution, les commandes opérationnelles voicecall
délèguent leur exécution au runtime voice-call géré par le Gateway, afin que la CLI ne lie pas un
deuxième serveur de Webhook. Si aucun Gateway n’est accessible, les commandes se rabattent sur
un runtime CLI autonome.
latency lit calls.jsonl depuis le chemin de stockage voice-call par défaut. Utilisez
--file <path> pour indiquer un autre journal et --last <n> pour limiter
l’analyse aux N derniers enregistrements (200 par défaut). La sortie inclut les valeurs minimales, maximales et moyennes,
ainsi que les p50 et p95 pour la latence des tours et les temps d’attente d’écoute.
Outil de l’agent
Nom de l’outil : voice_call.
| Action | Arguments |
|---|---|
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 |
Le Plugin voice-call fournit une Skills d’agent correspondante.
RPC du Gateway
| Méthode | Arguments | Notes |
|---|---|---|
voicecall.initiate |
to?, message, mode?, sessionKey?, requesterSessionKey? |
Se rabat sur la configuration toNumber lorsque to est omis. |
voicecall.start |
to, message?, mode?, dtmfSequence?, sessionKey? |
Identique à initiate, mais accepte également dtmfSequence avant la connexion. |
voicecall.continue |
callId, message |
Bloque jusqu’à la résolution du tour ; renvoie la transcription. |
voicecall.continue.start |
callId, message |
Variante asynchrone : renvoie immédiatement un operationId. |
voicecall.continue.result |
operationId |
Interroge une opération voicecall.continue.start en attente pour obtenir son résultat. |
voicecall.speak |
callId, message |
Parle sans attendre ; utilise le pont en temps réel lorsque realtime.enabled est activé. |
voicecall.dtmf |
callId, digits |
|
voicecall.end |
callId |
|
voicecall.status |
callId? |
Omettez callId pour répertorier tous les appels actifs. |
dtmfSequence est uniquement valide avec mode: "conversation" ; les appels en mode notification
doivent utiliser voicecall.dtmf après la création de l’appel s’ils ont besoin de chiffres
après la connexion.
Dépannage
Échec de l’exposition du Webhook lors de la configuration
Exécutez la configuration depuis le même environnement que celui qui exécute le Gateway :
openclaw voicecall setupopenclaw voicecall setup --jsonPour twilio, telnyx et plivo, webhook-exposure doit être au vert. Une
valeur publicUrl configurée échoue tout de même lorsqu’elle pointe vers un espace réseau local ou privé,
car l’opérateur ne peut pas rappeler ces adresses.
N’utilisez pas 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 ni d’autres plages de NAT
de niveau opérateur comme publicUrl.
Les appels sortants Twilio en mode notification envoient leur TwiML OPENCLAW_DOCS_MARKER:calloutOpen:U2F5 initial directement
dans la requête de création de l’appel ; le premier message vocal ne dépend donc pas
de la récupération du TwiML du Webhook par Twilio. Un Webhook public reste nécessaire pour les rappels
d’état, les appels de conversation, le DTMF avant connexion, les flux en temps réel et
le contrôle des appels après connexion.
Utilisez un seul chemin d’exposition publique :
{plugins: {entries: {"voice-call": { config: { publicUrl: "https://voice.example.com/voice/webhook", // or tunnel: { provider: "ngrok" }, // or tailscale: { mode: "funnel", path: "/voice/webhook" }, },},},},}Après avoir modifié la configuration, redémarrez ou rechargez le Gateway, puis exécutez :
openclaw voicecall setupopenclaw voicecall smokevoicecall smoke effectue une simulation, sauf si vous transmettez --yes.
Échec des identifiants du fournisseur
Vérifiez le fournisseur sélectionné et les champs d’identifiants requis :
- Twilio :
twilio.accountSid,twilio.authTokenetfromNumber, ouTWILIO_ACCOUNT_SID,TWILIO_AUTH_TOKENetTWILIO_FROM_NUMBER. - Telnyx :
telnyx.apiKey,telnyx.connectionId,telnyx.publicKeyetfromNumber, ouTELNYX_API_KEY,TELNYX_CONNECTION_IDetTELNYX_PUBLIC_KEY. - Plivo :
plivo.authId,plivo.authTokenetfromNumber, ouPLIVO_AUTH_IDetPLIVO_AUTH_TOKEN.
Les identifiants doivent être présents sur l’hôte du Gateway. La modification d’un profil d’interpréteur de commandes local n’affecte pas un Gateway déjà en cours d’exécution tant qu’il n’a pas redémarré ou rechargé son environnement.
Les appels démarrent, mais les Webhooks du fournisseur n’arrivent pas
Vérifiez que la console du fournisseur pointe vers l’URL publique exacte du Webhook :
https://voice.example.com/voice/webhookExaminez ensuite l’état d’exécution :
openclaw voicecall status --call-id <id>openclaw voicecall tailopenclaw logs --followCauses courantes :
publicUrlpointe vers un chemin différent deserve.path.- L’URL du tunnel a changé après le démarrage du Gateway.
- Un proxy transfère la requête, mais supprime ou réécrit les en-têtes d’hôte ou de protocole.
- Le pare-feu ou le DNS achemine le nom d’hôte public vers une autre destination que le Gateway.
- Le Gateway a été redémarré sans que le Plugin Voice Call soit activé.
Lorsqu’un proxy inverse ou un tunnel se trouve devant le Gateway, définissez
webhookSecurity.allowedHosts sur le nom d’hôte public, ou utilisez
webhookSecurity.trustedProxyIPs pour une adresse de proxy connue. N’utilisez
webhookSecurity.trustForwardingHeaders que lorsque la frontière du proxy est
sous votre contrôle.
La vérification de signature échoue
Les signatures du fournisseur sont vérifiées par rapport à l’URL publique qu’OpenClaw reconstruit à partir de la requête entrante. Si les signatures échouent :
- Vérifiez que l’URL du Webhook du fournisseur correspond exactement à
publicUrl, y compris le schéma, l’hôte et le chemin. - Pour les URL de l’offre gratuite de ngrok, mettez à jour
publicUrllorsque le nom d’hôte du tunnel change. - Assurez-vous que le proxy conserve les en-têtes d’hôte et de protocole d’origine, ou configurez
webhookSecurity.allowedHosts. - N’activez pas
skipSignatureVerificationen dehors des tests locaux.
Les participations à Google Meet via Twilio échouent
Google Meet utilise ce Plugin pour les participations par appel téléphonique via Twilio. Vérifiez d’abord Voice Call :
openclaw voicecall setupopenclaw voicecall smoke --to "+15555550123"Vérifiez ensuite explicitement le transport Google Meet :
openclaw googlemeet setup --transport twilioSi Voice Call fonctionne, mais que le participant ne rejoint jamais la réunion Meet, vérifiez le numéro
d’accès téléphonique de Meet, le code PIN et --dtmf-sequence. L’appel téléphonique peut fonctionner
alors que la réunion rejette ou ignore une séquence DTMF incorrecte.
Google Meet démarre la partie téléphonique Twilio via voicecall.start avec une
séquence DTMF préalable à la connexion. Les séquences dérivées du code PIN incluent le paramètre
voiceCall.dtmfDelayMs du Plugin Google Meet (12000 ms par défaut) sous forme de chiffres d’attente Twilio
placés en tête, car les invites de connexion téléphonique Meet peuvent arriver tardivement. Voice Call
redirige ensuite vers le traitement en temps réel avant que le message d’accueil ne soit demandé.
Utilisez openclaw logs --follow pour consulter la trace des phases en direct. Une participation Twilio à Meet
réussie consigne les événements dans cet ordre :
- Google Meet délègue la participation via Twilio à Voice Call.
- Voice Call stocke le TwiML DTMF préalable à la connexion.
- Le TwiML Twilio initial est consommé et servi avant le traitement en temps réel.
- Voice Call sert le TwiML en temps réel pour l’appel Twilio.
- Google Meet demande la lecture du message d’accueil avec
voicecall.speakaprès le délai suivant la séquence DTMF.
openclaw voicecall tail affiche toujours les enregistrements d’appels persistants ; cette commande est utile pour
l’état des appels et les transcriptions, mais toutes les transitions de Webhook ou en temps réel
n’y apparaissent pas.
L’appel en temps réel ne produit aucune parole
Vérifiez qu’un seul mode audio est activé : realtime.enabled et
streaming.enabled ne peuvent pas tous deux avoir la valeur true.
Pour les appels Twilio/Telnyx en temps réel, vérifiez également les points suivants :
- Un Plugin de fournisseur en temps réel est chargé et enregistré.
realtime.providern’est pas défini ou désigne un fournisseur enregistré.- La clé d’API du fournisseur est accessible au processus Gateway.
openclaw logs --followindique que le TwiML en temps réel a été servi, que la passerelle en temps réel a démarré et que le message d’accueil initial a été mis en file d’attente.