How-to guides

How to handle API errors

Parse and act on RFC 7807 error responses from the UrbanFox API

Before you start

The UrbanFox API returns structured errors following the RFC 7807 standard. This guide shows you how to parse these responses and take the correct action for each error type.

  • An authenticated integration making requests to the UrbanFox API

Check the error shape

Every error response follows RFC 7807 Problem Details:

{
  "title": "Application Error",
  "status": 404,
  "type": "item-not-found",
  "detail": "Case not found",
  "errors": null
}

Parse the response

Use the same flow in any HTTP client:

  1. Check the HTTP status code.
  2. For 4xx or 5xx responses, parse the JSON response body.
  3. Use the type field for programmatic handling.
  4. Use the detail and errors fields for logs, support messages, or user-facing validation feedback.

Handle by status code

Use the status code and type field for the first routing decision:

StatusTypeAction
400bad-requestMalformed request; check the body and parameters
401unauthorizedToken is invalid or expired; re-authenticate
403access-deniedMissing permission or tenant mismatch; check credentials
404item-not-foundThe resource does not exist; verify the ID
409item-already-existsResource already exists; use a different ID or fetch the existing one
409item-version-conflictThe item changed after you read it; read it again, decide whether your update still applies, and only then resend it with the current_version the response carries
409operation-idempotency-conflictYou reused an Idempotency-Key with a different body; use a new key for a new update
409transaction-contendedA concurrent write got there first and the API applied nothing; read again and retry
422validation-errorRequest body failed schema validation; see field errors
428precondition-requiredThe update needs case_version in the body; read the item and send the version you read
500internal-errorRetry with exponential backoff; report if persistent
502bad-gatewayUpstream dependency failed; retry after a short delay

A 400 with type set to invalid-operation-id means the Idempotency-Key header is missing or does not fit the accepted shape: it must be 1 to 255 characters from letters, digits, ., _, :, ~, + and -.

Retry a case update safely

A case status update (PUT /cases/{case_id}) requires two things from you. Other writes, such as end-user account updates, use neither:

  • case_version in the body: the version you last read. If the item changed in between, you get 409 item-version-conflict with the current version, and the API applies nothing.
  • Idempotency-Key header: one value per update you intend. The API accepts any 1 to 255 characters from letters, digits, ., _, :, ~, + and -; use a UUID v7, which is unique without coordination and sorts by time; the API also takes any other UUID version. Send the same value when you retry that update after a timeout or a lost response, and the API answers with the result of the first attempt instead of applying it again. A new update gets a new key. If you recover from a version conflict, the body changes, so use a new key for that too.

The case status update guide shows a complete request.

Handle validation errors

For 422 responses, the errors array contains one entry per invalid field:

{
  "title": "Validation Error",
  "status": 422,
  "type": "validation-error",
  "detail": "Request validation failed",
  "errors": [{ "field": "email", "message": "Invalid email format" }]
}

Iterate over errors[] to surface field-specific messages to users or logs. For other error types, use detail as the operational message.

If it's not working

SymptomAction
Response body is not JSONCheck you are calling the correct base URL
errors array is null on a 400Not all 400 responses are validation errors; use type to distinguish

See also