Errors
Every error answers the same JSON shape with a fitting HTTP status:
{
"error": {
"code": "not_enough_balance",
"message": "The card doesn't have enough for this reward.",
"request_id": "5f0c2a1e-8b3d-4c7a-9e21-6d4b8a0c3f19"
}
}- Branch on
code, never onmessage. Messages are English and may be reworded. detailslists field problems forinvalid_request:[{ "path": "body.stamps", "message": "…" }]. Paths start withbody.,query.,path.orheader.- Quote
request_id(also in theX-Request-Idheader of every answer) when you contact support. - New codes may be added within v1 (see Versioning): treat an unknown code by its HTTP status.
- A
422loyalty answer such asbelow_min_billornot_enough_balanceis a normal outcome to show the cashier, not a bug.
All codes
| Code | Status | Message | What to do |
|---|---|---|---|
unauthorized | 401 | The API key is missing, invalid or revoked. | Check the key; it may have been revoked. |
forbidden | 403 | This API key is limited to another branch. | This key is limited to one branch. Use its own branch, or a key for all branches (creating, changing or deleting menu categories and items, and managing webhook endpoints, always needs one). If you sent an Idempotency-Key, this answer is stored with it for 24 hours: once something has changed, send the request with a new key. (Creating a webhook endpoint, rotating its secret and sending a test event ignore the key: nothing is stored for them.) |
insufficient_scope | 403 | This API key doesn't have the permission for this request. | Ask the owner for a key with the permission named in details.required. |
api_disabled | 403 | API access is turned off for this business. | API access is turned off for this business. Ask the business owner; don't retry. |
business_suspended | 403 | This business is suspended; only reads are allowed. | Only reads work while the business is suspended. Don't retry writes. |
invalid_request | 400 | The request is not valid. | Fix the fields listed in details, then send the request again. |
not_found | 404 | Not found. | Check the id. It may have been deleted, or belong to another business or (for a branch key) another branch. If you sent an Idempotency-Key, this answer is stored with it for 24 hours: once something has changed, send the request with a new key. |
conflict | 409 | A conflicting change happened at the same time. Try again. | Something changed at the same moment. Retry; the same Idempotency-Key is fine. For a PATCH, read the item first. With details.reason "limit_reached" (creating an 11th webhook endpoint), retrying won't help: delete an endpoint first. |
idempotency_key_required | 400 | This request needs an Idempotency-Key header. | Send an Idempotency-Key header (your receipt number or a UUID). |
idempotency_conflict | 409 | This Idempotency-Key was already used for a different request. | Use a new Idempotency-Key for a different request. |
idempotency_in_progress | 409 | A request with this Idempotency-Key is still running. | Retry in a second with the same key. |
rate_limited | 429 | Too many requests. | Wait Retry-After seconds. |
server_error | 500 | Something went wrong on our side. | Retry with the same Idempotency-Key; contact support with the request_id if it persists. |
no_program | 422 | This business has no active loyalty program. | The business has no active loyalty program. Ask the owner to set one up. If you sent an Idempotency-Key, this answer is stored with it for 24 hours: once something has changed, send the request with a new key. |
below_min_bill | 422 | The bill is below the program's minimum. | Normal: the bill is under the program's minimum (card.program.min_bill). Tell the cashier; no points this time. If you sent an Idempotency-Key, this answer is stored with it for 24 hours: once something has changed, send the request with a new key. |
card_cap_reached | 422 | More stamps than the program allows per visit. | Send fewer stamps: at most card.program.max_stamps_per_visit per visit. If you sent an Idempotency-Key, this answer is stored with it for 24 hours: once something has changed, send the request with a new key. |
reward_unavailable | 422 | That reward is not available. | Pick a reward_id from the card's rewards. If you sent an Idempotency-Key, this answer is stored with it for 24 hours: once something has changed, send the request with a new key. |
not_enough_balance | 422 | The card doesn't have enough for this reward. | Normal: the card can't afford this reward yet. Show the balance to the cashier. If you sent an Idempotency-Key, this answer is stored with it for 24 hours: once something has changed, send the request with a new key. |
already_voided | 409 | This event was already voided. | Already undone. If you were retrying a void, treat this as success. |
program_closed | 409 | This event belongs to a closed program. | The event belongs to an older program and can't be undone through the API. The owner can adjust the card in the dashboard. |
would_go_negative | 409 | Voiding this would take the card below zero. | The customer already used these stamps or points. The owner can adjust the card in the dashboard. |
not_voidable | 409 | This event can't be voided. | This event came from a program change and can't be undone. |
order_not_actionable | 409 | The order can't take this action in its current status. | Read the order: its status doesn't allow this action (for example serving a rejected order). If you sent an Idempotency-Key, this answer is stored with it for 24 hours: once something has changed, send the request with a new key. |
payment_required | 409 | This business wants counter orders paid first: mark the order paid, then repeat the action. | The business wants counter orders paid first — before serving, or before preparing (then start is refused too). Send mark_paid with a method, then repeat the action. If you sent an Idempotency-Key, this answer is stored with it for 24 hours: once something has changed, send the request with a new key. |
already_paid | 409 | The order is already paid. | Nothing to do: the order is already paid. |
category_not_empty | 409 | Move or delete the category's items first. | Move or delete the category's items first. |
external_id_taken | 409 | Another item already uses this external_id. | Another category (for a category) or another item (for an item) already has this external_id; a category and an item may share one. Update that one, or use PUT …/by-external-id/{code}. |
bad_category | 422 | The category doesn't belong to this business. | Use a category_id from GET /menu. |
endpoint_inactive | 409 | The webhook endpoint is turned off; turn it on first. | The webhook endpoint is turned off. Turn it on (PATCH with status "active"), then send the test event again. |