Skip to content
Back to News
Changelog

The import route now refuses the same document sent twice

apipeppolbugfix

An Idempotency-Key protects a request. It does not identify a document, so the same invoice submitted under two different keys was two different requests — and on POST /v1/invoices/import, two real deliveries. Peppol does not de-duplicate, so nothing downstream would have collapsed them: a second copy lands in the recipient's ledger as an ordinary invoice.

POST /v1/invoices has looked at the document itself since 19 August. The import route did not. It does now, and both routes run the same check before the document reaches the provider.

A document is recognised by six things: account, environment, invoice number, credit-note flag, recipient, and sending legal entity.

The lookback stops at 30 days. Past that, a repeated invoice number is a question for BT-1 uniqueness, which this guard does not claim to enforce.

The contract said otherwise

ImportInvoiceResult already declared duplicateOf, inherited from the shared send schema, on a route that never produced it. Anyone reading "no duplicateOf, therefore no duplicate" was reading a field that could not appear, so its absence meant nothing.

409 is now documented on /invoices/import in the OpenAPI spec, and the field is produced.

If you retry after a failure

A retry landing outside the 15-minute window is flagged, not refused. We cannot tell a late retry from a deliberate resend — someone whose customer says the invoice never arrived makes exactly the same call — so the document goes and the 201 says so. If your client retries on a long ceiling, duplicateOf is the field to check.