Errors & idempotency
HTTP status codes, the validation error shape, retry guidance and which writes are safe to repeat.
Updated 2026-09-02
Status codes#
| Code | Meaning | What to do |
|---|---|---|
200 / 201 | Success (201 = created) | — |
202 | Accepted without body (MCP notifications) | — |
302 | Redirect (tracking endpoints) | Follow Location |
400 | Validation error, missing url with force_transparent, unsafe scheme, platform mismatch | Fix the request; do not retry unchanged |
401 | Missing / invalid / revoked API key, SDK key or HMAC signature | Check credentials |
404 | Unknown slug, link, install, click id, or hostname not bound | Verify ids and domain |
405 | Method not allowed (e.g. GET on write endpoints) | — |
409 | Conflict (rare: slug taken during creation) | Retry with a different slug |
410 | Smart link expired / capped | — |
429 | Rate limited | Wait Retry-After seconds, then retry with backoff |
5xx | Server error | Retry with exponential backoff (max 3) |
Validation errors#
Validation uses Zod; 400 bodies contain error.formErrors (whole-body issues) and error.fieldErrors (per field):
{ "error": { "formErrors": [], "fieldErrors": { "day": ["Invalid string: must match pattern /^\\d{4}-\\d{2}-\\d{2}$/"], "spend": ["Too small: expected number to be >=0"] } } }Auth and lookup errors use a short string: {"error":"unauthorized"}, {"error":"install not found"}.
Idempotency#
| Operation | Idempotent on |
|---|---|
POST /api/v1/conversions, /postback | order_id + event (value / currency updated on repeat) |
POST /api/v1/costs | campaign + day (upsert) |
POST /api/sdk/v1/install | app + device_id (returns the existing install; re-attribution only after reattributionDays) |
POST /api/sdk/v1/subscription | store + original_transaction_id |
POST /api/sdk/v1/push-token | install (token replaced) |
| SKAN / store webhooks | Store transaction / notification ids |
POST /api/sdk/v1/events, /ad-revenue, /api/v1/impressions, /tv-airings | Not idempotent — do not replay a successful batch |
MCP update_smart_link_routing, set_link_status | Full replacement — safe to repeat |
Retry guidance (matches the SDKs)#
Retry on transport errors, 408, 429 and 5xx. Never retry other 4xx. Backoff min(cap, 2^(attempt-1) s) with ±20 % jitter; cap 60 s for installs (unlimited attempts, resumed on next launch), 8 s and 3 attempts for event batches.