Idempotency and retries

Keep one logical operation stable across interrupted writes, and reconcile before changing its identity.

Persist the operation before sending

For supported company /v1 unsafe methods, send an Idempotency-Key header. In Node, use the request option { idempotencyKey }; in Python, use idempotency_key=.... Store a unique key for one logical operation, its method/path, original body bytes, company, key mode and credential identity before the first request. Never generate a new key for every network attempt.

curl --fail-with-body https://api.kuahr.com/v1/payroll/runs \
  -X POST -H "X-Api-Key: $KUA_API_KEY" \
  -H "Idempotency-Key: $PAYROLL_OPERATION_KEY" \
  -H "Content-Type: application/json" \
  -d '{"year":2026,"month":11}'

The draft tutorial explains its required Test key and salary-ready fixture. A header does not grant scope, bypass approval or promise a supported Test partition.

What a retry matches

The current implementation scopes receipts to credential identity, company/workspace, HTTP method, request path and key. Bodies are compared by the hash of their raw bytes, not equivalent JSON values. Changed whitespace or field ordering can therefore conflict. Only successful 2xx results are stored; authority is rechecked before a stored response is returned.

SituationRequired action
Same credential/path/key/body, retained successful receiptThe recorded response is returned; inspect its original operation ID.
Same key with changed body bytes409 IDEMPOTENCY_KEY_MISMATCH. Recover the original request; a new key belongs only to a genuinely new intended operation.
IDEMPOTENCY_LEGACY_RECONCILIATION_REQUIREDThe operation may already have completed. Reconcile it; do not send a new key to get past the conflict.
Validation/permission failureFix the stated prerequisite. A failed response is not a retained successful receipt.
Lost response, timeout or 5xxOutcome may be unknown. Keep the same request identity, inspect authoritative state and retry only within the operation’s supported policy.

Credential and retention boundaries

Rotating an API key creates a new credential identity while preserving its mode/name/scopes. Retrying an unknown write under that replacement credential is a new idempotency namespace, even with the same header key. Reconcile before rotation or replaying with another key.

The daily sweep removes nonfinancial v2 receipts older than seven days. Financial receipts and legacy reconciliation tombstones are retained by the current implementation. A missing or expired receipt is not proof that the original effect never happened. Reconcile old operations before intentionally creating another.

These rules apply to the public API at api.kuahr.com; see release notes for changes. Treat idempotency as a scoped retry mechanism, not a permanent business uniqueness constraint.

Keep other protocols separate

Webhook recovery has a body requestKey plus stable reason and caller rules; use replay guidance, not a new generic header key, to recover a delivery. OIDC token exchange uses a short-lived one-time authorisation code: if its outcome is lost, restart the controlled Kola authorisation journey rather than assuming the code can be replayed.

Set bounded retry behavior

Both SDKs leave retries to your application. Back off with jitter for transient errors and 429, cap attempts and preserve unknown outcomes for investigation. Do not retry validation or authorisation failures blindly. Keep durable event-ID deduplication for receivers and destination-side uniqueness for consequential effects.

Done when: an interrupted request reuses its original operation identity, a different business action gets a new identity, and expiry/rotation/legacy conflicts lead to reconciliation rather than duplicate execution.

Search documentation

↑ ↓ to move · Enter to open · Esc to close