Conventions
What holds across the whole API: format, ids, dates, amounts, pagination, sorting, search, errors and rate limit.
Format
- Base address:
https://api.kivoo.africa/v1. - Requests and answers in JSON, UTF-8 encoded: send
Content-Type: application/jsonwith a body. - Calls are made from a server: the API sends no CORS header, a browser cannot call it.
Ids
Every resource has a UUID id, for instance 3a6c2f1e-8b4d-4c1a-9e2f-5d7b8a9c0e1f. Keep it as is: it goes into the addresses (/v1/products/{id}) and comes back in webhook events.
Dates
Every date is ISO 8601, in UTC: 2026-09-12T10:05:00.000Z. A missing date is null (an order not paid yet has no paidAt). In period filters, from is inclusive and to exclusive.
Amounts
Amounts are integers, in minor units of the currency given by the currency field (ISO 4217 code), which is the store’s. The CFA franc (XAF) has no decimals: 5000 is 5,000 FCFA. Never divide an amount by 100 without looking at the currency.
Pagination
Lists are paginated with two query parameters:
| Parameter | Values | Default |
|---|---|---|
page | 1 or more | 1 |
pageSize | 1 to 100 | 25 |
The answer gives the page (data) and what you need to walk the next ones:
{
"page": 2,
"pageSize": 25,
"total": 42,
"data": [{ "id": "3a6c2f1e-8b4d-4c1a-9e2f-5d7b8a9c0e1f", "title": "Guide du freelance" }]
}While page × pageSize < total, pages remain. A page past the last one returns data: [].
Sorting and search
- Lists are sorted newest first (
createdAtdescending). - The
qparameter searches case-insensitively: the title of products, the name, email and phone of customers. - Each list has its own filters (
status,type,from,to…), described in the reference.
One store, and that one only
A key only sees its store. A resource of another store does not exist for it: Kivoo answers 404 (for instance product_not_found), never 403.
Errors
A refused request gets a 4xx or 5xx HTTP status and this body:
{
"statusCode": 404,
"code": "product_not_found",
"message": "Product not found",
"requestId": "5b0f6a2c-6a0e-4f65-9d2b-2f3d1c0b9a77",
"timestamp": "2026-09-26T10:00:00.000Z"
}| Field | Content |
|---|---|
statusCode | The HTTP status, repeated. |
code | A stable, machine-readable code: branch your code on it. |
message | An explanation in English, which may change. |
details | Only for some errors: the missing scope (required), the list of refused fields for validation_failed, what publishing needs (missing). |
requestId | The id of the request: give it to support when you write to Kivoo. |
timestamp | When the error happened. |
The codes the API returns:
| Status | code |
|---|---|
| 400 | validation_failed (invalid parameter or body), and a few precise codes: compare_at_invalid, phone_invalid, customer_identifier_required, payment_link_expiry_invalid |
| 401 | api_key_missing, api_key_invalid, api_key_revoked, api_key_expired (see Authentication) |
| 403 | insufficient_scope, store_unavailable |
| 404 | product_not_found, order_not_found, customer_not_found, payment_link_not_found |
| 409 | customer_conflict: another customer of the store already has this email or phone |
| 422 | product_incomplete (details.missing says what publishing needs), product_type_unavailable, feature_disabled, payment_link_invalid_pricing, payment_link_expiry_past |
| 429 | rate_limited |
| 5xx | An error on Kivoo’s side: try again later, with a growing delay. |
The page of each operation, in the reference, lists the codes it may return.
Rate limit
Each key may make 120 requests per minute, all routes together. Two keys of the same store each have their own counter. Every answer carries:
| Header | Content |
|---|---|
X-RateLimit-Limit | The number of requests allowed per minute (120). |
X-RateLimit-Remaining | The requests left in the current minute. |
X-RateLimit-Reset | The seconds before the counter starts again. |
Beyond that, Kivoo answers 429 with the rate_limited code and the Retry-After header, in seconds: wait that long before trying again.
An IP address presenting more than 20 unknown keys per minute also gets 429 rate_limited, without its keys being checked: fix the key your program sends rather than retrying.