code and a request_id.
Branch on the code; quote the request_id to support.
401 auth.api_key.invalid / auth.api_key.missing
401 auth.api_key.invalid / auth.api_key.missing
No bearer sent, or an unknown / expired / revoked key, or the key environment
does not match the call. Check:
Authorization: Bearer sk_test_… is present,
the key is active in the Console, and you are not mixing an sk_test_ key with
production data (or vice-versa).401 auth.signature.invalid (production writes)
401 auth.signature.invalid (production writes)
The HMAC did not match. The canonical string signs the full request path —
/api/partner/v1/..., not /v1/... — and the exact body bytes you send
(any reformatting breaks it). Sandbox sk_test_* keys are exempt from signing;
if you see this on sandbox you are signing unnecessarily and mismatching.
See Authentication.401 auth.timestamp.skew
401 auth.timestamp.skew
X-Timestamp is outside the ±5 minute window. Sync your server clock (NTP);
generate the timestamp at send time, not at object construction.409 idempotency.key.conflict
409 idempotency.key.conflict
The same
Idempotency-Key was reused with a different body. Use one key per
logical operation and reuse the same key only for retries of that exact
request. See Idempotency.429 rate_limit.exceeded
429 rate_limit.exceeded
Wait
Retry-After seconds, then retry — for writes, with the same
idempotency key. Throttle proactively on X-RateLimit-Remaining. See
Rate limiting.422 payment.amount.mismatch
422 payment.amount.mismatch
payment_confirmation amount must equal the quote net_amount, sent as the
same two-decimal string ("5400.00"). Do not round or recompute. See
Money & currency.422 booking.not.cancellable
422 booking.not.cancellable
The booking is already cancelled/completed, or past its cancellation window.
Inspect
cancellation_tiers on GET /bookings/{booking_number}.404 adventure.not.found in sandbox
404 adventure.not.found in sandbox
You used a production slug without the sandbox suffix. The sandbox mirrors the
production catalogue with every slug suffixed
-sandbox (e.g.
marrakech-3-jours-desert-sandbox). List GET /api/partner/v1/adventures and
use a returned slug verbatim. The catalogue refreshes weekly from production —
no manual seeding. See Sandbox.Empty /availability or a quote with no slot (sandbox)
Empty /availability or a quote with no slot (sandbox)
The sandbox availability mirrors production exactly (dates and rate packs are
cloned). An empty result means that production adventure has no upcoming
slot — pick another adventure from
GET /adventures. Not an API defect.Webhook signature does not verify
Webhook signature does not verify
HMAC the raw received bytes before any JSON re-serialization, with the
endpoint’s
signing_secret (returned once at creation) — not your API key.
Compare in constant time. See Webhooks."Try it" returns 404 in this portal
"Try it" returns 404 in this portal
The base URL is
https://api.safarapi.com/api/partner/v1. Paste a sandbox key
in the Authorization field. Catalogue calls also require the sandbox dataset to
be seeded (see above).support@safarapi.com with the request_id and the
endpoint — it correlates directly with Safariat logs.