SendaiDocs

API reference

View as Markdown

API overview

Base URLs, authentication, the two response conventions, error shapes, and how billing and delivery actually work — read this before writing a client.

The Sendai API is plain JSON over HTTPS, authenticated with an API key. This page covers the conventions that apply to every endpoint; each operation then has its own page with the exact request and response.

Base URLs

HostUse
https://api.sendai.co.zwProduction
https://staging-api.sendai.co.zwStaging — a build target for integration work

All paths in this reference are relative to the production host.

Authentication

One mechanism: an API key as a bearer token on every request.

Authorization: Bearer sk_live_4c8e…

Create keys in the dashboard under Settings → Developer, or with POST /api/v1/api-tokens. A key is valid until you revoke it. Full detail, including zero-downtime rotation, is in Authentication.

Two response conventions

This is the one thing to get right before you write a client. POST /api/v1/sms returns the message object bare, with no wrapper:

{
  "id": "0f9c1b3a-6d2e-4a1b-9c3d-7e5f2a8b4c10",
  "to": "263771000000",
  "status": "enqueued",
  "delivered_at": null
}

Every other endpoint wraps its payload in an envelope, with the payload under data:

{
  "status": "success",
  "message": "request successful",
  "data": { }
}

So GET /api/v1/sms/{id} gives you data.status, while POST /api/v1/sms gives you status at the top level. Unwrapping .data on a send returns undefined.

Errors

Branch on the HTTP status code — the error body comes in two shapes depending on the endpoint. The two SMS read/write endpoints (POST /api/v1/sms, GET /api/v1/sms/{id}) return an error.type:

{
  "status": "",
  "message": "not found",
  "error": { "type": "err_not_found", "description": "the resource does not exist" }
}

…while bulk sends, the key endpoints, and every authentication failure return an error.code:

{
  "status": "error",
  "message": "missing authentication",
  "error": { "code": "UNAUTHORIZED", "description": "missing authentication" }
}
StatusMeaning
400Malformed body, invalid id, or a from that is not an approved sender ID. Validation failures list fields in error.validation_errors.
401Missing, malformed, or revoked API key.
404No such resource on your account.
422insufficient balance — your prepaid balance does not cover the send (documented on the bulk operation).

Sender IDs

The from on a message is a sender ID — the name the recipient sees. Sender IDs are approved before use, so you cannot invent one at send time; an unapproved value is rejected with 400 unknown or unapproved sender id. Yours are captured at registration and approved on the network-operator side, usually in about 30 minutes. Manage them in the dashboard, or list them over the API with Sender IDs.

Billing and segments

Accounts are prepaid. Each send places a hold on your balance, and a send that your balance will not cover is rejected outright — never half-delivered. Check what you hold with Balances.

SMS is billed per segment, not per message:

EncodingWhenSingle segmentPer segment when concatenated
GSM-7Every character is in the GSM-7 alphabet160 chars153 chars
UCS-2Any character outside GSM-7 (an emoji, a curly quote, an accent)70 chars67 chars

One stray character switches the whole message to UCS-2 and can more than double its cost.

The API returns the amount held as charge, in ten-thousandths of charge_currency (10000 == 1.00, so 450 == 0.045). Note the type differs by endpoint: POST /api/v1/sms returns charge as a string ("450"), while POST /api/v1/sms/bulk returns it as an integer (450). Coerce on the way in.

Knowing whether it arrived

A 202 means Sendai accepted and queued your message — not that it arrived. Two fields track the journey:

  • statuscreatedenqueuedprocessingsuccess, or failed.
  • delivered_atnull until the carrier confirms delivery, then a timestamp.

status: success means the send pipeline completed. Confirmed handset delivery is delivered_at being non-null — that is the only delivery signal the API exposes. Carrier confirmation is asynchronous and can take seconds to hours, so poll GET /api/v1/sms/{id}, or register a webhook and let Sendai call you.

The resources

The reference is organised by resource — one page each, with the object first and one section per operation:

OperationCall
Send an SMSPOST /api/v1/sms
Retrieve an SMSGET /api/v1/sms/{id}
Send bulk SMSPOST /api/v1/sms/bulk
API tokensPOST / GET / DELETE /api/v1/api-tokens
BalancesGET /api/v1/accounts/{id}/balances
Sender IDsGET /api/v1/accounts/{id}/channel-identities

Prefer a machine-readable contract? Download the OpenAPI document — the same operations, ready for codegen and tooling.