defineVoice definition on an agent; it carries versions, and the published version is the one calls run. lua push voice calls create and version, then publishes with --auto-deploy or when you answer yes to its deploy prompt; --force alone never publishes. lua voice calls dispatch with a phone or web target, and lua voice list reads the compiled project, not this API; the voice-session route is not used by the CLI. The model and voice catalog, text-to-speech, and transcription are on Speech.
Verified against lua-cli 3.33.0.
Base URL and authentication
Every route sits underhttps://api.heylua.ai/developer, takes Authorization: Bearer <<YOUR_API_KEY>>, and checks a telephony scope on the agent in the path: telephony:read lists, telephony:write creates, updates, publishes, and opens voice sessions, telephony:manage deactivates, and telephony:provision places outbound calls. A POST that creates something answers 201. Keys, scopes, and the error envelope are on the REST API overview.
Voice object
string
Voice ID (
voiceId in the other routes).string
Voice name from the definition.
string
Optional description.
boolean
Whether the voice is enabled.
string
cli for every voice created through this API, provision-from-config included; manual is reserved.string
The published version string, when one exists.
string
User ID of the creator.
string
Timestamps.
object[]
Every pushed version:
id, version, description, active, createdAt, createdBy, and every configuration field of the version body, bundle included.Endpoints
Voice-agent routes sit under/developer/voice-agents/:agentId, where :agentId is the Lua agent; the voice-session route is under /developer/voice/:agentId.
GET /developer/voice-agents/:agentId
Lists the agent’s voices as{ voices: Voice[] }, each with all of its versions and their bundles. Scope telephony:read. 404 Agent not found.
POST /developer/voice-agents/:agentId
Creates a voice. Scopetelephony:write. Equivalent: lua push voice creates the voice on first push.
string
required
Letters, digits,
_, and - only; at most 64 characters. Unique among the agent’s active voices.string
At most 500 characters.
201 with the stored record: id, name, description, agentId, active, source, createdBy, createdAt, updatedAt. 400 A voice agent named "<name>" already exists for this agent.
POST /developer/voice-agents/:agentId/:voiceId/version
Pushes a new version of a voice. Scopetelephony:write. The body is the compiled form of a defineVoice configuration; this route validates each field on its own, while the pairing rules between llm, stt, and tts are enforced by lua push and by provision-from-config. Equivalent: lua push voice.
string
required
MAJOR.MINOR.PATCH with an optional -suffix, at most 100 characters; otherwise 400 Invalid semantic version: <version>. A version that exists answers 400 Version <version> already exists for voice agent <voiceId>.string
At most 500 characters.
object
required
Model spec:
{ kind: "inference", model, options? }, { kind: "plugin", provider: "deepgram" | "elevenlabs", class: "LLM" | "STT" | "STTv2" | "TTS", options }, or a realtime speech-to-speech model { kind: "realtime", provider: "openai" | "google" | "xai", options }.object
Speech-to-text spec, same forms. Omit when
llm is a realtime model.object
Text-to-speech spec, same forms, with an optional
voice. Omit when llm is a realtime model in full mode.string
Voice-activity detector, for example
silero; at most 100 characters.object
{ minSpeechDuration?, minSilenceDuration?, prefixPaddingDuration?, activationThreshold? }; stored and served, not validated here.string
Turn-detection mode; at most 100 characters.
string
Spoken when the call connects; at most 1,000 characters.
number
Tool calls allowed per turn.
number
Seconds of silence before the end user counts as away.
boolean
Start generating before the end user’s turn ends.
object
{ enabled?, mode?: "adaptive" | "vad", falseInterruptionTimeout?, resumeFalseInterruption?, minDelay?, maxDelay? }; 400 interruption.minDelay must be ≤ maxDelay otherwise.string
Language code for transcription; at most 100 characters.
boolean
Presence flags the compiler sets for hooks and voice tools in
bundle.boolean
Opt in to Krisp noise cancellation.
boolean
Store the call transcript in
Data under call:<sessionId> when the call ends.object
{ ambient?, thinking? }: a built-in clip name, an audio config object, or an array for a probabilistic mix; not validated here.number
Output volume, 0 to 100.
object
Word replacements applied before speech, as a string-to-string map; keys at most 256 characters, values at most 1,024, 32 KB in total.
string[]
Platform base tools to leave out of this voice.
string
Compiled hooks and voice tools, gzipped and base64-encoded; at most 10 MB.
201 with the stored version record: id, version, description, createdBy, createdAt, updatedAt, and every configuration field sent. 404 Voice agent not found.
GET /developer/voice-agents/:agentId/:voiceId/versions
Lists a voice’s versions, newest first. Scopetelephony:read.
Response
200 with { versions: [{ version, createdDate, createdBy, isCurrent, createdByEmail, createdByFullName }], activeVersionId }; createdByEmail and createdByFullName are null.
PUT /developer/voice-agents/:agentId/:voiceId/:version/publish
Makes a pushed version the one calls run, and re-enables a deactivated voice. Scopetelephony:write. Equivalent: lua push voice --auto-deploy.
Response
200 with { message: "Version <version> has been published successfully", voiceId, activeVersionId, publishedAt }. 404 Version <version> not found for voice agent <voiceId>.
PUT /developer/voice-agents/:agentId/:voiceId
Renames, describes, or enables and disables a voice. Scopetelephony:write.
string
Same rule as on create.
string
At most 500 characters.
boolean
Enable or disable the voice.
200 with the updated voice record, without versions.
DELETE /developer/voice-agents/:agentId/:voiceId
Deactivates a voice; nothing is removed, and its name becomes available to a new voice. Scopetelephony:manage.
Response
200 with { message: "Voice agent <name> deactivated", deleted: false, deactivated: true }.
POST /developer/voice-agents/:agentId/provision-from-config
Creates or reuses a voice by name, pushes the next patch version from a flat configuration, publishes it, and binds it to the agent, in one call. Scopetelephony:write. The admin dashboard’s phone-number connection uses this route; from code, prefer lua push voice. The body is the version body of POST .../:voiceId/version without version, plus:
string
Voice name, validated as on create. Defaults to
phone-<msisdn> with disallowed characters removed, or phone-voice.string
Number used to derive the default name.
201 with { voiceId, version }; store voiceId as the phone channel’s voiceId. When a later step fails, a freshly created voice is deactivated again and a reused voice gets its previous published version back. 400 Invalid voice config: … when the configuration fails validation.
POST /developer/voice-agents/:agentId/dispatch
Places an outbound call, or opens a room a browser can join, from a voice on the agent. Scopetelephony:provision on the agent; at most 100 dispatches per agent per minute. Equivalent: lua voice --phone <number> for phone, lua voice for browser; from agent code, Voice.call in the voice runtime.
string | object
required
Where to dial. An E.164 string is shorthand for
{ kind: "phone", number }. Objects: { kind: "phone", number, callerId? } (both E.164; callerId must be a number the agent’s organization owns), { kind: "meet", url } (a meeting link, at most 2,048 characters), or { kind: "web", returnToken?: true } (false is rejected: it would open a room nobody can join).string
Voice ID or name, at most 64 characters. Defaults to the agent’s first active voice in creation order.
object
JSON object seeded into the call’s initial context; at most 16 KB serialized.
string
Thread suffix for the call’s data scope, at most 100 characters. Defaults to
voice:<sessionId>.string
Overrides the channel kind:
phone, meeting, whatsapp, webchat, or unknown. Inferred from to.kind by default.201 with { sessionId, roomName, callId? } for a phone or meeting target, where roomName is lua-voice-<sessionId> and callId is the provider’s call ID, or { sessionId, roomName, joinUrl } for a web target.
POST /developer/voice/:agentId/session
Creates a media room and a client token for your own front end to talk to the agent with a standard LiveKit client; the hosted front end is voice chat in the web widget. Scopetelephony:write. The upstream call is bounded to 15 seconds; a timeout or transport failure answers 503 UPSTREAM_UNAVAILABLE with requestId and retryAfterSeconds, and any 4xx from the voice service passes through. The SDK’s Voice.createSession wraps this route for local runs only; see the voice runtime for the caveat.
string
default:"web"
Only
web is accepted; anything else is 400 channel must be 'web' for developer voice sessions.string
Your end user’s ID, so conversation memory and the transcript are scoped to them. Without it the voice session runs under a synthetic identity.
string
Voice to run, overriding the agent’s channel-bound voice.
string
Participant display name.
201 with { url, roomName, token, participantIdentity, agentName }: the media server URL (wss://), the room to join, the client access token (JWT), the identity the token grants, and the agent participant’s name in the room.
Errors
Example
Call a customer back from the agent’s first active voice with an order ID in context.See also
- Speech — the model and voice catalog, previews, text-to-speech, and transcription
- Voice calls — answer and place phone calls with a voice definition
- About voice — voice definitions, phone numbers, and live voice sessions
lua voice— try a voice from the terminal, browser, or phonelua channels— bind a number to the voice that answers

