API reference
API tokens
Create, list, and revoke the API keys that authenticate every request. The key secret is returned once, at creation, and never again.
API keys are the only credential on the API. Create your first key in the dashboard under Settings → Developer; once you hold one, you can manage keys over the API itself. For rotation runbooks and safe handling, see Authentication.
There is no single canonical token object: the create response is a snake_case object carrying the secret once, while the list response serialises the internal record with PascalCase field names. Each shape is documented with its operation below rather than pretended into one.
Create an API key
/api/v1/api-tokensCreates a key. data.token is the secret and is returned only here — Sendai stores a
hash, so a lost key cannot be recovered; revoke it and create another.
Request body
namestringrequiredIdentifies the key. Name it for the service that will use it — you will be glad of it when you come to rotate.
Response
data.id is what you pass to the revoke endpoint — keep it alongside the key. account_id
is the account the key authenticates as; the account-scoped
Balances and Sender IDs endpoints take it in their path.
Errors
| Status | When |
|---|---|
400 | name is missing — listed in error.validation_errors. |
401 | Missing, malformed, or revoked API key. |
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" }'
{
"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"
}
}
List API keys
/api/v1/api-tokensLists the keys on your account. This response is not normalised: it serialises the internal record directly, so field names are PascalCase rather than snake_case and include storage columns.
Read ID, Name, Active, CreatedAt and LastUsedAt; treat everything else (such as
TokenHash) as unstable and do not build against it. Key material is never returned.
LastUsedAt is the useful one for cleanup: null means never used, and a key untouched for
months is usually one you can revoke.
Errors
| Status | When |
|---|---|
401 | Missing, malformed, or revoked API key. |
curl https://api.sendai.co.zw/api/v1/api-tokens \
-H "Authorization: Bearer $SENDAI_API_KEY"
{
"status": "success",
"message": "",
"data": [
{
"ID": "9f2a4c8e-6b21-4d0f-a97b-5e8c1d2f6a3b",
"CreatedAt": "2026-07-06T09:12:03Z",
"UpdatedAt": null,
"DeletedAt": null,
"Name": "orders-service",
"TokenHash": "9b74c9897bac770ffc029102a200c5de0d3d2a2f4b2b6c9a2f0b4b2c1d8e3f7a",
"AccountId": "2d6f4b8e-1a90-4e21-8b7d-3f1a5c2e9a0c",
"Active": true,
"LastUsedAt": "2026-08-06T11:04:19Z"
}
]
}
Revoke an API key
/api/v1/api-tokens/{id}Revokes immediately — the next request using that key gets a 401.
Path parameters
idstring (uuid)requiredThe key'sID, from the create or list response.
Response
Note that revoking an id that does not exist does not return 404 — treat a 200 as
"this key is now not valid" rather than as proof the key existed.
Errors
| Status | When |
|---|---|
400 | id is not a valid UUID. |
401 | Missing, malformed, or revoked API key. |
curl -X DELETE https://api.sendai.co.zw/api/v1/api-tokens/9f2a4c8e-6b21-4d0f-a97b-5e8c1d2f6a3b \
-H "Authorization: Bearer $SENDAI_API_KEY"
{ "status": "success", "message": "api token revoked" }