Quickstart
Status: beta. Valinvo turns one JSON invoice into XRechnung 3.0.2 (Germany, UBL or CII), ZUGFeRD 2.5.2 / Factur-X 1.09.2 (a PDF with the invoice XML embedded), Factur-X, CII or UBL with the French rules (France) or Peppol BIS Billing 3.0.21 (Belgium and the Peppol network). Every generated invoice is checked against the official rules of its format before it is returned.
Terms: the API is for businesses only and is subject to the Terms of Use and the Data Processing Agreement. Without an API key, you accept them by sending the header X-Accept-Terms: 1.0 (the current version) with each request; requests without it are refused with 428 terms_not_accepted. API key holders accept in writing when the key is issued. Every API response links the terms in a Link: <…/terms>; rel="terms-of-service" header. The trial without an API key is for testing and evaluation: use sample data, not real invoices containing other people's personal data.
Support: never email real invoices. Send the validation result (rule IDs and field locations) or an anonymised copy with sample values to hello@bmk-hub.com, or use the contact form.
List formats
curl https://valinvo.bmk-hub.com/v1/formats
| Format ID | Format | Use it for |
|---|---|---|
xrechnung-ubl | XRechnung 3.0.2, UBL 2.1 | German public sector; German B2B |
xrechnung-cii | XRechnung 3.0.2, UN/CEFACT CII | The same, for recipients who want CII |
peppol-bis-ubl | Peppol BIS Billing 3.0.21, UBL 2.1 | Belgian B2B; any buyer on the Peppol network |
zugferd-en16931 | ZUGFeRD 2.5.2 / Factur-X 1.09.2, EN 16931 profile (PDF/A-3 + CII XML) | German B2B as a PDF people can read and software can process (beta; the extra fields of the French reform are not supported yet) |
factur-x-fr | Factur-X 1.09.2, EN 16931 + BR-FR (PDF/A-3 + CII) | French B2B: the Factur-X file for your plateforme agréée (beta; sending through a PA is not included) |
cii-fr | CII D22B, EN 16931 + BR-FR | The same French invoice as plain CII XML (beta) |
ubl-fr | UBL 2.1, EN 16931 + BR-FR | The same French invoice as UBL XML (beta) |
Create an invoice
Send the format and the invoice in one JSON body. Two complete examples: XRechnung (Germany) and Peppol (Belgium).
curl -X POST https://valinvo.bmk-hub.com/v1/invoices \
-H 'X-Accept-Terms: 1.0' \
-H 'Content-Type: application/json' \
-d @invoice.peppol-be.example.json \
-o invoice.xml -D -
The response is the XML file. The headers show the result of the official validation:
X-Validation-Status: valid
X-Validation-Warnings: 0
X-Rule-Set: peppol-bis-3.0.21-ubl-invoice
Add -H 'Accept: application/json' to get the XML and the full validation report in one JSON response.
With "format": "zugferd-en16931" the response is a PDF (save it with -o invoice.pdf); the official rules check the XML embedded in it. In JSON mode the PDF comes base64-encoded. An invoice without a delivery date gets one warning (PEPPOL-EN16931-R008, empty delivery element): the ZUGFeRD schema requires that element, so this warning is expected.
Free trial and API keys
Without a key you can create 2 invoices per day (per IP address) to try the API; the response headers X-Trial-Limit and X-Trial-Remaining show where you are, and after that you get 429 trial_limit_reached. Design partners get a beta key, sent as Authorization: Bearer <key>, which removes the daily limit. The validator stays free.
Without a key, send one request at a time (otherwise 429 one_request_at_a_time). Size limits for everyone: invoice JSON up to 2 MB and 2,000 lines; ZUGFeRD PDFs up to 500 lines and 5,000 characters per text field; at most 1,000 notes (100 in PDFs); numbers with at most 15 digits before and 10 after the decimal point (400 invalid_invoice); documents to validate up to 20 MB and 75,000 XML elements, about 3,000 detailed lines (413 document_too_large).
Rules of thumb
- Money, quantities and VAT rates are JSON strings (
"19.99"), never numbers. - We calculate line totals, VAT and totals. If you also send
totals, we check them and tell you which value differs. - Electronic addresses: XRechnung accepts email (
{"scheme": "EM", "value": "invoices@example.com"}). Peppol does not; use a Peppol scheme such as0208(Belgian enterprise number) or9930(German VAT number). - XRechnung needs
buyerReference(the Leitweg-ID for public buyers), a seller contact with name, phone and email, and payment instructions. - Peppol needs
buyerReferenceororderReferenceand allows only one note. - France (
factur-x-fr,cii-fr,ubl-fr): sendbillingFrame, the three notescodedNotesPMT,PMDandAAB(your recovery-fee, late-payment and discount terms), both SIRENs aslegalRegistrationIdwith scheme0002, and electronic addresses with scheme0225(e.g. the SIREN; no email addresses). Example. Billing frames:B1goods,S1services,M1mixed;B2/S2/M2already paid (prepaidAmount= total,dueDate= payment date);B4/S4/M4final invoice after a down payment;S5subcontractor;S6co-contractor;B7/S7sale already e-reported. Down-payment invoices:"type": "prepayment_invoice". Not supported:S3(public buyers), multi-sellerB8/S8/M8, two-way self-billingB9/S9/M9. - Other currencies: an invoice not in EUR can state the VAT total in your VAT accounting currency, as your accounting converted it:
"taxCurrency": {"code": "EUR", "vatTotal": "54.12"}. France requires this (EUR). - Credit notes: send
"type": "credit_note"with positive amounts and the corrected invoice inprecedingInvoice. UBL formats return a UBLCreditNote; there the due date belongs to the payment instructions, so a credit note withdueDateneedspayment(e.g. the refund account). - Belgium (Peppol): Belgian companies use scheme
0208with their ten-digit enterprise number (no dots), e.g.0123456749. - Denmark (Peppol): Danish sellers need
legalRegistrationIdwith their CVR number and scheme0184; payment codes 1, 10, 31, 42, 48, 49, 50, 58, 59, 93 or 97 (not 30). - Sweden (Peppol): VAT numbers are
SE+ 12 digits, organisation numbers 10 digits (scheme0007), VAT rates 25, 12 or 6 %; use payment code30between Swedish parties. - Norway (Peppol): organisation numbers use scheme
0192; registered companies addtaxRegistrationId: "Foretaksregisteret". - Slovakia (Peppol): Slovak companies use scheme
0245with the ten-digit DIČ (not9950). When seller and buyer are both Slovak we also require both full addresses and the exemption reason as text; sendtaxPointDate(date of supply) when it differs fromissueDate.
When something is wrong
You get 400 with every problem at once, each with the JSON path, the official rule and the EN 16931 business term:
{
"code": "invalid_invoice",
"message": "The invoice has 1 problem. Fix it and try again.",
"problems": [{
"path": "invoice.buyerReference",
"message": "Peppol requires buyerReference or orderReference.",
"rule": "PEPPOL-EN16931-R003",
"businessTerm": "BT-10"
}]
}
422 generated_invoice_invalid means the generated XML did not pass the official rules; validation.errors says why. Usually a field required by a national rule is missing.
Validate an existing invoice
curl -X POST https://valinvo.bmk-hub.com/v1/validate \
-H 'X-Accept-Terms: 1.0' \
-H 'Content-Type: application/xml' \
--data-binary @invoice.xml
{"ruleSet": "peppol-bis-3.0.21-ubl-invoice", "valid": true, "errors": [], "warnings": []}
The rule set is detected from the document: XRechnung 3.0 (UBL Invoice, UBL CreditNote, CII), Peppol BIS Billing 3.0 (UBL Invoice, UBL CreditNote) and ZUGFeRD 2.x / Factur-X in CII (profiles MINIMUM, BASIC WL, BASIC, EN 16931 and EXTENDED, checked against the 2.5.2 rules). An invalid invoice is a normal 200 result with valid: false and the list of errors.
A ZUGFeRD / Factur-X PDF can be sent as it is. We check the invoice XML embedded in it, the PDF itself against PDF/A-3 (with veraPDF) and that the PDF's metadata names the same profile and attachment as the XML; the rule set then reads e.g. zugferd-2.5.2-en16931+pdf-a-3b. PDF/A errors have rule IDs like PDF/A-3 6.2.11.4.1-1 (ISO 19005-3 clause and test), metadata errors start with ZF-XMP:
curl -X POST https://valinvo.bmk-hub.com/v1/validate \
-H 'X-Accept-Terms: 1.0' \
-H 'Content-Type: application/pdf' \
--data-binary @invoice.pdf
A PDF without an invoice attachment gets 400 no_embedded_invoice; an unreadable or password-protected one 400 invalid_pdf.
OpenAPI
The whole API as an OpenAPI 3 description: openapi.json. Import it into Postman, Insomnia or a code generator.
Quickstart · XRechnung validator · Peppol validator · ZUGFeRD / Factur-X validator · ZUGFeRD-csharp: your options · Error codes