Errors, retries and rate limits

The error envelopes of both APIs side by side, which errors to retry, idempotency, rate limits and their headers.

The Channel API and the Store API refuse requests in different shapes, and the gateway in front of both has a third. This page puts them side by side and says which refusals a client retries.

Three error shapes

SourceShapeCodes
Gateway, before any API{ "error": "<CODE>", "message": "..." }Upper case: INVALID_REQUEST, INVALID_CREDENTIALS, TOKEN_INVALID, TOKEN_EXPIRED, TENANT_INACTIVE
Channel API{ "error": { "code", "message", "details", "requestId" } }Lower case: validation_failed, forbidden, state_conflict, ...
Store API{ "code", "message", "fieldErrors" }Upper case: VALIDATION, CONFLICT, APPROVAL_REQUIRED, ...

A Channel API refusal:

{
  "error": {
    "code": "validation_failed",
    "message": "Request validation failed",
    "details": [
      { "path": "items[0].quantity", "code": "minimum", "message": "must be >= 1" }
    ],
    "requestId": "2f408915-2608-4f0e-90a7-2a7abd34f487"
  }
}

A Store API refusal:

{
  "code": "CONFLICT",
  "message": "Your cart changed. Review the current cart.",
  "fieldErrors": {}
}
  • Channel API details names each field at fault as { path, code, message }; requestId equals the X-Request-Id response header.
  • Store API message is written for the buyer and can be shown as is; fieldErrors is keyed by field on sign-up and password change.
  • Branch on the code, never on the message text.

Codes side by side

SituationChannel APIStore API
A field is missing, malformed or unknown400 validation_failed400 VALIDATION
No usable identity401 unauthenticated401 UNAUTHORIZED
The key lacks the scope, or the tenant does not match403 forbidden403 FORBIDDEN
Nothing at that id404 not_found404 NOT_FOUND, 404 PRODUCT_NOT_FOUND
A stale version or revision409 version_conflict409 CONFLICT
The document already exists409 duplicate (with onDuplicate: error)409 CONFLICT (a duplicate account on sign-up)
The state forbids the action409 state_conflict, details[0].code names the case409 CHECKOUT_PENDING, PAYMENT_PENDING, QUOTE_INCOMPLETE, QUOTE_PENDING, CART_CHOICE_REQUIRED
A business rule refuses it422 totals_mismatch, inventory_unavailable, unresolved_reference, inactive_customer403 APPROVAL_REQUIRED, CONFIRMATION_REQUIRED, CHECKOUT_DISABLED; 409 PRODUCT_VARIANT_REQUIRED
An idempotency key reused with another body422 idempotency_key_reused409 CONFLICT (a checkout key reused for another quote)
Too many requests429 rate_limited429 RATE_LIMITED
A dependency or the endpoint is briefly unavailable503 dependency_unavailable, 503 route_disabled503 UNAVAILABLE
Unexpected failure500 internal

Full lists: Channel API errors and Store API errors.

What to retry

AnswerRetryHow
429YesWait the seconds in Retry-After.
503YesExponential backoff with jitter. On the Channel API keep the same Idempotency-Key. A Store API write that answered 503 was not applied unless a later read shows it.
409 in_progress (Channel API)YesAfter Retry-After, with the same key.
500 internal (Channel API)Once or twiceWith the same Idempotency-Key: a failed request is not stored, so the retry runs again.
A timeout or network error on a writeYesWith the same Idempotency-Key (Channel API) or idempotencyKey (Store API). If the first attempt landed, its answer replays.
401 TOKEN_EXPIRED, or a Store API 401 on an expired buyer tokenAfter a refreshRefresh the token, then retry once.
409 version_conflict, 409 CONFLICTAfter a re-readRead the document or the cart again, re-apply the change.
409 QUOTE_PENDINGYesRepeat cart.get { "quote": true }.
409 CHECKOUT_PENDING, 409 PAYMENT_PENDINGPollcheckout.status or payments.status; never start a second checkout.
Other 400, 403, 404, 422NoFix the request: the same request gets the same answer.

Idempotency

Channel API. Every POST, PUT, PATCH and DELETE takes an Idempotency-Key header: 7 to 50 characters that name the request on your side (an order number, a webhook delivery id).

RetryAnswer
Same key, same bodyThe stored first answer, with Idempotent-Replayed: true. Answers are kept 24 hours.
Same key, different body422 idempotency_key_reused.
Same key while the first request runs409 in_progress with Retry-After.

Keys are scoped per tenant and per endpoint, and a failed request is not stored. Beyond the 24 hours, onDuplicate: update (the default on creates) makes a replayed document an update by externalId, so a full feed can be sent again safely.

Store API. Checkout, cart.addMany, cart claims, invoice payments, saved payment methods, subscriptions and applications take idempotencyKey (or requestId) in the body: 7 to 50 characters from A-Z a-z 0-9 _ -. A retry with the same key answers the first attempt. A lost checkout.start answer is found again with checkout.status { idempotencyKey, quoteFingerprint }.

Kentro Pixel. Events are deduplicated on event_id per property for at least 14 days; server requests may add X-Kentro-Idempotency-Key (up to 128 characters). See Pixel server API.

Rate limits

Channel API

  • Per tenant and per minute: 1,200 writes (POST, PUT, PATCH, DELETE) and 6,000 reads (GET) by default. Kentro sets higher limits per tenant.
  • Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (seconds until the window resets). A refusal is 429 rate_limited with Retry-After.
  • POST /v1/batch counts every operation it carries.
  • Bulk sizes: POST /v1/orders/import and POST /v1/products/import take up to 1,000 documents (answered 202, then GET /v1/operations/:id); POST /v1/batch takes up to 100 operations; POST /v1/fulfillment-queue/ship up to 100 entries; POST /v1/inventory/history up to 500 entries.

Store API

  • catalog.suggest (the search typeahead): 30 requests per 3 seconds per buyer, per guest, or per X-Storefront-Client-Ip for signed-out visitors.
  • application.start: 60 per minute; other application.* operations: 120 per minute.
  • session.signup and account.confirm, buyer sign-in and password reset are limited per account and per source address. Send X-Storefront-Client-Ip so the limits key on the buyer, not on the storefront server.
  • The gateway applies per-key limits as well. Bodies are limited to 64 KB, application uploads excepted.

Kentro Pixel

  • 600 events per minute per public key (bursts up to 1,200) and 6,000 per server key (bursts up to 12,000); 100 events per browser request, 500 per server request. See Limits, errors and troubleshooting.

Staying under the limits

  • Read X-RateLimit-Remaining and slow down before it reaches zero.
  • Load history through the import endpoints, not one write at a time.
  • Poll with updatedSince and a sensible interval instead of re-reading whole lists.
  • Queue writes behind a single worker per tenant, so a burst from the source system becomes a steady stream.

Did this page help you?