Queek docs

Versioning & errors

What v1 promises, how a change is announced, and the shape every failure answers in.

One version, and it only grows

There is a single major version, v1, in the path:

https://client.usequeek.com/v1

Inside v1:

  • fields and endpoints may be added;
  • nothing is removed, renamed, retyped, or given a new meaning;
  • enum values may be added, and your code must tolerate values it does not know.

A breaking change would ship as v2 beside v1, never in place. So the integration you write today keeps working: parse defensively, ignore unknown fields, and do not switch exhaustively on a status string without a default branch.

The same promise covers webhook payloads. Every webhook delivery states the contract it was built against in X-Queek-Api-Version.

How a retirement would be announced

Nothing in v1 is deprecated today. If something ever is, it is announced in the Changelog and on the responses themselves, at least nine months before it stops working:

HeaderMeaning
DeprecationRFC 9745 — when the endpoint was marked deprecated.
SunsetRFC 8594 — the date it stops answering.
Link: rel="deprecation"Where to read what replaces it.

Log these headers if you can. They are the earliest warning you will get.

Errors

A failure answers with one envelope, plus the legacy top-level keys older clients read:

{
  "status": "failed",
  "error_code": "invalid_client_key",
  "message": "This key is unknown or has been turned off. Check your connection in Headless API.",
  "data": null,
  "error": {
    "code": "invalid_client_key",
    "message": "This key is unknown or has been turned off. Check your connection in Headless API.",
    "doc_url": "https://docs.usequeek.com/docs/versioning-and-errors#invalid_client_key",
    "request_id": "5d1c9b3e-8f2a-4b6d-9c07-3e1f8a2b6d45"
  }
}

Branch on error.code, never on message. The code is stable — a new failure mode gets a new code, and an existing code never changes meaning — while the message is written for a human and may be reworded. error_code at the top level mirrors error.code for clients that predate the envelope.

error keyAlwaysMeaning
codeyesOne of the codes below.
messageyesHuman-readable. Not stable.
doc_urlyesDeep link to the code's row on this page.
request_idyesThe X-Request-Id of the call — quote it in support.
fieldvalidation onlyThe first offending field.
errorsvalidation onlyPer-field messages, { field: [message, …] }.

Some codes carry extra keys beside these that describe the repair in data — a metafield error names the handle and expected_type, for instance. They are listed with the code.

Validation failures answer 422 validation_failed with the offending fields:

{
  "status": "failed",
  "message": "The email field is required.",
  "error_type": "validation_error",
  "errors": { "email": ["The email field is required."] },
  "data": null,
  "error": {
    "code": "validation_failed",
    "message": "The email field is required.",
    "field": "email",
    "errors": { "email": ["The email field is required."] },
    "doc_url": "https://docs.usequeek.com/docs/versioning-and-errors#validation_failed",
    "request_id": "5d1c9b3e-8f2a-4b6d-9c07-3e1f8a2b6d45"
  }
}

The top-level message is the first failing rule; error.errors has all of them.

The codes the Headless API emits

The OpenAPI document publishes one shared error vocabulary (components.schemas.QueekError, also under x-queek-error-codes). It covers every Queek surface, including the Merchant API, apps and the dashboard, so it is longer than what a storefront call can return. This page lists the codes the Headless API emits. The Merchant API errors page lists the ones that belong to server-side calls and apps. The status in each row is the one the code travels with.

Keys and origins

error.codeStatusCause
client_key_required401No X-Client-Key header.
invalid_client_key401Unknown key, or a key that was turned off.
api_key_mode_mismatch401A _test_ key on a live store, or a _live_ key on a dev store. Keys never cross modes.
origin_required403A website key was used with no Origin, or with Origin: null from a sandboxed frame. Server code needs a Server or backend connection, an app a Mobile app connection, a build in an AI tool a Website connection in Development mode.
origin_not_allowed403This Origin is not one of the connection's allowed websites. The merchant allows it from the connection's Problems card.
plan_inactive403The live store's plan has no API access for its own keys. A dev store always has it, and an installed app's token is never refused for it.
agent_access_denied403An AI agent connection's key tried something beyond browsing products and building carts.
storefront_password_required401A dev store read without the store's storefront token.
route_not_available404Not part of this API — the endpoints are exactly those in the reference.

Request shape

These are the status-implied defaults: the code a status carries when no surface has named the failure more precisely.

error.codeStatusCause
bad_request400The request could not be understood.
unauthenticated401Authentication is required and none that fits was presented.
forbidden403Authenticated, but not allowed to do this.
not_found404No such resource — a slug the store does not have, for instance.
method_not_allowed405That method is not supported on this endpoint.
conflict409The request conflicts with the current state.
gone410The resource existed and no longer does.
payload_too_large413The body exceeds the size limit.
unsupported_media_type415Send Content-Type: application/json.
validation_failed422The body or query is invalid; see error.errors.
too_many_requests429Rate limited — honour Retry-After.
server_error500Queek's fault. Retry with backoff and quote the request_id.
service_unavailable503Temporarily unavailable. Retry with backoff.

Pagination

error.codeStatusCause
starting_after_invalid422The starting_after cursor is stale, edited, from another list, or used under different filters. Restart the walk without it.

Asking for a numbered page beyond 50 answers page_too_deep: switch to a cursor walk.

Idempotency

See Payments → Idempotency-Key for the full contract.

error.codeStatusCause
idempotency_key_reuse409This Idempotency-Key was already used with a different body. Use a new key.
idempotency_key_in_progress409A request with this key is still running. Retry in a moment; nothing ran twice.

Metafields

See Metafields. Each carries handle and owner_type in error; metafield_value_invalid adds expected_type; metafield_namespace_reserved carries namespace.

error.codeStatusCause
metafield_definition_missing422A metafield[…] filter names a handle the store has not defined.
metafield_value_invalid422The value cannot be held by the definition's type, or the field is a json / list.* type and cannot be filtered.
metafield_namespace_reserved422A definition was requested in the reserved queek namespace.

Payments

See Payments.

error.codeStatusCause
payment_amount_mismatch409The provider confirmed a different amount or currency than the transaction's. The order stays unpaid until a human reconciles it.
custom_gateway_forbidden403Only the store owner can change where payments are collected.
custom_gateway_plan_required403The plan does not include collecting into the merchant's own account.
custom_gateway_kyc_required403Identity verification is not complete.
custom_gateway_store_ineligible403A template or dev store cannot collect real payments.
custom_gateway_not_verified409No provider account has been verified yet.
custom_gateway_not_configured404The provider's keys have not been saved.
custom_gateway_verification_failed422The saved keys did not pass the verification round-trip.

AI availability

error.codeStatusCause
ai_unavailable_in_test_mode403An AI action was attempted outside a live or dev store. AI works on both.

Retries that are safe

ResponseWhat to do
429 too_many_requestsWait for Retry-After seconds, then retry.
500 server_error, 503 service_unavailableRetry with exponential backoff and jitter. Quote request_id if it keeps failing.
401, 403, 404, 422Do not retry. Fix the key, origin, id or body first.

GET calls are safe to retry as they are. For writes, send an Idempotency-Key header — operations that accept it document it as a parameter in the reference. Retrying the same key with the same body replays the stored response instead of acting twice; the same key with a different body is refused, which is the point of the header. Failures are never stored, so a retry after an error is a real retry.

When you need help

Quote the request_id from the failing response if it carries one — it is the correlation id that ties your call to Queek's own logs. You may also send your own with the X-Request-Id header (8–128 characters of A-Za-z0-9_.:-) and it will be used as-is.

On this page