SendaiDocs

Guides

View as Markdown

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:

RequestPOST /api/v1/api-tokens
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.

Response
201 Created
{
  "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.

RequestPOST /api/v1/sms
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.)

Response
202 Accepted
{
  "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/sms returns the message object as the whole body — there is no data to 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:

RequestGET /api/v1/sms/{id}
curl https://api.sendai.co.zw/api/v1/sms/0f9c1b3a-6d2e-4a1b-9c3d-7e5f2a8b4c10 \
  -H "Authorization: Bearer $SENDAI_API_KEY"
Response
200 OK
{
  "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 createdenqueuedprocessingsuccess (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:

StatusMeaning
400Malformed JSON, an invalid id, or a from that is not an approved sender ID.
401Missing, malformed, or revoked API key.
404No such message on your account.
422Your 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