API errors

Every failure uses the same envelope, with a code from the fixed set below. Branch on error.code, log requestId, and never parse error.message; it is written for your logs and is not stable.

json
{
  "ok": false,
  "error": {
    "code": "GONE",
    "message": "Share token has expired",
    "requestId": "3f2504e0-4f89-11d3-9a0c-0305e82c3301"
  }
}

Codes

StatusCodeCauseRetry?
202CDR_PENDINGThe record exists but its visual record has not finished rendering. Only on the share-receive path.Yes, after Retry-After
400INVALID_ARGUMENTA malformed request: an unrecognized field in a request body, metadataKey without metadataValue, a metadata key and value pair too long to index, an unusable pageToken, a non-boolean collect, acceptance criteria that failed validation, an acceptance phone number that is not a normalizable North American number, or a share token your own organization created.No
401UNAUTHENTICATEDMissing, malformed, unknown, or revoked API key.No
403API_ACCESS_DISABLEDYour organization is not enabled for the server-to-server API.No
403PASSTHROUGH_ORG_CANNOT_COLLECTYour organization has no billing set up, so it cannot collect. Only reachable on such an organization that was granted API access explicitly; otherwise API_ACCESS_DISABLED comes first.No
403PASSTHROUGH_ORG_CANNOT_CLAIMYour organization has no billing set up, so it cannot receive shared records. Same reachability caveat.No
404NOT_FOUNDNo such record or token for your organization, an organization record we could not read, or (before authentication) an unknown path. Also what a record that is still rendering returns on a direct read.Sometimes
405METHOD_NOT_ALLOWEDRight path, wrong HTTP method. The Allow header lists what it accepts.No
409CONFLICTTwo documents on our side share one identifier, either a record or an API key. A data problem, not something you caused.No, tell us
410GONEThe share token has expired. Ask the sender for a new one.No
410CDR_INVALIDThe record was flagged invalid. No organization can read, collect, or share it.No
422CDR_ACCEPTANCE_FAILEDThe record did not meet the acceptance requirements you sent. details.failures[] says which.No
500INTERNALOur fault.Yes, with backoff

Every code in that table is reachable. There are no others: the published error type contains exactly these, so a switch over them is exhaustive.

Structured detail

Two codes carry error.details. The rest carry none.

details.issuesarrayoptional

On INVALID_ARGUMENT from a rejected request body. Each entry is { path, message }, with path a dotted route to the field at fault: disclosure.requiredText.0.ruleId for a bad acceptance rule, or the key itself for an unrecognized field.

details.failuresarrayoptional

On CDR_ACCEPTANCE_FAILED. Each entry is { ruleId, status, code } naming a requirement the record did not satisfy. See acceptance criteria.

details.outcomestringoptional

On CDR_ACCEPTANCE_FAILED, alongside failures. Always the string rejected. Branch on the error code rather than on this.

What to retry

CDR_PENDING is the one worth building for. Wait for the interval in the Retry-After header, which is 2 seconds, and repeat the request. Records are usually readable within a few seconds of capture, so a handful of attempts is enough; if it persists for a minute, treat it as a failure and tell us.

404 on a record captured moments ago is the same situation wearing a different status. Reading a record directly does not distinguish "not finished rendering" from "never existed", so allow a few seconds before concluding a cdrId is bad.

500 deserves a retry with exponential backoff. Everything else is definitive: the same request will fail the same way, and retrying it is wasted work.

Authentication failures

Every rejected key returns 401 UNAUTHENTICATED, whether it was missing, malformed, unrecognized, or revoked. That is deliberate, since the response does not help someone find out which of their guesses was closest, but it means the code alone will not debug it for you. Check that the header is X-API-Key, that the value is the key identifier and secret joined by a single dot, and that the key has not been revoked.

403 API_ACCESS_DISABLED is different, and is about the organization rather than the key: the key is valid and the account is not enabled for API access. Passthrough organizations start this way, which is why they see this error on every endpoint rather than the more specific passthrough codes. Talk to us to have it enabled.

A revoked key can keep working for up to five minutes.

Two kinds of 404

A 404 has two sources, and they differ in whether your key was checked.

An unknown path is rejected by the router before authentication, so a typo in the URL returns 404 whether your key is good, bad, or absent. That 404 tells you nothing about your key.

A valid path with no matching record is the opposite: authentication ran and passed first. A bad key would have returned 401 and an organization without API access 403, so this 404 is genuinely about the record: it does not exist, it does not belong to your organization, or it has not finished rendering. Check the path against the CDR endpoints; if the path is right, the problem is the identifier rather than the key.