Skip to main content

Error Handling

The Turris Public API uses conventional HTTP response codes and returns detailed error information to help you handle issues gracefully.

HTTP Status Codes

Error Response Format

All error responses follow a consistent format:

Response Fields

errorMessage is prose and may be reworded without notice. errorType is a published enum — every endpoint’s error schema in the API reference lists the complete set of values.One exception to plan for: authentication failures returned by our identity provider are forwarded verbatim, so a 401 can carry a code outside the published list. Treat an unrecognised value as a generic failure of its HTTP status rather than as a parsing error.

Error Types

The most common values. The full enum is on every endpoint’s error schema in the API reference.

unauthorized

Authentication failed or was not provided.
Common causes:
  • Missing Authorization header
  • Expired JWT token
  • Invalid token format

forbidden

Authentication succeeded but the request is not allowed.
Common causes:
  • IP not in allowlist (for Restricted Access Tokens)
  • Insufficient permissions for the requested resource
forbidden is not the only 403. Calling the other persona’s surface returns invalid_organization_category, a missing entitlement returns product_feature_subscription_required, and a carrier at its associated-agency ceiling returns invite_limit_reached. Each has its own section below, because the fix for each is different. Branch on errorType.

invalid_organization_category

A 403 carrying this errorType means the credential is valid and the surface is wrong. A credential belongs to exactly one persona and none reaches both: a carrier credential reaches /v1/* and /v2/upstream/*, an agency credential reaches /v2/downstream/*.
Common causes:
  • An agency credential on a persona-scoped /v1/* route, or on /v2/upstream/* (the four /auth/* and /heartbeat routes carry no persona and serve either surface)
  • A carrier credential on /v2/downstream/*
  • One base URL reused for both surfaces
Nothing about the credential needs changing, so re-minting it will not help. To confirm which surface it is for, send the same token to GET /v2/auth/test-oauth: the response carries orgCategory, upstreamEntity or downstreamEntity (the enum also holds two enterprise values for surfaces that are not routable yet, so match rather than negate). Which API is mine? has the routing rules and the rest of the 403 breakdown.

product_feature_subscription_required

A 403 carrying this errorType means your credentials are fine and the entitlement is not. Entitlement is re-checked on every request, not only when the credential is created, so this can start appearing for an integration that worked yesterday. Three distinct causes, and the errorMessage names which one applies: The third case is the one worth reading carefully: it is a single endpoint being unavailable, not your integration being broken. GET /v2/upstream/webhooks needs the Webhook Settings feature, for example, while the rest of the carrier API does not. This applies to carriers and agencies alike, on every version. Each area of the API needs the same product feature as the matching screen in the Turris application, so the mapping is the one you already know from the UI: if you can see a section, its endpoints are available to your credential. Getting Started and Getting Started for Agencies list it per endpoint group.
Carrier /v1/* endpoints are included. Entitlement was previously not checked there, so an organization whose subscription or Public API feature is switched off may start receiving 403 on v1 calls that used to succeed. The fix is the same as for any other cause above: the flags, not your code.
Entitlement is cached briefly, so a feature that has just been switched on can take up to a minute to take effect. Requesting a new token does not shorten that: the cache is keyed on your organization, not on the token.

invite_limit_reached

A 403 carrying this errorType is a plan limit, not a permission problem. It is raised on the carrier association-create endpoints, POST /v2/upstream/downstream-entity-associations/add and POST /v2/upstream/downstream-entity-associations/invite (POST /v1/downstream-entity-associations/add and /invite on v1), when the organization already holds as many live associated agencies as its plan allows.
Retrying does not clear it. Remove an association you no longer need, or contact support@turrisfi.com to raise the ceiling. Nothing on the agency surface returns this code.

validation_error

Request validation failed.
Common causes:
  • Missing required fields
  • Invalid field formats
  • Mutually exclusive parameters provided together

not_found

The requested resource doesn’t exist.
A 404 also covers “exists, but is not yours.” On the agency surface, an id belonging to another organization returns 404 rather than 403 — a 403 would confirm the record exists, which is the same disclosure in a politer envelope. So a 404 on an id you believe is yours is worth checking against List Licensed Entities: the usual cause is a credential minted lower in your organization tree than you expected.

internal_server_error

Something went wrong on our servers.
If you receive a 500 error, please contact support with the requestId for investigation.

Handling Errors

Example: JavaScript/TypeScript

Example: Python

Best Practices

Don’t assume requests succeed. Check the HTTP status code and handle errors appropriately.
The requestId is essential for debugging. Log it when errors occur so you can reference it when contacting support.
For transient errors (429, 500, 503), implement exponential backoff retry logic.
OAuth tokens expire after 60 minutes. Implement proactive token refresh or handle 401 errors by refreshing and retrying.