API documentation
REST/JSON API for Peppol BIS Billing 3.0 (UBL 2.1). Base URL: https://efactuur.klik-shop.nl/v1
Authentication and credit
Send your key in the X-API-Key header. Get a free key on Free API key. Each key has 5 free documents per calendar month; after that, credit is used (€9 per 1,000, €29 per 5,000, incl. VAT). A document is one successful /v1/ubl or /v1/validate call. Input errors (422) on /v1/ubl and invalid requests (400/413) are not charged. Max. 60 requests per minute per key.
Message language: ?lang=en or ?lang=nl (otherwise from Accept-Language).
Privacy: documents are processed in memory only and are not stored or logged. Every response contains "stored": false.
POST /v1/ubl
JSON in, UBL 2.1 Peppol BIS Billing 3.0 out. Calculates line totals, VAT per rate and document totals (rounded to 2 decimals). Add ?format=xml to get the XML file directly.
curl -X POST https://efactuur.klik-shop.nl/v1/ubl -H "X-API-Key: ubl_your_key" -H "content-type: application/json" -d @factuur.json
{
"type": "invoice",
"number": "F2026-001",
"issue_date": "2026-11-01",
"due_date": "2026-11-15",
"buyer_reference": "PO-123",
"seller": {
"name": "Jouw Bedrijf",
"vat": "NL005310837B66",
"kvk": "98148362",
"address": { "street": "Straat 1", "city": "Leiden", "postal_code": "2315 VW", "country": "NL" },
"contact": { "email": "factuur@example.nl" }
},
"buyer": {
"name": "Voorbeeld B.V.",
"vat": "NL123456789B01",
"kvk": "12345678",
"address": { "street": "Hoofdstraat 1", "city": "Utrecht", "postal_code": "3511 AA", "country": "NL" }
},
"payment": { "iban": "NL91ABNA0417164300" },
"lines": [
{ "name": "Webshop teksten", "quantity": 3, "unit": "HUR", "price": "65.00", "vat_rate": 21 },
{ "name": "Boek", "quantity": 2, "price": "19.99", "vat_rate": 9 }
]
}
Response (200, shortened):
{
"document_type": "Invoice",
"xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Invoice xmlns=\"urn:oasis:names:specification:ubl:schema:xsd:Invoice-2\" xmlns:cac…",
"totals": {
"line_extension": "234.98",
"allowances": "0.00",
"charges": "0.00",
"tax_exclusive": "234.98",
"vat": "44.55",
"tax_inclusive": "279.53",
"prepaid": "0.00",
"payable": "279.53",
"currency": "EUR",
"vat_breakdown": [
{
"category": "S",
"rate": "21",
"taxable": "195.00",
"vat": "40.95"
},
{
"category": "S",
"rate": "9",
"taxable": "39.98",
"vat": "3.60"
}
]
},
"warnings": [
{
"field": "seller.endpoint",
"code": "DERIVED",
"message": "seller Peppol address derived: 0106:98148362. Check that this is the address the party is registered with in Peppol."
},
{
"field": "buyer.endpoint",
"code": "DERIVED",
"message": "buyer Peppol address derived: 0106:12345678. Check that this is the address the party is registered with in Peppol."
}
],
"self_check": {
"valid": true,
"errors": 0,
"warnings": 0,
"scope": "subset",
"issues": []
},
"stored": false
}
| Field | Description |
|---|---|
type | invoice (default) or credit_note |
number, issue_date | Invoice number and date (YYYY-MM-DD). Required. |
due_date, payment_terms | Due date and/or payment terms text. |
buyer_reference / order_reference | Peppol requires one of them (BT-10 or BT-13). Government buyers provide a reference. |
currency | ISO 4217, default EUR. |
seller, buyer | name, vat, kvk (NL), kbo (BE), legal_id {id, scheme}, endpoint {id, scheme}, address {street, additional, city, postal_code, region, country}, contact {name, phone, email}, trading_name. Without endpoint the Peppol ID is derived (KvK → 0106, KBO → 0208, otherwise the VAT number) and reported as a warning. |
payment | iban, bic, account_name, reference, means_code (default 58 SEPA credit transfer, or 30), mandate_id |
lines[] | name, quantity, unit (UN/ECE Rec 20, default C62), price (excl. VAT), vat_rate, vat_category (S, Z, E, AE, K, G, O, L, M), optional description, base_quantity, allowance/charge {amount, reason}, sku, gtin, period, note. |
allowances[], charges[] | Document-level allowance/charge: amount or percent + base_amount, reason, vat_rate/vat_category. |
prepaid, rounding | Prepaid amount and rounding amount. |
preceding_invoice | {number, date}: reference to the original invoice (for credit notes). |
exemption_reasons | E.g. {"E": "Exempt under …"}. For AE/K/G/O a default text and VATEX code are filled in. |
note, invoice_period, delivery, contract_reference, project_reference | Optional. |
Input errors return 422 with a message per field in English and Dutch, e.g. {"field": "lines[0].price", "code": "INVALID_NUMBER", "message": "…"}.
POST /v1/validate
Send the UBL XML as the body (Content-Type: application/xml) or as JSON {"xml": "…"}. Max. 2 MB.
curl -X POST "https://efactuur.klik-shop.nl/v1/validate?lang=en" -H "X-API-Key: ubl_your_key" -H "content-type: application/xml" --data-binary @factuur.xml
Response (shortened):
{
"valid": false,
"document_type": "Invoice",
"errors": 4,
"warnings": 0,
"issues": [
{
"layer": "en16931",
"severity": "error",
"rule": "BR-CO-15",
"location": "/Invoice/cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount",
"line": 97,
"message": "Invoice total amount with VAT (BT-112) = Invoice total amount without VAT (BT-109) + Invoice total VAT amount (BT-110).",
"value": "272.35 ≠ 277.35",
"explanation": "Totaal incl. btw (BT-112) moet gelijk zijn aan totaal excl. btw + btw. Oplossing: Herbereken TaxInclusiveAmount.",
"explanation_nl": "Totaal incl. btw (BT-112) moet gelijk zijn aan totaal excl. btw + btw.",
"fix_nl": "Herbereken TaxInclusiveAmount.",
"fix_en": "Recalculate TaxInclusiveAmount = TaxExclusiveAmount + VAT."
},
"…"
],
"scope": "subset",
"scope_note": "Server-side check: … This is NOT the complete official Schematron …",
"full_validator": "https://efactuur.klik-shop.nl/valideren",
"stored": false
}
The API checks server-side: well-formed XML, the UBL 2.1 structure (element names, order, cardinality, data types, code lists) and about 120 of the most common EN16931, Peppol BIS 3.0 and Dutch NL-R rules, re-implemented in code. This is not the complete official Schematron (the official rules are too heavy for our serverless hosting). National rules of other countries (DE, DK, NO, SE, …) are not in the API. For the complete official check use the free browser validator. The response always contains
"scope": "subset".Each issue has layer (xml, xsd, en16931, peppol, nl), severity (error/warning), rule (official rule ID where possible; our own checks start with KS-), location, line, message (official English rule text) and explanation/fix_nl/fix_en.
GET /v1/account
curl https://efactuur.klik-shop.nl/v1/account -H "X-API-Key: ubl_your_key"
Status codes
200 | OK |
400 | invalid body (free) |
401 | missing or unknown key |
402 | free documents used up and no credit |
413 | too large |
422 | input errors on /v1/ubl (free) |
429 | too many requests (see Retry-After) |
Headers: X-Quota-Remaining, X-Validation-Valid.
Limitations
- Klik&Shop is not a Peppol Access Point: the API creates and checks files but does not send anything.
- Supported: Peppol BIS Billing 3.0 (UBL Invoice and CreditNote). Not: XRechnung CIUS, Factur-X/ZUGFeRD, attachments via the generator.
- The generator checks its own output with the API subset (
self_check). We tested the generator against the complete official rules; if in doubt, run your file through the browser validator.