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:
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
pip install wirexaapi
2. Make a call
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:
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 -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.
pip install wirexaapi
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.
Request Parameters
| Parameter | Required | Description |
|---|---|---|
To | Required | Destination phone number in E.164 format (e.g. +15551234567) |
From | Required | Caller ID number assigned to your account |
Url | Required | URL that returns LAML/XML to control the call flow |
Method | Optional | HTTP method to request Url. Default: POST |
Timeout | Optional | Seconds to wait for answer before giving up. Default: 28 |
Record | Optional | true to record the entire call. Default: false |
StatusCallback | Optional | URL to receive call status updates (answered, completed, etc.) |
StatusCallbackEvent | Optional | Space-separated events: initiated ringing answered completed |
RecordingStatusCallback | Optional | URL to receive recording completion notification |
MachineDetection | Optional | Enable or DetectMessageEnd — enables answering machine detection |
AsyncAmd | Optional | true to detect AMD asynchronously while the call proceeds |
AsyncAmdStatusCallback | Optional | URL to receive async AMD result |
Response (201 Created)
{
"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
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.
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.
| Parameter | Description |
|---|---|
Status | Set to completed to hang up the call |
Url | Redirect the call to a new LAML URL |
# 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
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>
Content-Type: text/xml.Available Verbs
<Say>
Converts text to speech and plays it to the caller.
| Attribute | Default | Description |
|---|---|---|
language | en-US | BCP-47 language tag (e.g. es-CO, en-GB, pt-BR) |
voice | woman | Voice to use. See TTS providers for supported values. |
loop | 1 | Number 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.
| Attribute | Default | Description |
|---|---|---|
action | Current URL | URL to POST the collected digits to |
numDigits | unlimited | Stop after this many digits |
timeout | 5 | Seconds of silence before timeout |
finishOnKey | # | Key that ends input collection |
method | POST | HTTP 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:
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.
| Attribute | Default | Description |
|---|---|---|
action | URL to redirect after recording stops | |
maxLength | 3600 | Maximum recording duration in seconds |
timeout | 5 | Seconds of silence before auto-stop |
finishOnKey | # | Key that stops recording |
recordingStatusCallback | URL 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.
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:
human or machine_start or machine_end_beep or faxdef 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.
| Format | URL |
|---|---|
WAV | https://api.wirexaapi.com/recordings/{RecordingSid}.wav |
MP3 | https://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.
initiated | ringing | in-progress | completed | failed | no-answer | busyoutbound-api | inboundRecording Status Callback
Sent to RecordingStatusCallback when the recording file is ready.
.wav or .mp3)completedText-to-Speech Providers
Specify the provider and voice in the voice attribute of <Say>.
| Voice value | Provider | Example |
|---|---|---|
polly.Joanna | Amazon Polly | <Say voice="polly.Joanna"> |
polly.Miguel | Amazon Polly (es) | Spanish male voice |
google.en-US-Wavenet-F | Google Cloud TTS | WaveNet female |
google.es-US-Wavenet-B | Google Cloud TTS | Spanish WaveNet |
eleven.Rachel | ElevenLabs | Hyper-realistic voice |
azure.en-US-JennyNeural | Azure Cognitive | Neural voice |
woman | Auto (best available) | Automatic fallback chain |
man | Auto (best available) | Automatic fallback chain |