# Hostrail Docs — Full Machine Corpus

> Full machine-readable export of the Hostrail docs site.

## Scope

- This corpus describes the documentation on `https://docs.hostrail.dev`.
- It explains how Hostrail works, but it is not the live booking contract by itself.
- For a structured summary of this docs corpus, fetch `https://docs.hostrail.dev/capabilities.json`.
- For a whole-site navigation graph with typed edges, fetch `https://docs.hostrail.dev/graph.json`.
- For page-specific structured hints, fetch `https://docs.hostrail.dev/docs/meta/<slug>`.
- For public product discovery, fetch `https://hostrail.dev/llms.txt` or `https://hostrail.dev/capabilities.json`.
- For exact execution details, fetch `https://api.hostrail.dev/.well-known/agent-card.json` or `https://api.hostrail.dev/openapi.json`.
- For live price and cancellation truth, rely on quote output from the API.

## Suggested Use

- Use this file when you need broad semantic grounding across multiple docs pages.
- Use `https://docs.hostrail.dev/graph.json` when you want to traverse docs pages offline without repeated fetches.
- The graph uses `prerequisite`, `next_recommended`, `deepens`, `related`, and `switches_to_surface` edge types.
- If you already know the target page, prefer the per-page markdown route under `https://docs.hostrail.dev/llms.mdx/docs/.../content.md`.
- If you only need audience, prerequisites, or switching hints, prefer the per-page metadata route under `https://docs.hostrail.dev/docs/meta/...`.

---

# Hostrail Docs (/docs)

Audience: ai_agents_reading_docs, ai_agents, backend_integrators, human_developers
Best For: choosing the right entry lane into Hostrail docs, understanding the docs corpus versus live product surfaces
Depends On: docs-site machine surfaces
Switch To Surfaces: https://docs.hostrail.dev/llms.txt, https://hostrail.dev/capabilities.json, https://api.hostrail.dev/.well-known/agent-card.json





Hostrail is the booking layer for AI agents. Public discovery starts on
`https://hostrail.dev`; canonical human-readable docs live on
`https://docs.hostrail.dev`; typed contracts and protected execution live on
`https://api.hostrail.dev`. Depending on your runtime, cold-start from
markdown, capability metadata, or the typed API contract. The core booking flow
is four API calls:

```
POST /v1/search → POST /v1/quotes → POST /v1/holds → POST /v1/bookings
```

Every response follows the [envelope contract](/docs/concepts/envelope) — success responses carry `data` + `trace_id` + `next_actions`, and error responses carry a stable `error.code` + `remediation` + `next_actions` so agents can self-recover without human intervention.

## For AI agents [#for-ai-agents]

Start from the discovery surface your runtime actually uses:

* [llms.txt](https://hostrail.dev/llms.txt) — short machine-readable site index
* [llms-full.txt](https://hostrail.dev/llms-full.txt) — richer hotel, room-plan, and policy context
* [capabilities.json](https://hostrail.dev/capabilities.json) — supported flows, payment methods, and limitations
* [coverage.json](https://hostrail.dev/coverage.json) — live city and hotel rollout status
* [Agent Card](https://api.hostrail.dev/.well-known/agent-card.json) — machine-readable capabilities and contracts
* [OpenAPI spec](https://api.hostrail.dev/openapi.json) — full request/response schemas

If your runtime wants public-first discovery through standards metadata, also probe:

* [API catalog](https://hostrail.dev/.well-known/api-catalog)
* [ACP discovery](https://hostrail.dev/.well-known/acp.json)
* [OpenID configuration](https://hostrail.dev/.well-known/openid-configuration)
* [OAuth authorization server metadata](https://hostrail.dev/.well-known/oauth-authorization-server)
* [OAuth protected resource metadata](https://hostrail.dev/.well-known/oauth-protected-resource)

Then read the [agent quickstart](/docs/quickstart/for-agents) for a complete
cold-start guide.

Key resources:

* [MCP server card](https://hostrail.dev/.well-known/mcp/server-card.json) — transport and capability metadata for `https://mcp.hostrail.dev/mcp`
* [Error catalog](/docs/errors/reference) — all 44 error codes with remediation
* [Booking flow](/docs/guides/booking-flow) — canonical `book_a_room` flow with all endpoints

## Key concepts [#key-concepts]

<Cards>
  <Card title="Envelope contract" href="/docs/concepts/envelope">
    Every response follows the same shape. Match on `error.code`, follow `next_actions`.
  </Card>

  <Card title="Discovery" href="/docs/concepts/discovery">
    Agent Card, OpenAPI spec, error catalog — everything an agent needs to cold-start.
  </Card>

  <Card title="Error model" href="/docs/concepts/error-model">
    44 error codes with remediation and recovery actions. Agents never guess.
  </Card>

  <Card title="Idempotency" href="/docs/concepts/idempotency">
    Holds and bookings are idempotent. Retries are safe — even failed payments are sealed.
  </Card>
</Cards>

## Authentication [#authentication]

Execution is authenticated. Discovery is public.

* Root-site discovery surfaces on `hostrail.dev` do not require a credential.
* Protected API and MCP calls accept either an operator-managed agent
  secret (`Authorization: Bearer hsk_live_...`) or an OAuth access token
  minted via `client_credentials`.
* Bookings that act on behalf of a traveler also require a delegation JWT
  in `X-Delegated-User`.

See [Discovery](/docs/concepts/discovery) for the full auth scheme catalog.

# API Reference (/docs/api-reference/overview)

Audience: backend_integrators, sdk_authors, tool_calling_runtimes
Best For: exact endpoint groups and base URLs, mapping discovery surfaces to execution surfaces
Depends On: openapi
Switch To Surfaces: https://api.hostrail.dev/openapi.json, https://api.hostrail.dev/openapi.yaml



## Base URLs [#base-urls]

| Environment   | URL                                         |
| ------------- | ------------------------------------------- |
| Root site     | `https://hostrail.dev`                      |
| Docs          | `https://docs.hostrail.dev`                 |
| Production    | `https://api.hostrail.dev`                  |
| MCP           | `https://mcp.hostrail.dev/mcp`              |
| ACP discovery | `https://hostrail.dev/.well-known/acp.json` |
| Local         | `http://localhost:4000`                     |

## Root-site machine surfaces [#root-site-machine-surfaces]

Before calling protected booking routes, many runtimes should fetch one or more
of the public machine surfaces on `hostrail.dev`:

* [`/llms.txt`](https://hostrail.dev/llms.txt) — short machine-readable index
* [`/llms-full.txt`](https://hostrail.dev/llms-full.txt) — richer factual catalog
* [`/capabilities.json`](https://hostrail.dev/capabilities.json) — supported flows, payment methods, and platform limitations
* [`/coverage.json`](https://hostrail.dev/coverage.json) — live hotel and city rollout status

## Authentication [#authentication]

* Public discovery on `hostrail.dev` is unauthenticated.
* Protected API calls require `Authorization: Bearer <key>` and `X-Tenant-Id`.
* The bearer can be an operator-managed `hsk_live_*` / `hsk_test_*` secret
  or an OAuth access token minted via `client_credentials`.

## Endpoint groups [#endpoint-groups]

* **Discovery** — `llms.txt`, `llms-full.txt`, capabilities, coverage, Agent Card, AI plugin, API catalog, OAuth metadata, error catalog, delegation issuers
* **Supply** — List properties, get property details, search availability
* **Commerce** — Quotes, holds, bookings, cancellations, receipts, checkout
* **Guest Portal** — Magic link, self-service booking management
* **Admin** — Inventory, agents, webhooks, delegation issuers, AI config, KPIs

## ACP discovery [#acp-discovery]

The platform also publishes ACP discovery metadata:

* Root origin: [`https://hostrail.dev/.well-known/acp.json`](https://hostrail.dev/.well-known/acp.json)
* API origin: [`https://api.hostrail.dev/.well-known/acp.json`](https://api.hostrail.dev/.well-known/acp.json)

Current ACP services are deliberately limited to the platform capabilities that
already exist:

* `catalog`
* `pricing`
* `reservation`
* `booking`

Treat ACP as a capability index, not as a promise of a second ACP-native
checkout protocol beyond the existing REST and MCP execution lanes.

## OpenAPI specification [#openapi-specification]

* JSON: [`/openapi.json`](https://api.hostrail.dev/openapi.json)
* YAML: [`/openapi.yaml`](https://api.hostrail.dev/openapi.yaml)

Use the OpenAPI contract for exact request and response shapes. Use the docs site
for quickstarts, concepts, and recovery behavior.

## Property & room-type response shapes [#property--room-type-response-shapes]

Three response shapes exist for properties. Pick the narrowest that covers your use case.

| Shape               | Returned by                         | Room-type detail                              |
| ------------------- | ----------------------------------- | --------------------------------------------- |
| `PropertyOut`       | `GET /v1/properties` (list)         | —                                             |
| `PropertyDetailOut` | `GET /v1/properties/{id}`           | `room_types[]` with full `RoomTypeOut`        |
| `PropertyFullOut`   | `GET /v1/properties/{id}?full=true` | `room_types[]` plus property-level enrichment |

### `PropertyOut` (list) [#propertyout-list]

`id`, `slug`, `name`, `description`, `star_rating`, `address`, `geo`, `default_currency`, `timezone`.

### `PropertyFullOut` adds [#propertyfullout-adds]

| Field                            | Type             | Notes                                                       |
| -------------------------------- | ---------------- | ----------------------------------------------------------- |
| `amenities`                      | `string[]`       | Property-level amenity codes                                |
| `images`                         | `string[]`       | Absolute URLs                                               |
| `phone`                          | `string \| null` | E.164 when known                                            |
| `email`                          | `string \| null` | Contact inbox, not guest-facing                             |
| `pets_allowed`                   | `boolean`        |                                                             |
| `number_of_rooms`                | `number \| null` | Total sellable rooms on property                            |
| `checkin_time` / `checkout_time` | `string \| null` | Local time, `HH:mm`                                         |
| `policies[]`                     | object           | Cancellation/child policy versions attached to the property |

### `RoomTypeOut` [#roomtypeout]

In addition to `id`, `code`, `name`, `max_occupancy`, `default_inventory`, `rate_plans[]`, each room type now carries:

| Field             | Type             | Notes                                                  |
| ----------------- | ---------------- | ------------------------------------------------------ |
| `description`     | `string \| null` | Marketing-ready prose                                  |
| `bed_type`        | `string \| null` | e.g. `"king"`, `"twin"` — unconstrained string for now |
| `floor_size_sqm`  | `number \| null` | Square metres                                          |
| `amenities`       | `string[]`       | Room-level amenity codes                               |
| `images`          | `string[]`       | Absolute URLs                                          |
| `smoking_allowed` | `boolean`        |                                                        |

All additions are **nullable or defaulted** — existing agents that ignore unknown fields continue to work without changes.

# ACP discovery (/docs/concepts/acp)

Audience: ai_agents, backend_integrators, protocol_implementers
Best For: understanding ACP discovery metadata, mapping ACP claims to actual Hostrail surfaces
Depends On: discovery
Switch To Surfaces: https://hostrail.dev/.well-known/acp.json, https://api.hostrail.dev/.well-known/acp.json, https://hostrail.dev/capabilities.json



## What Hostrail publishes today [#what-hostrail-publishes-today]

Hostrail publishes ACP discovery metadata on both the root site and the API
origin:

* `GET https://hostrail.dev/.well-known/acp.json`
* `GET https://api.hostrail.dev/.well-known/acp.json`

These documents are discovery surfaces, not a second booking API. They tell an
agent runtime which commerce service groups the platform already supports
through the current REST and MCP lanes.

## Current ACP shape [#current-acp-shape]

```json
{
  "protocol": {
    "name": "acp",
    "version": "0.1"
  },
  "api_base_url": "https://api.hostrail.dev",
  "transports": ["https+json", "mcp"],
  "transport_endpoints": {
    "https+json": "https://api.hostrail.dev",
    "mcp": "https://mcp.hostrail.dev/mcp"
  },
  "capabilities": {
    "services": ["catalog", "pricing", "reservation", "booking"]
  }
}
```

## Service mapping [#service-mapping]

ACP service groups are descriptive mappings over the platform's existing
surfaces:

| ACP service   | Current platform capability                                                  |
| ------------- | ---------------------------------------------------------------------------- |
| `catalog`     | Hotel, room type, policy, and machine-readable discovery surfaces            |
| `pricing`     | Search and quote flows that return current rates and policy context          |
| `reservation` | Hold creation and release                                                    |
| `booking`     | Booking confirmation, retrieval, cancellation, receipts, and guest follow-up |

## What this does not imply [#what-this-does-not-imply]

Publishing `acp.json` does **not** mean Hostrail already implements:

* an ACP-native checkout session model
* ACP-specific payment objects
* ACP webhook or event contracts
* a second standalone ACP transport beyond the existing REST and MCP lanes

Agents should still execute the real booking flow through:

1. search
2. quote
3. hold
4. booking confirmation
5. guest servicing

## Recommended agent startup sequence [#recommended-agent-startup-sequence]

1. Fetch `https://hostrail.dev/.well-known/api-catalog`
2. Fetch `https://hostrail.dev/.well-known/acp.json`
3. Fetch `https://api.hostrail.dev/.well-known/agent-card.json`
4. Fetch `https://api.hostrail.dev/openapi.json`
5. If using protected API calls, fetch OAuth metadata and mint a bearer before the first live call

## When to use ACP discovery vs Agent Card [#when-to-use-acp-discovery-vs-agent-card]

Use ACP discovery when a runtime wants a coarse commerce-oriented capability
map. Use the Agent Card and OpenAPI when the runtime needs exact operation
names, request bodies, auth headers, and error semantics.

# How does delegation work? (/docs/concepts/delegation)

Audience: ai_agents, backend_integrators, auth_operators
Best For: acting on behalf of travelers, understanding X-Delegated-User requirements
Depends On: discovery, agent card or openapi
Switch To Surfaces: https://api.hostrail.dev/.well-known/agent-card.json, https://api.hostrail.dev/openapi.json



## What is delegation? [#what-is-delegation]

When an agent books or cancels a stay, the platform needs to know who the
traveler is. The agent attaches a short-lived JWT in the `X-Delegated-User`
header, signed by a trusted delegation issuer.

This is what lets Hostrail behave as a real booking layer for AI agents rather
than just an anonymous inventory API.

## Which routes require delegation? [#which-routes-require-delegation]

| Route                           | Delegation required |
| ------------------------------- | ------------------- |
| `POST /v1/bookings`             | Yes                 |
| `POST /v1/bookings/{id}/cancel` | Yes                 |
| All other routes                | No                  |

## JWT requirements [#jwt-requirements]

| Claim | Required | Description                               |
| ----- | -------- | ----------------------------------------- |
| `iss` | Yes      | Must match a registered delegation issuer |
| `sub` | Yes      | Traveler identifier                       |
| `aud` | Yes      | Must include the platform's audience      |
| `exp` | Yes      | Keep short (minutes, not hours)           |

Accepted algorithms: `RS256`, `ES256`.

## Error codes [#error-codes]

| Code                        | Status | Recovery                      |
| --------------------------- | ------ | ----------------------------- |
| `delegation_required`       | 401    | Mint a JWT and retry          |
| `delegation_invalid`        | 401    | Check `detail.reason`, remint |
| `delegation_issuer_unknown` | 401    | Escalate to tenant admin      |

`detail.reason` values for `delegation_invalid`: `expired`, `bad_signature`, `missing_claim`, `audience_mismatch`, `unsupported_algorithm`.

## Discovering trusted issuers [#discovering-trusted-issuers]

```bash
GET /.well-known/delegation-issuers
```

Returns active issuers with JWKS URL and allowed audiences.

In practice, most runtimes discover auth metadata from the root site first, then
use delegation only on traveler-bound write flows such as booking confirmation
and cancellation.

# How do agents discover this API? (/docs/concepts/discovery)

Audience: ai_agents, retrieval_first_assistants, tool_calling_runtimes
Best For: choosing the correct discovery surface, understanding the difference between docs, root, and api surfaces
Depends On: root-site discovery surfaces
Switch To Surfaces: https://hostrail.dev/llms.txt, https://hostrail.dev/capabilities.json, https://api.hostrail.dev/.well-known/agent-card.json, https://api.hostrail.dev/openapi.json



## Discovery endpoints [#discovery-endpoints]

| Document                   | URL                                                               | Purpose                                                             |
| -------------------------- | ----------------------------------------------------------------- | ------------------------------------------------------------------- |
| Root site                  | `GET https://hostrail.dev/`                                       | Public landing page, markdown negotiation, and machine-facing links |
| llms.txt                   | `GET https://hostrail.dev/llms.txt`                               | Short machine-readable index for retrieval-oriented runtimes        |
| llms-full.txt              | `GET https://hostrail.dev/llms-full.txt`                          | Expanded factual hotel, room-plan, and policy context               |
| Capabilities               | `GET https://hostrail.dev/capabilities.json`                      | Supported flows, payment methods, machine surfaces, and limits      |
| Coverage                   | `GET https://hostrail.dev/coverage.json`                          | Live hotel and city rollout status                                  |
| API catalog                | `GET https://hostrail.dev/.well-known/api-catalog`                | RFC 9727 linkset for API discovery                                  |
| ACP discovery              | `GET https://hostrail.dev/.well-known/acp.json`                   | Agentic Commerce Protocol capability map for the current platform   |
| OpenID configuration       | `GET https://hostrail.dev/.well-known/openid-configuration`       | OIDC-style auth discovery                                           |
| OAuth authorization server | `GET https://hostrail.dev/.well-known/oauth-authorization-server` | RFC 8414 auth metadata                                              |
| OAuth protected resource   | `GET https://hostrail.dev/.well-known/oauth-protected-resource`   | RFC 9728 resource metadata                                          |
| Agent Card                 | `GET https://api.hostrail.dev/.well-known/agent-card.json`        | Capabilities, flows, auth, and contracts                            |
| A2A Agent Card             | `GET https://api.hostrail.dev/.well-known/agent.json`             | Gemini and A2A discovery                                            |
| AI Plugin manifest         | `GET https://api.hostrail.dev/.well-known/ai-plugin.json`         | ChatGPT-style plugin/app import                                     |
| Error Catalog              | `GET https://api.hostrail.dev/.well-known/errors`                 | All error codes with remediation and next\_actions                  |
| Delegation Issuers         | `GET https://api.hostrail.dev/.well-known/delegation-issuers`     | Trusted JWT issuers for traveler delegation                         |
| OpenAPI Spec               | `GET https://api.hostrail.dev/openapi.json`                       | Full request/response schemas (OpenAPI 3.1)                         |
| MCP Server Card            | `GET https://hostrail.dev/.well-known/mcp/server-card.json`       | Transport metadata for `https://mcp.hostrail.dev/mcp`               |

**Recommended cold-start sequence:**

1. `GET https://hostrail.dev/llms.txt` or `GET https://hostrail.dev/capabilities.json` → discover public machine surfaces
2. `GET https://hostrail.dev/.well-known/api-catalog` or `GET https://hostrail.dev/.well-known/acp.json` → understand standards-oriented discovery metadata
3. `GET https://api.hostrail.dev/.well-known/agent-card.json` → understand exact capabilities and contracts
4. `GET https://api.hostrail.dev/.well-known/errors` → build error lookup table
5. `GET https://api.hostrail.dev/openapi.json` → learn exact request/response schemas
6. If the runtime wants standards-based auth, fetch root-site OAuth metadata before the first protected call

## Which discovery surface should I start from? [#which-discovery-surface-should-i-start-from]

| Runtime style             | Best first document                                    | Why                                      |
| ------------------------- | ------------------------------------------------------ | ---------------------------------------- |
| Retrieval-first assistant | `https://hostrail.dev/llms.txt`                        | Small, cheap machine index               |
| Long-context planner      | `https://hostrail.dev/llms-full.txt`                   | Richer factual grounding                 |
| Capability-aware agent    | `https://hostrail.dev/capabilities.json`               | Explicit supported flows and limitations |
| Standards-oriented agent  | `https://hostrail.dev/.well-known/api-catalog`         | Well-known linkset and auth metadata     |
| Tool-calling runtime      | `https://api.hostrail.dev/.well-known/agent-card.json` | Exact operation and auth contract        |
| SDK / backend integration | `https://api.hostrail.dev/openapi.json`                | Typed request and response schemas       |

## ACP discovery [#acp-discovery]

`/.well-known/acp.json` is a truthful summary of what the platform already
supports today:

* `catalog`
* `pricing`
* `reservation`
* `booking`

It is intentionally narrower than a full ACP-native checkout stack. Use it as a
high-level capability map, then switch to the Agent Card and OpenAPI for exact
route-level integration detail.

## Agent Card [#agent-card]

The Agent Card lists every operation, canonical flows, and auth schemes:

### Capabilities [#capabilities]

Each capability declares `method`, `href`, `idempotent`, and `next_rel` (what to do next):

```json
{
  "capabilities": [
    {
      "id": "search_availability",
      "method": "POST",
      "href": "/v1/search",
      "idempotent": false,
      "next_rel": "quote"
    }
  ]
}
```

### Canonical flows [#canonical-flows]

| Flow                       | Steps                               | When to use                        |
| -------------------------- | ----------------------------------- | ---------------------------------- |
| `book_a_room`              | search → quote → hold → book        | Golden path                        |
| `rebook_after_hold_expiry` | quote → hold → book                 | Recovery from `hold_expired` (410) |
| `cancel_with_refund`       | get\_booking → cancel               | Guest cancellation                 |
| `abandon_and_retry`        | release\_hold → quote → hold → book | Agent changes selection mid-flow   |
| `prove_what_happened`      | get\_booking → get\_audit           | Reconstruct booking timeline       |

### Auth schemes [#auth-schemes]

| Header                        | Required        | Purpose                          |
| ----------------------------- | --------------- | -------------------------------- |
| `X-Tenant-Id`                 | API lane        | Identifies the hotel tenant      |
| `Authorization: Bearer <key>` | Protected calls | Agent credential or OAuth bearer |
| `X-Delegated-User: <jwt>`     | Booking/cancel  | Traveler consent JWT             |

OAuth-aware runtimes can discover the issuer and resource metadata from
the root site, then mint `client_credentials` access tokens from
`https://api.hostrail.dev/oauth/token`.

### Pagination [#pagination]

The Agent Card declares the platform's pagination strategy under `pagination`:

```json
{
  "pagination": {
    "strategy": "complete",
    "description": "All list endpoints return the complete result set in a single response..."
  }
}
```

| Value      | Meaning                                                                                                                                 |
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `complete` | List endpoints return the full result set in one response. No `cursor`, `offset`, or `page` query params are accepted today.            |
| `cursor`   | Future: responses will carry `next` / `prev` links. Backward-compatible — agents ignoring the links continue to work on the first page. |
| `offset`   | Reserved. Not in use.                                                                                                                   |

**Today's contract:** agents do **not** need to implement pagination. Read `pagination.strategy` from the Agent Card at startup; if it flips to `cursor`, follow the `next` link from the response envelope until absent.

## Error catalog endpoint [#error-catalog-endpoint]

`GET /.well-known/errors` returns every error code the API can return. Agents should fetch this once at startup and build a local lookup table keyed on `code`.

See the full [Error Catalog](/docs/errors/reference) for all 44 codes.

## Cache behavior [#cache-behavior]

All discovery documents return `Cache-Control: public, max-age=300`. Delegation issuers use `max-age=60`.

# What is the envelope contract? (/docs/concepts/envelope)

Audience: ai_agents, backend_integrators, sdk_authors
Best For: parsing success and error envelopes, understanding next_actions and trace_id
Depends On: api reference
Switch To Surfaces: https://api.hostrail.dev/openapi.json, https://api.hostrail.dev/.well-known/errors



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

This is the contract that makes Hostrail's booking layer predictable across
quote, hold, booking, guest servicing, and error recovery.

## Success envelope [#success-envelope]

```json
{
  "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:..." }
}
```

| Field          | Always present | Description                                |
| -------------- | -------------- | ------------------------------------------ |
| `data`         | Yes            | The business payload                       |
| `trace_id`     | Yes            | W3C trace ID                               |
| `next_actions` | Yes            | What the agent can do next                 |
| `expires_at`   | No             | ISO datetime if the resource has a TTL     |
| `receipt`      | No             | HMAC-signed proof for high-value mutations |

## Error envelope [#error-envelope]

```json
{
  "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.hostrail.dev/errors#hold_expired",
    "trace_id": "9f8e7d6c5b4a3210",
    "next_actions": [{ "rel": "requote", "method": "POST", "href": "/v1/quotes" }]
  }
}
```

| Field                | Description                                            |
| -------------------- | ------------------------------------------------------ |
| `error.code`         | Stable identifier — **match on this, not HTTP status** |
| `error.detail`       | Structured data — IDs, states, amounts as typed fields |
| `error.remediation`  | Imperative instruction for the agent                   |
| `error.next_actions` | Recovery calls the agent should make                   |

## next\_actions vocabulary [#next_actions-vocabulary]

| Rel                 | Meaning                              |
| ------------------- | ------------------------------------ |
| `search`            | Search for availability              |
| `quote` / `requote` | Create a new quote                   |
| `create_hold`       | Hold inventory against a quote       |
| `confirm`           | Confirm a booking from a hold        |
| `cancel`            | Cancel a booking                     |
| `release`           | Release a hold back to inventory     |
| `retry`             | Retry the same operation             |
| `get` / `get_audit` | Retrieve the resource or audit trail |

## How to process responses [#how-to-process-responses]

```python
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.

# How does error handling work? (/docs/concepts/error-model)

Audience: ai_agents, backend_integrators, operator_tools
Best For: deciding how to recover from booking failures, understanding error.code, remediation, and next_actions
Depends On: envelope contract
Switch To Surfaces: https://api.hostrail.dev/.well-known/errors, https://docs.hostrail.dev/docs/errors/reference



Hostrail errors are designed for agent recovery, not just human debugging. The
goal is to let a runtime decide whether to retry, requote, re-authenticate, or
escalate without inventing hidden control flow.

## Design principles [#design-principles]

1. **Codes are stable.** Never renamed — agents branch on them.
2. **Remediation is imperative.** "Call `POST /v1/quotes` again" — not "An error occurred."
3. **Detail is structured.** IDs, states, amounts as typed fields.
4. **next\_actions is a closed vocabulary.** Adding a new `rel` is a contract change.
5. **Errors on idempotent paths are sealed.** Replays return the cached failure.

## Error categories [#error-categories]

| Status | Category          | Count | Agent strategy                                   |
| ------ | ----------------- | ----- | ------------------------------------------------ |
| 400    | Validation        | 1     | Fix `detail.field_errors`, retry                 |
| 401    | Auth / delegation | 4     | Reattach credentials or remint JWT               |
| 402    | Payment           | 1     | Retry with new token or surface provider message |
| 403    | Authorization     | 3     | Request elevated scope                           |
| 404    | Not found         | 11    | Discover valid IDs via search/list               |
| 409    | Conflict          | 14    | Read `detail`, follow `next_actions`             |
| 410    | Gone (expired)    | 2     | Requote and retry                                |
| 429    | Rate limit        | 1     | Back off for `retry_after` seconds               |
| 500    | Internal          | 2     | Retry with backoff, cite `trace_id`              |
| 502    | Upstream          | 2     | Retry with backoff                               |

## Error recovery decision tree [#error-recovery-decision-tree]

```
error.code?
├── *_not_found (404) → discover valid IDs via search/list
├── *_expired (410) → requote, resource TTL elapsed
├── allocation_failed (409) → inventory moved, search alternatives
├── payment_failed (402) → new payment token or surface provider_message
├── rate_limited (429) → sleep(retry_after), retry
├── delegation_* (401) → mint/remint JWT, or escalate to admin
├── idempotency_conflict (409) → replay original body or use new key
├── validation_failed (400) → fix detail.field_errors, retry
└── internal_error (500) → retry with backoff, cite trace_id
```

Every error also carries a `docs_url` pointing back into this docs site, so a
runtime or human operator can jump straight to the canonical explanation.

See the full [Error Catalog](/docs/errors/reference) for all 44 error codes.

# How does idempotency work? (/docs/concepts/idempotency)

Audience: ai_agents, backend_integrators, payment_integrators
Best For: safe retries for holds and bookings, understanding sealed idempotent failures
Depends On: booking flow, envelope contract
Switch To Surfaces: https://api.hostrail.dev/openapi.json



## Which endpoints are idempotent? [#which-endpoints-are-idempotent]

| Endpoint                             | Idempotent | Key required |
| ------------------------------------ | ---------- | ------------ |
| `POST /v1/holds`                     | Yes        | Yes          |
| `POST /v1/bookings`                  | Yes        | Yes          |
| All GET endpoints                    | Yes (safe) | No           |
| `POST /v1/search`, `POST /v1/quotes` | No         | No           |

## How it works [#how-it-works]

Send a client-chosen key via the `Idempotency-Key` HTTP header or `idempotency_key` body field.

```bash
curl -X POST https://api.hostrail.dev/v1/holds \
  -H "Idempotency-Key: hold_user123_quote456" \
  -H "Content-Type: application/json" \
  -d '{ "quote_id": "q_456" }'
```

Cached by `(tenant, scope, key, body_hash)`. Retries with same key + body return the exact cached response.

`POST /v1/search` and `POST /v1/quotes` are intentionally not idempotent today:
they reflect current availability, price, and policy state at the moment you
ask.

## Sealed errors [#sealed-errors]

Business errors on idempotent paths (like `payment_failed`) are **sealed**:

* First attempt executes logic and caches the result
* Retries return the cached error — payment provider NOT called again
* Response carries `Idempotent-Replayed: true`

## Best practices [#best-practices]

1. Generate deterministic keys from the logical intent: `hold_{user}_{quote}_{attempt}`
2. Retry with the same key on network errors
3. Use a new key when the intent changes
4. Don't retry with a new key after `payment_failed` unless you want to re-run payment

# How do receipts work? (/docs/concepts/receipts)

Audience: ai_agents, operator_tools, backend_integrators
Best For: auditability after booking mutations, verifying receipt payloads
Depends On: envelope contract, booking flow
Switch To Surfaces: https://api.hostrail.dev/openapi.json



Receipts are part of Hostrail's auditability story. They let an agent or human
operator prove what the platform says happened after a booking, cancellation, or
refund mutation.

## When are receipts issued? [#when-are-receipts-issued]

| Event             | Receipt kind        |
| ----------------- | ------------------- |
| Booking confirmed | `booking_confirmed` |
| Booking cancelled | `booking_cancelled` |
| Refund issued     | `refund_issued`     |

## Receipt structure [#receipt-structure]

```json
{
  "receipt": {
    "kind": "booking_confirmed",
    "subject": "b_123",
    "issued_at": "2026-05-01T12:00:00Z",
    "issuer": "commerce-api",
    "signature": "hmac-sha256:a1b2c3d4..."
  }
}
```

## Verifying receipts [#verifying-receipts]

```bash
POST /v1/receipts/verify
Content-Type: application/json

{ "kind": "booking_confirmed", "subject": "b_123", "issued_at": "...", "issuer": "commerce-api", "signature": "..." }
```

Returns `200 OK` if valid. Agents with the shared key can also verify locally using the canonical form.

Use receipts together with `trace_id`, booking state, and audit endpoints when
you need to reconstruct or prove a high-value workflow.

# Error Catalog (/docs/errors/reference)

Audience: ai_agents, backend_integrators, operator_tools
Best For: looking up exact error codes and remediation, mapping codes to recovery actions
Depends On: error model
Switch To Surfaces: https://api.hostrail.dev/.well-known/errors



Machine-readable version: `GET /.well-known/errors` returns this catalog as JSON.

## Envelope shape [#envelope-shape]

```json
{
  "error": {
    "code": "hold_expired",
    "message": "The hold TTL elapsed …",
    "detail": { "hold_id": "h_01HW…", "expired_at": "2026-04-15T19:03:11.000Z" },
    "remediation": "Create a new quote via POST /v1/quotes, then a new hold.",
    "retry_after": null,
    "docs_url": "https://docs.hostrail.dev/errors#hold_expired",
    "trace_id": "9f8e…",
    "next_actions": [{ "rel": "quote", "method": "POST", "href": "/v1/quotes" }]
  }
}
```

## Validation (400) [#validation-400]

| Code                | Remediation                                                  |
| ------------------- | ------------------------------------------------------------ |
| `validation_failed` | Inspect `detail.field_errors`, fix every listed path, retry. |

## Authentication (401) [#authentication-401]

| Code                        | Remediation                                    |
| --------------------------- | ---------------------------------------------- |
| `unauthenticated`           | Include a valid agent API key or bearer token. |
| `delegation_required`       | Attach a valid JWT as `X-Delegated-User`.      |
| `delegation_invalid`        | Check `detail.reason`, retry with fresh token. |
| `delegation_issuer_unknown` | Contact tenant admin to register the issuer.   |

## Payment (402) [#payment-402]

| Code             | Remediation                                                               |
| ---------------- | ------------------------------------------------------------------------- |
| `payment_failed` | Verify token, retry with new token, or surface `detail.provider_message`. |

## Authorization (403) [#authorization-403]

| Code                         | Remediation                                                 |
| ---------------------------- | ----------------------------------------------------------- |
| `forbidden`                  | Request elevated credentials or call within granted scopes. |
| `resource_access_denied`     | Ask admin to grant access to the specific resource.         |
| `principal_kind_not_allowed` | Re-authenticate with a credential of the required kind.     |

## Not found (404) [#not-found-404]

| Code                              | Thrown by          | Remediation                                          |
| --------------------------------- | ------------------ | ---------------------------------------------------- |
| `property_not_found`              | quotes, properties | Search again for valid IDs.                          |
| `room_type_not_found`             | quotes             | Call `GET /v1/properties/{id}`.                      |
| `rate_plan_not_found`             | quotes             | Call `GET /v1/properties/{id}`.                      |
| `quote_not_found`                 | holds              | Create a fresh quote.                                |
| `hold_not_found`                  | bookings           | Create new quote + hold.                             |
| `booking_not_found`               | bookings, cancel   | Confirm ID and tenant context.                       |
| `oversell_incident_not_found`     | admin              | Refresh oversell queue.                              |
| `agent_not_found`                 | admin              | List agents via `GET /v1/admin/agents`.              |
| `credential_not_found`            | admin              | List credentials via `GET /v1/admin/agents/{id}`.    |
| `booking_authorization_not_found` | admin              | Create fresh authorization.                          |
| `delegation_issuer_not_found`     | admin              | List issuers via `GET /v1/admin/delegation-issuers`. |

## Conflict (409) [#conflict-409]

| Code                                | Thrown by           | Remediation                                 |
| ----------------------------------- | ------------------- | ------------------------------------------- |
| `idempotency_conflict`              | Any idempotent POST | Replay original body or use fresh key.      |
| `allocation_failed`                 | holds               | Requote; try alternative dates/rooms.       |
| `allotment_exhausted`               | holds               | Try different date or room type.            |
| `oversell_not_detected`             | admin               | Refresh data before retrying.               |
| `quote_mismatch`                    | bookings            | Recreate quote → hold → booking chain.      |
| `hold_not_active`                   | bookings            | Create new quote + hold.                    |
| `booking_not_cancellable`           | cancel              | Inspect state first.                        |
| `booking_not_modifiable`            | modify              | Fall back to staff support.                 |
| `booking_modification_price_change` | modify              | Ask staff or choose same-length dates.      |
| `authorization_required`            | bookings            | Create authorization first.                 |
| `booking_authorization_invalid`     | bookings            | Inspect `detail.reason`, refresh.           |
| `no_capturable_payment`             | admin refund        | No captured payment.                        |
| `payment_not_refundable`            | admin refund        | Only captured/partially\_refunded payments. |
| `refund_exceeds_remaining`          | admin refund        | Reduce to `detail.remaining`.               |
| `property_not_promotable`           | admin               | Fix `detail.reasons` pre-conditions.        |
| `policy_not_found_for_rate_plan`    | quotes              | Contact tenant admin.                       |

## Expired (410) [#expired-410]

| Code            | Remediation                                       |
| --------------- | ------------------------------------------------- |
| `quote_expired` | Call `POST /v1/quotes` again.                     |
| `hold_expired`  | Create new quote + hold. Room may be unavailable. |

## Rate limit (429) [#rate-limit-429]

| Code           | Remediation                         |
| -------------- | ----------------------------------- |
| `rate_limited` | Back off for `retry_after` seconds. |

## Server (500) / Upstream (502) / Gateway timeout (504) [#server-500--upstream-502--gateway-timeout-504]

| Code                           | Status | Remediation                                                                                                                                                                                          |
| ------------------------------ | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `internal_error`               | 500    | Retry with backoff, cite `trace_id`.                                                                                                                                                                 |
| `payment_provider_error`       | 502    | Retry with backoff or try alternative payment method.                                                                                                                                                |
| `notification_delivery_failed` | 502    | Primary operation may have succeeded. Retry notification.                                                                                                                                            |
| `payment_timeout`              | 504    | Provider didn't respond in time — charge may or may not have captured. Retry with the **same** `Idempotency-Key`; the provider deduplicates. If timeouts persist, try an alternative payment method. |

`payment_timeout` is thrown by `POST /v1/bookings` when Stripe, PayPal, or VNPay exceeds the 10-second request budget. Because the outcome is ambiguous, safe recovery requires replay with the original idempotency key — **do not** generate a new key, which could double-charge.

## Webhooks [#webhooks]

| Code                              | Status | Remediation                                  |
| --------------------------------- | ------ | -------------------------------------------- |
| `webhook_endpoint_not_found`      | 404    | List endpoints via `GET /v1/admin/webhooks`. |
| `webhook_delivery_not_found`      | 404    | Check DLQ via deliveries endpoint.           |
| `webhook_delivery_not_replayable` | 409    | Wait for in-flight delivery to complete.     |

# How do I book a room? (/docs/guides/booking-flow)

Audience: ai_agents, backend_integrators
Best For: canonical booking execution flow, understanding quote to booking transitions
Depends On: discovery, agent card or openapi
Switch To Surfaces: https://api.hostrail.dev/.well-known/agent-card.json, https://api.hostrail.dev/openapi.json



## Overview [#overview]

The booking flow is four API calls. Each step returns `next_actions` pointing at
the next step.

```
POST /v1/search → POST /v1/quotes → POST /v1/holds → POST /v1/bookings
```

Use machine-readable discovery before you start:

* Retrieval-first runtimes can begin with `https://hostrail.dev/llms.txt`
* Capability-aware runtimes can begin with `https://hostrail.dev/capabilities.json`
* Tool-calling runtimes should fetch `https://api.hostrail.dev/.well-known/agent-card.json`
* Backend integrations should fetch `https://api.hostrail.dev/openapi.json`

Published hotel pages and markdown help with planning, but quote output is the
current source of truth for live price and cancellation terms.

## Step 1: Search availability [#step-1-search-availability]

```bash
POST /v1/search
```

| Field             | Type             | Required |
| ----------------- | ---------------- | -------- |
| `destination`     | string           | Yes      |
| `check_in`        | YYYY-MM-DD       | Yes      |
| `check_out`       | YYYY-MM-DD       | Yes      |
| `guests.adults`   | integer (1-20)   | Yes      |
| `guests.children` | array of `{age}` | No       |

**On success:** follow `next_actions[rel=quote]`.

## Step 2: Create a quote [#step-2-create-a-quote]

```bash
POST /v1/quotes
```

Returns `quote_id`, `price_breakdown`, `policy_snapshot`, and `expires_at`.

Treat this step as the booking truth boundary: prices, taxes, and cancellation
terms are authoritative here, not in `llms.txt` or hotel marketing copy.

**Errors:** `property_not_found` (404), `room_type_not_found` (404), `rate_plan_not_found` (404).

## Step 3: Hold inventory [#step-3-hold-inventory]

```bash
POST /v1/holds
Headers: Idempotency-Key: <unique-key>
Body: { "quote_id": "<id>" }
```

**Idempotent.** Returns `hold_id`, `state: "held"`, `expires_at` (default 15 min).

**Errors:** `quote_expired` (410) → requote. `allocation_failed` (409) → search alternatives. `allotment_exhausted` (409) → try different room/date.

**Time pressure:** Confirm before `expires_at` or get `hold_expired` on step 4.

## Step 4: Confirm booking [#step-4-confirm-booking]

```bash
POST /v1/bookings
Headers: Idempotency-Key: <key>, X-Delegated-User: <jwt>
Body: { hold_id, quote_id, guest: { primary: { full_name, email, phone } }, payment: { method, provider_token, billing_country } }
```

**Idempotent.** Retries return cached result including sealed errors.

**Errors:** `hold_expired` (410) → requote + rehold. `payment_failed` (402) → new token. `delegation_required` (401) → attach JWT.

## Other flows [#other-flows]

### Cancel a booking [#cancel-a-booking]

```bash
GET /v1/bookings/{id}
POST /v1/bookings/{id}/cancel  (X-Delegated-User required)
```

Fee computed from `policy_snapshot` frozen at booking time — not current policy.

### Abandon and retry [#abandon-and-retry]

```bash
POST /v1/holds/{id}/release    # returns inventory immediately
POST /v1/quotes                # quote new selection
POST /v1/holds                 # hold new selection
POST /v1/bookings              # confirm
```

### Prove what happened [#prove-what-happened]

```bash
GET /v1/bookings/{id}          # current state
GET /v1/bookings/{id}/audit    # full event timeline
```

## Related references [#related-references]

* [Discovery](/docs/concepts/discovery)
* [Envelope contract](/docs/concepts/envelope)
* [Idempotency](/docs/concepts/idempotency)
* [Error catalog](/docs/errors/reference)

# How do I handle errors? (/docs/guides/error-handling)

Audience: ai_agents, backend_integrators, operator_tools
Best For: implementing runtime recovery logic, matching error.code to next_actions
Depends On: error model, error catalog
Switch To Surfaces: https://api.hostrail.dev/.well-known/errors, https://api.hostrail.dev/openapi.json



Hostrail is designed so a runtime can recover from booking failures without
guessing. Errors are part of the execution contract, not an afterthought.

## The three-step pattern [#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 [#implementation]

```python
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 [#common-scenarios]

### Network failure during booking [#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 [#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 [#rate-limiting]

Default: 100 req/min per tenant. Back off for `retry_after` seconds. For sustained traffic, implement a token bucket.

### Markdown or hotel-page copy disagrees with quote output [#markdown-or-hotel-page-copy-disagrees-with-quote-output]

Treat quote output as authoritative. Root-site discovery surfaces such as
`llms.txt` and `llms-full.txt` are useful for planning and retrieval, but live
price and cancellation terms are confirmed in the quote response.

## Error lookup table [#error-lookup-table]

Fetch `GET /.well-known/errors` once at startup:

```python
catalog = fetch("GET /.well-known/errors")
error_lookup = {e["code"]: e for e in catalog["errors"]}
```

Also keep the docs site handy through each error's `docs_url`, which deep-links
back into the canonical explanation.

# How do I test in the sandbox? (/docs/guides/sandbox-testing)

Audience: sandbox_evaluators, backend_integrators, ai_agents
Best For: testing booking flows before live traffic, exercising failure paths safely
Depends On: discovery, booking flow
Switch To Surfaces: https://hostrail.dev/capabilities.json, https://api.hostrail.dev/openapi.json



Hostrail's sandbox is an evaluation lane for testing discovery, quote, hold, and
booking flows before live traffic. Some teams use a provisioned sandbox origin;
others run the stack locally.

## Sandbox environment [#sandbox-environment]

| Setting          | Value                                                                             |
| ---------------- | --------------------------------------------------------------------------------- |
| Base URL         | your provisioned sandbox origin, or `http://localhost:4000` for local integration |
| Payment provider | `stub` or your configured test gateway                                            |
| Hold TTL         | 15 minutes                                                                        |
| Rate limit       | 100 req/min per tenant                                                            |

## Test payment tokens [#test-payment-tokens]

| Token         | Behavior                                         |
| ------------- | ------------------------------------------------ |
| `tok_test`    | Payment succeeds                                 |
| `tok_fail`    | Payment declined (`payment_failed`, 402)         |
| `tok_timeout` | Provider timeout (`payment_provider_error`, 502) |

## Testing error scenarios [#testing-error-scenarios]

Before running write flows, fetch the same discovery surfaces your production
runtime will use:

* `https://hostrail.dev/llms.txt`
* `https://hostrail.dev/capabilities.json`
* `https://api.hostrail.dev/.well-known/agent-card.json`
* `https://api.hostrail.dev/openapi.json`

### Hold expiry [#hold-expiry]

Create a hold, wait 15 minutes, attempt booking → `hold_expired` (410).

### Allocation failure [#allocation-failure]

Hold all inventory, attempt another hold → `allocation_failed` (409).

### Idempotent replay [#idempotent-replay]

Create a hold with `Idempotency-Key: test`, repeat → cached response with `Idempotent-Replayed: true`.

### Rate limiting [#rate-limiting]

Send 101 requests in under 1 minute → `rate_limited` (429).

## Notes [#notes]

* The public web evaluation lane lives on `https://hostrail.dev/sandbox` when deployed.
* The production canonical API origin is `https://api.hostrail.dev`.
* Do not hard-code `https://sandbox.hostrail.dev` unless your deployment
  actually provisions that hostname.
* Treat quote output as the source of truth for live price and cancellation terms, even in sandbox.

# Terms of Service (/docs/legal/terms)

Audience: backend_integrators, human_developers, legal_reviewers
Best For: platform use constraints, integration responsibilities
Depends On: discovery
Switch To Surfaces: https://hostrail.dev, https://api.hostrail.dev, https://mcp.hostrail.dev/mcp



These terms govern access to the Hostrail platform, including:

* the public site at `https://hostrail.dev`
* the API at `https://api.hostrail.dev`
* the MCP endpoint at `https://mcp.hostrail.dev/mcp`
* the documentation site at `https://docs.hostrail.dev`

## Platform use [#platform-use]

* Public discovery surfaces may be fetched without an execution credential.
* Protected API and MCP calls require a valid agent credential or OAuth access token issued for the platform.
* End users must not be asked to paste platform bearer tokens into chat.

## Booking execution [#booking-execution]

* Quote output is the current source of truth for price, taxes, and cancellation terms.
* A hold reserves availability only for its stated TTL window.
* Booking confirmation may require delegated traveler context and payment authorization.

## Integration responsibilities [#integration-responsibilities]

* Keep agent credentials and OAuth client secrets in secure server-side storage.
* Do not expose operator credentials in prompts, browser source, or client-side logs.
* Respect rate limits, idempotency rules, and published error remediation guidance.

## Contact [#contact]

For commercial, operational, or legal questions, contact `api-support@hostrail.dev`.

# Agent Quickstart (/docs/quickstart/for-agents)

Audience: ai_agents, tool_calling_runtimes
Best For: end-to-end agent startup against the live booking platform, moving from discovery into authenticated booking execution
Depends On: root-site machine surfaces, agent card, openapi
Switch To Surfaces: https://hostrail.dev/llms.txt, https://hostrail.dev/capabilities.json, https://api.hostrail.dev/.well-known/agent-card.json, https://api.hostrail.dev/openapi.json



## Step 1: Discover the API [#step-1-discover-the-api]

Start with the public machine surfaces on the root site:

```bash
curl https://hostrail.dev/llms.txt
curl https://hostrail.dev/llms-full.txt
curl https://hostrail.dev/capabilities.json | jq '.'
curl https://hostrail.dev/coverage.json | jq '.'
curl https://hostrail.dev/.well-known/api-catalog | jq '.'
curl https://hostrail.dev/.well-known/acp.json | jq '.'
curl https://hostrail.dev/.well-known/openid-configuration | jq '.'
```

Then fetch the Agent Card — a machine-readable manifest that describes all capabilities, flows, auth schemes, and contracts:

```bash
curl https://api.hostrail.dev/.well-known/agent-card.json | jq '.'
```

The Agent Card contains:

* `capabilities[]` — every operation with `method`, `href`, `idempotent`, and `next_rel`
* `flows[]` — canonical step sequences (`book_a_room`, `cancel_with_refund`, etc.)
* `contracts.envelope` — success and error field lists
* `contracts.idempotency` — which endpoints accept `Idempotency-Key`
* `contracts.delegation` — JWT requirements for traveler-context routes

Also fetch the error catalog to build a local lookup table:

```bash
curl https://api.hostrail.dev/.well-known/errors | jq '.'
```

Returns all error codes with `status`, `remediation`, `next_actions`, and `docs_url`.

Use `/.well-known/acp.json` as the high-level commerce capability map if your
runtime starts from ACP-style discovery. Then switch to the Agent Card and
OpenAPI for exact request contracts.

Treat `llms.txt` and `llms-full.txt` as retrieval and planning context, not as
the final source of truth for live price or cancellation terms.

## Step 2: Authenticate [#step-2-authenticate]

Public discovery does not require a credential. Protected API and MCP calls do.

You have two supported execution lanes:

1. Operator-managed agent secret:
   * `Authorization: Bearer hsk_live_...`
2. OAuth access token minted via `client_credentials`:
   * `POST https://api.hostrail.dev/oauth/token`
   * `Authorization: Bearer eyJ...`

Direct API calls still require the tenant context header:

```bash
curl -H "X-Tenant-Id: <tenant-uuid>" \
     -H "Authorization: Bearer <agent-api-key>" \
     https://api.hostrail.dev/v1/properties
```

| Header                        | Required        | Purpose                            |
| ----------------------------- | --------------- | ---------------------------------- |
| `X-Tenant-Id`                 | API lane        | Identifies the hotel tenant        |
| `Authorization: Bearer <key>` | Protected calls | Agent credential or OAuth bearer   |
| `X-Delegated-User: <jwt>`     | Booking/cancel  | Traveler consent JWT (RS256/ES256) |

If your runtime is MCP-native, it can call `https://mcp.hostrail.dev/mcp`
with the same bearer token family instead of speaking REST directly.

## Step 3: Search availability [#step-3-search-availability]

```bash
curl -X POST https://api.hostrail.dev/v1/search \
  -H "X-Tenant-Id: <tenant-uuid>" \
  -H "Content-Type: application/json" \
  -d '{
    "destination": "Ho Chi Minh City",
    "check_in": "2026-05-01",
    "check_out": "2026-05-03",
    "guests": { "adults": 2, "children": [] }
  }'
```

Response carries `next_actions[rel=quote]` — follow it to create a quote for the selected room.

Treat quote output, not published markdown or hotel page copy, as the
current source of truth for price and cancellation terms.

## Step 4: Create a quote [#step-4-create-a-quote]

Lock a price and policy snapshot for a specific room + rate plan.

```bash
curl -X POST https://api.hostrail.dev/v1/quotes \
  -H "X-Tenant-Id: <tenant-uuid>" \
  -H "Authorization: Bearer <key>" \
  -H "Content-Type: application/json" \
  -d '{
    "hotel_id": "prop_abc",
    "room_type_id": "rt_123",
    "rate_plan_id": "rp_456",
    "stay": { "check_in": "2026-05-01", "check_out": "2026-05-03" },
    "guests": { "adults": 2, "children": [] }
  }'
```

The quote freezes pricing and cancellation policy. The quote has a TTL — hold before `expires_at`.
If `quote_expired` (410) is returned, call `POST /v1/quotes` again.

## Step 5: Hold inventory [#step-5-hold-inventory]

Reserve rooms for a short window (default 15 minutes).

```bash
curl -X POST https://api.hostrail.dev/v1/holds \
  -H "X-Tenant-Id: <tenant-uuid>" \
  -H "Authorization: Bearer <key>" \
  -H "Idempotency-Key: hold_attempt_1" \
  -H "Content-Type: application/json" \
  -d '{ "quote_id": "q_789" }'
```

**Idempotent:** retries with the same `Idempotency-Key` return the cached response.

Possible errors:

* `quote_expired` (410) → requote with `POST /v1/quotes`
* `allocation_failed` (409) → inventory changed, search alternatives
* `allotment_exhausted` (409) → daily ceiling reached, try different room/date

## Step 6: Confirm booking [#step-6-confirm-booking]

```bash
curl -X POST https://api.hostrail.dev/v1/bookings \
  -H "X-Tenant-Id: <tenant-uuid>" \
  -H "Authorization: Bearer <key>" \
  -H "Idempotency-Key: book_attempt_1" \
  -H "X-Delegated-User: <traveler-jwt>" \
  -H "Content-Type: application/json" \
  -d '{
    "hold_id": "h_abc",
    "quote_id": "q_789",
    "guest": {
      "primary": { "full_name": "Nguyen Van A", "email": "a@example.com", "phone": "+84901234567" }
    },
    "payment": {
      "method": "card",
      "provider_token": "tok_stripe_xxx",
      "billing_country": "VN"
    }
  }'
```

**Idempotent:** retries return the cached result — including sealed business errors like `payment_failed`.

**Delegation required:** `X-Delegated-User` JWT must be present. Without it → `delegation_required` (401).

Success response includes a signed `receipt` for auditability. Keep it — verify later via `POST /v1/receipts/verify`.

# Developer Quickstart (/docs/quickstart/for-developers)

Audience: backend_integrators, sdk_authors, human_developers
Best For: backend integration setup, first protected API call
Depends On: root-site machine surfaces, openapi
Switch To Surfaces: https://hostrail.dev/capabilities.json, https://api.hostrail.dev/openapi.json, https://api.hostrail.dev/.well-known/agent-card.json



## Prerequisites [#prerequisites]

* An agent credential or OAuth client configured for your tenant
* A tenant UUID for the hotel you're integrating with
* `curl` or any HTTP client

## 1. Verify the public discovery surface [#1-verify-the-public-discovery-surface]

```bash
curl https://hostrail.dev/llms.txt
curl https://hostrail.dev/capabilities.json | jq '.'
curl https://hostrail.dev/.well-known/api-catalog | jq '.'
```

Use the docs site at `https://docs.hostrail.dev` for quickstarts, concepts, and
error semantics. Use the root machine surfaces for runtime discovery.

## 2. Verify connectivity and fetch the typed contract [#2-verify-connectivity-and-fetch-the-typed-contract]

```bash
curl https://api.hostrail.dev/healthz
curl https://api.hostrail.dev/.well-known/agent-card.json | jq '.'
curl https://api.hostrail.dev/openapi.json | jq '.info'
```

Import the OpenAPI spec into Postman, Insomnia, or any compatible client.

## 3. Discover the auth metadata [#3-discover-the-auth-metadata]

```bash
curl https://hostrail.dev/.well-known/acp.json | jq '.'
curl https://hostrail.dev/.well-known/openid-configuration | jq '.'
curl https://hostrail.dev/.well-known/oauth-authorization-server | jq '.'
```

## 4. Make your first authenticated request [#4-make-your-first-authenticated-request]

```bash
curl -H "X-Tenant-Id: <your-tenant-uuid>" \
     -H "Authorization: Bearer <your-api-key>" \
     https://api.hostrail.dev/v1/properties
```

If you prefer the OAuth lane, mint a token first:

```bash
curl -X POST https://api.hostrail.dev/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials&client_id=<agent-id>&client_secret=<agent-secret>"
```

## 5. Run the golden path [#5-run-the-golden-path]

```bash
# export BASE=https://api.hostrail.dev
# export TENANT_ID=<tenant-uuid>
# export API_KEY=<hsk_live_or_test_secret>

# Search
curl -X POST $BASE/v1/search \
  -H "X-Tenant-Id: $TENANT_ID" -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"destination":"test","check_in":"2026-06-01","check_out":"2026-06-03","guests":{"adults":2,"children":[]}}'

# Quote (use IDs from search results)
curl -X POST $BASE/v1/quotes \
  -H "X-Tenant-Id: $TENANT_ID" -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"hotel_id":"<id>","room_type_id":"<id>","rate_plan_id":"<id>","stay":{"check_in":"2026-06-01","check_out":"2026-06-03"},"guests":{"adults":2,"children":[]}}'

# Hold
curl -X POST $BASE/v1/holds \
  -H "X-Tenant-Id: $TENANT_ID" -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: test-hold-001" \
  -H "Content-Type: application/json" \
  -d '{"quote_id":"<quote_id>"}'

# Confirm
curl -X POST $BASE/v1/bookings \
  -H "X-Tenant-Id: $TENANT_ID" -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: test-book-001" \
  -H "Content-Type: application/json" \
  -d '{"hold_id":"<hold_id>","quote_id":"<quote_id>","guest":{"primary":{"full_name":"Test","email":"test@example.com","phone":"+84900000000"}},"payment":{"method":"card","provider_token":"tok_test","billing_country":"VN"}}'
```

## 6. Connect via MCP [#6-connect-via-mcp]

```json
{
  "mcpServers": {
    "hospitality-commerce": {
      "url": "https://mcp.hostrail.dev/mcp",
      "transport": "streamable-http"
    }
  }
}
```

Use the same `Authorization: Bearer ...` lane for MCP that you use for
protected API calls.

# Docs for AI Agents (/docs/quickstart/for-docs-agents)

Audience: ai_agents_reading_docs, retrieval_first_assistants
Best For: choosing the right docs surface before reading markdown, understanding docs corpus vs live product surface
Depends On: docs-site machine surfaces
Switch To Surfaces: https://docs.hostrail.dev/llms.txt, https://docs.hostrail.dev/capabilities.json, https://hostrail.dev/capabilities.json, https://api.hostrail.dev/.well-known/agent-card.json



If you are an AI agent consuming the docs site itself, do not start by scraping
HTML. Hostrail publishes machine-readable documentation surfaces for this docs
site in addition to the booking platform surfaces on `hostrail.dev`.

## Recommended order [#recommended-order]

1. Fetch `https://docs.hostrail.dev/llms.txt`
2. Fetch `https://docs.hostrail.dev/capabilities.json` if you want structured hints about this docs corpus
3. Fetch `https://docs.hostrail.dev/graph.json` if you want the whole docs navigation graph in one request
4. If you need broader context, fetch `https://docs.hostrail.dev/llms-full.txt`
5. When you need one specific page, fetch its per-page markdown route under
   `https://docs.hostrail.dev/llms.mdx/docs/.../content.md`
6. If you only need page-level routing hints, fetch its metadata route under
   `https://docs.hostrail.dev/docs/meta/...`
7. Use `https://hostrail.dev/llms.txt`, `https://hostrail.dev/capabilities.json`,
   and the Agent Card only after you know which product surface you need

## Docs-site machine surfaces [#docs-site-machine-surfaces]

| Surface           | URL                                                      | Best use                                                            |
| ----------------- | -------------------------------------------------------- | ------------------------------------------------------------------- |
| Docs index        | `https://docs.hostrail.dev/llms.txt`                     | Cheap first fetch for what exists in the docs                       |
| Docs capabilities | `https://docs.hostrail.dev/capabilities.json`            | Structured summary of audiences, entry points, and machine surfaces |
| Docs graph        | `https://docs.hostrail.dev/graph.json`                   | Whole-site navigation graph with typed edges for offline traversal  |
| Full docs corpus  | `https://docs.hostrail.dev/llms-full.txt`                | Long-context grounding over the whole docs set                      |
| Per-page markdown | `https://docs.hostrail.dev/llms.mdx/docs/.../content.md` | Pull exactly one page in markdown form                              |
| Per-page metadata | `https://docs.hostrail.dev/docs/meta/...`                | Fetch structured hints for one page without parsing markdown        |
| Ask AI UI         | `https://docs.hostrail.dev/docs`                         | Human-assisted lookup, not primary machine interface                |

## When to use which surface [#when-to-use-which-surface]

* Use `docs.hostrail.dev/llms.txt` when you need to decide which docs page to open.
* Use `docs.hostrail.dev/capabilities.json` when you want structured hints before reading markdown.
* Use `docs.hostrail.dev/graph.json` when you want to plan multiple hops across the docs without repeated route lookups.
* Graph edges tell you whether a page is a prerequisite, a good next hop, a deeper follow-up, a loose related page, or a cue to switch to a live product surface.
* Use `docs.hostrail.dev/llms-full.txt` when you need broader semantic grounding.
* Use `docs.hostrail.dev/docs/meta/...` when you already know a page path and want its audience, best-for, prerequisites, and switching hints.
* Use a per-page markdown route when you already know the page you want.
* Use the root-site surfaces on `hostrail.dev` when you are discovering the
  live product surface, not the docs corpus.

## Important distinction [#important-distinction]

The docs site and the booking platform expose different machine-readable roles:

* `docs.hostrail.dev` tells you how Hostrail works
* `hostrail.dev` tells you what the public booking surface exposes
* `api.hostrail.dev` tells you the exact execution contract

Do not confuse documentation markdown with live booking truth. For execution:

* quote output is authoritative for live price and cancellation terms
* Agent Card and OpenAPI are authoritative for route and schema details
* docs pages explain intended behavior and recovery patterns

## Suggested agent workflow [#suggested-agent-workflow]

```text
Need conceptual guidance? → docs.hostrail.dev/llms.txt
Need structured site hints? → docs.hostrail.dev/capabilities.json
Need whole-site navigation graph? → docs.hostrail.dev/graph.json
Need broad grounding? → docs.hostrail.dev/llms-full.txt
Need page-level hints only? → docs.hostrail.dev/docs/meta/<slug>
Need one exact topic? → docs.hostrail.dev/llms.mdx/docs/.../content.md
Need live product capability? → hostrail.dev/capabilities.json
Need exact operation contract? → api.hostrail.dev/.well-known/agent-card.json or /openapi.json
```

## Related pages [#related-pages]

* [Agent Quickstart](/docs/quickstart/for-agents)
* [Developer Quickstart](/docs/quickstart/for-developers)
* [Discovery](/docs/concepts/discovery)

