Errors and the catalogue
Problem JSON and every catalogue code.
- In the sandbox
In plain words
When a call fails, the answer says whether the request or the invoice is wrong. For an invoice, a code names the field in the client's export and says who fixes it. The partner fixes bad requests and bad data; we fix our own mapping and retry failures on the route's side.
Tip
400 is our request format. 422 is the invoice, and its catalogue code names the field in the ERP export. Keep those two apart.
Problem JSON
Every error answer is problem JSON (RFC 9457): type, title and status, with detail when there is more to say. A 422 adds errors, a list of findings. Each has a catalogue code, who fixes it (who_fixes), a fix_hint and a message, and a field when the finding is about one field (see page 3.2). The highlighted line is the code.
curl -X POST "https://api-sandbox-eu.eurinvoice.com/invoices/xml?route=PEPPOL&invoice_ref=INV-2026-0051" \
-H "Authorization: Bearer <your-api-key>" \
-H "Content-Type: application/xml" \
-H "Idempotency-Key: order-2026-0051" \
--data-binary @not-an-invoice.xmlimport java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Path;
public class Example {
public static void main(String[] args) throws Exception {
HttpRequest request = HttpRequest.newBuilder(URI.create("https://api-sandbox-eu.eurinvoice.com/invoices/xml?route=PEPPOL&invoice_ref=INV-2026-0051"))
.header("Authorization", "Bearer <your-api-key>")
.header("Content-Type", "application/xml")
.header("Idempotency-Key", "order-2026-0051")
.POST(HttpRequest.BodyPublishers.ofFile(Path.of("not-an-invoice.xml")))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());
}
}import { readFile } from 'node:fs/promises';
const response = await fetch('https://api-sandbox-eu.eurinvoice.com/invoices/xml?route=PEPPOL&invoice_ref=INV-2026-0051', {
method: 'POST',
headers: {
Authorization: 'Bearer <your-api-key>',
'Content-Type': 'application/xml',
'Idempotency-Key': 'order-2026-0051',
},
body: await readFile('not-an-invoice.xml'),
});
console.log(response.status);
console.log(await response.text());{
"type": "https://eurinvoice.com/problems/validation-failed",
"title": "The invoice did not pass the checks",
"errors": [
{
"code": "EI-XML-TYPE",
"fix_hint": "Send the invoice itself, in the format agreed for the route.",
"who_fixes": "erp",
"source": "XML-safety",
"message": "The file is not an invoice in a format this route accepts. Please send the invoice in the agreed format."
}
],
"status": 422
}A path the service does not have answers 404 with {"detail": "Not Found"}, plain JSON.
What each status means
| Status | Meaning | What you do |
|---|---|---|
400 | The request cannot be read: not JSON, a field is wrong, or the Idempotency-Key is missing or not 8 to 100 characters. | Fix the request. |
401 | No key, or an unknown key. | Fix the key. |
403 | The key lacks the scope the call needs, or the call is for another client's data (forbidden). | Use a key with the scope, or the right client. |
404 | No such invoice, document, catalogue entry or care item for this client. | Check the id. |
409 | The Idempotency-Key was used with a different body, or its first request is still running. A cancel came too late. A credential already exists or was revoked. | Fix the call. |
413 | The body is over 5 MB (payload-too-large). | Send a smaller file. |
415 | The content type is not XML or PDF, or a PDF body is not a PDF. | Fix the content type. |
422 | The invoice failed a check. Nothing was queued. | Fix the findings marked erp. The ones marked us are ours. |
429 | Too many requests for the key. | Wait for the seconds in Retry-After, then retry. |
500 | The invoice could not be checked. Something failed on our side. | Retry the same call with the same key. |
503 | The service is busy (busy), or a credentials call came to a process with no master key set. | Retry the same call after Retry-After seconds. |
Every 409 has its own problem type, under https://eurinvoice.com/problems/: idempotency-conflict, request-in-progress, already-submitted, send-in-progress, dead-letter, credential-exists and credential-revoked. Read the type, not the title, to tell them apart. A 413 is not stored against the Idempotency-Key, so the same key can be used again with a smaller body.
A 429 comes per client and scope, with Retry-After (see limits). After a 202 you never resend. Rail-side errors that can pass are retried on our schedule, and a permanent rejection goes to the care list.
Who acts
Each catalogue code has one owner.
| Owner | Who it is |
|---|---|
erp | The partner: the ERP export or its master data. |
us | eurinvoice: mapping, serializer or configuration. |
business | The seller's business, with its buyer or accountant. |
client | The client, the seller: registration, credentials or authorisation. |
buyer | The buyer's side. |
route | The route's operator, an authority, access point or platform: wait and retry. |
The partner fixes 400, 401, 403, 409, 413 and 415. A 422 carries findings, and each says who fixes it: erp is the partner, us is eurinvoice.
The catalogue
The catalogue holds a code for every finding. This page shows a sample of twelve, one or two for each family of rules. Type a code and press Enter to jump to it. With a key, GET /catalogue/{code} explains any code (see catalogue lookup). The full catalogue is for partners: ask for it with sandbox access.
12 codes
| Code | Who acts | What is wrong | What to do |
|---|---|---|---|
EI-ID-IBANOur checks, identifier check | Partner | The IBAN fails its mod-97 check. | Correct the bank account in the company settings. |
EI-SCHEMAOur checks, schema | eurinvoice | The invoice JSON does not match the canonical model: a missing or unknown field, a wrong type or code, or a route rule on structure. | Read the JSON path in the error and correct the client mapping. |
EI-TOTALS-MISMATCHOur checks, pre-check | Partner | The totals the ERP sent (erp_totals) differ from the totals computed from the lines, so the document would not match the ERP's own books. | Find the difference (usually rounding per line versus per document, or a discount line the export left out) and fix the export or the mapping. |
BR-CO-16EN 16931 and syntax, route validator | eurinvoice | The amount due (BT-115) is not the total with VAT (BT-112) minus the paid amount (BT-113) plus rounding (BT-114). | We compute totals from the lines, so this appears only when totals came from outside (a pass-through file) or were edited. Rebuild the totals from the lines and resend. |
EI-XML-DTDEN 16931 and syntax, route validator | Partner | The XML declares a DOCTYPE. UBL, CII and FA(3) never use one, and a DOCTYPE is how external entities, remote DTD fetches and entity expansion get into a file (XXE). The file is refused before any validator or route reads it. | Export the invoice without the DOCTYPE line. If the ERP adds one on purpose, raise it with the ERP vendor: no e-invoicing format uses it. |
EI-XML-SYNTAXEN 16931 and syntax, route validator | Partner | The file is not well-formed XML: for example it is cut off, its encoding does not match its declaration, or a character such as & is not escaped. No validator can read it. | Export the file again and open it in any XML viewer. Look for a cut-off file, an encoding that differs from the declaration, or an unescaped &. |
EI-XML-TYPEEN 16931 and syntax, route validator | Partner | The XML is well-formed but not an invoice we validate: not a UBL Invoice or CreditNote, a UN/CEFACT CII invoice or a KSeF FA(3) invoice (for example a UBL Order, or an older FA(2) file). | Send the invoice itself, in the format agreed for the route. |
EI-PEPPOL-NO-ROUTEPeppol, rail answer, needs a rail | Partner | The access point found no recipient for the buyer's identifier: the buyer is not registered on Peppol for this document type. Final for this send. | Check the buyer's Peppol ID in the ERP customer record against the Peppol directory; if the buyer is not on Peppol, agree another channel with them. |
PEPPOL-EN16931-R001Peppol, route validator | eurinvoice | The business process (BT-23) should be present. Mustang reported it as a notice on our ZUGFeRD EN 16931 file, which is not a Peppol document. | No action for ZUGFeRD; our Peppol UBL always writes the process. |
BR-DE-5Germany, route validator | Partner | The seller's contact name (BT-41) is missing. | Add an invoicing contact person or department to the company settings. |
KSEF-440Poland, rail answer, needs a rail | eurinvoice | KSeF already holds an invoice with the same seller NIP, invoice type (RodzajFaktury) and number (P_2); it keeps that key for 10 years. The key is the number, not the file: in TEST a different file under an accepted number also got 440. The response gives the KSeF number and session of the copy it accepted first. A number KSeF rejected (430 or 450) is not held and can be used again. | Do not resend. Record the original KSeF number from the response and report the invoice as accepted under it. |
BR-RO-001Romania, route validator | eurinvoice | The specification identifier (BT-24) is not the CIUS-RO value. | Set profile ro-cius, which writes the CIUS-RO 1.0.1 identifier. |