Appearance
Handling errors
Errors are RFC 9457 problem documents, served as application/problem+json:
json
{
"type": "https://api.zefi.ai/problems/validation-failed",
"title": "Validation failed",
"status": 400,
"code": "VALIDATION_FAILED",
"detail": "One or more fields are invalid.",
"traceId": "Cw7-AggGliAEJiQ="
}Branch on code. Show detail to a human. Quote traceId if you contact us — it is how we find your request.
Two responses are not problem documents
Authentication is rejected at the gateway, before the service runs, so these two are plain JSON with no code:
| Response | Body | Means |
|---|---|---|
401 | {"message":"Unauthorized"} | No Authorization header |
403 | {"message":"Forbidden"} | Key is malformed, unknown, revoked or expired |
Treat any 403 whose body is not a problem document as an invalid credential. A 403 that is one is a permission problem — see below.
Codes
| Status | code | What to do |
|---|---|---|
400 | VALIDATION_FAILED | Fix the request. errors[] names the field. |
403 | INSUFFICIENT_SCOPE | The key lacks survey_links:read or :write. |
403 | WORKSPACE_MISMATCH | The key belongs to a different workspace than the path. |
404 | CAMPAIGN_NOT_FOUND | Wrong campaign id, or it belongs to another workspace. |
404 | SURVEY_LINK_NOT_FOUND | Already consumed, expired, revoked, or never existed. |
413 | BATCH_TOO_LARGE | More than 50 entries. Chunk and retry. |
429 | RATE_LIMIT_EXCEEDED | Back off — see below. |
Rate limits
Rate limiting has its own page — see Rate limits for the budgets, the headers every response carries, and how to back off.
What to retry
| Retry? | |
|---|---|
429 | Yes, after Retry-After. |
5xx | Yes, with backoff. |
400, 403, 404, 413 | No. Fix the request. |
401 | No. The credential is missing or wrong. |
Creating links is not idempotent: a retry that reaches us twice mints two sets of links. Prefer honouring Retry-After over aggressive retrying, and reconcile with reference if you are unsure whether a call landed.