Skip to main content

Overview

The API has two validation endpoints:
  • POST /api/validate/json converts your JSON to UBL and examines the result against the rules of EN 16931 and Peppol BIS Billing 3.0.
  • POST /api/validate/ubl examines a UBL XML file that you made yourself.
The two endpoints do not create a document and do not send data to the Peppol network.
Validation is not a separate mandatory call. POST /api/documents/ rejects a payload that does not pass the same rules. Use POST /api/validate/json while you develop, because it returns all rule failures and the generated UBL.

Why validate

  • No document is created: you can repeat the call as often as necessary.
  • All rule failures in one response: each issue has a rule ID and a message.
  • The generated UBL: a valid result contains the XML that the API makes from your JSON.
  • Fewer rejected create requests: you correct the payload before you call POST /api/documents/.

Validate a JSON document

Send the document JSON to POST /api/validate/json. The body is the same as the body of POST /api/documents/.
POST /api/validate/json does not compare the vendor with your company. POST /api/documents/ does: before you create the document, replace the vendor fields with those of your own company. When the document is valid, the API returns 201 Created with the result and the generated UBL:
201 Created
The ubl_document value is shortened in this example.
string
required
The identifier of this validation. It is not a document ID.
string
required
The name of the generated XML file (<id>.xml). For POST /api/validate/ubl, the name of the file that you uploaded.
boolean
required
false when one issue or more has the flag fatal or is a failure of the XML schema (rule_id is xsd-validation).
array
required
The rule failures and warnings. Each issue has message, type (error or warning) and schematron (the rule set that found the issue). An issue can also have rule_id, flag, location and test. For the description of each field, see Validation result.
string
The generated UBL XML. POST /api/validate/json returns this field only when is_valid is true. POST /api/documents/{document_id}/validate always returns it.
The ubl_document field shows the XML that the API makes from your JSON. Use it to see how each JSON field maps to UBL.

A document that breaks rules

A rule failure does not change the HTTP status code. The API returns 201 Created, is_valid is false, and issues contains the failures. Thus your code must read is_valid, not only the status code. This payload has two faults: the vendor enterprise number is not a real number, and the line has no amount. It is a failing example on purpose.
Request body with faults
201 Created
The response has no ubl_document, because the document is not valid. To correct the payload:
  1. Use an enterprise number that passes the modulo 97 check: BE1018265814 in place of BE0123456789. This removes PEPPOL-COMMON-R043.
  2. Add amount to the line: "1000.00" (quantity × unit_price). This removes BR-S-08 and PEPPOL-EN16931-R120.
For the cause and the correction of each frequent rule ID, see Frequent validation rules.

Totals that are not consistent

Before the API generates the UBL, it calculates the totals from the lines and compares them with subtotal, total_tax, total_discount, invoice_total and amount_due in your JSON. If a value is different from the calculated value by more than 1.00, or if amount_due is more than the calculated invoice total, the API stops. It returns 406 Not Acceptable with one text message, and no issues list:
406 Not Acceptable
The message can also contain Total tax mismatch, Total discount mismatch, Invoice total mismatch or Amount due (...) cannot be greater than invoice total (...). When more than one total is wrong, the list in the message has one entry for each total. POST /api/documents/ returns the same 406 for the same payload.
If you leave out a total field, the API calculates it. See Invoice totals and calculations.
A request that does not agree with the schema (for example a date that is not YYYY-MM-DD, or a currency code that is not supported) returns 422 before the rules run. See Request validation error (422).

Validate a UBL XML file

Use POST /api/validate/ubl to examine a UBL BIS Billing 3.0 XML file that you have.
This endpoint accepts multipart/form-data with one form field, file. It does not accept the XML as the raw request body. See Common mistake: raw XML body.
The API returns 201 Created with the validation result. A valid file gives:
201 Created
A file that breaks a rule gives the same status code, with is_valid set to false:
201 Created
The schematron field names the Schematron rule set that found the issue: CEN-EN16931-UBL, PEPPOL-EN16931-UBL, PEPPOL-EN16931-UBL-SB (self-billing) or XSD (the XML schema).

Common mistake: raw XML body

If you set Content-Type: application/xml and send the XML as the raw request body (for example with --data-binary @invoice.xml), the API returns this 422 response:
422 Unprocessable Entity
The correction: use multipart/form-data with a file form field, as the examples above show.

When to use UBL validation

Use POST /api/validate/ubl when you:
  • have UBL XML files from your ERP system and want to examine them before you send them
  • move from a different Peppol Access Point and have UBL documents that are already generated
  • get UBL files from an external source and want to examine them before you post them to POST /api/documents/ubl
If you create invoices from JSON, use POST /api/validate/json. It applies the same UBL rules and returns the generated UBL, so a second call to POST /api/validate/ubl is not necessary.
See also Send UBL documents.

Common validation problems

The currency field accepts these ISO 4217 codes: EUR, USD, GBP, JPY, CHF, CAD, AUD, NZD, CNY, INR, SEK, NOK, DKK, SGD, HKD The default is EUR. The tax_rate of a line is a percentage with two decimals as a string, for example "21.00", "6.00" or "0.00". For the rule ID reference, all HTTP status codes and the error body formats, see Errors and troubleshooting.

Development workflow

1

Build the invoice JSON

Start from the example in Validate a JSON document and replace the values with your data. For the fields, see Create e-invoices and the document schema.
2

Validate the JSON

Send the JSON to POST /api/validate/json. If the status code is 406 or 422, or if is_valid is false, correct the JSON and send it again.
3

Create the document

When is_valid is true, send the same JSON to POST /api/documents/. The API creates the document in the state DRAFT.

Complete example

These programs validate a document and create it only when the result is valid. Put your document JSON in the file invoice.json. The vendor in the file must be your own company.
validate-and-create.mjs
validate_and_create.py

Testing strategy

During development

Use POST /api/validate/json for each change of your payload:
  1. Edge cases: validate unusual documents (zero amounts, more than one VAT rate, a currency other than EUR).
  2. All document types: validate invoices, credit notes and debit notes.
  3. Short cycles: correct the JSON and validate again. No document is created.
  4. Automated tests: add validation calls to your test suite.
Develop and test with a sandbox company. A sandbox company runs in test mode: the API sends each document as UBL XML to the contact email address of the company, and nothing goes to the Peppol network. The API host and the endpoints are the same as for a production company. See Test mode and sandbox companies.

Before production

  1. Validate representative samples of all the invoice types that you send.
  2. Make sure that each receiver is registered on Peppol. See Look up Peppol participants.
  3. Validate all the tax categories and currency codes that you use.
  4. Validate complex documents (allowances, charges, many lines). See Advanced invoicing.

Best practices

Do not wait until production:
  • Validate each new invoice template.
  • Validate again after a change in your mapping or in the schema.
  • Add validation to your CI/CD pipeline.
Three results are possible. Handle each of them:
  • 201 and is_valid is true: continue with POST /api/documents/.
  • 201 and is_valid is false: record the rule_id and the message of each issue, and do not create the document.
  • 406 or 422: record the detail value, and do not create the document.
If you generate invoices from a template, validate one document of each template when the template changes. The create request applies the rules again to each document, so a validation call for each invoice in production is not necessary.

Next Steps

Create e-invoices

Create and send validated invoices

Errors and troubleshooting

Find the cause of a status code or a rule ID

Invoice totals and calculations

Calculate totals that pass the checks

Look up Peppol participants

Find the Peppol ID of a receiver