aaune

Recovery contract

Errors, retries, and idempotency

Every `/v1` response carries request, trace, contract, and server-time metadata. Errors use `application/problem+json` with a stable `code` and recovery guidance.

Error media type
application/problem+json
Correlation
X-Request-Id + X-Trace-Id + traceparent
Version
Aune-Contract-Version
Backoff
Retry-After + RateLimit-*

Retry matrix

  • `400` or `422`: fix the payload; do not retry unchanged.
  • `401`: acquire or refresh a token; do not downgrade to a shared credential.
  • `403`: request user approval for the missing scope; do not broaden scopes silently.
  • `404`: verify the opaque ID and authenticated principal; do not infer deletion or confirmation.
  • `409` with `option_refresh_required: true`: search again and ask the user to choose again.
  • `409` idempotency conflict: generate a new key only for a genuinely new user action.
  • `429`: wait at least `Retry-After`, then use exponential backoff with jitter.
  • `500`, `502`, `503`, `504`: retry reads; retry writes only with the unchanged idempotency key and payload.

Unknown confirmation outcome

If the connection ends after sending confirmation, retry the same option, payload, and idempotency key. A replay returns the same booking. Never search a replacement and confirm it automatically; that could create two user commitments.

Problem shape

Expired option
{
  "type": "https://aune-homepage-chat.vercel.app/problems/booking_option_expired",
  "title": "booking option expired",
  "status": 409,
  "detail": "Search for fresh options and ask the user to choose again.",
  "instance": "/v1/bookings",
  "code": "booking_option_expired",
  "option_refresh_required": true,
  "api_request_id": "req_...",
  "trace_id": "0123456789abcdef0123456789abcdef",
  "contract_version": "public_bookings_api_v1"
}