{"name":"Hospitality Commerce API","description":"Agent-first hotel booking platform. Search supply, lock a price with a quote, reserve inventory with a hold, and confirm the paid booking. Every response carries remediation and next_actions so an autonomous agent can self-recover from failure.","version":"0.0.0","protocol_version":"1","openapi_url":"https://api.agenthotel.dev/openapi.json","ai_plugin_url":"https://api.agenthotel.dev/.well-known/ai-plugin.json","docs_url":"https://docs.agenthotel.dev","error_catalog_url":"https://api.agenthotel.dev/.well-known/errors","auth":{"schemes":[{"type":"bearer","name":"Authorization","required":true,"description":"Agent credential: `Authorization: Bearer <agent_api_key>`. Issued via `POST /v1/admin/agents` or credential rotation. The bearer resolves to an agent principal whose scopes gate live search, quote, hold, booking, and cancellation routes."},{"type":"header","name":"X-Tenant-Id","required":false,"description":"Optional tenant hint for local development and selected internal-service flows. Do not treat this as the primary production auth scheme; tenant routing should come from the agent credential whenever possible."},{"type":"header","name":"X-Delegated-User","required":false,"description":"Short-lived JWT (RS256 or ES256) minted by a trusted delegation issuer, carrying the traveler consent the platform needs to act on their behalf. Required on mutating routes that confirm, cancel, or refund a booking so the audit trail records both \"which agent acted\" AND \"on whose behalf\". See `contracts.delegation` for accepted issuers, required claims, and the three failure codes agents should branch on."},{"type":"oauth2","name":"Authorization","required":false,"description":"Optional OAuth access token lane. Discover the issuer metadata at https://api.agenthotel.dev/.well-known/oauth-authorization-server and mint client_credentials tokens before calling protected routes."}]},"capabilities":[{"id":"search_availability","description":"Search hotels by destination, stay dates, and party size. Returns ranked candidates with price ranges you can use to pick a room before quoting.","method":"POST","href":"/v1/search","idempotent":false,"next_rel":"quote"},{"id":"create_quote","description":"Create a priced, policy-versioned quote for a specific room_type + rate_plan over specific dates. Pricing and policy are frozen on the quote; the quote has a short TTL — hold before it expires.","method":"POST","href":"/v1/quotes","idempotent":false,"next_rel":"create_hold"},{"id":"get_quote","description":"Retrieve a quote by id to re-read pricing and expiry.","method":"GET","href":"/v1/quotes/{id}","idempotent":true,"next_rel":"create_hold"},{"id":"create_hold","description":"Decrement inventory against a live quote. Holds have a short TTL and are scoped by an Idempotency-Key — retries return the cached response. Confirm before the TTL or inventory returns to the pool.","method":"POST","href":"/v1/holds","idempotent":true,"next_rel":"confirm"},{"id":"release_hold","description":"Explicitly release a hold before its TTL and return inventory to the pool. Use this when you mis-pick a room or abort the flow — it frees the room immediately instead of waiting for the hold to expire. Idempotent no-op if the hold is already released.","method":"POST","href":"/v1/holds/{id}/release","idempotent":true,"next_rel":"quote"},{"id":"confirm_booking","description":"Convert an active hold into a paid booking. Idempotent — retries return the cached response (including sealed business errors like payment_failed, so your retry budget is not spent re-running payment).","method":"POST","href":"/v1/bookings","idempotent":true,"next_rel":"get"},{"id":"get_booking","description":"Retrieve full booking detail, including payment state and the frozen policy snapshot.","method":"GET","href":"/v1/bookings/{id}","idempotent":true,"next_rel":"cancel"},{"id":"get_booking_audit","description":"Retrieve the full event timeline for a booking (created, confirmed, cancelled, refunded). Use this to prove what happened and when without needing admin credentials.","method":"GET","href":"/v1/bookings/{id}/audit","idempotent":true,"next_rel":"get"},{"id":"cancel_booking","description":"Cancel a booking. Cancellation fee and refund amount are computed from the policy_snapshot frozen on the booking at confirmation time, not the current policy, so outcomes are deterministic regardless of when the agent cancels.","method":"POST","href":"/v1/bookings/{id}/cancel","idempotent":false,"next_rel":"get"},{"id":"verify_receipt","description":"Verify a previously-issued response receipt. Agents keep receipts from confirm_booking / cancel_booking and can re-check them here (or locally against the shared key) to prove a transaction happened as claimed.","method":"POST","href":"/v1/receipts/verify","idempotent":true,"next_rel":null},{"id":"list_properties","description":"List properties in the tenant.","method":"GET","href":"/v1/properties","idempotent":true,"next_rel":"search"},{"id":"get_property","description":"Retrieve a property with its room types and rate plans — the inputs you need to build a quote body.","method":"GET","href":"/v1/properties/{id}","idempotent":true,"next_rel":"quote"}],"flows":[{"id":"book_a_room","description":"The golden path: discover supply, lock a price, reserve inventory, pay. Each step returns next_actions pointing at the next step.","steps":["search_availability","create_quote","create_hold","confirm_booking"]},{"id":"rebook_after_hold_expiry","description":"Recovery path when a hold_expired (410) error fires. Requote from the same inputs and retry the hold before the new TTL.","steps":["create_quote","create_hold","confirm_booking"]},{"id":"cancel_with_refund","description":"Cancel a booking and return funds per the frozen policy. The response includes the computed fee and refund amount.","steps":["get_booking","cancel_booking"]},{"id":"abandon_and_retry","description":"When the agent changes its mind mid-flow (user picked a different room, cheaper alternative appeared), explicitly release the current hold, requote against the new selection, and continue. Inventory is back in the pool immediately instead of waiting for the hold TTL.","steps":["release_hold","create_quote","create_hold","confirm_booking"]},{"id":"prove_what_happened","description":"Reconstruct the full history of a booking — creation, payment capture, cancellation, refunds — from outbox events. Agents use this to answer \"what did you do?\" questions from their users or their own planners.","steps":["get_booking","get_booking_audit"]}],"contracts":{"envelope":{"success_fields":["data","trace_id","next_actions","expires_at","receipt"],"error_fields":["error.code","error.message","error.detail","error.remediation","error.retry_after","error.docs_url","error.trace_id","error.next_actions"],"next_action_rels":["search","quote","requote","create_hold","confirm","cancel","release","extend","retry","get","get_audit","get_refund","issue_refund"]},"idempotency":{"body_field":"idempotency_key","header":"Idempotency-Key","scoped_endpoints":["/v1/holds","/v1/bookings"],"description":"Endpoints in scoped_endpoints require a client-chosen key, sent via the `Idempotency-Key` HTTP header (preferred) or the `idempotency_key` body field (fallback). Replays with the same (tenant, scope, key, body_hash) return the exact cached response — successes AND sealed business errors — and the reply carries `Idempotent-Replayed: true`, so your retry budget is spent once per logical attempt."},"trace_context":{"propagation":"w3c-traceparent","header":"traceparent","description":"Send the W3C `traceparent` header and we use its 32-char trace id as the envelope `trace_id`. Every response mirrors this back as the `X-Trace-Id` header so agents can stitch our work into their distributed trace without parsing the body."},"receipts":{"algorithm":"HS256","kinds":["booking_confirmed","booking_cancelled","refund_issued"],"verify_endpoint":"https://api.agenthotel.dev/v1/receipts/verify","canonical_form":"v1\\n<kid>\\n<issuer>\\n<kind>\\n<issued_at>\\n<payload_hash>\\n<canonical-json of subject>","description":"High-value mutations (booking confirmed, booking cancelled) embed an HMAC-signed receipt in the success envelope under `receipt`. Agents keep the receipt and can later prove the transaction happened by POSTing it to /receipts/verify, or by recomputing the signature locally from the shared key using `canonical_form` above."},"pagination":{"strategy":"complete","description":"All list endpoints return the complete result set in a single response. There is no cursor or offset-based pagination. If the catalog grows beyond a single-response size, pagination will be introduced with backward-compatible `next` / `prev` links — agents do not need to handle pagination today."},"delegation":{"header":"X-Delegated-User","token_type":"jwt","accepted_algorithms":["RS256","ES256"],"required_claims":["iss","sub","aud","exp"],"gated_routes":[{"method":"POST","href":"/v1/bookings"},{"method":"POST","href":"/v1/bookings/{id}/cancel"}],"error_codes":{"missing_header":"delegation_required","verification_failed":"delegation_invalid","issuer_not_trusted":"delegation_issuer_unknown"},"description":"Mutating routes that act on behalf of a real traveler require a short-lived JWT in `X-Delegated-User`, signed by a delegation issuer the tenant has registered. The platform verifies the signature, issuer, audience, and expiration, then records the traveler `sub` + `iss` on the call log alongside the acting agent id — so the audit trail answers \"which agent did this\" AND \"on whose behalf\". The three error codes let agents branch: `delegation_required` means mint a fresh JWT and retry; `delegation_invalid` means the token failed verification (see `detail.reason` for `expired` / `bad_signature` / `audience_mismatch`) and the agent should mint a fresh one; `delegation_issuer_unknown` is unrecoverable client-side and must be escalated to the tenant admin."}}}