Skip to main content

Idempotency & reliability

Three promises, and what backs each one:

1. Nothing is ever lost

Every business event is written to a durable event store in the same database transaction as the case change — an update cannot exist without its event. Webhook delivery retries on a ladder (0s → 30s → 2m → 10m → 1h → 6h → 24h); after that the event parks replayable for 30 days (self-serve). The poll API is a second, independent path to the same events.

2. Nothing is applied twice

BoundaryMechanism
Your batch pushesContent-hash dedupe: an identical committed batch is a guaranteed no-op. Optionally send an Idempotency-Key header — a replay returns the original response verbatim.
Your payment confirms (Mode B)Unique on (loan, reference) — a replay gets 409 DUPLICATE_CONFIRMATION with the original booking.
Our webhooksAt-least-once delivery — you dedupe on eventId (retries and replays are normal, not errors).

3. Order is recoverable, not guaranteed

Retries make in-order delivery impossible, so every event carries a per-case monotonic sequence. Receiver rule:

  • apply only if sequence > the last you applied for that loan;
  • on a gap, fetch GET /cases/{loan} for the authoritative snapshot;
  • payloads carry absolute values (totalCollected, outstandingAfter) — a missed intermediate event can never corrupt your books.
Seen in practice

Under concurrent delivery a case.closed (seq 4) can genuinely arrive before the payment.collected (seq 2–3) that caused it. A sequence-aware receiver handles this without special cases — the terminal event's absolute totals are already correct.