Errors
Every error response from /api/v1/* follows the same envelope. Below is the full catalog: what causes each, and how a well-behaved client handles it.
Envelope shape
{
"error": {
"type": "validation_error",
"code": "missing_idempotency_key",
"message": "Idempotency-Key header is required for this endpoint.",
"doc_url": "https://watchtraderhub.com/docs/api/errors#missing_idempotency_key",
"request_id": "req_…",
"param": "Idempotency-Key"
}
}- type: high-level category (drives SDK error class hierarchy).
- code: stable machine-readable identifier. Match on this, not on
message. - message: human-readable. Free to change between API versions.
- doc_url: link to the entry on this page.
- request_id: quote in support tickets to short-circuit triage.
- param: present on validation errors; the JSON path of the offending field.
Match on code, not message
message as we improve diagnostics. We promise not to change code values. Those are the contract.Catalog
Anchors are stable across API versions. Old persisted doc_urls of the form #errors-<code> still resolve here: both forms are rendered side by side on every entry.
missing_authorization
401authentication_errorNo Authorization header was sent.
Common causes
- The client did not include the `Authorization` header on the request.
- A proxy or middleware stripped the header before it reached the API.
Recovery
Send `Authorization: Bearer wth_<your-key>` on every request.
malformed_authorization
401authentication_errorThe Authorization header was present but did not match the expected `Bearer wth_<32 base62>` shape.
Common causes
- The scheme was not `Bearer` (e.g. `Basic`, `Token`).
- The key prefix was not `wth_`.
- The key body was not exactly 32 base62 characters (`A-Za-z0-9`).
Recovery
Re-copy the key from /channels/custom — Settings → API credentials. Keys regenerated through the dashboard always satisfy this format.
invalid_api_key
401authentication_errorThe key is well-formed but does not match any active integration.
Common causes
- The key was regenerated and the client is still using the previous value.
- The integration was deleted.
- The key belongs to a different environment (test vs live).
Recovery
Regenerate or copy the current key from /channels/custom — Settings → API credentials.
integration_disabled
403permission_errorThe integration was paused by the dealer.
Common causes
- The dealer toggled `Integration enabled` off in Settings.
Recovery
Re-enable the integration at /channels/custom — Settings → Activation & mode.
auth_lookup_failed
401authentication_errorA transient database error occurred while looking up the API key.
Common causes
- Brief database connectivity blip.
Recovery
Retry the request after a short backoff. Persistent failures should be reported to support.
invalid_request
400validation_errorThe request body failed schema validation.
Common causes
- A required field was missing.
- A field value violated a length, type, or enum constraint.
- A `metadata` payload exceeded 10,000 bytes.
Recovery
Read the `message` field — it surfaces the exact Zod issue and the offending field path.
invalid_json
400validation_errorThe request body was not valid JSON.
Common causes
- The client sent malformed JSON (trailing comma, unescaped quote, truncated body).
- Content-Type was application/json but the body was form-encoded.
Recovery
Validate the body with `JSON.parse` (or your language equivalent) before sending. Make sure `Content-Type: application/json` is set.
invalid_cursor
400validation_errorThe `after` cursor parameter could not be decoded.
Common causes
- The cursor value was hand-crafted instead of taken verbatim from the previous response's `next_cursor`.
- The cursor was URL-decoded twice.
Recovery
Pass `next_cursor` from the previous response unchanged. Cursors are opaque — do not parse or modify them.
missing_idempotency_key
400validation_errorPOSTs to mutating endpoints must include an `Idempotency-Key` header.
Common causes
- The client did not send the `Idempotency-Key` header on a POST/PUT.
Recovery
Send a unique key per logical operation (UUIDv4 is fine). Replay the same key + body to safely retry; replays return the cached response with `Idempotent-Replay: true`.
request_too_large
413validation_errorThe request body exceeded the size cap (1 MB on regular endpoints, 10 MB on `/bulk` endpoints).
Common causes
- A `metadata` field carried a large blob.
- A bulk-customer request packed too many records.
Recovery
Trim the payload, or split bulk operations into chunks of ≤1000 records.
key_reused_with_different_body
409idempotency_errorAn `Idempotency-Key` was reused with a body that differs from the original request.
Common causes
- A bug caused the client to mutate the request body before retrying.
- A non-deterministic field (e.g. timestamp, randomized id) appears in the body.
Recovery
Either use a new key for the new operation, or replay the exact same body. Idempotency records expire after 24 hours.
duplicate_channel_order_id
409conflict_errorAn order with this `channel_order_id` already exists for the integration.
Common causes
- The same order was POSTed twice without the `Idempotency-Key` header.
- The client generated a non-unique `channel_order_id`.
Recovery
Make sure `channel_order_id` is unique per logical order. Use `Idempotency-Key` for safe retries.
too_many_requests
429rate_limit_errorThe integration exceeded its rate limit (default: 600 requests / minute).
Common causes
- A burst of API calls overwhelmed the rolling window.
Recovery
Honour the `Retry-After` header. Inspect `X-RateLimit-{Limit,Remaining,Reset}` headers to pace clients. Contact support to raise the cap.
listing_not_found
404not_foundNo listing with that id is published on the custom integration for this dealer.
Common causes
- The id is wrong, malformed, or belongs to another dealer.
- The item was un-toggled from the custom channel.
- The item was sold or archived.
Recovery
Re-fetch the collection (`GET /v1/listings`) to discover currently-active ids.
order_not_found
404not_foundNo order with that id exists for this dealer.
Common causes
- The id is wrong or belongs to another dealer.
- The order has not yet been ingested.
Recovery
Re-fetch via `GET /v1/orders` or POST the order again with a fresh `Idempotency-Key`.
internal_error
500api_errorAn unexpected server-side error occurred.
Common causes
- A handler threw an unhandled exception.
Recovery
Retry with backoff. Persistent 500s should be reported to support — include the `request_id` from the response.
lookup_failed
500api_errorA pre-write lookup against the database failed.
Common causes
- Transient database connectivity issue.
Recovery
Retry the request after a short backoff.
query_failed
500api_errorA read query against the database failed.
Common causes
- Transient database connectivity issue.
Recovery
Retry the request after a short backoff.
upsert_failed
500api_errorThe customer upsert (`POST /v1/customers`) failed at the database layer.
Common causes
- Transient database error.
- A schema constraint violation that was not caught by the validator.
Recovery
Retry with the same `Idempotency-Key` to safely re-attempt. Persistent failures should be reported with the `request_id`.
bulk_upsert_failed
500api_errorThe bulk customer upsert (`POST /v1/customers/bulk`) failed at the database layer.
Common causes
- Transient database error.
- A schema constraint violation in one of the records.
Recovery
Retry with the same `Idempotency-Key`. To isolate a bad record, split the batch and POST in halves.
insert_failed
500api_errorThe order insert (`POST /v1/orders`) failed at the database layer.
Common causes
- Transient database error.
Recovery
Retry with the same `Idempotency-Key`.
cancel_failed
500api_errorThe order cancellation update failed.
Common causes
- Transient database error.
Recovery
Retry the cancel call. Persistent failures should be reported with the `request_id`.