> ## Documentation Index
> Fetch the complete documentation index at: https://developers.safarapi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Troubleshooting

> Symptom → cause → fix for the errors integrators actually hit

Every error carries a stable [`code`](/api-concepts/errors) and a `request_id`.
Branch on the code; quote the `request_id` to support.

<AccordionGroup>
  <Accordion title="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).
  </Accordion>

  <Accordion title="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](/introduction/authentication).
  </Accordion>

  <Accordion title="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.
  </Accordion>

  <Accordion title="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](/api-concepts/idempotency).
  </Accordion>

  <Accordion title="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](/api-concepts/rate-limiting).
  </Accordion>

  <Accordion title="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](/api-concepts/money).
  </Accordion>

  <Accordion title="422 booking.not.cancellable">
    The booking is already cancelled/completed, or past its cancellation window.
    Inspect `cancellation_tiers` on `GET /bookings/{booking_number}`.
  </Accordion>

  <Accordion title="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](/introduction/sandbox).
  </Accordion>

  <Accordion title="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.
  </Accordion>

  <Accordion title="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](/api-concepts/webhooks).
  </Accordion>

  <Accordion title="&#x22;Try it&#x22; 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).
  </Accordion>
</AccordionGroup>

Still stuck? Email `support@safarapi.com` with the `request_id` and the
endpoint — it correlates directly with Safariat logs.
