Zinevu
Basics

Errors

A stable machine code to branch on, and prose for the person reading the log.

Errors carry the human-readable reason at meta.message and the code at errors.code. Branch on the code, never on the message — the message is written for people and may be reworded at any time.

{  "meta": { "message": "This API key does not have the 'offers:read' scope." },  "errors": { "code": "insufficient_scope", "required_scope": "offers:read" }}

Status codes#

StatusMeans
400Malformed request — usually a body that is not JSON.
401The key is missing, unknown, revoked or expired.
402The plan does not include the API, or the account's lead quota is spent.
403The key is valid but lacks the scope named in the body.
404No such record in this account. Never a hint that it exists elsewhere.
409A conflict you can act on — a reused idempotency key, or a promotion in use.
413The payload is over a size limit. Nothing was written.
422The request is understood and refused. Every problem is listed.
429Rate limited. Read Retry-After.
503The API is temporarily switched off.

Rejected writes list everything#

A write is validated as a whole and applied as a whole. One bad row rejects the request with 422 and every problem it found, so one round trip tells you all of it — and nothing in the request was applied. A half-applied import would leave you quoting a mix of two catalogues, which is harder to spot and harder to undo than an import that plainly did not happen.

{  "meta": { "message": "2 problems with this request. Nothing was written." },  "errors": {    "code": "invalid_request",    "problems": [      "matrices[0].prices[1] must hold 4 price(s), 3 given.",      "matrices[2].code may contain letters, digits, underscore and hyphen only."    ]  }}

Codes worth branching on#

CodeStatusWhat to do
insufficient_scope403Add the scope named in required_scope to the key. It cannot be widened in flight.
plan_upgrade_required402The account is on Starter. Nothing to retry — tell the person, not the machine.
quota_reached402The plan's lead allowance for this period is spent, or billing is locked. Stop; do not retry.
idempotency_key_reused409That key already carried a different body. Pick a new key.
idempotency_in_progress409Your own earlier request is still running. Retry it unchanged in a moment.
promotion_in_use409The delete would cost you the record of which promotion won those offers. Repeat with ?force=1 if you mean it.
nothing_to_apply422Applying a promotion whose rate is 0 — which is how we record one taken off by hand.
invalid_request422Read errors.problems. Every one of them has to be fixed.

Retrying#

Retry 429 after Retry-After, and 503 with a backoff. Do not retry 4xx — a scope does not appear because you asked twice. When you do retry a write, send the same Idempotency-Key you sent the first time.