Skip to main content
Every error response uses the same JSON envelope. Branch on code, not on the message — messages are English-only (for your logs) and may be reworded; code is stable and documented.
{
  "code": "validation.required.field",
  "message": "Field 'rate_pack_id' is required.",
  "request_id": "req_8f3a1c9e2b7d40",
  "details": { "field": "rate_pack_id" }
}
FieldNotes
codeStable i18n code, form domain.subdomain.detail. Branch on this.
messageEnglish, for your logs. Never localized server-side.
request_idQuote this when contacting support — it correlates with Safariat logs.
detailsOptional structured context (offending field, expected value).

HTTP status mapping

StatusMeaning
400Malformed request (syntactic validation).
401API key missing, invalid, expired, or incorrect HMAC signature.
403Authenticated but not authorized (missing scope, non-allowlisted IP).
404Resource does not exist or is inaccessible for this partner.
409Same Idempotency-Key reused with a different body. No action performed.
422Syntactically correct but a business rule was violated.
429Rate limit exceeded for the current key.
500Internal error — Safariat is notified automatically.
503Service temporarily unavailable. Retry later.

Stable error codes

These codes are part of the contract and will not be renamed within v1.
CodeTypical statusWhen
auth.api_key.missing401No Authorization bearer sent.
auth.api_key.invalid401Unknown, expired, or revoked key.
auth.signature.invalid401X-Signature does not match (production writes).
auth.timestamp.skew401X-Timestamp outside the ±5 min window.
validation.required.field400A required field is missing — see details.field.
adventure.not.found404Unknown adventure_slug.
adventure.capacity.exhausted422No remaining capacity on the requested slot.
booking.not.cancellable422Booking already cancelled/completed or past its cancellation window.
payment.amount.mismatch422payment_confirmation amount differs from the quote.
partner.credit.limit.exceeded403Partner credit ceiling reached.
idempotency.key.conflict409Idempotency-Key reused with a different body — see Idempotency.
rate_limit.exceeded429See Rate limiting.
Unrecognized codes may be added over time (additive, never breaking). Treat an unknown code as a generic failure of its HTTP status class.