Errors
/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 | 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 | Authorization header format (Bearer jdo_…) and verify the token hasn't been revoked from the admin UI. |
| 403 | 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 | 404 on a known-good id usually means scope. |
| 409 | revision counter. |
| 429 | /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 | 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
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.