Hostrail Commerce API
Guides

How do I handle errors?

Build resilient integrations using error.code matching, next_actions recovery, and structured detail fields.

The three-step pattern

  1. Match on error.code — not HTTP status. Multiple codes share 409.
  2. Follow next_actions — typed recovery calls with rel, method, href.
  3. 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"]}

On this page