Why is this a tax problem rather than a data problem?
In most systems a duplicate is an operational nuisance: you find it, you delete it, you fix the cause. In KSeF none of those three steps is available in the usual form, and that changes what kind of defect this is.
When KSeF accepts a faktura ustrukturyzowana (structured invoice) it assigns a numer KSeF and the document stays in the register permanently. It can be corrected but never deleted. The second document is therefore not a stray row in a table; it is an invoice in legal circulation, issued by the Polish taxpayer, evidencing a supply that was invoiced once already. Removing its effects means issuing a correcting invoice and explaining why one was needed.
Three consequences follow that a purely technical view of duplicates misses. First, the counterparty sees it: the duplicate is in their KSeF retrieval feed, and an automated purchase-side process on their side may book it before anyone on yours has noticed. Second, from 1 January 2027 art. 108g of the VAT Act requires the numer KSeF to appear in the payment reference for a structured invoice, so a duplicate stops being contained within accounting and propagates into settlement, where mismatches are noticed by third parties. Third, from the same date the penalty regime in art. 106ni begins to apply, and the record of how the duplicate arose becomes evidence rather than internal documentation.
Where do duplicate structured invoices come from?
Networks fail asymmetrically. A request can reach the server and be processed while the response is lost on the way back. From the client's point of view the two cases — it never arrived
and it arrived but the answer was lost
— look identical: no response within the allotted time.
Three scenarios produce the same outcome:
- Connection timeout. The classic case. The default policy
retry three times with exponential backoff
, built into most HTTP libraries, manufactures duplicates in this context rather than preventing them. - Process restart mid-submission. The container is restarted between the request going out and the response being written down. On start-up the queue contains a document that looks unprocessed.
- Parallel processing of the same item. Two workers take the same job from the queue because a lock was not acquired correctly or had expired.
The partially failed batch
In KSeF API 2.0 the batch session processes each invoice independently instead of rejecting the whole batch because of one error. That is a change for the better, but it changes error handling with it. After a partially failed batch you must not resend the batch as a whole — the result has to be settled document by document. Integrations migrated from version 1.0, where resending the entire batch was the correct response, duplicate documents at exactly this point, and they do it in volume rather than one at a time.
What should a system do after a failed call?
The rule is simple and rarely implemented: after a failed call, do not send the document again — first establish what happened to it.
In practice that means a fixed sequence:
- Give the document its own reference identifier on your side, before making any call. It must be durable and deterministic — derived from the source document, not generated afresh at each attempt.
- Write the state
submission started
to durable storage before the call, not after it. - After a timeout, query the session or document status rather than resubmitting.
- Resubmit only where you have unambiguous information that the document was not accepted.
- On acceptance, store the numer KSeF and the UPO in a durable link to the source document.
Step two is the one most often omitted. A system that records state only after a response has been received has, following a restart, no trace that an attempt was ever made — and the queue will helpfully offer the document up again. Step three usually requires an explicit decision: the retry behaviour that is correct for a read is wrong for an issue, and in most stacks it has to be switched off deliberately rather than left at its default.
Why is a sent or not-sent flag insufficient?
A two-value representation cannot distinguish certainly not
from I do not know
, and the whole problem lives in that gap. The minimum state set looks like this.
| State | Meaning | Permitted action |
|---|---|---|
prepared | Document built, not yet submitted | Submit |
submission_in_flight | Request sent, no response | Status query only |
accepted | Numer KSeF assigned, UPO received | None — terminal state |
rejected | Unambiguous refusal with an error code | Correct and submit as a new attempt |
offline_pending | Issued offline, awaiting transmission | Transmit within the deadline for that mode |
submission_in_flight is the state that prevents duplicates. While a document remains in it, the only permitted operation is a query — never a resubmission. offline_pending carries its own deadline: a document issued under tryb offline24 (the elective offline mode) must be transmitted by the next working day after the date of issue, while the niedostępność (announced unavailability) and awaria (announced failure) modes run on their own clocks. Which mode applied, and on what basis, is recorded only in your system — see the KSeF audit trail.
How do you detect a duplicate before your counterparty does?
State control protects against duplicates arising in the transport layer. It does not protect against logical duplicates — two documents built from the same business event, for example after an import is re-run. For those, only reconciliation works, and it has to run on a cycle rather than at month-end close.
- Count reconciliation. The number of documents in the
acceptedstate on your side against the number of invoices in KSeF for the same period. - Logical repeat detection. A query for documents with the same counterparty, amount and date of sale issued within a short interval.
- Orphan control. Documents in KSeF with no counterpart in your register, and the reverse.
The third is the one that matters most operationally, because it catches the document that KSeF accepted at the moment your system concluded the submission had failed. Without that check, such an invoice functions in legal circulation while not functioning in your books — and the first person to notice is usually the customer.
There is a quick way to find out whether any of this is in place. Take an invoice from three months ago and establish whether an earlier attempt was made on it before the one that succeeded. If answering that requires reading application logs, you have no record of attempts — which means you cannot distinguish a duplicate from a coincidence, and you cannot show an inspector that a given document was submitted exactly once.
What do you do once a duplicate exists?
An invoice accepted by KSeF cannot be deleted. The surplus document is corrected by a correcting invoice, and it is worth remembering that correcting invoices are now prepared in the FA(3) logical structure even where the original document was created earlier under FA(2) or FA(1). That mismatch is itself a recurring source of rejections, which means the correction of a duplicate is a slightly higher-risk operation than the original submission was.
Beyond the correction, document the event: the cause, the moment it arose, the moment it was detected, how the effects were removed, and the change introduced to prevent recurrence. From 1 January 2027 that documentation stops being good practice. The penalties in art. 106ni are ceilings — up to 100% of the tax shown on an invoice issued outside KSeF, and up to 18.7% of the total amount due where no tax is shown — and the mechanism that moderates them is art. 189d of the Kodeks postępowania administracyjnego, which obliges the authority to take account of the gravity and circumstances of the breach, its frequency, and the party's previous conduct. See KSeF penalties from 1 January 2027.
Which entity in a group carries the consequence?
The obligation and any penalty attach to the Polish taxpayer whose invoice it is — not to the shared service centre, not to the group integration platform, not to the vendor that made the call. Whether an entity is caught at all turns on establishment rather than VAT registration: a taxpayer with neither a seat nor a fixed establishment in Poland is outside the obligation to issue structured invoices, and a foreign-established company with a Polish fixed establishment is in scope where that establishment participates in the supply. The limit comes from EU law — Article 218 of the VAT Directive, as amended by Council Directive (EU) 2025/516 of 11 March 2025, permits a Member State to require electronic invoices only from taxable persons established within its territory. Full scoping is set out under automating KSeF invoicing.
What does a group finance function have to do differently?
The engineering of idempotency is the same everywhere. What differs in a multinational is where the decisions are made, and each of these four is a common source of duplicates in practice.
Retry policy is usually set centrally, and centrally it is wrong. Group integration platforms and API gateways carry a house retry policy, applied by default to every outbound call. For Polish issuing endpoints that default has to be disabled explicitly, at the endpoint level, and the exception has to survive the next platform upgrade. This is the single highest-value change on the list and it is normally owned by a team that has never heard of KSeF.
The operation identifier has to be unique across the group, not within an entity. Where several Polish entities are served by one ERP instance and one queue, an identifier derived from the document number alone will collide between entities. Derive it from the issuing taxpayer's identity together with the source document, so that a retry is recognised and a genuinely different entity's document is not.
Reconciliation is per taxpayer. KSeF is queried on behalf of one taxpayer at a time, so a group-level count against a group-level ledger proves nothing about any individual entity. Build reconciliation per entity and roll it up afterwards, not the other way round.
The date of issue is a Polish date. KSeF recognises only P_1 as the date of issue. Where a group scheduler runs on a different clock or a different working calendar, P_1 can disagree with the moment the document actually left your systems — a quiet source of missed deadlines in the offline modes, and of reconciliation differences at period boundaries that get misdiagnosed as duplicates.
One market's design assumptions do not transfer. A group running a single e-invoicing programme across several countries usually reuses one submission component. Under a format-only mandate such as Germany's E-Rechnung, the invoice is exchanged directly between the parties: there is no state acceptance step, so there is no outcome that can be unknown and no identifier assigned by anyone but you. Poland is a clearance model — the state validates the document, assigns its identifier and returns it, and that acceptance step sits inside your issuing process rather than downstream of it. A component designed for the first model has no concept of submission_in_flight, and that missing state is where cross-border implementations produce their first duplicate. The glossary sets out the vocabulary each market uses.
What changes when an AI agent decides to retry?
A deterministic process retries according to a rule somebody wrote down. An AI agent may decide to retry on the basis of its own reading of an error message — and models are inclined to interpret an ambiguous result as a failure and try again. That inclination is precisely the wrong instinct here, because ambiguity is the state in which a resubmission is most likely to create a duplicate.
Where a model sits in the action loop, idempotency cannot be left to its judgement. It has to be enforced in a layer the agent passes through and cannot bypass: the operation identifier assigned outside the model, a repeated call bearing the same identifier refused, and every attempt recorded regardless of how the agent interpreted it. This is the general pattern the library sets out for approving AI actions before execution, with the evidence requirements under AI agent audit trails. The mechanics of the calls themselves are covered under designing a reliable KSeF API integration, and the surrounding control design under approval checkpoints.
In practice
BarzelVault enforces the idempotency check at the level of the operation, including where an AI agent initiates it, and issues a signed audit receipt for every attempt. The receipt is created before the call leaves the system, which is the part of the record that cannot be reconstructed afterwards.
Frequently asked questions
Does a timeout mean the invoice was not accepted?
No. It means only that no response arrived. The document may have been accepted and already assigned a numer KSeF.
How do I check after a failed call?
Query the status using your own reference identifier instead of sending the document again. Only an unambiguous negative justifies a resubmission.
Is a unique invoice number enough?
No. The invoice number is an attribute of the document, not of the operation. Two attempts at the same document are two operations, and it is the operation that needs an identifier.
Does a batch session fail as a whole?
Not in API 2.0 — each invoice is processed independently. Resending a whole batch after a partial failure creates duplicates.
What do we do once a duplicate exists?
Correct it with a correcting invoice in FA(3) and document the whole event. Deleting a document from KSeF is not possible.
Who is exposed — the group or the Polish entity?
The Polish taxpayer. There is no group filing, so the evidence must be attributable to a single entity.
Related reading
- Designing a reliable KSeF API integration
- Automating KSeF invoicing
- The KSeF audit trail
- KSeF penalties from 1 January 2027
- Governed workflow automation
- Glossary of local e-invoicing terms
In practice
The control has to run before the invoice becomes irreversible.
An accepted structured invoice can be corrected but never deleted, and from the penalty date every defect has a price. Barzel puts the approval threshold, the duplicate check and the signed record in front of submission, so the process can be defended on the day an auditor or the tax authority asks.
95 days leftKSeF penalties apply from 1 January 2027
BarzelVault
The AI action firewall: decide what an agent may do before it does it.
- Approval thresholds and policy checks enforced before execution; human approvals that expire and escalate.
- Cryptographically signed audit receipts: trigger, inputs, policy version, approver, outcome.
- Credential isolation, spend and action limits, and an emergency kill switch.
Free tier: 10,000 calls a monthPaid plans from $199 a monthLive on MCPize
BarzelOps
Governed workflow automation across the systems that run the business.
- Durable, idempotent execution: a timeout is retried once, never filed twice.
- Human approval checkpoints that pause the workflow and resume it.
- Isolation per entity or client, signed evidence receipts and a portable manifest; HubSpot, Xero, Gmail, Google Drive and Slack.
Free tier: 100 calls a dayPaid plans from $19 a monthLive on MCPize
Enterprise: written quote by email within two business days. No sales call.
Sources
- Ministerstwo Finansów, KSeF 2.0 integration guide — batch sessions and the overview of key changes, github.com/CIRFMF/ksef-api.
- Ministerstwo Finansów, Broszura informacyjna dotycząca struktury logicznej FA(3), 4 March 2026.
- Ustawa z dnia 11 marca 2004 r. o podatku od towarów i usług (Polish VAT Act), art. 106ga, art. 106ni, art. 108g — ISAP.
- Kodeks postępowania administracyjnego (Code of Administrative Procedure), art. 189d — ISAP.
- Ustawa z dnia 5 sierpnia 2025 r. o zmianie ustawy o podatku od towarów i usług oraz niektórych innych ustaw, Dz.U. 2025 poz. 1203 — ISAP.
- Ministerstwo Finansów, Zakres obowiązkowego KSeF — ksef.podatki.gov.pl.
- Council Directive (EU) 2025/516 of 11 March 2025 amending Directive 2006/112/EC as regards VAT rules for the digital age — amended Article 218.
This article is for information and does not constitute tax or legal advice. The Polish text of the instruments cited is the binding one.