E-invoice API: create, check and read XRechnung, ZUGFeRD and Peppol BIS
An e-invoice API turns invoice data into a file the buyer’s system can process and a tax administration accepts. Veriport does three things over HTTPS: POST /einvoice/create writes an XRechnung (UBL or CII), a ZUGFeRD / Factur-X PDF or a Peppol BIS Billing 3.0 invoice from one JSON model; POST /einvoice/validate checks any e-invoice against the XML schema and the published CEN, XRechnung and Peppol rules; POST /einvoice/parse reads a received one back into that JSON or a readable PDF. It never sends an invoice and never keeps one.
- Checked by the reference validators on 2026-10-28: KoSIT (XRechnung) 93 of 93 XML files accepted, KoSIT (Peppol BIS) 10 of 10 Peppol files accepted, veraPDF 72 of 72 PDFs compliant, Mustang 31 of 31 ZUGFeRD PDFs valid; our own validator names the same rule ids as KoSIT on 115 of 115 files, broken documents included.
- Nothing is dropped silently: every field you send is written into the file, or the request is refused with 422
einvoice_unsupportednaming the feature and its path. Every value the engine adds (a VATEX code, the § 19 UStG mention, a 0208 Peppol address) is listed back to you. - Prices: a created invoice weighs 20 calls ($0.058–$0.090 at pack prices), a check or a reading 5; 100 free calls a month, 4 free checks a day without a key; a refused request costs nothing.
- Allowances and charges on the document, EU reverse charge (AE) and intra-Community supplies (K) with both VAT identifiers, credit notes and corrected invoices, up to 500 lines, the Leitweg-ID for German public bodies, Belgian enterprise numbers and structured communications.
The three calls
One key for all three; Idempotency-Key on create (a retry within 24 hours returns the same file, rebuilt from the request, and is not charged again); up to 4.5 MB a request; RFC 9457 problem documents for every refusal.
| Endpoint | You send | You get back | Cost |
|---|---|---|---|
POST /einvoice/create | The invoice as JSON (seller, buyer, lines, VAT, allowances and charges, payment, references) and format | The file (XML, or the PDF for zugferd) with Veriport-Validation: valid; profile=…; errors=0; warnings=N; ?envelope=json for { file (base64), contentType, validation, defaults } | 20 calls |
POST /einvoice/validate | An XML or a hybrid PDF, raw or multipart | { valid, profile, syntax, errors[{rule, severity, path, message}], warnings[], checks }, rule ids as published | 5 calls (anonymous allowed) |
POST /einvoice/parse | An XML or a hybrid PDF | The invoice in the create model, and notInJson: what the file holds that the JSON does not; ?as=pdf a readable PDF/A-3 | 5 calls |
{"format":"xrechnung-ubl","number":"2026-017","issueDate":"2026-10-20","paymentTermsDays":14,"buyerReference":"EK-2026-0345","deliveryDate":"2026-10-15",
"seller":{"name":"Lena Hartmann — Webdesign","vatId":"DE136695976","address":{"street":"Lindenstraße 14","postalCode":"10969","city":"Berlin","countryCode":"DE"},
"contact":{"name":"Lena Hartmann","phone":"+49 30 1234567","email":"rechnung@hartmann-webdesign.example"}},
"buyer":{"name":"Mustermann Maschinenbau GmbH","address":{"street":"Industrieweg 7","postalCode":"70565","city":"Stuttgart","countryCode":"DE"},
"contact":{"email":"buchhaltung@mustermann.example"}},
"payment":{"iban":"DE02120300000000202051"},
"lines":[{"name":"Relaunch Website","quantity":24,"unitCode":"HUR","unitPrice":95,"vat":{"category":"S","rate":19}}]}{
"valid": true,
"profile": "xrechnung",
"format": "xrechnung-ubl",
"syntax": "ubl",
"container": "xml",
"attachment": null,
"errors": [],
"warnings": [],
"checks": {
"schema": {
"status": "passed",
"name": "UBL 2.1 (OASIS) and UN/CEFACT CII D16B XML schemas, from the KoSIT XRechnung validator configuration v2026-08-31"
},
"ruleSets": [
{
"id": "cen-ubl",
"name": "CEN EN 16931 validation artefacts (UBL)",
"release": "1.3.16",
"sha256": "bafada015efbc5248bf5e05ad2191e1d9833ef96e9dd5f4bce420a747342da85",
"fatal": 0,
"warnings": 0
},
{
"id": "xrechnung-ubl",
"name": "XRechnung 3.0.2 schematron (UBL)",
"release": "2.6.0",
"sha256": "ca5e07afd04e72cd283d581590ffff3a15f4aac9d2f40c993345d78e67ea22b4",
"fatal": 0,
"warnings": 0
}
],
"pdfa": null,
"veriport": 0
},
"ms": 84
}The same create call in six languages
Send the JSON, save the file, read the header. A refusal is a problem document whose code is stable: einvoice_unsupported (with feature and path) or einvoice_invalid (with violations[]: rule, severity, path, message).
# create: the file comes back; its check is in the Veriport-Validation header
curl -X POST "https://veriport.io/api/v1/einvoice/create" \
-H "Authorization: Bearer vp_live_YOUR_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: inv-2026-017" \
--data @invoice.json -o xrechnung-2026-017.xml -D -
# validate any e-invoice (no key needed for 4 checks a day)
curl -X POST "https://veriport.io/api/v1/einvoice/validate" -H "Content-Type: application/xml" --data-binary @received.xml
# parse a received Factur-X into the same JSON, or a readable PDF
curl -X POST "https://veriport.io/api/v1/einvoice/parse" -H "Authorization: Bearer vp_live_YOUR_KEY" \
-H "Content-Type: application/pdf" --data-binary @facture.pdfimport { readFile, writeFile } from "node:fs/promises";
const invoice = JSON.parse(await readFile("invoice.json", "utf8")); // the InvoiceInput model, format included
const res = await fetch("https://veriport.io/api/v1/einvoice/create", {
method: "POST",
headers: { Authorization: "Bearer " + process.env.VERIPORT_KEY, "Content-Type": "application/json", "Idempotency-Key": "inv-" + invoice.number },
body: JSON.stringify(invoice),
});
if (!res.ok) {
const p = await res.json(); // RFC 9457: p.code einvoice_unsupported (p.feature, p.path) or einvoice_invalid (p.violations)
throw new Error(`${p.code}: ${p.detail}`);
}
console.log(res.headers.get("Veriport-Validation")); // valid; profile=xrechnung; errors=0; warnings=0
await writeFile("xrechnung.xml", Buffer.from(await res.arrayBuffer()));import json, os, requests
invoice = json.load(open("invoice.json", encoding="utf-8")) # the InvoiceInput model, format included
r = requests.post("https://veriport.io/api/v1/einvoice/create", json=invoice, timeout=60,
headers={"Authorization": f"Bearer {os.environ['VERIPORT_KEY']}", "Idempotency-Key": f"inv-{invoice['number']}"})
if not r.ok:
p = r.json() # einvoice_unsupported: p["feature"], p["path"]; einvoice_invalid: p["violations"]
raise RuntimeError(f"{p['code']}: {p['detail']}")
print(r.headers["Veriport-Validation"])
open("xrechnung.xml", "wb").write(r.content)<?php
$invoice = file_get_contents("invoice.json"); // the InvoiceInput model, format included
$ch = curl_init("https://veriport.io/api/v1/einvoice/create");
curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_POSTFIELDS => $invoice, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 60,
CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("VERIPORT_KEY"), "Content-Type: application/json", "Idempotency-Key: inv-2026-017"]]);
$file = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
if ($status >= 400) {
$p = json_decode($file, true); // einvoice_unsupported: feature, path; einvoice_invalid: violations
throw new RuntimeException($p["code"] . ": " . $p["detail"]);
}
file_put_contents("xrechnung.xml", $file);package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body, _ := os.ReadFile("invoice.json") // the InvoiceInput model, format included
req, _ := http.NewRequest("POST", "https://veriport.io/api/v1/einvoice/create", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("VERIPORT_KEY"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", "inv-2026-017")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
file, _ := io.ReadAll(res.Body)
if res.StatusCode >= 400 {
panic(string(file)) // RFC 9457 problem document: code, detail, feature, violations
}
fmt.Println(res.Header.Get("Veriport-Validation"))
os.WriteFile("xrechnung.xml", file, 0o644)
}using System.Net.Http.Headers;
using System.Text;
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("VERIPORT_KEY"));
var req = new HttpRequestMessage(HttpMethod.Post, "https://veriport.io/api/v1/einvoice/create")
{
Content = new StringContent(File.ReadAllText("invoice.json"), Encoding.UTF8, "application/json"),
};
req.Headers.Add("Idempotency-Key", "inv-2026-017");
var res = await http.SendAsync(req);
if (!res.IsSuccessStatusCode) throw new Exception(await res.Content.ReadAsStringAsync()); // RFC 9457 problem document
Console.WriteLine(string.Join("", res.Headers.GetValues("Veriport-Validation")));
await File.WriteAllBytesAsync("xrechnung.xml", await res.Content.ReadAsByteArrayAsync());Formats
The format field picks the file; the JSON is the same for all four.
| format | Standard | Syntax | Seller (v1) | File |
|---|---|---|---|---|
xrechnung-ubl | XRechnung 3.0.2 (German CIUS of EN 16931) | UBL 2.1 Invoice / CreditNote | Germany | XML |
xrechnung-cii | XRechnung 3.0.2 | UN/CEFACT CII D16B | Germany | XML |
zugferd | ZUGFeRD 2.x / Factur-X 1.0, profile EN 16931 (« COMFORT ») | CII D16B embedded as factur-x.xml | Germany | PDF/A-3b |
peppol-bis | Peppol BIS Billing 3.0 with the Belgian rules (enterprise number under 0208, structured communication, franchise mention) | UBL 2.1 Invoice / CreditNote | Belgium | XML |
Validator evidence: every sample through the official validators
Every sample case was written through the API’s own code path and checked by the reference validators, with the verdict each tool gave (run of 2026-10-28, pnpm validate:official; the raw reports are kept with the samples). 41 cases, 175 files; 12 broken documents (one published rule broken each): KoSIT gave the expected verdict on 12 of 12, with the rule named. The last row compares this API’s own validator (POST /einvoice/validate) with KoSIT rule id by rule id, broken documents included.
- KoSIT validator: 1.6.3 (SHA-256
799e64befca9…) - XRechnung validator configuration (XRechnung 3.0.2, CEN schematron 1.3.16, xrechnung-schematron 2.6.0): v2026-08-31 (SHA-256
2530cd107c41…) - Peppol BIS Billing 3.0 rules (PEPPOL-EN16931-UBL.sch): 3.0.21 (SHA-256
9616cfffb418…) - SchXslt (schematron to XSLT, run on the Saxon HE inside the KoSIT validator): 1.10.1 (SHA-256
4f4f21edab7b…) - veraPDF (greenfield): 1.30.3 (SHA-256
b512bf249455…) - Mustang CLI: 2.26.0 (SHA-256
42d7868cb682…)
| Validator | What it checks | Result |
|---|---|---|
| KoSIT validator 1.6.3 + XRechnung configuration v2026-08-31 | XML schema (UBL 2.1 / CII D16B), CEN EN 16931 rules 1.3.16, XRechnung 3.0.2 rules 2.6.0 (XRechnung UBL and CII); EN 16931 rules on the XML inside the ZUGFeRD PDF | 93 of 93 XML files accepted |
| KoSIT validator 1.6.3 + Peppol BIS Billing 3.0.21 | XML schema (UBL 2.1), CEN EN 16931 rules 1.3.16, PEPPOL-EN16931-UBL 3.0.21 (compiled with SchXslt 1.10.1) | 10 of 10 Peppol files accepted |
| veraPDF 1.30.3 | ISO 19005-3 PDF/A-3B validation profile (ZUGFeRD PDFs and readable copies) | 72 of 72 PDFs compliant |
| Mustang 2.26.0 | ZUGFeRD / Factur-X: PDF/A-3 (veraPDF inside), XMP and attachment, embedded XML against the Factur-X EN 16931 schematron and arithmetic | 31 of 31 ZUGFeRD PDFs valid |
| In-process validator (POST /einvoice/validate) against KoSIT | the same rule ids, errors and warnings, on every XML file | 115 of 115 files agree |
What the check runs
The XML schema first (UBL 2.1 or CII D16B, the files the KoSIT configuration ships, on libxml2), then the official schematron of the document’s profile, evaluated in-process from the files as published: the CEN EN 16931 rules 1.3.16 always; the XRechnung 3.0.2 rules 2.6.0 for an XRechnung, with the severities of the KoSIT configuration (a unit code outside Recommendation 20 is a warning there, fatal elsewhere); the Peppol BIS Billing 3.0.21 rules for a Peppol document. Rule ids and messages are the published ones: CEN and Peppol in English, the XRechnung BR-DE rules in German.
Veriport’s own checks of national practice come as warnings only, never as a published rule: a German VAT identifier whose check digit fails (VP-DE-001), a Leitweg-ID whose check digits fail (VP-DE-003), a Belgian rate other than 21, 12 or 6 % (VP-BE-004), a structured communication whose mod 97 fails (VP-BE-002)… The engine itself holds 156 rules it checks before writing, 95 of them EN 16931 rules, each one tested on a document that breaks it.
Not checked per request: the PDF/A-3 conformance of a hybrid PDF (veraPDF does it on the sample set; checks.pdfa says not_checked), and the Factur-X MINIMUM, BASIC WL and EXTENDED profiles (refused with 422).
What the engine adds, and tells you
Only where a rule needs a value you did not give, and each one is returned in defaults (and counted in the Veriport-Defaults header):
VATEX-EU-AEon a reverse-charge breakdown andVATEX-EU-ICon an intra-Community supply without a reason of your own (BR-AE-10, BR-IC-10).- The statutory mention for
seller.vatScheme: "small_business": § 19 UStG for a German Kleinunternehmer, the franchise of article 56bis for a Belgian business (BR-E-10). - The electronic address:
0208+ the enterprise number for a Belgian party on Peppol,EM+ the contact e-mail on XRechnung and ZUGFeRD (BT-34, BT-49 are mandatory there). - The Steuernummer repeated as seller identifier (BT-29) when a German seller has no VAT identifier (BR-CO-26), the buyer’s address as deliver-to address of an intra-Community supply (BR-IC-12), the due date from
paymentTermsDays.
What v1 does not do, and what it refuses
Nothing is ever dropped silently: a field the writers cannot put in the file is refused with 422 einvoice_unsupported, naming the feature and its path, and nothing is charged. Fields it refuses: attachments (BG-24), prepaid_amount (BT-113), rounding_amount (BT-114), deliver_to_party (BT-80), project_reference (BT-11), receiving_advice (BT-15), despatch_advice (BT-16), tender_reference (BT-17), invoiced_object (BT-18), accounting_reference (BT-19), tax_representative (BG-11), payee (BG-10), tax_point_date (BT-7, BT-8), tax_currency (BT-6, BT-111), payment_means (BG-19), payment_discount (BT-20), line_period (BG-26), line_allowances_charges (BG-28), item_identifiers (BT-158), item_origin_country (BT-159), item_attributes (BG-32), line_note (BT-127), line_references (BT-128), price_details (BT-147, BT-148), line_net_amount (BT-131), seller_identifier (BT-29), buyer_identifier (BT-46), computed_totals (BG-22, BG-23), and any field the model does not know (unknown_field). Values it refuses: a currency other than EUR (currency); VAT categories G (export), O (outside the scope), L, M and B (vat_category_g …); a seller outside Germany (XRechnung, ZUGFeRD) or Belgium (Peppol BIS) (seller_country), a buyer outside the EU (buyer_outside_eu); negative quantities (negative_line: issue a credit note), more than 500 lines (line_count); payment by card or direct debit (payment_means), invoice types other than 380, 381, 384 (document_type); two different exemption reasons for one VAT category and rate (multiple_exemption_reasons: EN 16931 holds one per breakdown).
- It does not send invoices over Peppol or any other network: that needs a certified access point. Send the file through your access point or a provider such as Recommand; Veriport produces and checks the file.
- It does not keep documents: the input and the file live in memory for the request; the usage row keeps the endpoint and the format, the log line the size and the timing. Archiving (GoBD in Germany) stays with you.
- It does not give tax advice: it writes and checks the format from the data you send. The VAT category, the exemption reason and the rate are your decision (or your tax adviser’s).
- v1 formats only: XRechnung 3.0.2, ZUGFeRD 2.x / Factur-X EN 16931, Peppol BIS Billing 3.0 with the Belgian rules. Later, on demand: XRechnung 4.0 (expected spring 2027), the ZUGFeRD XRECHNUNG profile, Factur-X EXTENDED, the French platform (PA) specifics, sellers outside Germany and Belgium.
Price per 1,000 invoices, against the named alternatives
Read on the providers’ public pricing pages on 2026-10-28. They are access points: their price includes sending the invoice over Peppol, which Veriport does not do; ours is the file and its check. Our prices include the tax of your country, charged by Stripe as merchant of record.
| Offer | What it is | Per document | Per 1,000 |
|---|---|---|---|
| Recommand 200 plan | €29 a month for 200 documents sent or received over Peppol | €0.145 (€0.20 above 200) | €145 |
| e-invoice.be Pro | Peppol sending, per invoice | €0.25 (Enterprise from €0.18) | €250 |
| Veriport create, 10,000-call pack | $29 once, 20 calls an invoice | $0.058 | $58.00 |
| Veriport create, 2,000-call pack | $9 once | $0.090 | $90.00 |
| Veriport create, monthly plan | $15 a month for 5,000 calls = 250 invoices | $0.060 | $60.00 |
| Veriport validate or parse | 5 calls a document | $0.014–$0.022 | $14.50–$22.50 |
Free before any of this: 100 calls a month with a key (five created invoices, or twenty checks), 4 checks a day without one. A refused request (400, 413, 422) is never counted.
Questions
- Does Veriport send the invoice to my customer?
- No. Sending over Peppol needs a certified access point, and e-mailing an XRechnung or a ZUGFeRD PDF is your system’s job. The API returns the file; you send it, or your access point does.
- Do you store the invoices?
- No. The JSON and the file exist in memory during the request and are gone after it. The usage row keeps the endpoint and the format; the log line keeps the size, the line count and the timing, never the content. An Idempotency-Key replay rebuilds the file from your repeated request instead of storing it.
- Is the produced file guaranteed to be accepted?
- Every file is checked before it leaves: the XML schema and the official rules of its format run on it, and a fatal finding withholds the file (422
einvoice_invalid). That is the same check the receiving side runs. What no format check can decide is whether the content is right: the VAT category, the rate, the exemption reason are yours. - Which XRechnung version, and what about 4.0?
- XRechnung 3.0.2, the version in force (KoSIT configuration v2026-08-31). XRechnung 4.0 is expected in spring 2027; it will be a new format value, announced in the changelog, with its own evidence run.
- Can I validate without a key?
- Yes: 4 checks a day per address, the free tools use exactly that. With a free key, 100 calls a month (20 checks).
- Is this tax advice?
- No. The API writes and checks the format from the data you send, and says which published rule a document breaks. Whether a supply is reverse charged or exempt is a tax question for you or your adviser.
100 calls a month, no card: five invoices or twenty checks.
Get a free keySources
- KoSIT validator (the reference validator for XRechnung), release 1.6.3 — KoSIT, consulted 2026-10-28
- Validator configuration XRechnung v2026-08-31 (XRechnung 3.0.2, CEN schematron 1.3.16, xrechnung-schematron 2.6.0) — KoSIT, consulted 2026-10-28
- EN 16931 validation artefacts 1.3.16 (UBL and CII schematron, the business rules BR-*) — CEN/TC 434, published with the European Commission (Connecting Europe), consulted 2026-10-28
- Peppol BIS Billing 3.0 — specification, identifiers, code lists and rules — OpenPeppol AISBL, consulted 2026-10-28. « Peppol » is a trademark of OpenPeppol; used descriptively.
- Peppol BIS Billing 3.0 release 3.0.21: PEPPOL-EN16931-UBL.sch and the CEN files it ships — OpenPeppol AISBL, consulted 2026-10-28
- Factur-X 1.0 (the Franco-German hybrid invoice: PDF/A-3 with CII, profiles MINIMUM to EXTENDED) — FNFE-MPE, consulted 2026-10-28
- ZUGFeRD 2.x (identical to Factur-X 1.0; the EN 16931 profile is « COMFORT ») — FeRD (Forum elektronische Rechnung Deutschland), consulted 2026-10-28
- veraPDF, the industry-supported PDF/A validator (release 1.30.3) — veraPDF consortium (PDF Association, Open Preservation Foundation), consulted 2026-10-28
- Mustang project (ZUGFeRD / Factur-X library and validator, release 2.26.0) — Mustang project (open source), consulted 2026-10-28
- § 14 UStG (Ausstellung von Rechnungen): the e-invoice for domestic B2B supplies, receipt since 1 January 2025, issue from 1 January 2027 (turnover above €800,000) and 1 January 2028 — Bundesministerium der Justiz (gesetze-im-internet.de), consulted 2026-10-28
- SPF BOSA — e-invoicing between Belgian businesses, structured (Peppol BIS) since 1 January 2026 — SPF BOSA (Belgium), consulted 2026-10-28
- Facturation électronique et plateformes agréées: reception by every business from 1 September 2026, issue by SMEs from 1 September 2027 — DGFiP (impots.gouv.fr), consulted 2026-10-28
- Recommand — pricing (certified Peppol access point with an API): 25 documents free then €0.30; €29 a month for 200 (€0.20 each above); €99 for 1,000 — Recommand, consulted 2026-10-28. Prices exclude VAT; a document is sent or received over Peppol: the price includes the transmission Veriport does not do.
- e-invoice.be — Peppol invoice generator and pricing: Pro from €0.25 per invoice, Enterprise from €0.18 — e-invoice.be, consulted 2026-10-28. Per sent invoice: the price includes the Peppol transmission.