Skip to content
Tendral Health

Errors

Authenticated endpoints answer every failure, including a failure on our side, with one envelope. Branch your code on error.type; use error.code for the exact reason.

The envelope

Error response
{
  "error": {
    "type": "invalid_request_error",
    "code": "MISSING_PARAMETER",
    "message": "`since` query parameter is required.",
    "details": { "parameter": "since" },
    "doc_url": "https://developer.tendralhealth.com/docs/errors#MISSING_PARAMETER",
    "request_id": "req_018f5a2e7c127c8da123cafed00dfeed"
  }
}
  • message is for people and your logs. Do not parse it; it can change.
  • details is present when there is more to say, such as the parameter at fault.
  • doc_url links to this page, to the entry for that code.
  • request_id matches the X-Request-Id header. Quote it when you write to us.

Types

TypeHTTPRetry?
authentication_error401No
permission_error403No
invalid_request_error400, 404No
idempotency_error409No
rate_limit_error429Yes, after Retry-After
api_error500Yes, with backoff

New codes can be added to an existing type. Code that branches on type keeps working when they are.

Codes

UNAUTHORIZED

The bearer key is missing, malformed, expired or revoked.

HTTPTypeRetry?
401authentication_errorNo

What to do: Send Authorization: Bearer with a current key. If the key was rotated or revoked, use its replacement.

INSUFFICIENT_SCOPE

The key is valid but does not carry the scope this endpoint needs.

HTTPTypeRetry?
403permission_errorNo

What to do: error.details.required_scope names the scope. Ask for a key that carries it.

INVALID_TOKEN

A path token is not in the expected format.

HTTPTypeRetry?
400invalid_request_errorNo

What to do: Check the token against the pattern the reference gives for that path parameter.

INVALID_CAMPAIGN_ID

A campaign id is not in the prefixed cmp_ form.

HTTPTypeRetry?
400invalid_request_errorNo

What to do: Pass the prefixed id exactly as the API returned it.

TOKEN_NOT_FOUND

The token is well formed, but no record exists for it.

HTTPTypeRetry?
404invalid_request_errorNo

What to do: Check that the record has been published to you. A record that was withdrawn is not found.

MISSING_PARAMETER

A required query parameter is missing.

HTTPTypeRetry?
400invalid_request_errorNo

What to do: error.details.parameter names it. Add it and send the request again.

INVALID_PARAMETER

A parameter is present but has the wrong type, format or value.

HTTPTypeRetry?
400invalid_request_errorNo

What to do: error.details.parameter names the parameter. Common causes: a timestamp or date that is not ISO 8601, a limit outside 1–500, a cursor that is not one the API returned.

INVALID_EXPAND

A value in expand[] is not allowed on this endpoint.

HTTPTypeRetry?
400invalid_request_errorNo

What to do: error.details.allowed lists the values this endpoint accepts.

INVALID_TYPE_FILTER

A value in type or types[] is not one of the event types this endpoint lists.

HTTPTypeRetry?
400invalid_request_errorNo

What to do: error.details.allowed lists the accepted types.

IDEMPOTENCY_KEY_REUSED

An Idempotency-Key was sent again with a different request body.

HTTPTypeRetry?
409idempotency_errorNo

What to do: Use a new key for a new operation. Reuse a key only to retry the identical request.

RATE_LIMITED

The key has used its requests for the current window.

HTTPTypeRetry?
429rate_limit_errorYes, after Retry-After

What to do: Wait the number of seconds in Retry-After, then retry. Do not retry sooner.

INTERNAL_ERROR

Something failed on our side. Nothing about your request was wrong.

HTTPTypeRetry?
500api_errorYes, with backoff

What to do: Retry with exponential backoff. If it keeps failing, write to us with the request_id.