Guides
Quickstart
Send your first SMS with the Sendai API — confirm your sender ID, get an API key, POST a message, and poll it until the carrier confirms delivery.
This guide takes you from zero to a delivered SMS: confirm your sender ID, get an API key,
send a message, and confirm it arrived. Every request uses the base URL
https://api.sendai.co.zw.
Before you send: your sender ID
The from on every message must be a sender ID already approved on your account — an
unapproved value is rejected with 400 unknown or unapproved sender id, so this is worth 30
seconds before your first call.
You captured your sender ID (up to 3, each 3–11 letters or numbers) when you registered.
Approval runs on the network-operator side and usually takes about 30 minutes — check its
status in the dashboard under Settings, or list your identities over the API with
Sender IDs and use any address whose verification_status is
verified.
Two more things while you wait: accounts are prepaid, so top up your balance in the
dashboard before the first send; and everywhere below, replace YOUR_SENDER_ID with your
approved sender ID.
1. Get an API key
Create your first key in the dashboard under Settings → Developer. Once you hold one, you can mint further keys over the API — one per service is good practice:
curl -X POST https://api.sendai.co.zw/api/v1/api-tokens \
-H "Authorization: Bearer $SENDAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "orders-service" }'
The response contains the key once — store it securely; it cannot be retrieved again.
{
"status": "success",
"message": "api token created — save this token, it will not be shown again",
"data": {
"id": "9f2a4c8e-6b21-4d0f-a97b-5e8c1d2f6a3b",
"name": "orders-service",
"token": "sk_live_4c8e6b21d0f34a97b5e8c1d2f6a3b904",
"account_id": "2d6f4b8e-1a90-4e21-8b7d-3f1a5c2e9a0c",
"created_at": "2026-07-06T09:12:03Z"
}
}
Export it as SENDAI_API_KEY and it authenticates every call below. For rotation and safe
handling, see Authentication.
2. Send an SMS
to is an MSISDN in international format, from is your approved sender ID, and message
is the text body.
curl -X POST https://api.sendai.co.zw/api/v1/sms \
-H "Authorization: Bearer $SENDAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "263771000000",
"from": "YOUR_SENDER_ID",
"message": "Your code is 4821"
}'
Sendai validates and prices the message, holds the charge against your balance, and returns
202 Accepted. (Responses on this page show an account whose approved sender ID is
Sendai SMS.)
{
"id": "0f9c1b3a-6d2e-4a1b-9c3d-7e5f2a8b4c10",
"to": "263771000000",
"message": "Your code is 4821",
"sender_id": "Sendai SMS",
"operator": "econet",
"charge": "450",
"charge_currency": "USD",
"operator_response": "",
"received_at": "2026-07-06T09:12:03Z",
"status": "enqueued",
"created_at": "2026-07-06T09:12:03Z",
"delivered_at": null
}
This response is not enveloped.
POST /api/v1/smsreturns the message object as the whole body — there is nodatato unwrap. Every other endpoint does wrap its payload. See two response conventions.
Two more things to note: the sender comes back as sender_id, not from; and charge is
a string on this endpoint, in ten-thousandths of charge_currency ("450" == 0.045). See
billing and segments.
3. Confirm it arrived
Poll GET /api/v1/sms/{id} with the id from the send response. This response is
enveloped, so the message sits under data:
curl https://api.sendai.co.zw/api/v1/sms/0f9c1b3a-6d2e-4a1b-9c3d-7e5f2a8b4c10 \
-H "Authorization: Bearer $SENDAI_API_KEY"
{
"status": "success",
"message": "request successful",
"data": {
"id": "0f9c1b3a-6d2e-4a1b-9c3d-7e5f2a8b4c10",
"status": "success",
"operator_response": "DELIVRD",
"delivered_at": "2026-07-06T09:12:44Z"
}
}
delivered_at is the delivery signal. status moves created → enqueued →
processing → success (or failed) and tells you the send pipeline finished;
delivered_at going from null to a timestamp is the carrier confirming it reached the
handset. A message can sit at status: success with delivered_at: null for a while — that
is normal, not a failure.
Carrier confirmation is asynchronous and can take seconds to hours, so poll on an interval rather than in a tight loop — or skip polling entirely and register a webhook.
Errors
Branch on the HTTP status code:
| Status | Meaning |
|---|---|
400 | Malformed JSON, an invalid id, or a from that is not an approved sender ID. |
401 | Missing, malformed, or revoked API key. |
404 | No such message on your account. |
422 | Your prepaid balance does not cover the send (bulk endpoint). |
Note that the two error bodies differ: POST /api/v1/sms and GET /api/v1/sms/{id} return
error.type, everything else returns error.code. See errors.
Next steps
- Send to many recipients at once → Bulk SMS
- Get delivery pushed to you → Webhooks
- Let an AI agent send from your account → AI agents
- Every endpoint, request, and response → API reference