Registry API v1

Errors

Every error uses the same envelope, so a single handler can cover the whole API.

Error envelope

Shape
{
  "error": {
    "code": "invalid_query",
    "message": "One or more query parameters are invalid.",
    "issues": [{ "path": "limit", "message": "Number must be less than or equal to 100" }]
  }
}

code is stable and safe to branch on; message is human-readable and may be reworded. Some codes add extra keys: issues (validation), fields (protected fields), limit and retry_after_seconds (rate limiting).

Authentication and permissions

StatusCodeHow to handle
401missing_api_keyAdd the Authorization or x-api-key header.
401invalid_api_keyCheck the key value; do not retry automatically.
401revoked_api_keyThe key was revoked. Request a new one.
401expired_api_keyThe key passed its expiry date.
403organisation_inactiveContact Collect24; API access is not active.
403missing_scopeYour key lacks the required scope.
403not_permittedThe artwork is outside your organisation's profile.
403no_write_targetNo gallery or collection profile is linked to your key.

Requests and validation

StatusCodeHow to handle
400invalid_queryUnknown or out-of-range query parameter. Fix the client.
400invalid_jsonThe body was not valid JSON.
404artwork_not_foundUnknown id, or the work left the public projection.
404artist_not_foundUnknown artist slug or id.
404gallery_not_foundUnknown or non-public gallery profile.
404museum_not_foundUnknown or non-public museum profile.
422invalid_bodyField-level validation failed; see issues.
422protected_fieldRemove the listed Collect24-managed fields.

Rate limiting, idempotency and server errors

StatusCodeHow to handle
429rate_limitedWait for Retry-After / retry_after_seconds, then retry. Back off, do not hammer.
409idempotency_key_reusedSame key, different body. Use a new key.
409idempotent_request_in_progressThe first call is still running; retry after a short pause.
500internal_errorRetry with exponential backoff; contact us if it persists.

Retry policy

Retry 429 and 5xx with exponential backoff and jitter. Do not retry 400, 401, 403, 404 or 422: those need a change on your side. When retrying a POST, reuse the same Idempotency-Key so a duplicate record can never be created.

Handling errors
const response = await fetch(url, { headers });

if (response.status === 429) {
  const wait = Number(response.headers.get("retry-after") ?? 30);
  await new Promise((resolve) => setTimeout(resolve, wait * 1000));
  // retry
}

if (!response.ok) {
  const { error } = await response.json();
  switch (error.code) {
    case "missing_scope":
    case "not_permitted":
      throw new Error(`Permission problem: ${error.message}`);
    case "protected_field":
      throw new Error(`Remove these fields: ${error.fields.join(", ")}`);
    default:
      throw new Error(`${error.code}: ${error.message}`);
  }
}