Errors
Every /v1 error response carries the same JSON shape so you can switch on code in client code instead of parsing message strings. The anonymous external-register endpoint keeps its legacy NestJS body shape — see the external-register guide for that one specifically.
{
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": [
{ "path": ["data", "@participant.email"], "message": "Expected a valid email" }
]
}Status codes you should handle
| Status | When |
|---|---|
| 400 | Body, query, or path parameter failed Zod validation. Fix and retry; the response details array lists each issue with a JSON path. Bodies sent as application/x-www-form-urlencoded are rejected — the API only accepts JSON. |
| 401 | Missing, malformed, revoked, or expired bearer token. Re-check the Authorization header format (Bearer jdo_…) and verify the token hasn't been revoked from the admin UI. |
| 403 | Token is valid but the request named a clientCode or eventCode outside the account's allowed scope. This is configuration, not a coding error — contact your administrator if you need wider access. |
| 404 | The entity doesn't exist, was soft-deleted, or sits outside the token's scope (we don't distinguish — that would leak existence). A 404 on a known-good id usually means scope. |
| 409 | Optimistic concurrency conflict — another write landed between your read and write. Re-fetch the entity and retry; do not assume your client-side state is correct. Only returned for aggregates with a revision counter. |
| 429 | Rate-limited. The anonymous external-register endpoint allows 20 requests/minute per source IP. Authenticated /v1 endpoints enforce a per-account quota (default 60 req/min, raise on request). Every response carries X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset (unix seconds); 429 responses additionally carry Retry-After. |
| 5xx | Server-side problem. Safe to retry with exponential backoff; the request was almost certainly not partially applied (writes are transactional). Persistent 5xx → contact support with your token's prefix and a recent x-request-id header if you've captured one. |
Retrying safely
- GETs are idempotent — retry freely with exponential backoff (e.g. 1s, 2s, 4s, capped at 30s).
- POSTs / PATCHes on financial documents accept an
idempotencyKey; pass the same key on retry to dedupe. - 4xx errors (validation, auth, scope) are NOT retryable — fix the request first.
- 429 retry-after delays follow the
Retry-Afterheader when present.
Validation error details
For 400 responses driven by Zod, the details array carries one entry per offending field. Each entry's path is the JSON path into the request body (or query params for list endpoints) and message is the human-readable issue. Display them per-field to users rather than showing a single generic error.