Hand in an invoice the client received through a route
- In the sandbox
Since 0.35.0 (17 Oct 2026). The body is the XML exactly as the route delivered it (UBL, CII or FA(3)); nothing is
changed and nothing is sent. The file is stored first, sealed under the client's data key, because in law it is
already received; then the route's own validators check it, as for a pass-through send, and the result arrives as a
status event (received, then validated or validation_failed, with direction: in) on the client's webhook and
on GET /received/{id}/events. A file that fails the rules is kept and reads validation_failed with the errors; it
is never dropped. A file that is not an invoice to keep is refused with 422 and nothing is stored, as on
POST /invoices/xml: a DOCTYPE (EI-XML-DTD, never parsed), XML that is not well-formed (EI-XML-SYNTAX), or a
document that is not an invoice the route knows (EI-XML-TYPE). A check that could not run (a validator down) is
retried, and after the last retry the invoice goes on the care list and stays received. A client hands in at most
120 documents a minute and 2,000 a day, whatever key it uses (429, not kept under the Idempotency-Key). The file is
served back as a download (Content-Disposition: attachment).
One route document is one received invoice for the client: by rail_id when it is given, by the file's SHA-256
when it is not. The same document again answers 202 with duplicate_of; the same rail_id with other bytes
answers 409 received-id-conflict, and nothing is stored. Received invoices are kept apart from the invoices the
client sends: they are not in GET /invoices, the series, the metrics or the monthly report, and they never block
a send of the same file. A received invoice is deleted with the client's retention once nothing is open on it (its
check ended, or a person handled its care row), and is in the client's export under received/. The submit scope;
a client key acts for its own client only.
apiKeyAuthorizationBearer <token>Send your key as a bearer token: Authorization: Bearer <your-api-key>. Health is the only call that needs no key.
route*stringThe route the document came through.
"PEPPOL""PL-KSEF""RO-EFACTURA""FR-PA""DE-XRECHNUNG"rail_id?stringThe route's own id for the document (the access point's document id, the KSeF number, ANAF's download id). 1 to 200 letters, digits and . _ : / @ + = # -.
^[A-Za-z0-9._:/@+=#-]{1,200}$client?stringThe client that received it. The operator's key names it (left out, the operator's own local client); a client key may leave it out or name its own client, and any other answers 403.
^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$Idempotency-Key*stringA unique key per logical request (a UUID is fine). A key that begins erp: (an ERP transport recipe's erp:{connector}:{scope}:{entity}:{id}, with the operator's key or a client's) has no window; it is kept with its first answer until the client, or the invoice it made, is deleted, so a changed document handed again weeks later still gets 409 idempotency-conflict. Such a key is taken only on an ERP connector export (POST /invoices with a connector this build reads, an export that is a non-empty JSON object, and with the operator's key client); on any other request, a canonical document, POST /invoices/xml or a cancel, it answers 400 idempotency-key-not-allowed and nothing is kept, and an export with a number longer than 40 characters gets the 422 EI-SCHEMA it gets under any other key, also kept under no key. When the operator releases a dead letter (cancels it), the earlier answers about that invoice go with it, the submission's and, since 0.35.0, an earlier refused cancel's, so a retry of that cancel runs again and answers 200 with the invoice cancelled. Since 0.32.0 a 5xx answer and a 422 connector-settings-missing are not kept, so the same request with the same key can be sent again. With the operator's key it may not begin with client: (400), the form client keys are stored under.
8 <= length <= 100body*stringapplication/xmlStored, and queued for the route's check unless it already failed; or already held (duplicate_of).
application/json- response
Since 0.35.0. An invoice the client received through a route, as POST /received took it. Like Invoice, it
carries no invoice content: no seller, buyer or amounts.
id*string^rcv_[0-9a-f]{24}$invoice_ref*stringThe file's own invoice number (BT-1; FA(3) Fa/P_2), else the rail_id, else the id.
invoice_number?stringroute*RouteThe country route. The same values as country_route in status events.
"PEPPOL""PL-KSEF""RO-EFACTURA""FR-PA""DE-XRECHNUNG"client*stringenvironment*string"sandbox""production"state*string"received""validated""validation_failed"direction*"in""in"rail_id?stringThe route's own id for the document, as given on POST /received.
document_sha256*string^[a-f0-9]{64}$duplicate_of?stringOnly on POST /received, when the document was already held for the client and route.
links?errors?array<>documents?array<>created_at*stringdate-timeupdated_at*stringdate-timecurl -X POST "https://example.com/received?route=PEPPOL" \ -H "Authorization: Bearer <your-api-key>" \ -H "Idempotency-Key: order-2026-0001" \ -H "Content-Type: application/xml" \ -d 'string'{ "id": "string", "invoice_ref": "string", "invoice_number": "string", "route": "PEPPOL", "client": "string", "environment": "sandbox", "state": "received", "direction": "in", "rail_id": "string", "document_sha256": "string", "duplicate_of": "string", "links": { "self": "string", "events": "string" }, "errors": [ { "code": "string" } ], "documents": [ { "kind": "received-xml", "sha256": "string", "href": "string" } ], "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z"}List and search received invoices GET
Since 0.35.0. The same parameters and answer as GET /invoices, for received invoices only; state is received, validated or validation_failed. A client key sees its own client's; the operator every client's, or one client's with client.
Look up an error or reason code GET
Returns the catalogue entry for a rule ID, route error code or French reason code (IDs and aliases).