> ## 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

> Voice definitions, phone numbers, the widget's voice mode, and the live sessions that connect them

Voice is how an [agent](/concepts/agents) talks: a voice definition says which speech and language models it uses and how it behaves on a call; a [channel](/concepts/channels) (a phone number, the web widget, a meeting) decides when a call reaches it; and a voice session is one live call. The pieces are separate so one agent can answer a phone line and the widget with the same persona, skills, and knowledge as its text conversations.

## How a voice answers a call

A voice is declared with `defineVoice`, registered on `LuaAgent.voices`, and pushed with `lua push voice`.

```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?',
  maxToolSteps: 3,
});
```

`llm`, `stt`, and `tts` name the models. In cascade form, speech is transcribed (`stt`), the transcript goes to the language model, and the reply is synthesised (`tts`). With a realtime `llm` from OpenAI, Google, or xAI, one speech-to-speech model does all three: `stt` must be omitted (setting it is a validation error), and `tts` is optional for a custom synthesised voice. Around that core sit the call settings: `greeting`, `vad` and `turnDetection` (how the agent knows the caller has finished), `interruption` (whether and how the caller can barge in), `maxToolSteps`, `userAwayTimeout`, and `excludeTools`.

A voice uses the agent's skills as a text conversation does, plus `tools` of its own for call-only actions. A tool made with `LuaVoiceTool` receives a voice context with `say(text)`, `transferToHuman(number)` (by SIP refer or by bridging a second leg), `endCall()`, and `handoff(otherVoiceName)`, which passes the call to another voice on the same agent; `ToolFlag` values on a tool (`IGNORE_ON_ENTER`, `DISALLOW_INTERRUPTION`) shape how it behaves mid-call. Hooks run at the edges: `onEnter` before the greeting (load the caller with `User.get({ phone })`), `onUserTurnCompleted` before each model turn (inject knowledge), and `onExit` when the call ends. The persona's `voice` variant applies on calls; see [About persona](/concepts/persona).

Phone numbers are managed in `lua channels` under **Manage phone numbers**: search by country and digits, purchase (a number that also takes SMS runs on one carrier, a voice-only number on another with lower inbound latency), bind a number to the agent and choose which voice answers it, unbind, list, and release. US SMS registration (10DLC and toll-free) and bring-your-own SIP trunks are handled through the REST API and the admin side; see [SMS and phone numbers](/channels/sms-and-phone-numbers). The web widget takes calls when its `voiceModeEnabled` option is on, which is the default; it is set in `window.LuaPop.init()` or in the admin dashboard's **Chat widget** settings, and the first voice defined on the agent answers. See [Voice chat in the widget](/channels/web-widget/voice-chat).

Every call is a LiveKit session. A platform worker joins a room for the call, whether the other party is a phone leg, a browser, or a meeting, resolves the agent's voice, and runs the models. `Voice.call({ to })` places an outbound call from a tool, webhook, or job and returns `{ sessionId, roomName, callId?, joinUrl? }`; `POST /developer/voice/<agentId>/session` returns `{ url, roomName, token, participantIdentity }` for a frontend you build with the LiveKit client SDK.

Voices are versioned and ride agent versions: an agent version records the agent's default voice, and `lua version diff` shows a voice change. A channel picks its voice by ID in its configuration; without one, the first voice on the agent answers.

Testing has three surfaces. `lua voice` opens a live voice session against the sandbox: in the browser by default, through your terminal's microphone and speaker with `--terminal` (requires `sox`), or on your phone with `--phone +15551234567`, where the agent calls you; `--voice`, `--agent`, `--context '{"orderId":"ABC"}'`, and `--thread-id` narrow the voice session. `lua voice test` runs `*.voice.test.ts` files with Jest or Vitest, and `lua voice list` prints the voices in the compiled manifest. After release, `lua logs --type calls` lists recent calls.

## Voice and text channels

The same agent, persona, skills, and features serve both. What differs is latency and form: a tool that takes seconds is felt on a call, a reply is spoken rather than rendered, and there are no cards or buttons. Keep call tools fast, put spoken-style guidance in `persona.voice`, and use `excludeTools` to hide tools that only make sense in text.

## When to use it

* Inbound support or sales on a phone number: bind a purchased number to a voice.
* Talk-to-the-agent on your site: enable the widget's voice mode.
* Outbound reminders or callbacks from code: `Voice.call` from a job or webhook.
* Don't use voice for flows that need documents, links, or structured input; keep those on text channels.

## Limits

| Item                                        | Value                                                |
| ------------------------------------------- | ---------------------------------------------------- |
| Realtime `llm` providers                    | OpenAI, Google, xAI                                  |
| Browser voice session opened by `lua voice` | The CLI ends it after 1 hour                         |
| Default voice                               | First entry in `voices` unless the channel names one |

## Next steps

<Columns cols={2}>
  <Card title="Voice calls" href="/channels/voice-calls">Bind a number, answer calls, and go live.</Card>
  <Card title="defineVoice reference" href="/reference/sdk/voice">Every model, call, and hook option.</Card>
  <Card title="Voice runtime reference" href="/reference/sdk/voice-runtime">`Voice.call` and voice sessions for your own frontend.</Card>
  <Card title="Voice chat in the widget" href="/channels/web-widget/voice-chat">Enable and style voice mode.</Card>
</Columns>
