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

# WaveML reference

> The verbs a Wave call flow speaks — Wave's answer to TwiML.

**WaveML** is the small XML language that describes what happens on a call: play a
prompt, collect a digit, bridge to an agent, record, hand off to an AI voice
agent. If you know [TwiML](https://www.twilio.com/docs/voice/twiml), WaveML will
feel familiar.

<Note>
  **How call flows are authored today.** WaveML is what Wave's voice engine
  executes. Right now, call flows are provisioned for your account (dashboard +
  your Wave contact) rather than authored through a public API — a call-flow
  authoring API is on the roadmap. This page is the **reference** for the language
  itself, so you know exactly what a flow can do and can spec yours precisely.
</Note>

## The response envelope

When a call needs instructions, the engine sends your flow a request and expects a
WaveML document in reply. Verbs run top to bottom; the call ends on `<Hangup>` or
an empty `<Response>`.

```xml theme={null}
<?xml version="1.0" encoding="UTF-8"?>
<Response wave_tenant="acme" wave_tenant_cc="20">
  <Say node_id="n1">Welcome to Acme.</Say>
  <Hangup node_id="n2"/>
</Response>
```

On the **first** reply of a call, set `wave_tenant` and its concurrency limit
`wave_tenant_cc` on the root `<Response>`. Every verb carries a `node_id` — a
cursor the engine echoes back on its next request so your flow knows where the
call is. See [How voice works](/voice/overview) for the full request/response loop.

***

## Play

Play an audio file (WAV/MP3 over HTTPS).

```xml theme={null}
<Play node_id="n1">https://cdn.example.com/welcome.wav</Play>
```

| Attribute | Description                            | Default |
| --------- | -------------------------------------- | ------- |
| `node_id` | Unique id for this step.               | —       |
| `loop`    | Times to repeat.                       | `1`     |
| `digit`   | A DTMF digit that interrupts playback. | —       |

## Say

Speak text with the engine's text-to-speech. Arabic and English are supported.

```xml theme={null}
<Say node_id="n1" language="ar">أهلاً بك في أكمي</Say>
```

| Attribute  | Description                           | Default |
| ---------- | ------------------------------------- | ------- |
| `node_id`  | Unique id for this step.              | —       |
| `voice`    | Voice hint for the TTS.               | —       |
| `language` | BCP-47 language tag, e.g. `ar`, `en`. | —       |

<Tip>
  Pre-recorded `<Play>` audio starts instantly; `<Say>` goes through
  text-to-speech. For fixed prompts, prefer `<Play>`.
</Tip>

## Gather

Collect DTMF (keypad) input. Nest a `<Say>` or `<Play>` inside as the prompt; the
digits the caller presses arrive on the next request as `dtmf`.

```xml theme={null}
<Gather node_id="menu" numDigits="1" timeout="8">
  <Say>For sales press 1, for support press 2.</Say>
</Gather>
```

| Attribute     | Description                                | Default |
| ------------- | ------------------------------------------ | ------- |
| `node_id`     | Unique id for this step.                   | —       |
| `numDigits`   | How many digits to collect.                | —       |
| `timeout`     | Seconds to wait for input.                 | —       |
| `finishOnKey` | Key that ends collection early (e.g. `#`). | —       |
| `tries`       | Re-prompt attempts.                        | —       |

## Dial

Bridge the call to another number or SIP user. The dialed party sees `callerId`.

```xml theme={null}
<Dial node_id="n1" callerId="0115209300" record="true">920012345</Dial>
```

| Attribute   | Description                           | Default |
| ----------- | ------------------------------------- | ------- |
| `node_id`   | Unique id for this step.              | —       |
| `callerId`  | Number presented to the dialed party. | —       |
| `timeout`   | Seconds to wait for an answer.        | —       |
| `timeLimit` | Max call duration, seconds.           | —       |
| `dialMusic` | Ringback / music URL.                 | —       |
| `record`    | `true` records the bridged leg.       | `false` |

## Enqueue

Place the caller in a call-center queue. Agents ring when they're logged in —
manage them with the [Queues API](/voice/queues). The queue name is the element's
text; queues are created on demand.

```xml theme={null}
<Enqueue node_id="n1" strategy="ring-all" moh="https://cdn.example.com/hold.wav">support</Enqueue>
```

| Attribute  | Description                  | Default    |
| ---------- | ---------------------------- | ---------- |
| `node_id`  | Unique id for this step.     | —          |
| `strategy` | `ring-all` or `round-robin`. | `ring-all` |
| `moh`      | Music-on-hold URL.           | —          |

## Record

Record the call. Because `<Enqueue>`/`<Say>` have no inline record attribute, a
standalone `<Record background="true"/>` arms recording for a call that then flows
into other verbs.

```xml theme={null}
<Response wave_tenant="acme">
  <Record node_id="rec" background="true"/>
  <Enqueue node_id="n1">support</Enqueue>
</Response>
```

| Attribute    | Description                                                          | Default |
| ------------ | -------------------------------------------------------------------- | ------- |
| `node_id`    | Unique id for this step.                                             | —       |
| `background` | `true` records in the background and continues the flow immediately. | `false` |

## AIAgent

Hand the call to a real-time AI voice agent. Speech-to-text, a language model, and
text-to-speech run live, with barge-in. The element's text is the agent's persona
prompt.

```xml theme={null}
<AIAgent node_id="n2">
  You are Noura, a friendly outreach voice for Alfa Electric.
  If the customer asks for a human, end with the outcome word "transfer".
  If they ask never to be contacted, end with the outcome word "dnc".
</AIAgent>
```

`<AIAgent>` is **not terminal**. When the conversation ends, the engine comes back
to your flow with this verb's `node_id` and an `aiagent_result` outcome word —
`completed`, `transfer`, `dnc`, or any word your prompt defines — and your flow
decides what happens next:

```xml theme={null}
<!-- aiagent_result=transfer -> connect a human -->
<Response>
  <Dial node_id="n3" callerId="0115209300">920012345</Dial>
  <Hangup node_id="n4"/>
</Response>
```

| Attribute | Description                                | Default |
| --------- | ------------------------------------------ | ------- |
| `node_id` | Unique id for this step.                   | —       |
| `voice`   | Voice hint for the agent's text-to-speech. | —       |

<Warning>
  Always handle an unrecognized `aiagent_result` with a safe fallback — treating
  it like `completed` and hanging up is a good default.
</Warning>

## Pause

Wait silently.

```xml theme={null}
<Pause node_id="n1" length="2"/>
```

| Attribute | Description              | Default |
| --------- | ------------------------ | ------- |
| `node_id` | Unique id for this step. | —       |
| `length`  | Seconds to wait.         | `1`     |

## Hangup

End the call.

```xml theme={null}
<Hangup node_id="n1"/>
```

***

## Next steps

<CardGroup cols={2}>
  <Card title="How voice works" icon="phone-volume" href="/voice/overview">
    The request/response loop the engine drives.
  </Card>

  <Card title="Queues" icon="users-line" href="/voice/queues">
    Sign agents in and out of `<Enqueue>` queues.
  </Card>
</CardGroup>
