Guides
How do I handle errors?
Build resilient integrations using error.code matching, next_actions recovery, and structured detail fields.
The three-step pattern
- Match on
error.code— not HTTP status. Multiple codes share 409. - Follow
next_actions— typed recovery calls withrel,method,href. - Read
detail— structured data (IDs, states, amounts) for context.
Implementation
def handle_response(response):
if "data" in response:
return response["data"]
error = response["error"]
if error["code"] == "hold_expired":
action = find_action(error["next_actions"], rel="requote")
return call(action["method"], action["href"], original_params)
elif error["code"] == "payment_failed":
return ask_user_for_new_payment(error["detail"]["provider_message"])
elif error["code"] == "rate_limited":
sleep(error["retry_after"])
return retry()
else:
log(f"{error['code']}: {error['remediation']}")
if error["next_actions"]:
return call(error["next_actions"][0])
raise UnrecoverableError(error["code"], error["trace_id"])Common scenarios
Network failure during booking
The booking endpoint is idempotent. Retry with the same Idempotency-Key — you'll get the cached response (success or sealed error).
Two agents book the same room
Inventory protected by holds. Only one agent can hold a room at a time — the second gets allocation_failed (409).
Rate limiting
Default: 100 req/min per tenant. Back off for retry_after seconds. For sustained traffic, implement a token bucket.
Error lookup table
Fetch GET /.well-known/errors once at startup:
catalog = fetch("GET /.well-known/errors")
error_lookup = {e["code"]: e for e in catalog["errors"]}