Wirexa API — Voice Call API Reference

Wirexa API is a CPaaS (Communications Platform as a Service) that lets you make and receive voice calls programmatically. It is compatible with Twilio and SignalWire SDKs — you can migrate existing code by changing the base URL.

Base URL for all API requests:

https://api.wirexaapi.com
Twilio / SignalWire compatible: If you already use the SignalWire Python SDK, just replace signalwire_space_url with wirexaapi.com and the rest of your code stays the same.

Quick Start

Make your first call in under 2 minutes.

1. Install the SDK

bash
pip install wirexaapi

2. Make a call

python
from wirexaapi import Client

client = Client(
    "your_project_id",
    "your_api_token",
    signalwire_space_url="wirexaapi.com"
)

call = client.calls.create(
    to="+15551234567",
    from_="+15559876543",
    url="https://your-backend.com/voice",
    status_callback="https://your-backend.com/status",
    record=True,
)

print(call.sid, call.status)
# CAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx  queued

3. Return LAML from your backend

When the call connects, Wirexa API sends a POST request to your url. Your backend must respond with LAML/XML:

python (Flask)
from flask import Response

def voice():
    xml = """<Response>
  <Say language="en-US">Hello! Thanks for calling.</Say>
  <Gather numDigits="1" timeout="7"
          action="https://your-backend.com/gather">
    <Say>Press 1 for sales, 2 for support.</Say>
  </Gather>
  <Hangup/>
</Response>"""
    return Response(xml, mimetype="text/xml")

Authentication

All REST API requests use HTTP Basic Authentication. Use your Project ID as the username and your API Token as the password.

curl
curl -X POST https://api.wirexaapi.com/api/laml/2010-04-01/Accounts/{ProjectID}/Calls.json \
  -u "{ProjectID}:{ApiToken}" \
  -d "To=+15551234567" \
  -d "From=+15559876543" \
  -d "Url=https://your-backend.com/voice"

You can find your credentials in the dashboard under Project Settings.

Python SDK

The wirexaapi package is a drop-in replacement for the SignalWire Python SDK.

bash
pip install wirexaapi
Migrating from SignalWire? Replace from signalwire.rest import Client with from wirexaapi import Client and set signalwire_space_url="wirexaapi.com". No other changes needed.

Create Call

Initiates an outbound call to the specified number.

POST /api/laml/2010-04-01/Accounts/{AccountSid}/Calls.json

Request Parameters

ParameterRequiredDescription
ToRequiredDestination phone number in E.164 format (e.g. +15551234567)
FromRequiredCaller ID number assigned to your account
UrlRequiredURL that returns LAML/XML to control the call flow
MethodOptionalHTTP method to request Url. Default: POST
TimeoutOptionalSeconds to wait for answer before giving up. Default: 28
RecordOptionaltrue to record the entire call. Default: false
StatusCallbackOptionalURL to receive call status updates (answered, completed, etc.)
StatusCallbackEventOptionalSpace-separated events: initiated ringing answered completed
RecordingStatusCallbackOptionalURL to receive recording completion notification
MachineDetectionOptionalEnable or DetectMessageEnd — enables answering machine detection
AsyncAmdOptionaltrue to detect AMD asynchronously while the call proceeds
AsyncAmdStatusCallbackOptionalURL to receive async AMD result

Response (201 Created)

json
{
  "sid": "CAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "account_sid": "your_project_id",
  "to": "+15551234567",
  "from": "+15559876543",
  "status": "queued",
  "direction": "outbound-api",
  "date_created": "Mon, 16 Mar 2026 10:00:00 +0000"
}

Python SDK

python
call = client.calls.create(
    to="+15551234567",
    from_="+15559876543",
    url="https://your-backend.com/voice",
    status_callback="https://your-backend.com/status",
    record=True,
    machine_detection="Enable",
    async_amd=True,
    async_amd_status_callback="https://your-backend.com/amd",
)

Get Call

Retrieve the current status and details of a call.

GET /api/laml/2010-04-01/Accounts/{AccountSid}/Calls/{CallSid}.json
python
call = client.calls("CAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx").fetch()
print(call.status)    # queued | ringing | in-progress | completed | failed | no-answer | busy
print(call.duration)  # duration in seconds (available after completion)

Modify Call

Update an active call — redirect it to a new URL or hang it up.

POST /api/laml/2010-04-01/Accounts/{AccountSid}/Calls/{CallSid}.json
ParameterDescription
StatusSet to completed to hang up the call
UrlRedirect the call to a new LAML URL
python
# Hang up
client.calls("CAxxxx").update(status="completed")

# Redirect to new flow
client.calls("CAxxxx").update(url="https://your-backend.com/new-flow")

List Calls

GET /api/laml/2010-04-01/Accounts/{AccountSid}/Calls.json
python
calls = client.calls.list(limit=50)
for call in calls:
    print(call.sid, call.status, call.to)

LAML / XML — Introduction

LAML (Language Markup Language) is an XML-based language that controls call flows. When Wirexa API connects a call, it fetches your Url and executes the verbs in order.

<Response>
  <Say>Hello world.</Say>
  <Hangup/>
</Response>
All responses must be valid XML with Content-Type: text/xml.

Available Verbs

<Say>
Convert text to speech using the configured TTS provider.
<Play>
Play an audio file (MP3 or WAV) from a public URL.
<Gather>
Collect DTMF digits from the caller. Can nest Say/Play.
<Record>
Record the caller's voice. Saves as WAV and MP3.
<Hangup>
End the call immediately.
<Dial> / <Transfer>
Transfer the call to another number or SIP endpoint.

<Say>

Converts text to speech and plays it to the caller.

AttributeDefaultDescription
languageen-USBCP-47 language tag (e.g. es-CO, en-GB, pt-BR)
voicewomanVoice to use. See TTS providers for supported values.
loop1Number of times to repeat. 0 = infinite.
<Response>
  <Say language="en-US" voice="polly.Joanna">
    Your appointment is confirmed for tomorrow at 3 PM.
  </Say>
</Response>

<Play>

Plays an audio file from a public URL.

<Response>
  <Play>https://your-cdn.com/audio/greeting.mp3</Play>
</Response>

<Gather>

Waits for the caller to press digits (DTMF). Posts the collected input to action.

AttributeDefaultDescription
actionCurrent URLURL to POST the collected digits to
numDigitsunlimitedStop after this many digits
timeout5Seconds of silence before timeout
finishOnKey#Key that ends input collection
methodPOSTHTTP method for action
<Response>
  <Gather numDigits="1" timeout="7"
          action="https://your-backend.com/gather">
    <Say>Press 1 for sales, 2 for support, 3 to repeat.</Say>
  </Gather>
  <Say>We did not receive your input. Goodbye.</Say>
  <Hangup/>
</Response>

Your action endpoint receives Digits in the POST body:

python (Flask)
def gather():
    digits = request.form.get("Digits")
    if digits == "1":
        xml = "<Response><Say>Transferring to sales.</Say></Response>"
    elif digits == "2":
        xml = "<Response><Say>Transferring to support.</Say></Response>"
    else:
        xml = "<Response><Redirect/></Response>"  # repeat
    return Response(xml, mimetype="text/xml")

<Record>

Records the caller's voice. Sends a webhook when recording is complete.

AttributeDefaultDescription
actionURL to redirect after recording stops
maxLength3600Maximum recording duration in seconds
timeout5Seconds of silence before auto-stop
finishOnKey#Key that stops recording
recordingStatusCallbackURL to POST when recording file is ready
<Response>
  <Say>Please leave your message after the tone.</Say>
  <Record maxLength="60" timeout="3"
          recordingStatusCallback="https://your-backend.com/recording"/>
  <Say>Thank you. Goodbye.</Say>
  <Hangup/>
</Response>

<Hangup>

Ends the call immediately. No attributes required.

<Response><Hangup/></Response>

<Transfer> / <Dial>

Transfers the call to another phone number.

<Response>
  <Say>Transferring your call now.</Say>
  <Transfer>+15559998888</Transfer>
</Response>

Answering Machine Detection (AMD)

AMD automatically detects whether a human or an answering machine answered the call. Use it to skip voicemails or play a different message.

Async AMD (recommended)

The call proceeds immediately while AMD runs in the background. The result is sent to AsyncAmdStatusCallback.

python
call = client.calls.create(
    to="+15551234567",
    from_="+15559876543",
    url="https://your-backend.com/voice",
    machine_detection="Enable",
    async_amd=True,
    async_amd_status_callback="https://your-backend.com/amd",
)

Your AMD callback receives:

AnsweredBy
human or machine_start or machine_end_beep or fax
CallSid
The call SID
MachineDetectionDuration
Milliseconds taken to detect
python (Flask — AMD callback)
def amd_callback():
    answered_by = request.form.get("AnsweredBy")
    call_sid    = request.form.get("CallSid")

    if answered_by == "human":
        xml = "<Response><Say>Hello! This is a reminder for your appointment.</Say></Response>"
    else:
        # Machine — hang up or leave a different message
        xml = "<Response><Hangup/></Response>"

    return Response(xml, mimetype="text/xml")

Recordings

Call recordings are stored server-side and accessible via public URLs. Both WAV and MP3 formats are available.

FormatURL
WAVhttps://api.wirexaapi.com/recordings/{RecordingSid}.wav
MP3https://api.wirexaapi.com/recordings/{RecordingSid}.mp3

The RecordingSid is delivered via the RecordingStatusCallback webhook once the file is ready.

Webhooks

Wirexa API sends two separate POST callbacks at the end of each call.

Call Status Callback

Sent to StatusCallback when the call status changes.

CallSid
Unique call identifier
CallStatus
initiated | ringing | in-progress | completed | failed | no-answer | busy
To
Destination number
From
Caller ID number
Direction
outbound-api | inbound
CallDuration
Duration in seconds
AnsweredBy
AMD result if enabled

Recording Status Callback

Sent to RecordingStatusCallback when the recording file is ready.

RecordingSid
Unique recording identifier
RecordingUrl
Base URL for the recording (append .wav or .mp3)
RecordingStatus
completed
RecordingDuration
Duration in seconds
CallSid
Associated call SID

Text-to-Speech Providers

Specify the provider and voice in the voice attribute of <Say>.

Voice valueProviderExample
polly.JoannaAmazon Polly<Say voice="polly.Joanna">
polly.MiguelAmazon Polly (es)Spanish male voice
google.en-US-Wavenet-FGoogle Cloud TTSWaveNet female
google.es-US-Wavenet-BGoogle Cloud TTSSpanish WaveNet
eleven.RachelElevenLabsHyper-realistic voice
azure.en-US-JennyNeuralAzure CognitiveNeural voice
womanAuto (best available)Automatic fallback chain
manAuto (best available)Automatic fallback chain
If a provider fails, the engine automatically falls back to the next available provider — ensuring your call always plays audio.