Appearance
Read responses and errors
A single object
json
{ "data": { "id": "evt_01JB...", "object": "event", "livemode": false, "created_at": "...", "updated_at": "..." } }Every object has id, object (its type name), livemode, created_at and updated_at. Field names are snake_case and timestamps are ISO 8601 in UTC.
A list
json
{
"data": [ ],
"meta": { "pagination": { "limit": 20, "has_more": true, "next_cursor": "c_eyJ...", "prev_cursor": null } }
}Pass next_cursor back as cursor to get the next page. See pagination.
Money
Money is an object with an integer amount in minor units and an ISO 4217 currency. It is never a float.
json
{ "amount": 150000, "currency": "KES" }That is KES 1,500.00. Phone numbers are always E.164, for example +254712345678.
Response headers
| Header | Meaning |
|---|---|
Vabu-Request-Id | req_.... Quote it when you contact support. |
Vabu-Version | The API version that handled the request. |
RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset | Your rate limit state. |
Retry-After | On 429, seconds to wait. |
Idempotent-Replayed | true when the response is a stored replay. See idempotency. |
Errors
Every error response, including from middleware, has this shape.
json
{
"error": {
"type": "invalid_request_error",
"code": "ticket_type_sold_out",
"message": "VIP is sold out.",
"param": "items[0].ticket_type",
"details": { "items.0.quantity": ["Only 0 left."] },
"request_id": "req_01JB...",
"doc_url": "https://docs.vabu.live/errors#ticket_type_sold_out"
}
}Handle errors in this order:
- Branch on
error.code. Codes are stable. Messages are for people and can change. - Use
error.typefor a fallback.rate_limit_errorandapi_errorare worth retrying. The rest need a fix to the request. - Show
error.detailsnext to the matching form field.error.paramnames the field. - Log
error.request_id.
Read errors for the retry rules, and the error codes page for the list.