coderClaw

Voice Call (plugin)

Voice calls for CoderClaw via a plugin. Supports outbound notifications and multi-turn conversations with inbound policies.

Current providers:

Quick mental model:

Where it runs (local vs remote)

The Voice Call plugin runs inside the Gateway process.

If you use a remote Gateway, install/configure the plugin on the machine running the Gateway, then restart the Gateway to load it.

Install

coderclaw plugins install @coderclaw/voice-call

Restart the Gateway afterwards.

Option B: install from a local folder (dev, no copying)

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

Restart the Gateway afterwards.

Config

Set config under plugins.entries.voice-call.config:

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

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

          telnyx: {
            apiKey: "...",
            connectionId: "...",
            // Telnyx webhook public key from the Telnyx Mission Control Portal
            // (Base64 string; 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,
            streamPath: "/voice/stream",
          },
        },
      },
    },
  },
}

Notes:

Stale call reaper

Use staleCallReaperSeconds to end calls that never receive a terminal webhook (for example, notify-mode calls that never complete). The default is 0 (disabled).

Recommended ranges:

Example:

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

Webhook Security

When a proxy or tunnel sits in front of the Gateway, the plugin reconstructs the public URL for signature verification. These options control which forwarded headers are trusted.

webhookSecurity.allowedHosts allowlists hosts from forwarding headers.

webhookSecurity.trustForwardingHeaders trusts forwarded headers without an allowlist.

webhookSecurity.trustedProxyIPs only trusts forwarded headers when the request remote IP matches the list.

Example with a stable public host:

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

TTS for calls

Voice Call uses the core messages.tts configuration (OpenAI or ElevenLabs) for streaming speech on calls. You can override it under the plugin config with the same shape — it deep‑merges with messages.tts.

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

Notes:

More examples

Use core TTS only (no override):

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

Override to ElevenLabs just for calls (keep core default elsewhere):

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

Override only the OpenAI model for calls (deep‑merge example):

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

Inbound calls

Inbound policy defaults to disabled. To enable inbound calls, set:

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

Auto-responses use the agent system. Tune with:

CLI

coderclaw voicecall call --to "+15555550123" --message "Hello from CoderClaw"
coderclaw voicecall continue --call-id <id> --message "Any questions?"
coderclaw voicecall speak --call-id <id> --message "One moment"
coderclaw voicecall end --call-id <id>
coderclaw voicecall status --call-id <id>
coderclaw voicecall tail
coderclaw voicecall expose --mode funnel

Agent tool

Tool name: voice_call

Actions:

This repo ships a matching skill doc at skills/voice-call/SKILL.md.

Gateway RPC