Documentation menu

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:

  1. A call reaches the platform (someone dials your number, or you place an outbound call).
  2. We POST the call's details to your webhook URL.
  3. 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.

VerbWhat it doesBehaviour
<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:

ElementWhat it doesUsed in
<Number> A destination phone number, used inside <Dial>. <Dial>

Verb reference

<Say>

Speak text to the caller using text-to-speech.

Blocking — the next verb waits until this one finishes.

Attributes

NameTypeRequiredDefaultValuesDescription
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.

Blocking — the next verb waits until this one finishes.

Attributes

NameTypeRequiredDefaultValuesDescription
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>.

Blocking — the next verb waits until the far end hangs up.

Attributes

NameTypeRequiredDefaultValuesDescription
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.

Blocking — stops at maxLength, the finish key, or hangup. Put prompts BEFORE it.

Attributes

NameTypeRequiredDefaultValuesDescription
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.

Non-blocking — the enclosed verbs run normally; recording stops at the closing tag. Put the verbs you want captured INSIDE it.

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.

Ends this document — any verbs after it are ignored.

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.

Blocking — waits for the given time, then continues.

Attributes

NameTypeRequiredDefaultValuesDescription
length integer Optional 1 any Seconds to pause.
<Pause length="2"/>

<Hangup>

End the call immediately.

Ends the call — any verbs after it never run.
<Hangup/>

<Number>

A destination phone number, used inside <Dial>.

Only valid as a child of <Dial>. You may list more than one.

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.