Voice Call (Plugin)

Sprachanrufe für OpenClaw über ein Plugin. Unterstützt ausgehende Benachrichtigungen und Multi-Turn-Konversationen mit Richtlinien für eingehende Anrufe.

Aktuelle Provider:

  • twilio (Programmable Voice + Media Streams)
  • telnyx (Call Control v2)
  • plivo (Voice API + XML Transfer + GetInput Speech)
  • mock (Entwicklung/ohne Netzwerk)

Kurzmodell:

  • Plugin installieren
  • Gateway neu starten
  • Unter plugins.entries.voice-call.config konfigurieren
  • openclaw voicecall ... oder das voice_call-Tool nutzen

Wo es läuft (lokal vs. remote)

Das Voice-Call-Plugin läuft innerhalb des Gateway-Prozesses.

Wenn du ein Remote-Gateway nutzt, installiere/konfiguriere das Plugin auf dem Rechner, auf dem das Gateway läuft, und starte dann das Gateway neu, um es zu laden.

Installation

Option A: Aus npm installieren (empfohlen)

openclaw plugins install @openclaw/voice-call

Starte das Gateway danach neu.

Option B: Aus einem lokalen Ordner installieren (Entwicklung, ohne Kopieren)

openclaw plugins install ./extensions/voice-call
cd ./extensions/voice-call && pnpm install

Starte das Gateway danach neu.

Konfiguration

Konfiguration unter plugins.entries.voice-call.config setzen:

{
  plugins: {
    entries: {
      "voice-call": {
        enabled: true,
        config: {
          provider: "twilio", // oder "telnyx" | "plivo" | "mock"
          fromNumber: "+15550001234",
          toNumber: "+15550005678",

          twilio: {
            accountSid: "ACxxxxxxxx",
            authToken: "...",
          },

          telnyx: {
            apiKey: "...",
            connectionId: "...",
            // Telnyx-Webhook-Public-Key aus dem Telnyx Mission Control Portal
            // (Base64-String; kann auch über TELNYX_PUBLIC_KEY gesetzt werden).
            publicKey: "...",
          },

          plivo: {
            authId: "MAxxxxxxxxxxxxxxxxxxxx",
            authToken: "...",
          },

          // Webhook-Server
          serve: {
            port: 3334,
            path: "/voice/webhook",
          },

          // Webhook-Sicherheit (empfohlen für Tunnel/Proxies)
          webhookSecurity: {
            allowedHosts: ["voice.example.com"],
            trustedProxyIPs: ["100.64.0.1"],
          },

          // Öffentliche Erreichbarkeit (wähle eine)
          // publicUrl: "https://example.ngrok.app/voice/webhook",
          // tunnel: { provider: "ngrok" },
          // tailscale: { mode: "funnel", path: "/voice/webhook" }

          outbound: {
            defaultMode: "notify", // notify | conversation
          },

          streaming: {
            enabled: true,
            streamPath: "/voice/stream",
            preStartTimeoutMs: 5000,
            maxPendingConnections: 32,
            maxPendingConnectionsPerIp: 4,
            maxConnections: 128,
          },
        },
      },
    },
  },
}

Hinweise:

  • Twilio/Telnyx erfordern eine öffentlich erreichbare Webhook-URL.
  • Plivo erfordert eine öffentlich erreichbare Webhook-URL.
  • mock ist ein lokaler Entwicklungs-Provider (keine Netzwerkaufrufe).
  • Telnyx erfordert telnyx.publicKey (oder TELNYX_PUBLIC_KEY), es sei denn skipSignatureVerification ist true.
  • skipSignatureVerification ist nur für lokales Testen gedacht.
  • Wenn du den kostenlosen ngrok-Tarif nutzt, setze publicUrl auf die exakte ngrok-URL; Signaturverifizierung wird immer durchgesetzt.
  • tunnel.allowNgrokFreeTierLoopbackBypass: true erlaubt Twilio-Webhooks mit ungültigen Signaturen nur wenn tunnel.provider="ngrok" und serve.bind Loopback ist (ngrok Local Agent). Nur für lokale Entwicklung verwenden.
  • Kostenlose ngrok-Tier-URLs können sich ändern oder Interstitial-Verhalten hinzufügen; wenn publicUrl abweicht, schlagen Twilio-Signaturen fehl. Für Produktion bevorzuge eine stabile Domain oder Tailscale Funnel.
  • Standard-Streaming-Sicherheit:
    • streaming.preStartTimeoutMs schließt Sockets, die nie einen gültigen start-Frame senden.
    • streaming.maxPendingConnections begrenzt nicht authentifizierte Pre-Start-Sockets insgesamt.
    • streaming.maxPendingConnectionsPerIp begrenzt nicht authentifizierte Pre-Start-Sockets pro Quell-IP.
    • streaming.maxConnections begrenzt die Gesamtzahl offener Media-Stream-Sockets (ausstehend + aktiv).

Bereinigung veralteter Anrufe

Nutze staleCallReaperSeconds, um Anrufe zu beenden, die nie einen finalen Webhook erhalten (zum Beispiel Notify-Modus-Anrufe, die nie abschließen). Standard ist 0 (deaktiviert).

Empfohlene Bereiche:

  • Produktion: 120300 Sekunden für Notify-artige Flows.
  • Halte diesen Wert höher als maxDurationSeconds, damit normale Anrufe abgeschlossen werden können. Ein guter Startpunkt ist maxDurationSeconds + 30–60 Sekunden.

Beispiel:

{
  plugins: {
    entries: {
      "voice-call": {
        config: {
          maxDurationSeconds: 300,
          staleCallReaperSeconds: 360,
        },
      },
    },
  },
}

Webhook-Sicherheit

Wenn ein Proxy oder Tunnel vor dem Gateway sitzt, rekonstruiert das Plugin die öffentliche URL für die Signaturverifizierung. Diese Optionen steuern, welchen weitergeleiteten Headern vertraut wird.

webhookSecurity.allowedHosts erlaubt Hosts aus Weiterleitungs-Headern.

webhookSecurity.trustForwardingHeaders vertraut weitergeleiteten Headern ohne Allowlist.

webhookSecurity.trustedProxyIPs vertraut weitergeleiteten Headern nur, wenn die Remote-IP der Anfrage in der Liste enthalten ist.

Webhook-Replay-Schutz ist für Twilio und Plivo aktiviert. Wiederholte gültige Webhook- Anfragen werden bestätigt, aber für Seiteneffekte übersprungen.

Twilio-Konversations-Turns enthalten ein Turn-spezifisches Token in <Gather>-Callbacks, sodass veraltete/wiederholte Sprach-Callbacks keinen neueren ausstehenden Transkript-Turn erfüllen können.

Beispiel mit einem stabilen öffentlichen Host:

{
  plugins: {
    entries: {
      "voice-call": {
        config: {
          publicUrl: "https://voice.example.com/voice/webhook",
          webhookSecurity: {
            allowedHosts: ["voice.example.com"],
          },
        },
      },
    },
  },
}

TTS für Anrufe

Voice Call nutzt die Core-messages.tts-Konfiguration (OpenAI oder ElevenLabs) für Streaming-Sprache bei Anrufen. Du kannst sie unter der Plugin-Konfiguration mit der gleichen Struktur überschreiben — sie wird per Deep-Merge mit messages.tts zusammengeführt.

{
  tts: {
    provider: "elevenlabs",
    elevenlabs: {
      voiceId: "pMsXgVXv3BLzUgSXRplE",
      modelId: "eleven_multilingual_v2",
    },
  },
}

Hinweise:

  • Edge TTS wird für Sprachanrufe ignoriert (Telefonie-Audio braucht PCM; Edge-Ausgabe ist unzuverlässig).
  • Core-TTS wird verwendet, wenn Twilio Media Streaming aktiviert ist; sonst fallen Anrufe auf native Provider-Stimmen zurück.

Weitere Beispiele

Nur Core-TTS verwenden (kein Override):

{
  messages: {
    tts: {
      provider: "openai",
      openai: { voice: "alloy" },
    },
  },
}

Nur für Anrufe auf ElevenLabs überschreiben (Core-Standard anderswo beibehalten):

{
  plugins: {
    entries: {
      "voice-call": {
        config: {
          tts: {
            provider: "elevenlabs",
            elevenlabs: {
              apiKey: "elevenlabs_key",
              voiceId: "pMsXgVXv3BLzUgSXRplE",
              modelId: "eleven_multilingual_v2",
            },
          },
        },
      },
    },
  },
}

Nur das OpenAI-Modell für Anrufe überschreiben (Deep-Merge-Beispiel):

{
  plugins: {
    entries: {
      "voice-call": {
        config: {
          tts: {
            openai: {
              model: "gpt-4o-mini-tts",
              voice: "marin",
            },
          },
        },
      },
    },
  },
}

Eingehende Anrufe

Die Richtlinie für eingehende Anrufe ist standardmäßig disabled. Um eingehende Anrufe zu aktivieren, setze:

{
  inboundPolicy: "allowlist",
  allowFrom: ["+15550001234"],
  inboundGreeting: "Hello! How can I help?",
}

inboundPolicy: "allowlist" ist eine Anrufer-ID-Prüfung mit geringer Sicherheit. Das Plugin normalisiert den vom Provider gelieferten From-Wert und vergleicht ihn mit allowFrom. Webhook-Verifizierung authentifiziert Provider-Zustellung und Payload-Integrität, beweist aber nicht PSTN/VoIP-Rufnummern-Besitz. Behandle allowFrom als Anrufer-ID-Filterung, nicht als starke Identitätsprüfung.

Auto-Antworten nutzen das Agent-System. Feinjustierung über:

  • responseModel
  • responseSystemPrompt
  • responseTimeoutMs

CLI

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 --call-id <id>
openclaw voicecall tail
openclaw voicecall expose --mode funnel

Agent-Tool

Tool-Name: voice_call

Aktionen:

  • initiate_call (message, to?, mode?)
  • continue_call (callId, message)
  • speak_to_user (callId, message)
  • end_call (callId)
  • get_status (callId)

Dieses Repository liefert ein passendes Skill-Dokument unter skills/voice-call/SKILL.md.

Gateway-RPC

  • voicecall.initiate (to?, message, mode?)
  • voicecall.continue (callId, message)
  • voicecall.speak (callId, message)
  • voicecall.end (callId)
  • voicecall.status (callId)