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

> Place outbound voice calls from runtime code with the Voice object

The runtime `Voice` object places outbound [voice](/concepts/voice) calls from agent code: the platform allocates a room, dials the target, and starts the agent's [voice definition](/reference/sdk/voice) with the context you pass. `voice.call` (lowercase) is an alias. Available in tools, jobs, webhooks, triggers, processors, and workflow code steps.

*Verified against lua-cli 3.33.0.*

```ts theme={null}
import { Voice } from 'lua-cli';
```

## Quick example

A call resolves as soon as it is placed; whether anyone answers is a runtime outcome, not an error.

```ts theme={null}
import { Voice } from 'lua-cli';

const call = await Voice.call({
  to: '+15551234567',
  voice: 'support-line',
  context: { reason: 'recovery', orderId: 'ORD-4471' },
});
```

## Methods

### call(input)

Places an outbound call to a phone number, a meeting, or a browser.

```ts theme={null}
Voice.call(input: VoiceDispatchInput): Promise<VoiceDispatchOutput>
```

<ParamField path="to" type="string | { kind: 'phone' | 'meet' | 'web', ... }" required>
  Where to dial. An E.164 string is shorthand for `{ kind: 'phone', number }`. `{ kind: 'phone', number, callerId? }` dials a number, with an optional outbound caller id the organization owns; `{ kind: 'meet', url }` joins a meeting by URL; `{ kind: 'web', returnToken? }` allocates a browser voice session and, with `returnToken: true`, returns a `joinUrl`.
</ParamField>

<ParamField path="voice" type="string">
  The id or name of the voice to run. Defaults to the agent's first active voice. It must belong to this agent.
</ParamField>

<ParamField path="context" type="Record<string, unknown>">
  A JSON-serializable object the model sees on the call's first turn, so it can act without a lookup. The voice's hooks read it as `ctx.session.userdata.dispatchContext`.
</ParamField>

<ParamField path="threadId" type="string" default="voice:<sessionId>">
  Thread suffix that scopes `User`, `Data`, and the other runtime objects inside the call. The default gives each call isolated storage.
</ParamField>

<ParamField path="channel" type="'phone' | 'meeting' | 'whatsapp' | 'webchat' | 'unknown'">
  Overrides the channel label inferred from `to.kind`, for analytics and billing.
</ParamField>

**Returns**

<ResponseField name="result" type="VoiceDispatchOutput">
  <Expandable title="properties">
    <ResponseField name="sessionId" type="string">The voice session's id.</ResponseField>
    <ResponseField name="roomName" type="string">The room the call landed in.</ResponseField>
    <ResponseField name="callId" type="string">The provider's call id: the SIP participant id for `phone`, the meeting participant id for `meet`, absent for `web`.</ResponseField>
    <ResponseField name="joinUrl" type="string">The URL a browser opens to join, for `web` with `returnToken: true`.</ResponseField>
  </Expandable>
</ResponseField>

**Example**

```ts theme={null}
import { LuaJob, Data, Voice } from 'lua-cli';

export default new LuaJob({
  name: 'abandoned-cart-calls',
  description: 'Call customers who left a basket yesterday',
  schedule: { type: 'cron', expression: '0 10 * * *', timezone: 'America/New_York' },
  async execute() {
    const carts = await Data.get('abandoned-carts', { called: false }, 1, 20);
    for (const cart of carts.data) {
      const call = await Voice.call({
        to: cart.data.phone,
        voice: 'recovery-line',
        context: { basketId: cart.data.basketId, total: cart.data.total },
      });
      await Data.update('abandoned-carts', cart.id, { called: true, sessionId: call.sessionId });
    }
    return { placed: carts.data.length };
  },
});
```

**Errors** — the call throws when the dispatch is refused. In a deployed agent the error is a `VoiceDispatchError` with `code` and `statusCode`; in `lua test` it is a plain `Error` carrying the server's message, or `Voice dispatch failed`.

* `Voice "<name>" not active on agent <agentId> (deactivated, missing, or owned by a different agent).`
* `Agent <agentId> has no active LuaVoice configured. Push a voice before dispatching.`
* `Caller ID is not authorized for this agent.`
* `Voice dispatch rate limit exceeded for agent <agentId> (100/min)`: more than 100 accepted dispatches in a minute.
* `Unsupported dispatch target kind`.

### createSession(input?)

Creates a room and a client token for a voice session your own front end joins with a standard LiveKit client.

```ts theme={null}
Voice.createSession(input?: VoiceSessionInput): Promise<VoiceSessionOutput>
```

<ParamField path="input.channel" type="string" default="web">
  The voice channel for the voice session.
</ParamField>

<ParamField path="input.userId" type="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.
</ParamField>

<ParamField path="input.voiceId" type="string">
  A voice id that overrides the agent's channel-bound voice.
</ParamField>

<ParamField path="input.displayName" type="string">
  The participant's display name.
</ParamField>

**Returns** — `url` (the `wss://` server URL), `roomName`, `token` (the client access token), `participantIdentity`, and `agentName`. Hand `url` and `token` to the client's `room.connect`.

<Info>
  Local runs only. Neither the deployed runtime nor `lua test` injects `createSession`, so agent code fails with `Voice.createSession is not a function`; the member works only when Node runs your file directly with the `lua-cli` package and your developer credentials.
</Info>

**Errors** — `Voice session creation failed` when the platform gives no message.

## Types

`VoiceApi` is exported from `lua-cli`. The input and output shapes of both methods are not exported; they are the inline types described under each method.

## See also

* [`defineVoice`](/reference/sdk/voice) — the voice definition a call runs
* [About voice](/concepts/voice) — phone numbers, meetings, and browser voice
* [Voice calls](/channels/voice-calls) — connect a SIP trunk and a phone number
* [`lua voice`](/reference/cli/voice) — push, activate, and test voices
