Skip to main content
Version: 2026-05

Errors

This is the canonical reference for the &Open API error model. Every endpoint reports failures the same way, so once you handle errors here you handle them everywhere. Each endpoint's reference page lists the specific failures you can expect from that call and links back to this page for the shared contract.

The error model

The API uses conventional HTTP status codes to signal the broad outcome of a request:

  • 2xx: the request succeeded.
  • 4xx: the request failed because of something in the request: a missing field, an invalid value, an unknown resource, or a permission problem. The body tells you exactly what to fix.
  • 5xx: something failed on our side. These are rare.

Every non-2xx response also carries a structured JSON body so failures are machine-readable, not just a status code.

Response body

All error responses share one shape: a top-level errors array, always an array, even when there is a single error. Validation failures return every problem at once, so a client can surface them together rather than one round-trip at a time.

{
"errors": [
{
"type": "validation_error",
"code": "required",
"message": "is required",
"param": "recipient.email"
},
{
"type": "validation_error",
"code": "invalid_reference",
"message": "does not exist",
"param": "campaign_id"
}
]
}

Each object in the array describes one problem:

FieldTypeDescription
typestringThe error category, one of the closed set below.
codestringA machine-readable code scoped to the type, in snake_case. An open enum (see Error codes).
messagestringA human-readable explanation of this specific occurrence. For logs and developers, not end users.
paramstringThe request field that caused the error, in dot notation (for example recipient.email). Present only for field-level errors.

Every response, success or error, also carries a Request-Id header. Include it when contacting support; it lets us find the exact request in our logs.

Status codes and categories

Every error has an HTTP status code and a type. Each type maps to one or more statuses, shown below. The table is the full set of error statuses the API returns, plus the closed list of type values, both taken directly from the API contract:

StatusMeaning
400The request is malformed or violates a precondition. Typical triggers are a missing or unsupported `AndOpen-API-Version` header, a request body that is not valid JSON, or pagination parameters that fail server-side validation (`limit` non-numeric or non-positive, `after` referencing a record that does not exist).
401Authentication failed: missing or invalid bearer token.
403Authorized but not permitted to access this resource.
404The requested resource does not exist.
409An identical request under the same idempotency key is still being processed. Retry after the interval in the Retry-After header.
422Validation error: the request was parseable but contains invalid data. All validation errors are returned at once.
429Too many requests. Rate limit exceeded.
500Internal server error.
503Service unavailable. The account may be under maintenance. Check the `Retry-After` header.

Every error object carries a type from this closed set: validation_error, authentication_error, authorization_error, not_found_error, rate_limit_error, api_error.

type is a closed enum: it will not grow without a new API version, so it is safe to switch on exhaustively. code is open; see below.

Error codes

code is an open enum: it gives a specific, stable reason within the type, but new codes may be added under an existing type without a version bump. Match the codes you know; for any you don't, fall back to the type or message. Codes are always snake_case.

The codes below are the ones in use today, grouped by their type.

validation_error (400, 422)

The request was well-formed but a value failed validation. All validation problems for a request are returned together, each with the param it applies to.

A body that fails validation returns 422. A query parameter that fails validation returns 400, because the request is rejected before it reaches the resource. Both carry type: "validation_error", so branch on the status when you need to tell them apart. These are the 400 cases:

  • limit that is not a positive integer (invalid_format)
  • sort naming an unsupported field (invalid_format)
  • owner=me or sender=me sent under service authentication, which has no connecting user (invalid_format)
  • a malformed created_at range operator or timestamp (invalid_format)
  • an after cursor referencing no existing record (invalid_reference)

Each names the offending parameter in param. Only unsupported_api_version and malformed_request return 400 as an api_error.

The codes:

  • required: a required field was missing or empty.
  • invalid: the value is not acceptable for the field, in a way the more specific codes here don't cover.
  • invalid_format: the value is the right kind of thing but malformed. For example an email that isn't a valid address, or a malformed UUID.
  • invalid_reference: the value is a well-formed identifier, but no such record exists or it isn't reachable in this context. An unknown campaign_id on Create a gift request returns this, as does an after cursor that references no record.
  • not_unique: the value duplicates one that must be unique.
  • out_of_stock: a requested line item cannot be fulfilled because the variant has no available stock. The param points at the offending line item.
  • idempotency_key_mismatch: the Idempotency-Key on the request was used in the last 24 hours with a different body. See Create a gift request.
  • idempotency_conflict: a request sent without an Idempotency-Key reuses the identifying details of a recent request but carries a different body. Supply a key to make the two distinct.

authentication_error (401)

The request could not be authenticated. See Authentication for how to present a token, and Environments for the regional base URLs; a token is only valid in the region it was issued for.

  • unauthorized: the Authorization header is missing, or the token in it is unrecognized, expired, or presented to the wrong region. The API returns one code for all of these, so a 401 never tells an unauthenticated caller which of them it was.

authorization_error (403)

The request was authenticated, but the token is not allowed to perform it.

  • forbidden: your token isn't allowed to access this resource.
  • insufficient_scope: the token is valid but lacks the scope this operation requires.

not_found_error (404)

  • not_found: the resource named in the path does not exist, or is not visible to your account. The API does not distinguish "absent" from "not yours"; both return not_found.

rate_limit_error (429)

  • rate_limited: you have exceeded the rate limit. The response carries RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset headers, plus a Retry-After header telling you how long to wait before retrying.

api_error (400, 409, 500, 503)

A request-level problem that doesn't fit the categories above, or a fault on our side.

  • unsupported_api_version: the AndOpen-API-Version header is missing or not supported. Returned as a 400, before authentication.
  • malformed_request: the request could not be parsed at all, typically a body that is not valid JSON. Returned as a 400.
  • request_in_progress: an identical write is still being processed, so this one was neither run nor rejected. Returned as a 409; wait the number of seconds in the Retry-After header and send it again.
  • internal_error: an unexpected error on our side. Returned as a 500; rare and usually transient, so retry after a short delay.
  • service_unavailable: the service is temporarily unavailable. Returned as a 503; honor the Retry-After header before retrying.

Handling errors

Handle errors in this order:

  1. Check the status code. Retry 5xx, 429, and 409 unchanged, backing off using Retry-After where it is present. Change the request before you retry any other 4xx; it will fail the same way.
  2. Branch on type to handle the category.
  3. Refine on code where you need specific handling.
  4. Use param to attach validation messages to the right field in your UI.
  5. Log the Request-Id so a failure can be traced if you contact support.