Concepts
How does idempotency work?
Holds and bookings use client-chosen idempotency keys. Retries return cached responses — including sealed business errors.
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
Send a client-chosen key via the Idempotency-Key HTTP header or idempotency_key body field.
curl -X POST https://api.agenthotel.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.
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
- Generate deterministic keys from the logical intent:
hold_{user}_{quote}_{attempt} - Retry with the same key on network errors
- Use a new key when the intent changes
- Don't retry with a new key after
payment_failedunless you want to re-run payment