Mon-Fri 10am - 6pm
Voice Broadcast
Base URL for your account: https://cpaas.studio/api/v1
GET
POST
/api/v1/voice/broadcast
Sends a broadcast to a list of numbers from one of your account's DIDs. Requires a key scoped to voice.obd or all. This endpoint accepts a request; it does not bill or deliver synchronously.
Parameters
| Param | Type | Required | Notes |
|---|---|---|---|
did |
string | yes | The phone number of one of your account's active DIDs enabled for Voice Broadcast (not a row id). Matched on the national number so a country code or leading zero on either side never causes a mismatch. |
numbers |
array or string | yes | The recipient list — JSON array, repeated form/query key, or comma-separated string. Maximum 1,000 numbers per request. |
audio_uid |
string | one of audio_uid/audio_reference | An approved, operator-delivered, enabled asset from your Audio Library. Takes precedence if both are supplied. |
audio_reference |
string | one of audio_uid/audio_reference | Resolves to your most recently uploaded asset with this reference. The response always echoes the resolved audio_uid. |
schedule_at |
string (date) | no | Omit to send immediately. ISO-8601 with a UTC offset is honoured exactly; a bare datetime is read in your account's configured timezone. |
ring_time |
integer | no | One of 10,15,20,25,30,45,60. Defaults to your account's standard default (30). |
retry_count |
integer | no | One of 0,1,2,3. Defaults to 0. |
retry_interval |
integer | no | One of 15,30,60,120,180,300. Defaults to 60. |
reference |
string, max 64 | no | Your own label for this broadcast, echoed back on the response. Not unique — does not prevent duplicate sends. Use the Idempotency-Key header for replay protection. |
name |
string, max 255 | no | A label for the broadcast. Defaults to reference if supplied, otherwise a generated label. |
caller_id |
— | do not supply | Derived from did server-side. Supplying this field is rejected, not silently ignored. |
Request example
curl -X POST "https://cpaas.studio/api/v1/voice/broadcast" \
-H "X-API-KEY: cpk_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Idempotency-Key: 3f29b1c2-6e2a-4c1a-9e3b-7a2f0d5c9e11" \
-d "did=9876500000" \
-d "audio_uid=AUD7K2M9QX4WQ" \
-d "numbers=9876543210,9876543211" \
-d "reference=order-4471"
<?php
$ch = curl_init('https://cpaas.studio/api/v1/voice/broadcast');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'X-API-KEY: cpk_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'Idempotency-Key: 3f29b1c2-6e2a-4c1a-9e3b-7a2f0d5c9e11',
'Content-Type: application/json',
],
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode([
'did' => '9876500000',
'audio_uid' => 'AUD7K2M9QX4WQ',
'numbers' => ['9876543210', '9876543211'],
'reference' => 'order-4471',
]),
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
Success response (data)
{
"id": "BCQVCUU6YTAA",
"reference": "order-4471",
"status": "queued",
"total_numbers": 3,
"summary": {
"accepted": 2,
"duplicate": 0,
"invalid": 1
},
"credits_estimated": 2,
"audio_uid": "AUD7K2M9QX4WQ",
"scheduled_at": null,
"numbers": [
{
"id": "BCQVCUU6YTAA-1",
"mobile": "9876543210",
"status": "pending",
"attempts": 0,
"duration_seconds": null,
"billsec": null,
"answered_at": null,
"updated_at": "2026-08-15T12:00:00+00:00"
},
{
"id": "BCQVCUU6YTAA-2",
"mobile": "9876543211",
"status": "pending",
"attempts": 0,
"duration_seconds": null,
"billsec": null,
"answered_at": null,
"updated_at": "2026-08-15T12:00:00+00:00"
},
{
"id": "BCQVCUU6YTAA-3",
"mobile": "12345",
"status": "invalid",
"attempts": 0,
"duration_seconds": null,
"billsec": null,
"answered_at": null,
"updated_at": "2026-08-15T12:00:00+00:00"
}
],
"created_at": "2026-08-15T12:00:00+00:00"
}
Errors
| Code | Status | When |
|---|---|---|
AUDIO_NOT_FOUND |
404 | audio_uid/audio_reference doesn't match any of your broadcast-type assets. |
AUDIO_NOT_APPROVED |
422 | The asset is not approved, or has not been delivered to the operator yet. |
AUDIO_DISABLED |
422 | The asset has been turned off via Audio Enable/Disable. |
BCAST_DID_INVALID |
422 | did doesn't match any of your account's active, voice.obd-enabled DIDs. |
BCAST_NO_VALID_NUMBERS |
422 | Every submitted number was invalid or a duplicate. |
BCAST_ALL_NUMBERS_INVALID |
422 | Every submitted number specifically failed format normalization. |
BILLING_INSUFFICIENT_CREDITS |
402 | Your voice.obd wallet balance doesn't cover the estimated cost. |
REQ_IDEMPOTENCY_KEY_CONFLICT |
409 | The Idempotency-Key header matches a prior request, but this request's body is different. |
REQ_SCHEDULE_IN_PAST |
422 | schedule_at resolved to a time that had already passed by the time the request was processed. |
REQ_SCHEDULE_INVALID_FORMAT |
422 | schedule_at isn't a value the date parser accepts — a more specific promotion of REQ_VALIDATION_FAILED for that one field. |
REQ_NUMBERS_LIMIT_EXCEEDED |
422 | numbers exceeds the per-request cap (1,000 by default) — a more specific promotion of REQ_VALIDATION_FAILED for this one case. |
REQ_AUDIO_SELECTION_INVALID |
422 | Neither or both of audio_uid/audio_reference were supplied where exactly one is required. |
REQ_VALIDATION_FAILED |
422 | Missing/invalid did, caller_id supplied, or any other field-level rule failure not covered by a more specific code above. |
Notes
- accepted != billed. A 200/202 response means your broadcast was accepted, not billed and not delivered — actual credit debit happens per-chunk, asynchronously, after this request has already returned.
- credits_estimated is exactly that — an estimate from the pre-flight balance check, never a charged amount.
- numbers[] contains every number you submitted, exactly once each — accepted, duplicate, and invalid alike. There is no separate rejected_numbers/accepted_numbers array any more.
- Array order is NOT guaranteed to match your original submission order across the accepted/duplicate/invalid boundary — match entries back to your input by mobile, not by array position.
- An optional Idempotency-Key request header is the real replay-safety mechanism for this endpoint — reference alone does not prevent a duplicate send. Keys expire after 24 hours.
- There is no contact-groups selection parameter on this endpoint — resolve any saved group's numbers on your own side before calling it.
Need more context? See Getting started for authentication, the response envelope, and the full error-code reference.