TransaktDocs
Guides

Handling errors

Problem details responses, stable codes, and what to retry.

Every error response is RFC 9457 problem details, with media type application/problem+json. There are no plain-text or empty error bodies.

{
  "type": "https://docs.<platform-domain>/errors/api_client.product_not_allowed",
  "title": "The API client may not read this product.",
  "status": 403,
  "code": "api_client.product_not_allowed",
  "args": { "product": "planner" },
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
  "instance": "/api/v1/entitlements/72f988bf-86f1-41af-91ab-2d7cd011db47"
}
MemberUse it for
codeBranching. Stable, dotted, lower snake case ({area}.{reason}) and part of the contract. Each has a page.
typeThe documentation page of the code on this site.
statusThe HTTP status, the fallback for a code you do not know yet.
argsNamed values about the failure (always present, possibly {}). Never secrets.
titleEnglish, fixed per code, for your logs. Do not show it to users or parse it.
traceIdLog it, and quote it when you contact support.
errorsOnly on request.validation_failed: field paths mapped to lists of { code, args }.

Validation errors

{
  "code": "request.validation_failed",
  "status": 400,
  "errors": {
    "email": [{ "code": "validation.email", "args": {} }],
    "product": [{ "code": "validation.required", "args": {} }]
  }
}

Metered usage is the exception: each record is checked on its own, and a record's validation failure is reported in its result (outcome: "Refused" with error.code), not in errors.

Field codes include validation.required, validation.min_length, validation.max_length, validation.range, validation.min_items, validation.max_items, validation.email, validation.guid, validation.enum (with allowed), validation.timestamp, validation.one_of_required (with fields) and validation.unknown_parameter. Unknown JSON properties are refused (request.malformed_body), so mistakes surface early.

Statuses and what to do

StatusMeaningRetry?
400The request is malformed or invalid.No; fix the request.
401Credentials missing, invalid, expired or revoked (auth.credentials_invalid).After getting a new token or key.
403Identified, but not allowed: scope, product, tenant.No; check the API client's scopes and products.
404Not found, or not in your workspace.No.
409Conflicts with the current state.Possibly, after re-reading the state.
412, 428Concurrency preconditions (If-Match).After re-reading.
422Breaks a business rule that does not depend on timing.No.
429Rate limited (request.rate_limited).Yes, after Retry-After seconds.
500Unexpected failure (server.error).Yes, with backoff.
502, 503A dependency (for example Microsoft) failed, or the service is unavailable.Yes, with backoff.

A 401 never says which part of the credential failed. Not-found and forbidden answers never reveal whether a resource exists in another partner's workspace.

Writing a robust client

  • Branch on code; fall back on status for codes you do not know. New codes and new args are additive changes and can appear at any time.
  • Retry 429, 5xx and network failures with exponential backoff and jitter; honour Retry-After.
  • Send an Idempotency-Key (a UUID per intent, reused on retries) where an endpoint honours one; a replay returns the stored answer with Idempotent-Replayed: true.
  • Log traceId with every failure.

On this page