Skip to content

missing_idempotency_key

HTTP 400 type: validation_error param: Idempotency-Key
{
"error": {
"type": "validation_error",
"code": "missing_idempotency_key",
"message": "POST /v1/invoices requires an Idempotency-Key header. Generate one UUID per sale and reuse it across retries of that sale.",
"param": "Idempotency-Key",
"upstream": null,
"doc_url": "https://docs.fiskhub.com/errors/missing_idempotency_key"
}
}

What happened

POST /v1/invoices was called without an Idempotency-Key header. The request was refused before anything was validated, signed or stored — nothing was fiscalized.

The header is mandatory here and only here. It is optional on every other POST.

Should you retry?

Yes, with the header added. Nothing happened, so resending with a key is safe.

The fix

Generate one key per sale, at the start of the transaction, and reuse it for every retry of that sale.

Terminal window
curl -sS https://api.fiskhub.com/v1/invoices \
-H "Authorization: Bearer $FISKHUB_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{ "device_id": "…", "occurred_at": "…", "total": "4.50", "payment_method": "K" }'
const sale = {
idempotencyKey: randomUUID(), // once, at the start of the transaction
// ...
};
// Every attempt to fiscalize THIS sale uses that same key, unchanged.
await post("/v1/invoices", body, { "Idempotency-Key": sale.idempotencyKey });

A UUIDv4 is the recommended form. Any string up to 255 characters works, as long as it is unique per sale.

Why it is required rather than optional

The first invariant of this system is that a sale is never fiscalized twice: one transaction, at most one JIR, forever.

A double-fiscalized sale is not a duplicate row somebody tidies up later. It is a second fiscal receipt filed with the Croatian Tax Administration for money that was taken once. The records then show revenue that does not exist and the operator’s VAT return is wrong — and the operator carries that, not the integrator who sent the second request.

The circumstances that produce a double-submission are ordinary: a lost response on a marginal connection, a client timeout after our fiscalization already succeeded, a supervisor process restarting mid-request, an operator retrying a machine that appeared to hang. The Idempotency-Key makes all of those safe.

Making it optional would mean the safe path is the one you have to remember. See idempotency.