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=desc for newest first.
  • Keep going while has_more is true. When it is false, next_cursor is null.
  • A cursor is opaque. Do not build or edit one. A bad cursor returns 400 with the code invalid_cursor.
  • New records created while you page appear at the end, so a full sync can simply continue until has_more is false.

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" }
StatusCodeMeaning
400invalid_requestThe body or a query value is not valid
400invalid_cursorThe cursor is not valid
401invalid_api_key, revoked_api_keySee Authentication
403insufficient_scope, plugin_inactive, tenant_inactiveSee Authentication
403subscription_read_only, subscription_suspendedThe subscription is overdue, so writing or reading is blocked
403email_verification_requiredThe owner has not verified the email address yet
404not_foundThe record does not exist or belongs to another business
409conflictThe change clashes with existing data, for example a duplicate product code
413payload_too_largeThe body is larger than 1 MB
422unknown_store, unknown_productA referenced outlet or product does not exist
422idempotency_key_reusedThe Idempotency-Key was used with a different request
429rate_limitedToo many requests
5xxserver_error, unavailableSomething 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 422 idempotency_key_reused.
  • A request that is still running returns 409 idempotency_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, cost and total are integers in the smallest unit of the business currency. For rupiah, 22000 means Rp22.000. For a currency with cents, 1250 means 12.50.
  • Quantities are numbers and may have decimals, for example 0.5 kilogram.
  • Timestamps are RFC 3339 in UTC, for example 2026-11-04T08:30:00Z. In filters such as from and to you can also send a date like 2026-11-04, which is read in the business time zone.
  • IDs are text. Treat them as opaque.
How is this guide?

Last updated on