Introduction
The Xcall API is built from the ground up as a REST API and can interact with any HTTP client in any programming language. This documentation provides instructions on how to quickly integrate our voice services into your application. It is easy to understand and provides code samples to point you in the right direction.
Get Authenticated
Our API uses HTTP Basic Authentication over an encrypted communication channel to verify users. All API requests require authentication using valid API credentials.You can obtain XCall API credentials form our web platform in 2 quick steps:
Step 1. Visit https://xcall.com.ng to create a user account. If you already have an Xcall user account, you can skip this step.
Step 2: Login using the user account from step 1.
- Navigate to Settings --> API Credentials
- Enter your App name to identify your credential, and click on "Generate".
- Click on "Save" to confirm and save the credentials.
Request Headers
These are the standard request headers that are needed when authenticating with the API. The API endpoints accepts json content types for requests and will produce a json response. You shoud specify "Content-Type: application/json" and "Accept: application/json" in the header section of API requests.
| Parameter | Type | Description |
|---|---|---|
| Authorization (Required) | string | Must include
the word "Basic" followed by the auth token you generated. Should look like this:
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
|
| content-Type (Required) | string | The requests content type. Must be application/json |
| Accept (Required) | array_string | The requests response type. Must be application/json |
Voice Messaging
Creating Outbound Calls
URL: https://api.xcall.com.ng/v3/callThis API endpoint sends automated calls to one or multiple phone numbers and plays a recorded audio message or text-to-speech message.
Request Parameters
| Parameter | Type | Description |
|---|---|---|
| broadcastName (optional) | string | A name or title that describes the broadcast. |
| from (optional) | string | Sender ID. This number will show on recipients' phone numbers when the call arrives. Only verified phone numbers in E.164 format are accepted. |
| to (required) | array_string | Array of destination (recipients') phone numbers. Phone numbers must be in E.164 format. (Example: "2347031234567") |
|
ttsMsg | string | Text-to-Speech message. This is the text that will be converted to
speech. Adding a pause between words is possible by using the comma character “,”. For example, if you want to have a 2 second pause after each word, then the text parameter should look like this “one,,, two,,,, three,,,,”. Each comma creates a pause of 0.5 seconds. |
|
ttsVoice | string | Defines the voice in which text would be synthesized. Options are 'uk-female', 'us-female', 'uk-male', 'us-male'. |
|
speechRate | double (1) | Defines the speed of speech in the resulting message. Effective only when using text-to-speech. Supported range is from 0.50 (slow speech) to 2.00 (fast speech). Default is 0.85. |
| audioFileUrl | string | You can specify a URL of an audio recording to play to the call recipients instead of text-to-speech. The audio file must be in .mp3 format and must be downloadable from the URL specified. The File size must not exceed 5MB. |
| startTime (optional) | unix timestamp | Unix timestamp that specifies the time to begin the calls. If startTime is not set, the calls will begin immediately. |
| notifyUrl | string | The URL on your callback server to which the Delivery report will be sent. |
Request Example
POST /v3/call HTTP/1.1 Host: https://api.xcall.com.ng Authorization: Basic c2FtbXllYmlubmVAZ21haWwuY29tOjRpNnk2ZzQ==
Content-Type: application/json Accept: application/json {
"from":"234703XXXXXXX",
"to":[
"234810XXXXXXX",
"234803XXXXXXX"
],
"ttsMsg": "Hello Jane! This call is from ABC Logistics. We want to notify you that you have a parcel to pickup at our office. Please come with a valid ID.
Thank you.",
"ttsVoice": "uk-male"
}
curl -X POST \
-H "Authorization: Basic c2FtbXllYmlubmVAZ21haWwuY29tOjRpNnk2ZzQ==" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"from":"234703XXXXXXX",
"to":[
"234810XXXXXXX",
"234803XXXXXXX"
],
"ttsMsg": "Hello Jane! This call is from ABC Logistics. We want to notify you that you have a parcel to pickup at our office. Please come with a valid
ID. Thank you.",
"ttsVoice": "uk-male"
}' "https://api.xcall.com.ng/v3/call"
<?php /* Define sender ID $from= "234703XXXXXXX"; /* populate destination array. */
$to = []; $to[]= "234810XXXXXXX"; $to[]= "234803XXXXXXX"; /* If message is text to speech, define its parameters */ $ttsMsg= "Hello Jane! This call is from ABC
Logistics. We want to notify you that you have a parcel to pickup at our office. Please come with a valid ID. Thank you."; $ttsVoice= "uk-male"; /*if message
is recorded audio, define its URL */ $audioFileUrl= "http://www.example.com/sounds/audio.mp3"; $requestData = array(
"broadcastName" => $name,
"ttsMsg" => $ttsMsg,
"ttsVoice" => $ttsVoice,
"audioFileUrl" => $audioFileUrl,
"to" => $to,
"from" => $from,
"startTime" => "567889900" /* Unix timestamp to begin the calls. Defaut is to begin calls immediately. */ ); /* convert requestData to json format */
$requestData = json_encode($requestData); /* Make API request */ $curl = curl_init(); curl_setopt_array($curl, array(
CURLOPT_URL => "https://api.xcall.com.ng/v3/call",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => $requestData,
CURLOPT_HTTPHEADER => array(
"authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
"content-type: application/json",
"accept: application/json"
), )); $response = curl_exec($curl); $err = curl_error($curl); curl_close($curl); if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
?>
Response Parameters
| Parameter | Type | Description |
|---|---|---|
| result | string | Indicates the result of an API request. The value is "OK" if successful and "ERROR" if unsuccessful. |
| msg | string | A description of the result. |
| broadcastId | string | ID that uniquely identifies the broadcast request. You can use this ID to retrieve delivery report and call log. |
| msgLength | int | Duration (in seconds) of voice message |
| dstCount | int | Number of call recipients or destination phone numbers. |
| reservedCredit | int | The credit units reserved for the calls. When the request is completed, unused credit is returned to user's account. |
Creating Outbound IVR Calls
URL: https://api.xcall.com.ng/v3/ivrThis API endpoint makes outbound interactive voice calls to one or multiple phone numbers. You can send automated phone calls to multiple recipients, ask questions, and record responses in real time.
Creating an IVR broadcast is in two steps:
- Step 1: You create an IVR template.
- Step 2: You create outbound calls using the template from step 1.
Step 1: Creating an IVR Template
An IVR Template is the major element of an IVR broadcast in our system. It defines the call flow logic for an IVR call.
To create an IVR template, logon to the client web portal. Navigate to Settings → IVR Templates → New IVR Template and follow the instructions on the page. Note the ID of the template you have created. You can use a single template for multiple IVR broadcasts.
Step 2: Create broadcast using template from step 1
Simply make an api request using the template ID from step 1 above. The request parameters are described below.
Request Parameters
| Parameter | Type | Description |
|---|---|---|
| broadcastName | string | A name or title that describes the broadcast. |
| templateId (required) | string | ID of the IVR template that defines an outbound IVR call/broadcast. |
| to (required) | array-string | Array of destination (recipients') phone numbers. Phone numbers must be in E.164 format (example: "2347031234567") |
| from | string | Sender ID. This number will show on recipients' phone numbers when the call arrives. Only verified phone numbers are accepted. |
| maxCallLength | int | Maximum permissible duration of IVR call in seconds. The system will terminate the call when this time elapes. Default is 120 seconds. |
| customData | array_string | Array of values that corresponds to custom variables in IVR template. Array keys must match the variable names in IVR template. |
| startTime | Unix timestamp | Unix timestamp that specifies the time to begin the calls. If startTime is not set, the calls will begin immediately. |
| notifyUrl | string | The URL on your your server to which Delivery report will be sent. |
| maxRetries | int | The maximum number of retry attempts. Limit is 2. |
| waitTime | int | The time (in minutes) to wait before retry. |
Request Example
POST /v3/ivr HTTP/1.1 Host: https:api.xcall.com.ng Authorization: Basic c2FtbXllYmlubmVAZ21haWwuY29tOjRpNnk2ZzQ==
Content-Type: application/json Accept: application/json {
"broadcastName": "Test call",
"templateId": "123DG2345D7BB178",
"from":"2349081235467",
"messages":[
{
"to":"2347031234567",
"customData":{
"name": "John",
"appointment": "September 12, 2019"
}
},
{
"to":"2348151234568",
"customData":{
"name": "Mary",
"appointment": "September 10, 2019"
}
}
],
"startTime": "56789467820",
"maxRetries": "3",
"waitTime": "5",
"notifyUrl": "https://www.mydomain.com/voice/process_callbacks"
}
curl -X POST \
-H "Authorization: Basic c2FtbXllYmlubmVAZ21haWwuY29tOjRpNnk2ZzQ==" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"templateId": "123DG2345D7BB178",
"from":"2349081235467",
"messages":[
{
"to":"2347031234567",
"customData":{
"name": "John",
"appointment": "September 12, 2019"
}
},
{
"to":"2348151234568",
"customData":{
"name": "Mary",
"appointment": "September 10, 2019"
}
},
],
"startTime": "56789467820",
"maxRetries":"3",
"waitTime": "5",
"notifyUrl": "https://www.mydomain.com/voice/process_callbacks"
}' "https://api.xcall.com.ng/ivr"
<?php /* Define sender ID $from= "2348191234567"; $templateId= "1475"; /* populate
messages. */ $messages = []; $messages[]= array(
"to"=> 2347031234567",
"customData"=> array(
"name": "John",
"appointment": "September 11"
); $messages[]= array(
"to"=> 2348151234568",
"customData"=> array(
"name": "Mary",
"appointment": "September 14
); $requestData = array(
"broadcastName" => $name,
"from" => $from
"templateId" => $templateId,
"messages" => $messages
"startTime" => "56789467820",
"maxRetries" => "3",
waitTime" => "5",
"notifyUrl" => "https://www.mydomain.com/voice/process_callbacks" ); /* convert requestData to json format */ $requestData = json_encode($requestData); /*
Make API request */ $curl = curl_init(); curl_setopt_array($curl, array(
CURLOPT_URL => "https://api.xcall.com.ng/api/ivr",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => $requestData,
CURLOPT_HTTPHEADER => array(
"authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
"content-type: application/json",
"accept: application/json"
), )); $response = curl_exec($curl); $err = curl_error($curl); curl_close($curl); if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
?>
Response Parameters
| Parameter | Type | Description |
|---|---|---|
| result | string | Indicates the result of an API request. The value is "OK" if successful and "ERROR" if unsuccessful. |
| msg | string | A description of the result. |
| broadcastId | int | ID that uniquely identifies the request. With this ID you can retrieve the status and call records. |
| msgLength | int | Duration (in seconds) of voice message |
| dstCount | int | Number of call recipients or destination phone numbers. |
| reservedCredit | int | The credit units reserved for the calls. When the request is completed, unused credit is returned to user's account. |
Receiving Voice Delivery Reports (via webhook)
You can receive delivery reports for voice broadcasts, outbound IVR, and survey campaigns automatically when the campaign completes. Specify a webhook URL (using the notifyUrl parameter) when you create the broadcast, or register a webhook URL on your account for the events below. Xcall will POST a JSON payload to that URL when the campaign finishes.
| Event | Payload | Fired when |
|---|---|---|
| voice.campaign.delivery.report | Full per-contact CDR (array of call records). | Campaigns with fewer than 500 destination contacts. |
| voice.campaign.delivery.summary | Aggregate summary: counts, dispositions, and (for IVR) response breakdown. | All completed campaigns, regardless of size. |
For campaigns with fewer than 500 contacts, both events are dispatched. For larger campaigns, only
voice.campaign.delivery.summary is dispatched — the full per-contact CDR remains available on demand via /v3/voiceReport.
Each webhook request includes the following HTTP header, which you can use to verify authenticity:
x-xcall-signature: <your-webhook-token>
Event: voice.campaign.delivery.report
Full delivery report, containing one call record per destination. Sent for campaigns with fewer than 500 contacts.
Payload Example
{
"event": "voice.campaign.delivery.report",
"timestamp": "2026-09-29T09:02:47Z",
"data": {
"broadcastId": "31940",
"name": "apiTest",
"type": "simple broadcast",
"dateCreated": "2026-09-29T08:11:04Z",
"startTime": "2026-09-29T08:15:00Z",
"endTime": "2026-09-29T09:02:47Z",
"status": "completed",
"destinations": 2,
"msgFile": "tts_audio_4n4h3k5t8y9b.mp3",
"cdr": [
{
"sessionId": 6231407,
"from": "2348037891009",
"to": "2348130429229",
"sentAt": "2026-09-29T08:15:02Z",
"result": "failed",
"disposition": "ringing, no answer",
"answerTime": null,
"duration": 0
},
{
"sessionId": 6231408,
"from": "2348037891009",
"to": "2348187957011",
"sentAt": "2026-09-29T08:15:03Z",
"result": "success",
"disposition": "Answered",
"answerTime": "2026-09-29T08:15:09Z",
"duration": 22
}
]
}
}
Payload Fields
| Field | Type | Description |
|---|---|---|
| event | string | Always voice.campaign.delivery.report. |
| timestamp | string | Campaign completion time, RFC 3339 UTC. |
| data | object | Delivery report object. Fields described below. |
| data.broadcastId | string | The ID that uniquely identifies the voice broadcast session. |
| data.name | string | Name of the broadcast. |
| data.type | string | The type of Xcall voice service: simple broadcast, personalized broadcast, ivr, or survey. |
| data.dateCreated | string | Date the broadcast was created, RFC 3339 UTC. |
| data.startTime | string | Time the broadcast started, RFC 3339 UTC. |
| data.endTime | string | Time the broadcast completed, RFC 3339 UTC. |
| data.status | string | Campaign status. Always completed for this event. |
| data.destinations | integer | Total number of destination phone numbers. |
| data.msgFile | string | Filename of the audio message file. |
| data.cdr | array | Array of call record objects. See Call Record Fields below. |
Call Record Fields (each element of data.cdr)
| Field | Type | Description |
|---|---|---|
| sessionId | integer | The ID that uniquely identifies the call session. |
| from | string | The phone number that appears as caller ID. |
| to | string | The destination phone number. |
| sentAt | string | Time the call was dispatched, RFC 3339 UTC, or null if the call was never dispatched. |
| result | string | Result of the voice call: success or failed. |
| disposition | string | Disposition of the destination number. Possible values include answered, unreachable, ringing, no answer, or the raw carrier status. |
| answerTime | string | Time the call was answered, RFC 3339 UTC, or null if the call was never answered. |
| duration | integer | Billed call duration, in seconds. |
| ivrResponse | array | Present only for campaigns of type ivr. Array of prompt-response objects, each containing question (IVR prompt name), digitPressed (DTMF digit, or null), and digitDescr (description of the pressed digit). |
Event: voice.campaign.delivery.summary
Aggregate summary of the campaign. Sent for all completed campaigns, regardless of size. Because the payload is compact and does not grow with campaign size, this is the recommended event for integrations that monitor campaign outcomes at scale.
Payload Example
{
"event": "voice.campaign.delivery.summary",
"timestamp": "2026-09-29T09:02:47Z",
"data": {
"broadcastId": "31940",
"name": "apiTest",
"type": "simple broadcast",
"dateCreated": "2026-09-29T08:11:04Z",
"startTime": "2026-09-29T08:15:00Z",
"endTime": "2026-09-29T09:02:47Z",
"status": "completed",
"destinations": 1200,
"msgFile": "tts_audio_4n4h3k5t8y9b.mp3",
"summary": {
"destinations": 1200,
"dialed": 1195,
"answered": 890,
"failed": 305,
"pending": 5,
"dispositions": {
"answered": 890,
"unreachable": 120,
"ringing, no answer": 185
}
},
"detailEndpoint": "/v3/voiceReport"
}
}
Payload Fields
The top-level event, timestamp, and all data.* fields up to and including data.msgFile are identical to the full report event. The differences are the fields below.
| Field | Type | Description |
|---|---|---|
| data.summary | object | Aggregate counts for the campaign. Replaces data.cdr from the full report event. |
| data.summary.destinations | integer | Total contacts targeted. |
| data.summary.dialed | integer | Contacts whose call was dispatched. |
| data.summary.answered | integer | Contacts whose call was answered successfully. |
| data.summary.failed | integer | Contacts whose call was dispatched but did not succeed. |
| data.summary.pending | integer | Contacts still awaiting dispatch at report time. Normally 0 for a completed campaign. |
| data.summary.dispositions | object | Map of disposition label to count. Keys depend on the carriers used; typical keys are answered, unreachable, and ringing, no answer. |
| data.summary.ivrPrompts | array | Present only for campaigns of type ivr. Array of prompt objects. Each prompt contains prompt (prompt name) and responses (array of objects with digit, description, and count). |
| data.detailEndpoint | string | Relative path to the endpoint that serves per-contact detail for this campaign (paginated). Combine with the API host to build the absolute URL, e.g. https://api.xcall.com.ng + data.detailEndpoint. |
Receiving Webhooks (PHP Example)
The example below reads the raw JSON body, inspects the event field, and dispatches to the appropriate handler. You can subscribe to one or both events from your account's webhook settings.
<?php /* Read the raw JSON body from the incoming POST. */ $raw =
file_get_contents("php://input"); $payload = json_decode($raw, true); if (!is_array($payload) || empty($payload["event"])) {
http_response_code(400);
exit("Bad payload");
}
$event = $payload["event"]; $data = $payload["data"]; /* Respond 2xx promptly. Queue the data for async processing if your
handler needs more than a couple of seconds. */ if ($event === "voice.campaign.delivery.summary") {
/* ---- Summary event (always fired) ---- */
$broadcastId = $data["broadcastId"];
$summary = $data["summary"];
error_log(sprintf(
"Campaign %s finished: %d answered, %d failed, %d pending",
$broadcastId,
$summary["answered"],
$summary["failed"],
$summary["pending"]
));
/* Per-contact detail is available on demand: */
/* $detailUrl = "https://api.xcall.com.ng" . $data["detailEndpoint"]; */
} elseif ($event === "voice.campaign.delivery.report") {
/* ---- Full report event (campaigns under 500 contacts) ---- */
$broadcastId = $data["broadcastId"];
$cdr = $data["cdr"];
foreach ($cdr as $record) {
error_log(sprintf(
" %s -> %s: %s (%s)",
$record["from"],
$record["to"],
$record["result"],
$record["disposition"]
));
}
} else {
/* Unknown event; log and ignore. */
error_log("Unhandled webhook event: " . $event);
}
/* Respond 200 so Xcall records the delivery as successful. */ http_response_code(200); echo "OK"; ?>
Delivery Guarantees and Retries
- Xcall considers a webhook delivered when your endpoint responds with an HTTP 2xx status within 30 seconds.
- If delivery fails, Xcall retries with exponential backoff: approximately 5 minutes, 15 minutes, 1 hour, 6 hours, and 24 hours after the initial attempt.
- After the final retry, the webhook is marked as failed and no further attempts are made.
- To avoid duplicate processing, treat each webhook as idempotent, keyed on the pair event + data.broadcastId.
Notes
- When both events fire for the same campaign, they arrive within a few seconds of each other. Ordering between the two events is not guaranteed.
- If you set the notifyUrl parameter when creating the broadcast, both events are delivered to that URL. Otherwise, each event is delivered to the webhook URL registered on your account for that event.
- All timestamps in the payload are RFC 3339 UTC.
- The detailEndpoint field is a relative path. Prepend the API host https://api.xcall.com.ng to build the absolute URL.
Getting Voice Delivery Reports (via query)
URL: https://api.xcall.com.ng/v3/voiceReportThis API endpoint returns the delivery report for a completed voice broadcast, outbound IVR, or survey campaign. The report includes a summary of the campaign and an array of per-contact call records. The endpoint supports pagination so large campaigns can be retrieved in batches.
Delivery reports are available for completed campaigns only.
Request Parameters
| Parameters | Type | Description |
|---|---|---|
| broadcastId (required) | string | The ID that uniquely identifies the voice broadcast request. Must belong to the authenticated user. |
| limit (optional) | integer | Number of call records to return per page. Default is 50. Maximum is 500. Values outside the valid range are reset to the default. |
| offset (optional) | integer | Zero-based index of the first call record to return. Useful for pagination. Default is 0. |
Request Example
POST /v3/voiceReport HTTP/1.1 Host: https://api.xcall.com.ng Authorization: Basic c2FtbXllYmlubmVAZ21haWwuY29tOjRpNnk2ZzQ==
Content-Type: application/json Accept: application/json {
"broadcastId": "5128",
"limit": "25",
"offset": "0"
}
curl -X POST \
-H "Authorization: Basic c2FtbXllYmlubmVAZ21haWwuY29tOjRpNnk2ZzQ==" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"broadcastId": "5128",
"limit": "25",
"offset": "0"
}' "https://api.xcall.com.ng/v3/voiceReport"
<?php /* Define broadcast ID */ $broadcastId = "5128"; /* Build request payload */
$requestData = array(
"broadcastId" => $broadcastId,
"limit" => "25",
"offset" => "0" ); /* Convert requestData to json format */ $requestData = json_encode($requestData); /* Make API request */ $curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://api.xcall.com.ng/v3/voiceReport",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => $requestData,
CURLOPT_HTTPHEADER => array(
"authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
"content-type: application/json",
"accept: application/json"
), )); $response = curl_exec($curl); $err = curl_error($curl); curl_close($curl); if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
?>
Response on Success
HTTP/1.1 200 OK Content-Type: application/json {
"result": "OK",
"report": {
"broadcastId": "5128",
"name": "October Promo",
"type": "simple broadcast",
"dateCreated": "2026-09-29T08:11:04Z",
"startTime": "2026-09-29T08:15:00Z",
"endTime": "2026-09-29T09:02:47Z",
"status": "completed",
"destinations": 1200,
"msgFile": "promo_oct.mp3",
"cdr": [
{
"sessionId": 918273,
"from": "07030000000",
"to": "2348031234567",
"sentAt": "2026-09-29T08:15:02Z",
"result": "success",
"disposition": "answered",
"answerTime": "2026-09-29T08:15:09Z",
"duration": 22
},
{
"sessionId": 918274,
"from": "07030000000",
"to": "2348039876543",
"sentAt": "2026-09-29T08:15:03Z",
"result": "failed",
"disposition": "ringing, no answer",
"answerTime": null,
"duration": 0
}
],
"pagination": {
"limit": 25,
"offset": 0,
"total": 1200,
"returned": 2,
"hasMore": true
}
}
}
Response Parameters
| Parameters | Type | Description |
|---|---|---|
| result | string | Indicates the result of the API request. "OK" if successful, "ERROR" if unsuccessful. |
| report | object | Object containing the campaign delivery report. Present when result is "OK". |
| report.broadcastId | string | The ID that uniquely identifies the voice broadcast session. |
| report.name | string | Name of the broadcast. |
| report.type | string | The type of Xcall voice service. One of: simple broadcast, personalized broadcast, ivr, survey. |
| report.dateCreated | string | The date the broadcast was created, in RFC 3339 UTC format (e.g. 2026-09-29T08:11:04Z). |
| report.startTime | string | The time the broadcast started, RFC 3339 UTC. |
| report.endTime | string | The time the broadcast completed, RFC 3339 UTC. |
| report.status | string | The status of the broadcast. For this endpoint, always "completed". |
| report.destinations | integer | The total number of destination phone numbers. |
| report.msgFile | string | The filename of the audio message file. |
| report.cdr | array | Array of per-contact call records. See Call Record Fields below. |
| report.pagination | object | Pagination metadata. See Pagination Fields below. |
Call Record Fields (each element of report.cdr)
| Parameters | Type | Description |
|---|---|---|
| sessionId | integer | The ID that uniquely identifies the call session. |
| from | string | The phone number that appears as caller ID. |
| to | string | The destination phone number. |
| sentAt | string | The time the call was dispatched, RFC 3339 UTC, or null if the call was never dispatched. |
| result | string | The result of the voice call. Possible values are success or failed. |
| disposition | string | The disposition of the destination number. Possible values include answered, unreachable, ringing, no answer, or the raw carrier status. |
| answerTime | string | The time the call was answered, RFC 3339 UTC, or null if the call was never answered. |
| duration | integer | The billed duration of the call in seconds. |
| ivrResponse | array | Present only for campaigns of type ivr. An array of prompt-response objects. Each object contains: question (the IVR prompt name), digitPressed (the DTMF digit pressed by the recipient, or null), and digitDescr (discription of the pressed digit). Omitted for all other campaign types. |
Pagination Fields (report.pagination)
| Parameters | Type | Description |
|---|---|---|
| limit | integer | The page size actually used (after clamping to the valid range). |
| offset | integer | The offset actually used. |
| total | integer | The total number of call records available for this campaign. |
| returned | integer | The number of records returned in this response. |
| hasMore | boolean | true if further pages exist, false on the last page. |
Pagination Example
To retrieve all 1,200 call records of a campaign with a page size of 100, request the following offsets in sequence: 0, 100, 200, …, 1100. Stop when pagination.hasMore is false. For example, the last page would be requested with:
{
"broadcastId": "5128",
"limit": 100,
"offset": 1100
}
Error Responses
| Result | Message | Cause |
|---|---|---|
| ERROR | Authorization denied! | Missing or invalid API credentials (HTTP 401). |
| ERROR | Error. Required parameter missing. | broadcastId was not supplied in the request body. |
| ERROR | No matching record found. | The campaign does not exist, or does not belong to the authenticated user. |
| ERROR | Delivery report unavailable. | The campaign exists but is not in the completed state. |
SMS Messaging
Send simple text message
Endpoint: https://api.xcall.com.ng/v3/smsThis API enables you to send a text message (same message) to one or multiple recipients.
Request Parameters
| Parameter | Type | Description |
|---|---|---|
| broadcastName | string | A name or title that describes the SMS broadcast. |
| from | string | Sender ID for the message. It can be alphanumeric or numeric. Alphanumeric sender ID length should be between 3 and 11 characters. Numeric sender ID length should be between 3 and 14 characters. |
| to | array_string | Array of destination (recipients') phone numbers in E.164 format. |
|
smsText | string | Text of the message that will be sent. |
|
flash | boolean | Can be true or false. If the value is set to true, a flash SMS will be sent. Otherwise, a normal SMS will be sent. The default value is false. |
|
sendAt | string | The unix timestamp at which the message should be sent. Default is to send message immediately. |
|
notifyUrl | string | The webhook URL on your server to which delivery report will be sent. |
Request Example
POST /v3/sms HTTP/1.1 Host: https://api.xcall.com.ng Authorization: Basic c2FtbXllYmlubmVAZ21haWwuY29tOjRpNnk2ZzQ==
Content-Type: application/json Accept: application/json {
"from":"XYZ",
"to":[
"2347031234567",
"2348057654321"
],
"smsText": "This is a test SMS",
"notifyUrl": "http://mydomain.com/sms/report"
}
curl -X POST \
-H "Authorization: Basic c2FtbXllYmlubmVAZ21haWwuY29tOjRpNnk2ZzQ==" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"from":"XYZ",
"to":[
"2347031234567",
"2348057654321"
],
"smsText": "This is a test SMS",
"notifyUrl": "http://mydomain.com/sms/report"
}' "https://api.xcall.com.ng/v3/sms"
<?php /* Define sender ID $from= "XYZ"; /* populate destination array. */ $to =
array("2347031234567", "2348057654321"); $smsText = "This is a test SMS"; $notifyUrl= "http://mydomain.com/sms/report"; $requestData = array(
"from" => $from,
"to" => $to,
"smsText" => $smsText,
"notifyUrl" => $notifyUrl,
"sendAt" => "567889900" /* Unix timestamp to send text message. Defaut is to begin immediately. */ ); /* convert requestData to json format */ $requestData
= json_encode($requestData); /* Make API request */ $curl = curl_init(); curl_setopt_array($curl, array(
CURLOPT_URL => "https://api.xcall.com.ng/v3/sms",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => $requestData,
CURLOPT_HTTPHEADER => array(
"authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
"content-type: application/json",
"accept: application/json"
), )); $response = curl_exec($curl); $err = curl_error($curl); curl_close($curl); if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
?>
Response on Success
HTTP/1.1 200 OK Content-Type: application/json {
"result":"OK",
"description":"Request accepted",
"broadcastId":"1315145",
"units_used":"2",
"credit_balance":"1216.5",
"messages":[
{
"to":"2347031234567",
"status": "PENDING",
"messageId": "145698"
},
{
"to":"2348057654321",
"status": "PENDING",
"messageId": "605569"
}
]
}
Response Parameters
| Parameter | Type | Description |
|---|---|---|
| result | string | Indicates the result of the request. Possible
values are:
"OK" - indicates that API request accepted. |
| description | string | A description of the result. |
| broadcastId | string | The ID that uniquely identifies the request. You can use this ID to retrieve delivery report and call log. |
| messages | array-string | An array of message objects. One object
per message. Includes:
to - The message destination phone number.
|
Get SMS Credit Balance
Endpoint: https://api.xcall.com.ng/v3/get_sms_balance
This endpoint allows an authenticated user to retrieve their current SMS credit balance.
Each user is limited to 100 requests per day to this endpoint. Once the daily limit is reached, subsequent requests to this endpoint
will be rejected until the next day (00:00 UTC).
The get_sms_balance.php endpoint should be used sparingly — ideally for dashboards or occasional syncs.
It is not recommended to use this endpoint to check for credit balance before every sms send request. Instead, you can rely on the
'credit_balance' field in send responses to cache the lastest known balance and avoid extra API calls.
Sample Request
GET URL: https://api.xcall.com.ng/v3/get_balance.php
This endpoint does not require any body parameters. Ensure that standard authentication headers are included in the request.
Sample Responses
Successful Response
Content-Type: application/json {
"result":"OK",
"description":"Request successful",
"user":"john@abc.com",
"credit_balance":"1520.75"
}
Rate Limit Exceeded
Content-Type: application/json {
"result":"ERROR",
"user":"john@abc.com",
"description":"Rate limit exceeded. You have reached your 100 requests per day quota."
}