Discovery + 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.
This is the contract that makes Hostrail's booking layer predictable across quote, hold, booking, guest servicing, and error recovery.
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:..." }
}| 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": {
"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
| 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
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_actionsKey rules:
- Match on
error.code, not HTTP status — multiple codes share 409. detailis structured — access fields directly, never regex the message.next_actionstells you what to call next — prefer over hardcoded URLs.
How do agents discover this API?
Root-site machine surfaces, Agent Card, OpenAPI, and MCP server metadata — the discovery documents an agent fetches before making any business call.
How does error handling work?
44 stable error codes with structured detail, imperative remediation, and typed recovery actions for recovery-safe booking automation.