Pagination, errors and idempotency
How lists are paged, how errors look, how to retry a write safely and how money and dates are formatted.
Responses
A single object comes back as data:
{ "data": { "id": "01K9ZT6Q2X3M8N4V7B5C1D0EFG", "name": "Es Kopi Susu" } }A list comes back as data with paging fields:
{ "data": [], "has_more": false, "next_cursor": null }Pagination
Lists use cursors. Pass limit (1 to 200, default 50) and, for the next page, the next_cursor of the previous one:
curl "https://api.bakasir.com/v1/sales?limit=100&cursor=MDFLOVpUNlEyWDNNOE40VjdCNUMxRDBFRkc" \
-H "Authorization: Bearer bak_your_key_here"- Results are ordered from oldest to newest. Add
order=descfor newest first. - Keep going while
has_moreistrue. When it isfalse,next_cursorisnull. - A cursor is opaque. Do not build or edit one. A bad cursor returns
400with the codeinvalid_cursor. - New records created while you page appear at the end, so a full sync can simply continue until
has_moreisfalse.
Errors
Every error has the same shape. code is stable and meant for your program, error is a human readable sentence.
{ "error": "not found", "code": "not_found" }| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | The body or a query value is not valid |
| 400 | invalid_cursor | The cursor is not valid |
| 401 | invalid_api_key, revoked_api_key | See Authentication |
| 403 | insufficient_scope, plugin_inactive, tenant_inactive | See Authentication |
| 403 | subscription_read_only, subscription_suspended | The subscription is overdue, so writing or reading is blocked |
| 403 | email_verification_required | The owner has not verified the email address yet |
| 404 | not_found | The record does not exist or belongs to another business |
| 409 | conflict | The change clashes with existing data, for example a duplicate product code |
| 413 | payload_too_large | The body is larger than 1 MB |
| 422 | unknown_store, unknown_product | A referenced outlet or product does not exist |
| 422 | idempotency_key_reused | The Idempotency-Key was used with a different request |
| 429 | rate_limited | Too many requests |
| 5xx | server_error, unavailable | Something failed on our side. Retry with backoff |
Idempotency
Networks fail. To retry a write without creating it twice, send an Idempotency-Key header with any unique value up to 200 characters on POST, PATCH and DELETE requests:
curl https://api.bakasir.com/v1/customers \
-H "Authorization: Bearer bak_your_key_here" \
-H "Idempotency-Key: import-2026-11-04-row-18" \
-H "Content-Type: application/json" \
-d '{"name": "Sari Dewi", "phone": "081200000000"}'- The first response is stored for 24 hours. A retry with the same key and the same body returns it again, with the header
Idempotent-Replayed: true, and nothing is created twice. - The same key with a different request returns
422idempotency_key_reused. - A request that is still running returns
409idempotency_in_progress. Wait a moment and retry. - Server errors (5xx) are not stored, so you can retry them with the same key.
Money, quantities and dates
- Amounts such as
price,costandtotalare integers in the smallest unit of the business currency. For rupiah,22000means Rp22.000. For a currency with cents,1250means 12.50. - Quantities are numbers and may have decimals, for example
0.5kilogram. - Timestamps are RFC 3339 in UTC, for example
2026-11-04T08:30:00Z. In filters such asfromandtoyou can also send a date like2026-11-04, which is read in the business time zone. - IDs are text. Treat them as opaque.
Last updated on

