Skip to main content
Voice is how an agent talks: a voice definition says which speech and language models it uses and how it behaves on a call; a channel (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.
src/voices/support-line.voice.ts
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. 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. 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. 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

Next steps

Voice calls

Bind a number, answer calls, and go live.

defineVoice reference

Every model, call, and hook option.

Voice runtime reference

Voice.call and voice sessions for your own frontend.

Voice chat in the widget

Enable and style voice mode.