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

# Voice calls

> Answer and place phone calls with a voice definition, a bound number or your own SIP trunk, and test the line before you release

After this guide, a phone number rings your [agent](/concepts/agents), the [voice](/concepts/voice) you chose answers with its greeting, and your code can call end users back. Calls need a voice definition in your project, a number bound to the agent (bought through Lua or delivered over your own SIP trunk), and a release that includes the voice. Browser calls are a separate setup; see [Voice chat in the widget](/channels/web-widget/voice-chat).

*Verified against lua-cli 3.33.0.*

**Before you begin**

* A project created with `lua init` and signed in with `lua auth configure`, with `orgId` in its project config.
* For your own trunk: a PBX or carrier that can send SIP to a domain you are given, from fixed IP ranges or with digest credentials.

<Steps>
  <Step title="Code: define and push a voice">
    Declare the voice with [`defineVoice`](/reference/sdk/voice), register it on `LuaAgent.voices`, and push it. The `greeting` is what callers hear first.

    ```ts src/voices/support-line.voice.ts theme={null}
    import { defineVoice } from 'lua-cli';

    export default defineVoice({
      name: 'support-line',
      llm: 'openai/gpt-5.2-chat-latest',
      stt: 'deepgram/nova-3',
      tts: { model: 'cartesia/sonic-3', voice: '9626c31c-bec5-4cca-baa8-f8ba9e84c8bc' },
      greeting: 'Hi, this is Acme support. How can I help?',
    });
    ```

    ```bash theme={null}
    lua push voice
    ```

    Tools made with `LuaVoiceTool` can `say()`, `transferToHuman()`, `endCall()`, or `handoff()` to another voice; `excludeTools` hides on-screen tools, such as list and card senders, from calls.
  </Step>

  <Step title="Terminal: buy and bind a number">
    Run `lua channels`, choose **Manage phone numbers**, then **Purchase a number**. Answer `N` to `Allow customers to text this number too? (otherwise voice-only)` for a voice-only number with lower inbound latency, or `Y` for a number that also takes SMS (see [SMS and phone numbers](/channels/sms-and-phone-numbers)). Accept the bind prompt and pick the voice that answers.

    ```bash theme={null}
    lua channels
    ```

    The menu ends with `📞 Your agent (<agentId>) is now reachable at +<number> via voice "<name>"`. A number is bound to one agent; rebinding it elsewhere needs an unbind first. `lua channels` always asks which voice answers; a phone channel created over the REST API without `voiceId` uses the agent's first voice.
  </Step>

  <Step title="Optional: Bring your own SIP trunk">
    Create a trunk with `POST /developer/orgs/<orgId>/sip-trunks`: `region` (`us`, `eu`, or `ap`; default `us`), an `acl` of CIDR ranges, `enableDigestAuth`, and `tls` and `srtp` modes (`optional`, `always`, or `never`; default `optional`). The response gives you the domain, the endpoint `sip:*@<domain>.sip-<region>.vonage.com`, the transports (`UDP`, `TCP`, and `TLS`, or `TLS` only when `tls` is `always`), and the port (5060, or 5061 with TLS only). The trunk is `provisioning` until it becomes `active`.

    Secure it with up to 16 ACL entries (`POST …/acl` with `ip` in CIDR notation; the last entry can't be removed) or up to 200 digest users (`POST …/credentials` with a `username` of 5 to 32 alphanumeric characters and dashes; the password is returned once, and `POST …/credentials/<username>/rotate` issues another). Then route calls with `POST …/sip-trunks/<trunkId>/agents/<agentId>`, which makes that agent the trunk's catch-all; a trunk routes to one agent. Bind a number on your trunk outside the CLI menu with `POST /admin/agents/<agentId>/channels` and `{ "type": "phone", "phone": { "msisdn": "<number>", "voiceId": "<voiceId>", "transport": "byo-sip" } }` (scope `channels:manage`).
  </Step>

  <Step title="Terminal: call the line">
    Have the agent call you on the sandbox; the command exits once the call is dispatched. `--caller-id` presents a number your organization owns and is refused otherwise.

    ```bash theme={null}
    lua voice --voice support-line --phone +15551234567 --caller-id +15557654321
    ```

    Then dial the bound number from your phone to hear the inbound path: the greeting, then the agent. `lua voice --terminal` talks through your microphone (requires `sox`), and `lua voice test` runs `*.voice.test.ts` files; see [`lua voice`](/reference/cli/voice).
  </Step>

  <Step title="Verify">
    List recent calls. Each row is one voice session with its channel and duration.

    ```bash theme={null}
    lua logs --type calls
    ```
  </Step>
</Steps>

## Channel behavior

| Capability                 | Supported                                                                                                                                          |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| Inbound calls              | Yes, on a bound number or a linked SIP trunk; `Lua.request.channel` is `phone`, a value the `Channel` type admits only through its string fallback |
| Outbound calls from code   | Yes; `Voice.call({ to: '+15551234567', voice, context })`, or `{ kind: 'phone', number, callerId }`                                                |
| Outbound calls over REST   | `POST /developer/voice-agents/<agentId>/dispatch`, scope `telephony:provision`                                                                     |
| Caller id                  | An active number owned by the organization; anything else fails with `Caller ID is not authorized for this agent.`                                 |
| Per-call threads           | Off by default: a caller shares one conversation across calls; `threadScoping: true` on the phone channel gives each call its own thread           |
| Skills, knowledge, persona | Same as text; `persona.voice` applies on calls                                                                                                     |

Outbound calls are rate limited per agent per minute; the API answers `429` when the quota is exceeded. Bound numbers and trunks are channel configuration and take effect immediately; the voice itself is code and goes live with your release.

## Limits

| Limit                                        | Value                            |
| -------------------------------------------- | -------------------------------- |
| Browser voice session opened by `lua voice`  | Ended by the CLI after 1 hour    |
| Phone numbers in `--phone` and `--caller-id` | E.164, `+` and 6 to 15 digits    |
| SIP trunk ACL entries                        | 16                               |
| SIP trunk digest users                       | 200; username 5 to 32 characters |
| Voices answering one number                  | 1                                |

## Troubleshooting

<AccordionGroup>
  <Accordion title="This agent has no LuaVoice.">
    The bind found no active voice on the agent. Push a voice with `lua push voice` and rerun `lua channels`; a number can't be bound to an agent without one.
  </Accordion>

  <Accordion title="Caller ID is not authorized for this agent.">
    `--caller-id` (or `callerId` in `Voice.call`) names a number the agent's organization doesn't own, or one that is suspended or released. Use a number from **List my numbers**, or omit the flag.
  </Accordion>

  <Accordion title="The number answers with a different voice or no greeting">
    The channel names a voice by id. After you delete or rename a voice, rebind the number and pick the current one. Voice code changes reach callers only after `lua version promote`.
  </Accordion>
</AccordionGroup>

Release the voice like any other primitive: `lua push voice`, then `lua version create` and `lua version promote <n>`; the agent version snapshot records the default voice, and `lua version diff` shows a voice change. See [Release an agent to production](/ship/releasing).

## Next steps

<Columns cols={2}>
  <Card title="About voice" href="/concepts/voice">Voice definitions, voice sessions, and how a channel picks its voice.</Card>
  <Card title="defineVoice reference" href="/reference/sdk/voice">Models, turn detection, hooks, and call-only tools.</Card>
  <Card title="Voice runtime reference" href="/reference/sdk/voice-runtime">`Voice.call` for outbound calls from code.</Card>
  <Card title="Meetings" href="/channels/meetings">Send the same voice into a video meeting.</Card>
</Columns>
