Skip to main content

Overview

The Document schema defines the structure of invoices, credit notes and debit notes in the e-invoice.be API. You send this schema when you call POST /api/documents/ or POST /api/validate/json.
The schema requires only the items array, with a minimum of one line. All other fields are optional in the schema. But POST /api/documents/ accepts a document only if it also has:
  • A vendor identifier (vendor_tax_id or vendor_company_id) and a customer identifier (customer_peppol_id, customer_tax_id or customer_company_id). The derived Peppol ID of the sender must be one of the peppol_ids of your company (406 if not).
  • invoice_id (rule BR-02) and vendor_name.
  • A description (or name), a quantity and a unit_price for each line. A line without quantity or unit_price counts as 0.00 in the totals.
  • For an invoice with a positive amount due: due_date or payment_term (rule BR-CO-25).
When a field is absent, the API uses these values: invoice_date is the current date, unit is NAR, a missing address becomes Unknown Street and Unknown City, and a line with tax_rate 0.00 gets tax category Z when tax_code is S.
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.

Document metadata

enum
default:"INVOICE"
Type of document to create.
  • INVOICE - Standard invoice
  • CREDIT_NOTE - Credit note (refund or correction)
  • DEBIT_NOTE - Debit note
  • SELFBILLING_INVOICE - Self-billed invoice
  • SELFBILLING_CREDIT_NOTE - Self-billed credit note
See Self-billing and debit notes for the two self-billing types and for debit notes.
enum
default:"DRAFT"
Document state. See Document states. Read-only: POST /api/documents/ always sets DRAFT and ignores the value that you send.
enum
default:"OUTBOUND"
Document direction. Read-only: POST /api/documents/ always sets OUTBOUND and ignores the value that you send.
  • OUTBOUND - Document that you send to a customer
  • INBOUND - Document that you receive from a supplier

Document states

You can send a document only when it is in the DRAFT or FAILED state. For the transitions, the retry behaviour and the webhook events of each state, see Document lifecycle and delivery tracking. See Document lifecycle and delivery tracking for the state diagrams, the transitions, the retries and the related webhook events.

Vendor (supplier) information

string
Legal name of the vendor.Example: "E-INVOICE BV"
string
VAT number of the vendor, with the country prefix. The API changes the value to uppercase and removes all characters other than A-Z and 0-9. See Normalisation and rounding.Example: "BE1018265814"The API derives the Peppol ID of the vendor from this value. For the document types other than self-billing, the vendor is the Peppol sender. See Peppol ID routing.
string
Company registration number of the vendor, for example the Belgian enterprise number (CBE) or the Dutch KVK number. The API uses it to derive the Peppol ID of the vendor when vendor_tax_id is absent.Example: "1018265814"
string
Address of the vendor as one string. See Addresses for the format that the API can split correctly.Example: "Brusselsesteenweg 119/A, 1980 Zemst, BE"
string
Department or person at the vendor address. The API writes this value as the contact name only when vendor_email is present.Example: "Accounts Department"
string
Contact email address of the vendor.Example: "billing@e-invoice.be"

Customer (buyer) information

string
Legal name of the customer.Example: "OpenPeppol VZW"
string
VAT number of the customer, with the country prefix. The API changes the value to uppercase and removes all characters other than A-Z and 0-9.Example: "BE0848934496"
string
Company registration number of the customer. The API uses it to derive the Peppol ID of the customer when customer_peppol_id and customer_tax_id are absent.Example: "0848934496"
string
Peppol participant ID of the customer, in the format scheme:identifier. When you send this field, the API uses it as the Peppol ID of the customer and does not derive it from customer_tax_id or customer_company_id. The customer is the Peppol receiver, except in a self-billing document, where the customer is the Peppol sender.Example: "0208:0848934496"The API does not store this field with the document. The value is kept only as the receiver of the generated UBL. See Look up Peppol participants to find and check a Peppol ID before you send.
string
Your internal reference for the customer. The API writes it as the party identification of the customer. It is also the fallback value of BuyerReference when buyer_reference and purchase_order are absent. The API does not use it to find the Peppol ID of the customer.Example: "CUST-12345"
string
Address of the customer as one string. See Addresses.Example: "Rond-point Schuman 6, 1040 Brussels, BE"
string
Department or person at the customer address. The API writes this value as the contact name only when customer_email is present.Example: "Accounts Payable"
string
Contact email address of the customer.Example: "ap@openpeppol.example"

Invoice details

string
Unique invoice number.Example: "INV-2026-001"
string
Issue date in ISO 8601 format (YYYY-MM-DD). When it is absent, the API uses the current date.Example: "2026-10-01"
string
Payment due date in ISO 8601 format (YYYY-MM-DD). The API does not write this field to the UBL of a credit note.Example: "2026-10-31"
string
Buyer reference (BT-10), for example a cost centre or a reference that the buyer asks for. The API writes it to cbc:BuyerReference.Example: "CC-4410"
string
Purchase order reference of the customer (BT-13). The API writes it to cac:OrderReference/cbc:ID, and to cbc:BuyerReference when buyer_reference is absent. Do not use it for the number of the original invoice of a credit note: use preceding_invoice_id.Example: "PO-12345"
string
Sales order reference of the vendor (BT-14). The API writes it to cac:OrderReference/cbc:SalesOrderID. When purchase_order is absent, the API writes NA as the order reference ID.Example: "SO-2026-0042"
string
Number of the invoice that this document corrects (BT-25), for example the invoice that a credit note credits. The API writes it to cac:BillingReference/cac:InvoiceDocumentReference/cbc:ID.Example: "INV-2026-001"
string
Issue date of the preceding invoice (BT-26) in ISO 8601 format (YYYY-MM-DD). The API writes it only together with preceding_invoice_id.Example: "2026-10-01"
string
Free-text note.Example: "Full refund - goods returned"
string
Description of the payment terms.Example: "Payment due within 30 days"

Financial fields

The API calculates subtotal, total_discount, total_tax, invoice_total and amount_due when they are absent. When you send them, the API compares them with its own calculation. See Invoice totals and calculations for the formulas.
enum
default:"EUR"
Currency code (ISO 4217).The currency field accepts these ISO 4217 codes:EUR, USD, GBP, JPY, CHF, CAD, AUD, NZD, CNY, INR, SEK, NOK, DKK, SGD, HKDThe default is EUR.Example: "EUR"
number
Taxable base amount (after document-level allowances and charges, before tax).Corresponds to UBL cac:LegalMonetaryTotal/cbc:TaxExclusiveAmountExample: 1000.00
number
Total of the document-level allowances (discounts only, not charges).Corresponds to UBL cac:LegalMonetaryTotal/cbc:AllowanceTotalAmountExample: 50.00
number
Total VAT amount.Corresponds to UBL cac:TaxTotal/cbc:TaxAmountExample: 210.00
number
Total amount with tax (subtotal + total_tax).Corresponds to UBL cac:LegalMonetaryTotal/cbc:TaxInclusiveAmountExample: 1210.00
number
Amount due for payment after prepayments.Corresponds to UBL cac:LegalMonetaryTotal/cbc:PayableAmountExample: 1210.00
number
Previous outstanding balance. This is an internal field: the API does not write it to the generated UBL.Example: 100.00

Tax information

enum
default:"S"
Tax category code (UNCL5305).
  • S - Standard rate (most common)
  • Z - Zero rated
  • E - Exempt from tax
  • AE - VAT reverse charge
  • K, G, O, L, M, B - Other special cases
The API derives the tax category of each line from its tax_rate: a rate more than 0.00 gives S. A rate of 0.00 gives the tax_code of the document, or Z when tax_code is S.When tax_code is K (intra-community supply), each line item must have a tax_rate of 0.00 (422 if not).
enum
VAT exemption reason code (when tax_code is E, AE, K, G, O, L, M or B).Example: "VATEX-EU-IC" for an intra-community supply, "VATEX-EU-AE" for a reverse chargeWhen vatex is VATEX-EU-O (not subject to VAT), vendor_tax_id and customer_tax_id must be empty (406 if not).
string
Text that explains the VAT exemption. When you send vatex_note, the UBL contains only this text as the exemption reason, and not the vatex code.Example: "Reverse charge applies - Art. 196 EU VAT Directive"

Service period

string
Start date of the service period (ISO 8601: YYYY-MM-DD).Example: "2026-09-01"
string
End date of the service period (ISO 8601: YYYY-MM-DD).Example: "2026-09-30"

Additional addresses

Of these fields, the API writes only shipping_address and shipping_address_recipient to the generated UBL (as cac:Delivery). The billing, service and remittance fields are kept with the document, but the receiver does not get them in the UBL.
string
Billing address (if different from the customer address).Example: "Rond-point Schuman 6, 1040 Brussels, BE"
string
Recipient at the billing address.Example: "Accounts Payable Department"
string
Delivery address. The API writes it to cac:Delivery/cac:DeliveryLocation/cac:Address with the same split rule as the party addresses. When tax_code is K and there is no shipping address, the API writes only the country of the customer as the delivery location.Example: "Industrieweg 5, 3000 Leuven, BE"
string
Recipient at the delivery address. The API writes it as the name of the delivery party.Example: "Warehouse Manager"
string
Address of the service location.Example: "Industrieweg 5, 3000 Leuven, BE"
string
Recipient at the service address.
string
Remittance (payment) address.Example: "Brusselsesteenweg 119/A, 1980 Zemst, BE"
string
Recipient at the remittance address.

Line items

array
required
Array of line items (minimum 1).See the LineItem schema for the fields.Example:

Payment details

array
Array of payment instructions. The API writes one cac:PaymentMeans element for each entry, always with payment means code 30 (credit transfer).Example:

Allowances and charges

array
Document-level allowances (discounts).Example of a 10% allowance:
array
Document-level charges (fees).Example:
See Advanced invoicing for the effect of allowances and charges on the totals.

Tax details

array
Tax breakdown for each rate. Optional. The API stores and returns the entries as you send them. It does not calculate them, and it does not use them for the tax breakdown in the UBL (the API always calculates that breakdown).Each entry has amount (number, the tax amount) and rate (string, for example "21.00").

Attachments

array
Files in this array are embedded in the UBL. See Attachments and PDF for the permitted file types and for the construct_pdf option.Example:

How fields map to UBL

The API converts the JSON document to UBL BIS Billing 3.0. The table shows the conversions that are not self-evident. The response of POST /api/validate/json contains the generated UBL, thus you can examine the result for your own payload. The MIME type of an attachment in the UBL is file_type when it is one of application/pdf, image/png, image/jpeg, text/csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet (xlsx) or application/vnd.oasis.opendocument.spreadsheet (ods). For a different file_type, the API uses the extension of file_name (.pdf, .png, .jpg, .jpeg, .csv, .xlsx, .ods) to find the MIME type.

Addresses

vendor_address, customer_address and shipping_address are single strings. The API splits each string into the parts of a UBL address with these rules:
  1. The API splits the string on new lines. If there is only one line, it splits the string on a comma followed by a space (", ").
  2. Part 1 is the street (cbc:StreetName).
  3. Part 2 is the postal code and the city. When the part has the form “postal code, space, city”, the API writes cbc:PostalZone and cbc:CityName. If not, the full part is the city.
  4. The last part is the country (cac:Country/cbc:IdentificationCode) only if it has 2 or 3 letters and no other characters. The API changes it to uppercase.
  5. For the vendor and the customer, if the last part is not a country code, the API uses the first two characters of the tax ID of that party. If there is no tax ID, the country is BE. For shipping_address, the API does not use the tax ID: the country is BE when the string has no country code.
Use this format, with the ISO 3166-1 alpha-2 country code as the last part:
The first address gives this UBL:
The API does not read a country name as a country. With "..., 1000 Brussels, Belgium", the API ignores Belgium and takes the country from the tax ID prefix, or uses BE. An address in a different country, with a country name and without a tax ID, gets the country BE. Always end the address with the two-letter country code.
Do not put a comma followed by a space in the street part (for example "Main Street 1, box 5"). The API reads the text after the comma as the postal code and city. A last part that has only 2 or 3 letters is always read as a country code.
When the address is absent or has no usable parts, the API writes Unknown Street and Unknown City.

Normalisation and rounding

The API changes some values before it stores the document and generates the UBL. Rounding of monetary amounts is half-up to 2 decimals (0.005 becomes 0.01). The API rounds in this sequence:
  1. Each line amount is rounded.
  2. For each tax group, the API adds the rounded line amounts, subtracts the document-level allowances and adds the document-level charges. A tax group is one combination of tax category and tax rate.
  3. The tax of each group is taxable amount × rate / 100, rounded.
  4. total_tax is the sum of the group taxes.
The API thus calculates VAT for each tax group, not for each line. If you calculate VAT for each line and add the results, your total_tax can be different by some cents. See Invoice totals and calculations.

Example

Complete invoice:
Invoice
Replace the vendor fields with the data of your company. The API rejects a document if the Peppol ID of the vendor is not one of the peppol_ids of your company.

Next Steps

LineItem

Look up the fields of an invoice line.

Create e-invoices

Create and send an invoice with this schema.

Invoice totals and calculations

Calculate the totals that the API checks.

Validation during development

Validate a payload and examine the generated UBL.