Idempotency

POST /v1/invoices requires Idempotency-Key. Use it to make invoice submission retries safe without creating duplicate submissions.

Idempotency-Key

Send an Idempotency-Key header with every POST /v1/invoices request.

Required header
Idempotency-Key: inv-submit-sandbox-fv-0001

Reuse the same key only when retrying the same invoice submission request.

Request fingerprint

The API compares a canonical request fingerprint built from:

  • normalized external_id
  • decoded raw invoice_xml
  • sorted string-only metadata
  • for test mode only, the normalized sandbox scenario

Missing Ksef-Sandbox-Scenario and explicit accepted are the same semantic sandbox request.

Replay

Replaying the same Idempotency-Key with the same canonical request returns the exact stored HTTP status and response body.

If the original successful submission returned 201 Created, the replay also returns 201 Created.

Replay returns the stored POST response
{  "id": "inv_2kCYq4m4G7YbUjvH0kC1dg",  "object": "invoice",  "mode": "test",  "status": "processing",  "organization_id": "org_2kCYq4m4G7YbUjvH0kC1dg",  "external_id": "fv-2026-0001",  "created_at": "2026-05-31T10:15:30Z",  "updated_at": "2026-05-31T10:15:30Z",  "ksef_number": null,  "upo_available": false,  "retrying": false,  "next_retry_at": null,  "last_error": null,  "metadata": {    "order_id": "ord_123"  }}

Conflict

Reusing the same Idempotency-Key with a different canonical request returns idempotency_conflict.

idempotency_conflict
{  "error": {    "type": "conflict_error",    "code": "idempotency_conflict",    "message": "The same idempotency key was used with a different request.",    "param": "Idempotency-Key",    "request_id": "req_0Y9a6cW9xQz9xwWzYxA0fY",    "retryable": false  }}

In-progress request

If the same key is still being processed, the API can return idempotency_request_in_progress.

idempotency_request_in_progress
{  "error": {    "type": "conflict_error",    "code": "idempotency_request_in_progress",    "message": "The same idempotency key is already being processed.",    "param": "Idempotency-Key",    "request_id": "req_0Y9a6cW9xQz9xwWzYxA0fY",    "retryable": true  }}

Previous live attempt failed

If a previous live attempt failed before KSeF submission, the original key is terminal. Submit the same request again with a new Idempotency-Key.

idempotency_previous_attempt_failed
{  "error": {    "type": "conflict_error",    "code": "idempotency_previous_attempt_failed",    "message": "The previous live submission attempt failed before KSeF submission. Submit the same request again with a new Idempotency-Key.",    "param": "Idempotency-Key",    "request_id": "req_0Y9a6cW9xQz9xwWzYxA0fY",    "retryable": false  }}

Reconciliation required

If a previous live submit outcome is ambiguous, the API returns idempotency_reconciliation_required. Do not automatically retry the same key.

idempotency_reconciliation_required
{  "error": {    "type": "conflict_error",    "code": "idempotency_reconciliation_required",    "message": "The previous live submission outcome is not safe to retry automatically. Operator reconciliation is required before using this idempotency key again.",    "param": "Idempotency-Key",    "request_id": "req_0Y9a6cW9xQz9xwWzYxA0fY",    "retryable": false  }}

Read requests

Read operations do not require Idempotency-Key:

  • GET /v1/invoices/{id}
  • GET /v1/invoices/{id}/upo