Skip to content
Elara One docs

Responses and errors

The success and error envelopes, every error code with its HTTP status and what to do, the shape of validation errors, and which errors to retry.

Every response from the API has the same outer shape, and every error has a stable code you can branch on. Branch on error.code, not on the HTTP status or the message: several codes share a status, and messages can be reworded.

Success

A successful response wraps its result in data:

200 OK
{ "data": { "id": 101, "name": "Oak desk lamp", "slug": "oak-desk-lamp" } }

Lists add links and meta for pagination. Some responses add a meta object with extra facts, such as meta.order_access_token after a guest checkout or meta.shipping_option_cleared after an address change.

  • Times are ISO 8601, in UTC.
  • Money is always a money object, never a bare number.
  • New fields can appear at any time, so ignore fields you do not know. See versioning.

Errors

Every error uses one envelope:

422 Unprocessable Content
{
  "error": {
    "code": "COUPON_NOT_APPLICABLE",
    "message": "Spend £20.00 more to use this code.",
    "details": {
      "code": ["Spend £20.00 more to use this code."],
      "reason": "threshold",
      "remaining": { "amount": 2000, "currency": "GBP", "formatted": "£20.00" }
    }
  }
}
FieldMeaning
codeA stable, upper-case code. Always present.
messageA sentence that names the problem and, where it can, the fix. Safe to log. Present on almost every error; a few store-identification errors carry only the code.
detailsExtra facts for that code, when there are any.

Every response also carries an X-Request-Id header. Log it, and quote it if you contact support about a request.

Validation errors

422 VALIDATION_FAILED means the request was understood but some input was refused. details maps each refused field to a list of messages. Nested fields use dots:

422 Unprocessable Content
{
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "The billing.postcode field is required. (and 1 more error)",
    "details": {
      "billing.postcode": ["The billing.postcode field is required."],
      "billing.country_id": ["The billing.country_id field is required."]
    }
  }
}

Show each message next to its field. A few business checks use VALIDATION_FAILED too, under a key that is not a request field: for example, an item that has run out of stock at checkout is reported under cart.

Error codes

Store and access

StatuscodeMeaningWhat to do
400TENANT_KEY_MISSINGNo X-Tenant-Key headerSend the store key on every call
404TENANT_NOT_FOUNDUnknown key, a suspended store, or a rotated key after its overlapUse the store's current key
402TENANT_PAYMENT_REQUIREDThe store is temporarily unavailableShow a "shop temporarily unavailable" page. Do not retry the request.
403ORIGIN_NOT_ALLOWEDA browser call from a website the store has not registeredRegister the website, or call from your server. See stores and keys.
403BROWSER_CALLS_DISABLEDA browser call while browser calls are switched offCall from your server with the secret
403TENANT_SECRET_REQUIREDA server call without X-Tenant-SecretSend the secret on every server call
403TENANT_SECRET_INVALIDA wrong secret, or a secret-only route called without itUse the current secret from Settings → Storefront API
401UNAUTHENTICATEDA missing, expired or revoked bearer tokenSign the customer in again, or check the merchant API key
403FORBIDDENThe token is valid but not allowed to do this, for example a merchant API key without the abilityUse a key with the right ability
404FEATURE_NOT_ENABLEDThe store's plan does not include this feature, such as the shopHide the feature for this store
404MODULE_NOT_ENABLEDA module, such as live chat, is not switched on for the storeHide the feature for this store

Requests

StatuscodeMeaningWhat to do
422VALIDATION_FAILEDSome input was refusedShow details next to the fields
404NOT_FOUNDThe resource does not exist, or is not visible to this callerCheck the id, slug or reference
400 and othersHTTP_ERRORA request the API could not accept, such as an unknown filter, sort or include, or the wrong HTTP method; it keeps its real statusFix the request
409IDEMPOTENCY_IN_PROGRESSA request with this Idempotency-Key is still runningRetry shortly with the same key
422IDEMPOTENCY_KEY_REUSEDThis key was already used for a different requestMake a new key for a new action. See idempotency.
429RATE_LIMITEDToo many requestsWait for Retry-After. See rate limits.
429STAGING_LIMITA staging store (where your plan includes them) has used its daily allowanceWait for Retry-After, the end of the UTC day
500SERVER_ERRORSomething went wrong on our sideRetry with backoff. Quote X-Request-Id if it persists.
503CAPTCHA_UNAVAILABLEA form's spam check could not get an answerRetry the submission with the same Idempotency-Key

Carts

StatuscodeMeaningWhat to do
404CART_NOT_FOUNDThe cart token is unknown, expired, or its cart became an orderCreate a new cart
404CART_LINE_NOT_FOUNDThe line id is not in this cartRefresh the cart
422COUPON_INVALIDNo such codeShow message
422COUPON_EXPIREDThe code has not started or has ended (details.reason)Show message
422COUPON_USED_UPThe code has reached its usage limitShow message
422COUPON_LIMIT_REACHEDThis shopper has used the code as often as allowedShow message
422COUPON_NOT_APPLICABLENothing in the basket qualifies (details.reason, and details.remaining for a spend threshold)Show message
422GIFT_LINE_LOCKEDA free gift's quantity cannot be changedLet the shopper remove it instead
422ADDRESS_NOT_FOUNDA saved address_id that is not the customer's, or sent without signing in (details.reason is not_found or sign_in_required)Refresh the address book, or ask the shopper to sign in or type the address
422SHIPPING_ADDRESS_REQUIREDShipping options were asked for before a shipping address was setSend the shopper to the address step

Checkout and payments

StatuscodeMeaningWhat to do
402PAYMENT_FAILEDThe payment was declined or not taken; details.decline_code says why on a provider declineSee payments
409SHIPPING_OPTION_CHANGEDThe chosen shipping option is gone or its price changedShow details.options and let the shopper choose again
409PROMOTIONS_CHANGEDThe basket's discounts changed since payment was set upShow the new total from details.cart and set up payment again
409PAYMENT_REFUNDEDThe payment was taken but the order could not be placed, so it was refundedStart a new checkout
409PAYMENT_REFUND_PENDINGAs above, with the refund still being retriedDo not ask the shopper to pay again for this cart
409PAYMENT_ALREADY_USEDThe payment already paid for another checkoutSet up a new payment
409PAYMENT_SETUP_IN_PROGRESSAnother request is setting up this cart's PaymentIntentRetry shortly
422PAYMENT_GATEWAY_UNAVAILABLEThe gateway is unknown or not set up for this storeOnly offer gateways from checkout/config
403ORDER_ACCESS_TOKEN_INVALIDA guest order token is missing, tampered with or expired (details.reason is invalid or expired)Ask for the order reference and email again

Accounts, content and CRM

StatuscodeMeaningWhat to do
409ACCOUNT_SHAREDThe account shares its customer record, so only the store can erase itAsk the shopper to contact the store
403PREVIEW_TOKEN_INVALIDA page preview link is invalid or expired (details.reason)Ask the editor for a new preview link
422CONSENT_CONFIRMATION_INVALIDA marketing consent confirmation link or code could not be confirmedAsk the person to request a new confirmation
404CONVERSATION_NOT_FOUNDThe chat token does not match a conversationStart a new conversation
409CONTACT_EXISTSA CRM contact with this email already exists; details.id names itUpdate that contact instead
409ORDER_TRANSITION_NOT_ALLOWEDAn order status change the order's current state does not allowCheck the order's state first
409REFUND_NOT_ALLOWEDA refund the order's payments do not allowCheck the amount and the order's payments

Which errors to retry

RetryErrors
Yes, after a short wait, with the same Idempotency-Key500 SERVER_ERROR, network errors and timeouts, 409 IDEMPOTENCY_IN_PROGRESS, 409 PAYMENT_SETUP_IN_PROGRESS, 503 CAPTCHA_UNAVAILABLE
Yes, after Retry-After429 RATE_LIMITED, 429 STAGING_LIMIT
Only after the shopper changes something402 PAYMENT_FAILED, the 409 checkout conflicts, 422 errors
No400, 401, 403 and 404 errors: the same request gets the same answer

Use exponential backoff with jitter for automatic retries, and stop after a few attempts.

Branch on error.code
const res = await fetch(url, init)

if (!res.ok) {
  const { error } = await res.json()
  switch (error.code) {
    case 'VALIDATION_FAILED':
      return showFieldErrors(error.details)
    case 'CART_NOT_FOUND':
      return startNewCart()
    case 'RATE_LIMITED':
      return retryAfter(Number(res.headers.get('Retry-After') ?? 1))
    default:
      console.error(error.code, error.message, res.headers.get('X-Request-Id'))
      return showGenericError()
  }
}