Lewati ke konten utama

Retries and idempotency

Both directions are built so that "send it again" is always safe.

Your calls to XQUARE: Idempotency-Key​

Send an Idempotency-Key header on every POST. Use a value that identifies the action on your side — a receipt number, a scan id, a UUID you store with the attempt.

  • Same key and same body, within 24 hours → you get the first response back, with the header Idempotent-Replayed: true; nothing happens twice.
  • Same key with a different body (or another endpoint) → 409 idempotency_key_reused; use a new key for a new action.
  • Same key while the first request is still running → 409 idempotency_request_in_progress with Retry-After; retry with the same key.
  • No key on a POST → 400 idempotency_key_required.
  • Keys are scoped to the API key that sent them: another API key's identical Idempotency-Key never replays your response.
  • A 5xx answer is not stored: retrying with the same key runs the request again.
  • Answers you can fix without changing the request are not stored either: 401, 403, 408, 409, 425, 429, and credits_exhausted / credits_suspended / no_payer / allowance_exhausted. Fix the key or plan, top up (or wait for Retry-After) and retry with the same key. Other 4xx (400, 404, 422) are stored for 24 hours like a success.

Retry on network errors, timeouts, 429 (after Retry-After) and 5xx, with the same key. Do not retry other 4xx — fix the request.

Voucher and sponsor redemptions are also protected inside the database: a retry with the same Idempotency-Key that gets past the replay above still redeems (and charges) only once.

XQUARE's deliveries to you: retries​

A delivery counts as delivered when your endpoint answers 2xx. Otherwise:

Your answerWhat XQUARE does
2xxDelivered. Done.
timeout, connection error, 408, 429, 5xxRetries on the schedule below
410 GoneStops, and pauses the endpoint — you said it is gone
any other 4xx, any 3xxStops at once (no retry)

Retry schedule — the wait before each next attempt:

After attempt1234567
Wait1 min5 min30 min2 h6 h12 h24 h

Each wait has up to 10 % random jitter. The 8th failed attempt is final, about 44.6 hours after the first. If you send Retry-After (in seconds) and it is shorter than the next wait, XQUARE retries sooner; it never waits longer than the schedule.

A delivery that fails for good is marked failed. You can send it again from the Connections page in the vendor panel; a resend keeps the same delivery id. An endpoint that keeps failing is marked failing and then disabled, and the Space owner gets a notice (see Receive and verify webhooks).

Deduplicating on your side​

Retries mean you can receive the same event more than once. Deduplicate on the event id in the body (stable for that event), or on X-XQ-Delivery (stable across retries and resends of one delivery). Store the ids you have processed for at least 3 days — longer than the full retry window.

Order is not guaranteed: two events for the same voucher can arrive out of order after a retry. Use created_at in the envelope, or fetch the current state from the API, when order matters.