Skip to main content
Every error carries a stable code and a request_id. Branch on the code; quote the request_id to support.
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).
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.
X-Timestamp is outside the ±5 minute window. Sync your server clock (NTP); generate the timestamp at send time, not at object construction.
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.
Wait Retry-After seconds, then retry — for writes, with the same idempotency key. Throttle proactively on X-RateLimit-Remaining. See Rate limiting.
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.
The booking is already cancelled/completed, or past its cancellation window. Inspect cancellation_tiers on GET /bookings/{booking_number}.
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.
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.
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.
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).
Still stuck? Email support@safarapi.com with the request_id and the endpoint — it correlates directly with Safariat logs.