Place outbound calls from your Sozuri voice number, read a one-time code to someone over the phone, and look up every call with its status, duration and price. Same API key and project name as SMS.

POST https://sozuri.net/api/v1/voice/calls

Before you start

  • Your project needs an active voice number. Request one in the dashboard under Project → Voice → Numbers; calls go out from that number, and it is the number your customers see.
  • What a call does once it is answered is set on the number, under When someone calls this number: play a message, run a call flow (an IVR you build under Voice → Call flows), send the caller to your call centre, forward to another phone, or send it to your own URL (ask support for the format your server answers with). An outbound call placed through this API is handled the same way when the person picks up. A voice OTP call is the exception: it only reads the code.
  • Authenticate exactly as for SMS: your API key as Authorization: Bearer <key> (or an apiKey field in the query string or body) plus project, your project's name. See Authentication.
  • Unlike the SMS API, every voice error comes back with a 4xx status and a machine-readable code, so branch on the HTTP status: 201 means at least one call was placed.

Place a call

POST https://sozuri.net/api/v1/voice/calls
FieldRequiredTypeDescription
projectYesStringYour project's name.
toYesString or arrayWho to call, in international format (254700000000). Several numbers as an array or a comma-separated string; at most 100 per request. Each number is its own call.
fromNoStringWhich of your active voice numbers to call from. Leave it out to use your project's first active number.
tagNoStringUp to 60 characters to group these calls by, e.g. reminder. Letters, digits, _ and - are kept (the first 40); filter on it later with List recent calls.
curl -X POST https://sozuri.net/api/v1/voice/calls \
  -H "Authorization: Bearer $SOZURI_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{ "project": "your_project_name", "to": ["254700000000", "254700000001"], "tag": "reminder" }'

201 Created, one entry per call placed:

{
  "status": "ok",
  "from": "+254711000000",
  "testMode": false,
  "calls": [
    { "callSid": "9b2f1c3e-0d4a-4e8b-9a51-6f0c2d7e8a10", "to": "254700000000", "status": "queued", "tag": "reminder_3f9a0c1b2d4e_1" },
    { "callSid": "0c7e5a12-8b3f-4d26-b1e4-2a9f6c0d3e57", "to": "254700000001", "status": "queued", "tag": "reminder_3f9a0c1b2d4e_2" }
  ]
}
  • callSid is the call's id: keep it to look the call up.
  • tag is your tag plus a code for this request and the call's position in it, so every call can be told apart.
  • Each number is dialled separately, so some can be refused while others ring. When that happens the answer is still 201 and carries rejected, one entry per number that was not called: { "to": "...", "code": "...", "message": "..." }. When no number could be called the answer is 422 with the first refusal's code and message.
  • testMode is true only when the call was simulated and nobody's phone rang. It is false on a live project.

Read a one-time code (voice OTP)

POST https://sozuri.net/api/v1/voice/otp

Calls one number and reads the code one digit at a time, then repeats it: "Your Acme verification code is 4, 8, 2, 1. Again, 4, 8, 2, 1. Goodbye." The call is never recorded, and the code is not stored, logged or returned.

FieldRequiredTypeDescription
projectYesStringYour project's name.
toYesStringThe one number to call, international format. Up to 32 characters.
codeYesStringThe code to read, up to 12 characters. Only its digits are read; anything else is dropped. A code with no digits is refused (invalid_code).
brandNoStringThe name read before the code, up to 60 characters. Default Sozuri.
repeatNoIntegerHow many times the code is read, 1 to 3. Default 2.
voiceNoStringA text-to-speech voice: en-US-Standard-C (the default, female), en-US-Standard-B (male), en-GB-Standard-A, en-GB-Standard-B. A value we don't recognise uses the default.
fromNoStringWhich of your active voice numbers to call from. Default: your project's first active number.
curl -X POST https://sozuri.net/api/v1/voice/otp \
  -H "Authorization: Bearer $SOZURI_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{ "project": "your_project_name", "to": "254700000000", "code": "4821", "brand": "Acme" }'

201 Created:

{
  "status": "ok",
  "callSid": "5d1e9f07-3a2b-4c6d-8e90-1f2a3b4c5d6e",
  "to": "254700000000",
  "from": "+254711000000",
  "testMode": false
}

Look up a call

GET https://sozuri.net/api/v1/voice/calls/{callSid}

One call of your project, with its timeline. A callSid that is not your project's answers 404 not_found.

curl "https://sozuri.net/api/v1/voice/calls/9b2f1c3e-0d4a-4e8b-9a51-6f0c2d7e8a10?project=your_project_name" \
  -H "Authorization: Bearer $SOZURI_API_KEY" -H "Accept: application/json"
{
  "status": "ok",
  "call": {
    "callSid": "9b2f1c3e-0d4a-4e8b-9a51-6f0c2d7e8a10",
    "direction": "outbound",
    "from": "+254711000000",
    "to": "254700000000",
    "status": "completed",
    "statusLabel": "Completed",
    "durationSecs": 221,
    "billableSecs": 240,
    "price": 16.0,
    "currency": "KES",
    "tag": "reminder_3f9a0c1b2d4e_1",
    "recordingUrl": null,
    "createdAt": "2026-11-08T09:15:02+03:00",
    "endedAt": "2026-11-08T09:18:51+03:00",
    "events": [
      { "sequence": 1, "event": "start", "occurredAt": "2026-11-08T09:15:09+03:00" },
      { "sequence": 2, "event": "status", "occurredAt": "2026-11-08T09:18:51+03:00" },
      { "sequence": 3, "event": "billed", "occurredAt": "2026-11-08T09:18:51+03:00" }
    ]
  }
}
FieldDescription
directionoutbound (you placed it) or inbound (someone called your number).
status / statusLabelThe call's state and its label: see Call statuses.
durationSecsHow long the call lasted once answered. null until it ends.
billableSecs, price, currencyWhat the call was charged, set when it ends (see Billing). null while it is in progress.
recordingUrlThe recording, when the number records calls. null otherwise. Voice OTP calls are never recorded.
eventsThe call's timeline, oldest first: e.g. start (answered), dtmf (a key pressed), flow (call-flow steps run), record, enqueue / dequeue (call centre), status (the final report) and billed. Only on this endpoint, not in the list.

List recent calls

GET https://sozuri.net/api/v1/voice/calls

Your project's calls, newest first, in the same shape as a single call without events.

Query parameterDescription
limitHow many to return, 1 to 200. Default 50.
statusOnly calls in this state, e.g. completed or no_answer. An unknown value is ignored.
tagOnly calls whose tag starts with this, e.g. reminder for every call placed with that tag.
curl "https://sozuri.net/api/v1/voice/calls?project=your_project_name&status=no_answer&tag=reminder&limit=20" \
  -H "Authorization: Bearer $SOZURI_API_KEY" -H "Accept: application/json"
{ "status": "ok", "count": 1, "calls": [ { "callSid": "0c7e5a12-8b3f-4d26-b1e4-2a9f6c0d3e57", "direction": "outbound", "status": "no_answer", "...": "..." } ] }

List your voice numbers

GET https://sozuri.net/api/v1/voice/numbers

Your project's numbers that are active, suspended or still being requested.

{
  "status": "ok",
  "numbers": [
    { "number": "+254711000000", "type": "regular", "label": "Main line", "status": "active", "handlerType": "flow", "aiMode": false }
  ]
}
FieldValues
typeregular, tollfree, premium or sip.
statusactive (can call and be called), suspended (paused, e.g. an unpaid monthly fee) or requested (not assigned yet). Only an active number can be used as from.
handlerTypeWhat answers a call to the number: say (play a message), flow (a call flow), queue (the call centre), forward (another phone) or webhook (your own URL).
aiModeReserved for numbers answered by an AI agent. Always false today.

Call statuses

statusstatusLabelMeaning
queuedQueuedAccepted, not dialled yet.
ringingRingingDialling, or ringing at the other end.
in_progressIn progressAnswered; the call is live.
on_holdOn holdThe caller is waiting in your call-centre queue.
completedCompletedAnswered and ended normally.
no_answerNo answerRang out, was rejected or busy, or the number could not be reached.
failedFailedThe call could not be placed.

Billing

  • A call is charged when it ends, per minute at your project's voice rate: the answered duration, at least one minimum call, rounded up to the next whole increment. A 3-minute-41-second call on a per-minute rate is charged as 4 minutes.
  • A call nobody answered lasts 0 seconds and costs nothing.
  • Before dialling, your balance must cover one minimum call, or the request is refused with 402 insufficient_balance and nothing is dialled.
  • The charge is on the call itself: billableSecs, price and currency.
  • On a postpay account, calls are priced and added to your invoice instead of taken from a balance.

Error responses

A refusal from the voice API looks like this:

{ "status": "error", "code": "no_voice_number", "message": "This project has no active voice number. Request one in the dashboard." }
HTTPcodeMeaning
402insufficient_balanceYour balance does not cover one minimum call. Top up and retry.
404not_foundNo call with that callSid on your project.
422no_voice_numberYour project has no active voice number, or from is not one of them.
422too_many_recipientsMore than 100 numbers in to.
422no_recipientsto held no number once blanks were removed.
422invalid_codeVoice OTP: code has no digits.
422voice_disabledVoice calling is switched off on the platform for now. Retry later.
422provider_not_configuredYour number cannot place calls yet. Contact support.
422otherNone of the numbers could be called; message says why for the first one.

Two refusals come before the request reaches the voice API, and have the shape every Sozuri API uses for them: a wrong key or project name is 401, and a missing or malformed field is 422 with the fields listed:

{
  "error_code": "VALIDATION_FAILED",
  "message": "The submitted data failed validation.",
  "retryable": false,
  "errors": { "code": ["The code field is required."] }
}

Ready to start sending?

Create a free Sozuri project, grab your API key, and send your first message in minutes.