Skip to content

Errors

Every failed request returns a non-2xx status and a body in one shape:

{
"error": {
"code": "store_closed",
"message": "This store is currently closed.",
"request_id": "req_9f2c1a7b3d4e",
"details": { }
}
}
  • code is stable and meant for your switch statements. Never match on message — the wording can improve.
  • message is safe to log and, for business-rule errors, to show to a customer.
  • request_id is also in the X-Request-Id response header. Quote it when you contact support.
  • details appears when there is more to say, for example errors[] for validation failures.
HTTPMeaningRetry?
200 / 201 / 202Success / created / accepted for later processing—
400The request is malformed or fails validationNo — fix the request
401Missing or invalid keyNo
403Key lacks a scope, or endpoint not available to this keyNo
404Resource does not exist or is not yoursNo
409Conflict with the current stateNo — read the order first
413Body larger than 256 KBNo
422Valid request, but a business rule refuses itNo — tell the customer
429Rate limitedYes — after Retry-After
500 / 502Something failed on our sideYes — with back-off
CodeHTTPWhen
missing_api_key401No Authorization header
invalid_api_key401Unknown, expired or revoked key, or suspended account
insufficient_scope403The key lacks the required scope (details.required_scope)
sandbox_only403Sandbox endpoint called with a live key
CodeHTTPWhen
validation_error400A parameter or the body is invalid. details.errors lists each field and message
invalid_json400The body is not valid JSON
payload_too_large413Body over 256 KB
not_found404Unknown id or route — or a resource that belongs to another account
rate_limited429Over your per-minute limit

Orders (business rules, 422 unless stated)

Section titled “Orders (business rules, 422 unless stated)”
CodeWhenWhat to show
store_closedThe store is closed, paused or busy“This restaurant isn’t taking orders right now”
out_of_zoneThe address is outside the delivery area“We don’t deliver to this address”
below_minimum_orderBasket is below the store’s minimumThe minimum from delivery.min_order
item_unavailableAn item is sold out, hidden or not sold by this storeRefresh the menu
address_incompletefloor, apartment or area missing (details.missing_fields)Ask for them — the rider needs them
fulfilment_not_supportedThe store does not offer delivery / pickupHide that option
cash_not_allowedCash is not enabled for your accountOffer card
idempotency_conflict (409)Same key, different bodyA bug in your key generation
order_not_cancellable (409)The kitchen has started or the order is finishedContact the restaurant
invalid_payment_method (409)Payment link requested for a non-card order—
already_paid (409)Order is already paid—
payment_not_possibleThe order cannot be paid (for example cancelled)—
order_rejectedOther order-level refusal; read messageShow the message
CodeHTTPWhen
invalid_url422Webhook URL is not https, not public, or does not resolve
webhook_not_configured422Test or rotate requested before a URL is set
order_not_advanceable409Sandbox order is already in a final state
CodeHTTPWhen
upstream_error502A downstream system (restaurant network, payments) failed — retry with the same idempotency key
internal_error500Unexpected failure on our side — retry; if it persists, send us the request_id
const body = await res.json();
if (!res.ok) {
switch (body.error.code) {
case 'store_closed':
case 'out_of_zone':
case 'below_minimum_order':
case 'item_unavailable':
return showToCustomer(body.error.message); // expected, user-facing
case 'rate_limited':
case 'upstream_error':
case 'internal_error':
return retryLater(res.headers.get('Retry-After')); // transient
default:
log.error({ code: body.error.code, requestId: body.error.request_id });
throw new Error('Unexpected API error'); // a bug on our side or yours
}
}