Idempotency

Network retries are inevitable: requests time out, connections drop, your job runner reschedules. The OneFinOps API gives you a deterministic way to retry without risk of duplicate IRNs or duplicate e-Way Bills.

The header

Send Idempotency-Key: <your-key> on every state-changing call (POST, PUT, PATCH, DELETE).

POST /v1/einvoices HTTP/1.1
Host: api.in.onefinops.com
Authorization: Bearer ...
Idempotency-Key: invoice-INV-2026-00001-attempt-1
Content-Type: application/json

Pick anything stable per logical business action — most customers use their own internal request id or a hash of (invoice_id, action, attempt). Keys are scoped per OAuth client; reusing a key under a different client is a separate logical request.

What we guarantee

ScenarioBehaviour
First request with key KRuns normally; response is cached under K.
Replay with key K and identical bodyReturns the cached response with header Idempotent-Replay: true.
Replay with key K and different bodyReturns 409 idempotency.key_reused_mismatch.
Same key after 24 hoursTreated as a brand-new request.
GET request with a keyHeader is ignored — GETs are inherently idempotent.

The body-hash check is exact (SHA-256 of the raw request body). Re-serializing JSON in a different field order will trip the mismatch check — pick a key per logical attempt, not per retry, and don't re-serialise between attempts.

What we do not guarantee

  • Cross-environment replay. Sandbox and production are separate stores; the same key in both is two distinct requests.
  • Side effects beyond the immediate response. Idempotency caches the API response, not downstream effects. If your handler queues an event and the call is replayed within 24 hours, the queued event is not re-emitted — but anything you did before the call (e.g. wrote to your own DB) is yours to manage.

Recommended pattern

For e-invoices and e-Way Bills, key your idempotency on a deterministic, durable id from your own system:

Idempotency-Key: einvoice-<invoice_id>
Idempotency-Key: ewb-<invoice_id>-v1

Bump the suffix (-v2, -v3) only when the logical request changes — a body edit, a retry with corrected fields. That way, every retry of the same logical attempt collapses onto the same key.

What X-Request-Id is for

If you don't send Idempotency-Key, we'll fall back to X-Request-Id, then Request-Id, then we generate one server-side. The fallback gives the same dedup behaviour but is intended primarily for logging correlation — for production integrations, send Idempotency-Key explicitly so the intent is unambiguous.

The chosen id is echoed on every response as X-Request-Id and embedded in error envelopes' requestId field for support correlation.