Hospitality Commerce API
Concepts

What is the envelope contract?

Every API response follows the same shape — success or failure. Agents never need to guess the response structure.

Every response from the API follows the envelope contract (ADR-0011). Agents can parse any response with the same logic.

Success envelope

{
  "data": { "booking_id": "b_123", "confirmation_code": "HCX-A1B2C3", "state": "confirmed" },
  "trace_id": "9f8e7d6c5b4a3210",
  "next_actions": [
    { "rel": "get", "method": "GET", "href": "/v1/bookings/b_123" },
    { "rel": "cancel", "method": "POST", "href": "/v1/bookings/b_123/cancel" }
  ],
  "expires_at": null,
  "receipt": { "kind": "booking_confirmed", "subject": "b_123", "signature": "hmac-sha256:..." }
}
FieldAlways presentDescription
dataYesThe business payload
trace_idYesW3C trace ID
next_actionsYesWhat the agent can do next
expires_atNoISO datetime if the resource has a TTL
receiptNoHMAC-signed proof for high-value mutations

Error envelope

{
  "error": {
    "code": "hold_expired",
    "message": "Hold h_abc123 expired at 2026-04-16T10:15:00Z",
    "detail": { "hold_id": "h_abc123", "expired_at": "2026-04-16T10:15:00Z" },
    "remediation": "Create a new quote via POST /v1/quotes, then a new hold.",
    "retry_after": null,
    "docs_url": "https://docs.agenthotel.dev/errors#hold_expired",
    "trace_id": "9f8e7d6c5b4a3210",
    "next_actions": [{ "rel": "requote", "method": "POST", "href": "/v1/quotes" }]
  }
}
FieldDescription
error.codeStable identifier — match on this, not HTTP status
error.detailStructured data — IDs, states, amounts as typed fields
error.remediationImperative instruction for the agent
error.next_actionsRecovery calls the agent should make

next_actions vocabulary

RelMeaning
searchSearch for availability
quote / requoteCreate a new quote
create_holdHold inventory against a quote
confirmConfirm a booking from a hold
cancelCancel a booking
releaseRelease a hold back to inventory
retryRetry the same operation
get / get_auditRetrieve the resource or audit trail

How to process responses

if "data" in response:
    process(response["data"])
else:
    error = response["error"]
    match error["code"]:
        "hold_expired" → follow next_actions[rel=requote]
        "payment_failed" → retry with new token
        "rate_limited" → sleep(error["retry_after"])
        _ → log(trace_id), read remediation, follow next_actions

Key rules:

  1. Match on error.code, not HTTP status — multiple codes share 409.
  2. detail is structured — access fields directly, never regex the message.
  3. next_actions tells you what to call next — prefer over hardcoded URLs.

On this page