Errors
The problem body every failure answers with, the machine codes to switch on, and what each one means you should do.
Every failure answers RFC 9457 problem details with Content-Type: application/problem+json.
{
"type": "about:blank",
"title": "Consent Required",
"status": 409,
"code": "consent_required",
"detail": "The patient has no current telehealth consent.",
"request_id": "iad1::abc123-1757340131234-9f2b7c4a"
}
Switch on code. It is the stable machine string; detail is human text that can be reworded at
any time, and title is fixed per code. request_id identifies the one request that failed and is
the first thing to quote when you ask for help.
The codes
code | Status | What it means |
|---|---|---|
invalid_request | 400 | The request is malformed, or a required header is missing |
unauthorized | 401 | Missing, malformed, expired or revoked credential |
forbidden | 403 | The credential or the grant does not carry the scope, or a test-only route was called with a live credential |
baa_required | 403 | The integration has not accepted the agreement currently in force |
not_found | 404 | No such resource for this integration and organization |
conflict | 409 | The resource is in a state that will not take this |
consent_required | 409 | A case was filed without a current telehealth and privacy consent |
idempotency_conflict | 409 | The same Idempotency-Key was reused with a different request |
gone | 410 | The export expired; its bundle and its file links were deleted together |
payload_too_large | 413 | The body exceeded 1 MB |
rate_limited | 429 | Too many requests; a Retry-After header names the wait |
internal | 500 | A fault on our side |
live_not_available | 501 | This resource has no live path yet |
unavailable | 503 | A dependency was unreachable; always safe to retry |
Validation failures
A 400 invalid_request from schema validation carries issues, one per field, each addressed by
the dotted path that produced it.
{
"code": "invalid_request",
"detail": "The request body is invalid.",
"issues": [
{ "path": "phone", "message": "Must be an E.164 phone number." },
{ "path": "address.postal_code", "message": "Must be a US postal code." }
]
}
The four worth handling separately
baa_required is not a permission problem. Somebody signs the agreement in the developer
portal and the same credential starts working. Do not retry it.
consent_required is answered by going back to the patient, not by changing the request.
Record the missing consent and file the case again.
live_not_available means the operation is published and sandboxed but has no live path yet.
It is a 501 rather than a 403 so your client does not treat it as something a scope would fix.
idempotency_conflict means you reused a key with a different body. See Rate limits and
idempotency.
What to retry
| Answer | Retry |
|---|---|
| 429 | Yes, after Retry-After |
| 500, 502, 503, 504 | Yes, with backoff |
| 400, 401, 403, 404, 409, 413 | No; the same request will fail the same way |
Retry a write only with the same Idempotency-Key you sent the first time. A write with no key
must not be retried, because you cannot tell a timeout from a success.
Both SDKs implement exactly this ladder. See SDKs.
Errors that carry no body
A request to a host that does not serve the path answers 404 with no problem body. api.cuvo.co
serves /v1, developers.cuvo.co serves the portal and these docs, and mcp.cuvo.co serves
/mcp. Calling /v1 on the docs host is a 404 rather than a quiet cross-serve.