↑ ↓ to navigate · ↵ to open
Documentation menu
Getting started
Building calls
Operate
Building calls
Call control (verbs)
Verbs are the instructions that tell SautiPBX what to do on a live call — speak, play audio, dial someone, record. You send them as XML in response to a voice webhook, and we run them in order.
What is a verb?
A verb is a single XML element like <Say> or <Dial>. Each one performs one action on the call. You never call verbs over the API directly — instead:
- A call reaches the platform (someone dials your number, or you place an outbound call).
- We POST the call's details to your webhook URL.
- Your server replies with a call-control document: a
<Response>element wrapping the verbs you want run.
That is the whole model. No SDK, no call state to track — you return XML, we execute it.
The response document
Reply to the webhook with Content-Type: application/xml. The root element is always <Response>, and the verbs inside run top to bottom.
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Say>Karibu. Please hold while we connect you.</Say>
<Dial><Number>+254700000000</Number></Dial>
</Response>
Two things to know about how verbs run:
- Most verbs block. The next verb waits until the current one finishes —
<Say>waits for the speech to end,<Dial>waits for the other party to hang up. Each verb below says how it behaves. - Some verbs end the document.
<Hangup>ends the call and<Redirect>hands off to another document — anything after them is ignored. A chain of redirects is capped at 10 hops.
Supported verbs
Eight verbs make up the V1 vocabulary, plus one child element. Click any verb for its full reference.
| Verb | What it does | Behaviour |
|---|---|---|
| <Say> | Speak text to the caller using text-to-speech. | Blocking — the next verb waits until this one finishes. |
| <Play> | Play an audio file to the caller from a URL. | Blocking — the next verb waits until this one finishes. |
| <Dial> | Bridge the caller to a phone number. The destination goes in a child <Number>. | Blocking — the next verb waits until the far end hangs up. |
| <Record> | Record the caller and post the audio to your webhook. | Blocking — stops at maxLength, the finish key, or hangup. Put prompts BEFORE it. |
| <RecordSession> | Record the whole conversation — both directions — while the verbs inside it run. | Non-blocking — the enclosed verbs run normally; recording stops at the closing tag. Put the verbs you want captured INSIDE it. |
| <Redirect> | Hand control to another verb document at the given URL. | Ends this document — any verbs after it are ignored. |
| <Pause> | Wait silently before the next verb. | Blocking — waits for the given time, then continues. |
| <Hangup> | End the call immediately. | Ends the call — any verbs after it never run. |
Child elements carry data for a verb and are never used on their own:
| Element | What it does | Used in |
|---|---|---|
| <Number> | A destination phone number, used inside <Dial>. | <Dial> |
Verb reference
<Say>
Speak text to the caller using text-to-speech.
Attributes
| Name | Type | Required | Default | Values | Description |
|---|---|---|---|---|---|
| voice | enum | Optional | woman |
woman man |
The text-to-speech voice. |
| language | string | Optional | en-US |
any | BCP-47 language tag for the voice, e.g. en-US. |
| loop | integer | Optional | 1 |
any | How many times to repeat the text. 0 repeats until the caller hangs up. |
Text content: The words to speak, as plain text.
<Say voice="woman" language="en-US">Karibu SautiPBX. Please hold.</Say>
<Play>
Play an audio file to the caller from a URL.
Attributes
| Name | Type | Required | Default | Values | Description |
|---|---|---|---|---|---|
| loop | integer | Optional | 1 |
any | How many times to repeat the file. 0 repeats until the caller hangs up. |
Text content: A publicly reachable URL to an audio file (e.g. a WAV) to play.
<Play loop="2">https://example.com/hold-music.wav</Play>
<Dial>
Bridge the caller to a phone number. The destination goes in a child <Number>.
Attributes
| Name | Type | Required | Default | Values | Description |
|---|---|---|---|---|---|
| timeout | integer | Optional | 30 |
any | Seconds to wait for the destination to answer. |
| callerId | string | Optional | the call’s own number |
any | Caller ID to present to the callee. Must be a number you own and that is active — otherwise it falls back to the call’s own number. |
| record | enum | Optional | false |
true false |
Reserved. Whole-bridge recording is not active yet; to record a dialled conversation, wrap the <Dial> in <RecordSession>. |
| action | URL | Optional | — | any | When set, after the dial ends we POST the result (DialCallStatus, DialCallDuration) to this URL and continue with the verbs it returns. |
Child elements: <Number>
<Dial timeout="30" callerId="+254709080010">
<Number>+254700000000</Number>
</Dial>
<Record>
Record the caller and post the audio to your webhook.
Attributes
| Name | Type | Required | Default | Values | Description |
|---|---|---|---|---|---|
| maxLength | integer | Optional | 3600 |
any | Maximum recording length in seconds. This is also the hard ceiling. |
| playBeep | enum | Optional | true |
true false |
Play a beep before recording starts. |
| finishOnKey | string | Optional | # |
any | DTMF key the caller presses to stop early. An empty string disables the key-stop. |
| action | URL | Optional | — | any | When set, after recording we POST the result (RecordingDuration, RecordingPath) to this URL and continue with the verbs it returns. |
<Record maxLength="60" playBeep="true" finishOnKey="#"/>
<RecordSession>
Record the whole conversation — both directions — while the verbs inside it run.
Child elements: <Say>, <Play>, <Dial>, <Record>, <Pause>, <Redirect>, <Hangup>
<RecordSession>
<Say>This call is recorded for quality.</Say>
<Dial><Number>+254700000000</Number></Dial>
</RecordSession>
<Redirect>
Hand control to another verb document at the given URL.
Text content: A URL. We fetch the next verb document from it and continue there.
<Redirect>https://example.com/next.xml</Redirect>
<Pause>
Wait silently before the next verb.
Attributes
| Name | Type | Required | Default | Values | Description |
|---|---|---|---|---|---|
| length | integer | Optional | 1 |
any | Seconds to pause. |
<Pause length="2"/>
<Hangup>
End the call immediately.
<Hangup/>
<Number>
A destination phone number, used inside <Dial>.
Text content: One phone number in E.164 format, e.g. +254700000000.
<Number>+254700000000</Number>
Grouping & nesting
Two verbs contain other elements:
<Dial>wraps one or more<Number>children — the destinations to bridge.<RecordSession>wraps the verbs whose conversation you want recorded. Unlike<Record>, it does not block: the verbs inside run normally and recording stops at the closing tag.
<Response>
<RecordSession>
<Say>This call is recorded for quality.</Say>
<Dial timeout="30">
<Number>+254700000000</Number>
</Dial>
</RecordSession>
</Response>
Copy-paste recipes
Greet the caller, then connect them
<Response>
<Say voice="woman">Welcome to Acme. Connecting you to support.</Say>
<Dial timeout="25" callerId="+254709080010">
<Number>+254700000000</Number>
</Dial>
</Response>
Take a voicemail, then process it on your server
When the recording finishes, we POST RecordingDuration and RecordingPath to the action URL alongside the usual call fields, and continue with whatever verbs it returns.
<Response>
<Say>Please leave a message after the beep.</Say>
<Record maxLength="120" finishOnKey="#" action="https://yourapp.com/voicemail"/>
</Response>
Play hold music, then hand off to another document
<Response>
<Play>https://example.com/hold-music.wav</Play>
<Redirect>https://yourapp.com/queue/next</Redirect>
</Response>
Editing verbs visually? The portal's Verb Bin editor has autocomplete and inline hints for every element and attribute on this page — driven by the same schema, so the editor and these docs never disagree.