KSeF Faktury
EN
Join the start list
Early access

The API is in early access: we hand out test keys (KSeF TEST) in batches to people on the launch list. Live mode comes later.

Request access

Guides

Idempotency and safe retries

A KSeF invoice is a legal document with a gapless number, so a duplicate is worse than a delay. Two mechanisms make sure every order yields exactly one invoice.

Why it matters

You sent an order and the connection dropped before the answer. Was the invoice created? Without idempotency a retry could issue a second invoice with the next number and send it to KSeF. With idempotency you retry without worry and get the same invoice.

Two layers of protection

  1. The Idempotency-Key header on every POST. We store the first response for 24 hours and return it for the same key and the same body.
  2. The order's natural key. Regardless of the header, one order.external_id yields one invoice and one refund.external_id one correction. Sending the order again returns the existing invoice with 200 instead of 201, also after 24 hours.

How Idempotency-Key works

Situation Response
First request with a key The normal response, e.g. 201
Same key and same body within 24 h The same response plus Idempotent-Replayed: true
Same key, different body 409 idempotency_key_reused
Empty key or longer than 255 characters 400 idempotency_key_invalid
POST /v1/invoices
curl -X POST https://app.ksefshop.pl/v1/invoices \
  -H "Authorization: Bearer $KSEF_API_KEY" \
  -H "Idempotency-Key: order-SO-1001" \
  -H "Content-Type: application/json" \
  -d '{
  "order": {
    "external_id": "SO-1001",
    "number": "1001",
    "paid_at": "2026-10-19T09:13:05Z",
    "currency": "PLN",
    "payment_method": "card",
    "buyer": {
      "nip": "5260250274",
      "name": "Testowa Firma sp. z o.o.",
      "address_line1": "ul. Prosta 20",
      "address_line2": "00-850 Warszawa",
      "email": "jan@example.com"
    },
    "lines": [
      {
        "name": "Kubek ceramiczny 350 ml",
        "sku": "KUB-350",
        "quantity": 2,
        "unit_price": "49.00",
        "vat_rate": "23"
      },
      {
        "name": "Książka: Podstawy księgowości",
        "sku": "KS-001",
        "quantity": 1,
        "unit_price": "59.90",
        "discount": "9.90",
        "vat_rate": "5"
      }
    ],
    "shipping": [
      {
        "name": "Kurier InPost",
        "price": "15.99",
        "vat_rate": "23"
      }
    ]
  }
}'

Choosing a key

The key describes the operation, not the attempt. Good keys are order-SO-1001 for an invoice and refund-RF-1 for a correction. A random UUID is fine too if you generate it once and store it with the order. A new UUID per attempt defeats the purpose.

What to retry

  • Network error or timeout: retry with the same key.
  • 429 rate_limited: wait as many seconds as Retry-After says.
  • 5xx: retry with exponential backoff (1, 2, 4, 8 s...).
  • Other 4xx: do not retry, fix the request. The exception is 409 pdf_not_ready when downloading the PDF.
Retrying with the same key
# --retry repeats on network errors, 429 and 5xx and honours Retry-After
curl --retry 5 --retry-connrefused -X POST https://app.ksefshop.pl/v1/invoices \
  -H "Authorization: Bearer $KSEF_API_KEY" \
  -H "Idempotency-Key: order-SO-1001" \
  -H "Content-Type: application/json" \
  -d @order-SO-1001.json

Idempotency covers every POST: /v1/invoices, /v1/invoices/{id}/corrections and /v1/webhook_endpoints. GET and DELETE requests are idempotent by nature.

KSeF Faktury

API early access

We hand out test keys in batches. Leave your email: we will write when your access is ready.