Pricing & limits
Every validate, generate, convert, parse and peppol/lookup request uses one call; peppol/belgium-list uses one call per company checked. Failed requests (4xx) are not charged; usage and rules are free. The same calls are shared with the MCP server.
Included every month
Free account: 100 calls
Pro: 1,000 calls
Call packs (prepaid, excl. VAT)
- 2,000 calls — €19 (0.95 ct/call)
- 10,000 calls — €79 (0.79 ct/call)
- 50,000 calls — €299 (0.6 ct/call)
Pack credits are used after the monthly allowance and stay valid for 12 months. Rate limit: 120 metered requests per minute per account. Every metered response carries X-Usage-Calls-This-Month, X-Usage-Allowance and X-Usage-Pack-Credits-Remaining headers.
Authentication
Create a key in the dashboard (free account, up to 10 active keys) and send it as a bearer token. Keep keys on the server — never in browser code.
Authorization: Bearer iv_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Base URL: https://peppol-invoice-compass.lovable.app/api/public. Responses are JSON with Cache-Control: no-store; CORS is enabled. Invoice contents are processed in memory and never stored.
Validate an invoice
POST/api/public/v1/validate
Send the invoice in one of three ways:
Content-Type: application/xml— raw UBL or CII XML (up to 5 MB)Content-Type: application/pdf— a Factur-X / ZUGFeRD PDF; the embedded XML is extractedContent-Type: application/json—{"xml": "…", "filename": "…", "profile": "peppol"}
Query profile = auto (default) | peppol | en16931 | xrechnung. The XRechnung profile checks a subset of the German BR-DE rules on top of EN 16931.
curl -X POST "https://peppol-invoice-compass.lovable.app/api/public/v1/validate?profile=peppol" \ -H "Authorization: Bearer $INVOICEVERIFY_KEY" \ -H "Content-Type: application/xml" \ --data-binary @invoice.xml
Response
200 OK for every readable document — check valid. Each finding has the rule ID, severity, line, column, XPath and the expected and actual value. Explanations: rule reference or GET /v1/rules/{id}.
{
"valid": false,
"format": {
"id": "peppol-bis-3",
"label": "Peppol BIS Billing 3.0 (UBL 2.1)",
"syntax": "UBL",
"document": "Invoice"
},
"profile": "peppol",
"invoice": {
"number": "2026/0918",
"issueDate": "2026-10-20",
"dueDate": "2026-11-19",
"currency": "EUR",
"seller": {
"name": "Atelier Hoorn BV",
"vatId": "BE0123456749"
},
"buyer": {
"name": "Vlaamse Kantoorwerken NV",
"vatId": "BE0987654394"
},
"totals": {
"taxExclusive": 2144,
"taxAmount": 422.64,
"taxInclusive": 2566.64,
"payable": 2566.64
},
"lineCount": 3
},
"counts": {
"error": 5,
"warning": 0,
"info": 0
},
"sections": {
"document": {
"status": "pass",
"errors": 0,
"warnings": 0
},
"header": {
"status": "pass",
"errors": 0,
"warnings": 0
},
"parties": {
"status": "pass",
"errors": 0,
"warnings": 0
},
"lines": {
"status": "fail",
"errors": 1,
"warnings": 0
},
"vat": {
"status": "fail",
"errors": 3,
"warnings": 0
},
"totals": {
"status": "fail",
"errors": 1,
"warnings": 0
},
"payment": {
"status": "pass",
"errors": 0,
"warnings": 0
}
},
"rulesChecked": 139,
"findings": [
{
"rule": "BR-CO-14",
"source": "EN16931",
"severity": "error",
"section": "vat",
"title": "VAT total does not match breakdown",
"message": "VAT breakdown adds up to 403.04, but the VAT total is 422.64.",
"line": 88,
"column": 19,
"xpath": "/Invoice/cac:TaxTotal/cbc:TaxAmount",
"expected": "403.04",
"actual": "422.64"
},
{
"rule": "BR-S-08",
"source": "EN16931",
"severity": "error",
"section": "vat",
"title": "Standard rate taxable amount mismatch",
"message": "Lines, allowances and charges in S 21% add up to 1912.50, but the taxable amount is 1960.00.",
"line": 90,
"column": 25,
"xpath": "/Invoice/cac:TaxTotal/cac:TaxSubtotal[1]/cbc:TaxableAmount",
"expected": "1912.50",
"actual": "1960.00"
}
],
"durationMs": 4
}Generate invoice XML from JSON
POST/api/public/v1/generate
Send structured invoice data; line totals, the VAT breakdown and the amount due are calculated for you, the result is serialised and validated. Send Accept: application/xml to receive the raw XML. The full InvoiceData schema is in the OpenAPI spec.
| format | Output |
|---|---|
| peppol | Peppol BIS Billing 3.0 (UBL 2.1) |
| xrechnung-ubl | XRechnung 3.0 (UBL 2.1) |
| xrechnung-cii | XRechnung 3.0 (UN/CEFACT CII) |
| cii | Factur-X / ZUGFeRD EN 16931 XML (CII) |
curl -X POST https://peppol-invoice-compass.lovable.app/api/public/v1/generate \
-H "Authorization: Bearer $INVOICEVERIFY_KEY" \
-H "Content-Type: application/json" \
-d @invoice.json
# invoice.json
{
"format": "peppol",
"invoice": {
"number": "INV-2026-0042",
"issueDate": "2026-10-28",
"dueDate": "2026-11-27",
"currency": "EUR",
"buyerReference": "PO-7781",
"seller": {
"name": "Studio Noord BV", "vatId": "NL859876543B01",
"endpointId": "NL859876543B01", "endpointScheme": "9944",
"street": "Vismarkt 1", "city": "Groningen", "postalCode": "9712 CA", "country": "NL"
},
"buyer": {
"name": "Brasserie Lumière SRL", "vatId": "BE0403170701",
"endpointId": "0403170701", "endpointScheme": "0208",
"city": "Brussel", "country": "BE"
},
"payment": { "meansCode": "58", "iban": "NL91ABNA0417164300" },
"lines": [
{ "name": "Brand workshop", "quantity": 2, "unitCode": "DAY", "unitPrice": 850,
"vatCategory": "AE", "vatRate": 0 }
]
}
}Convert between formats
POST/api/public/v1/convert?format=xrechnung-ubl
Send a UBL or CII invoice (or a Factur-X PDF) and get it back in the target format — for example Factur-X CII → Peppol UBL, or Peppol UBL → XRechnung. The response includes sourceFormat and the validation result of the output.
curl -X POST "https://peppol-invoice-compass.lovable.app/api/public/v1/convert?format=peppol" \ -H "Authorization: Bearer $INVOICEVERIFY_KEY" \ -H "Content-Type: application/pdf" \ -H "Accept: application/xml" \ --data-binary @factur-x.pdf -o invoice-peppol.xml
Read an invoice into JSON
POST/api/public/v1/parse
Turns UBL, CII or Factur-X into the same InvoiceData JSON used by /v1/generate — handy for booking incoming invoices or round-tripping corrections.
{ "format": "Peppol BIS Billing 3.0 (UBL Invoice)", "valid": true,
"counts": { "error": 0, "warning": 0, "info": 0 },
"invoice": { "number": "INV-2026-0042", "issueDate": "2026-10-28", "seller": { … }, "lines": [ … ] } }Check Peppol receivability
GET/api/public/v1/peppol/lookup?id=BE0403170701
Pass a VAT number, company number (KBO/BCE, KvK, SIREN/SIRET), GLN, Leitweg-ID or a Peppol ID. Every relevant identifier is checked live in the Peppol SML/SMP — for Belgium both 0208 (enterprise number) and 9925 (VAT) — with the supported document types, endpoint and advice on which ID to send to. Optional country hint for ambiguous numbers.
{
"query": "BE0403170701",
"inputKind": "Belgian VAT number",
"verdict": "receivable",
"recommendedId": "0208:0403170701",
"canReceive": { "invoice": true, "creditNote": true },
"company": { "name": "ELECTRABEL", "country": "BE" },
"participants": [
{ "id": "0208:0403170701", "status": "registered", "smpHost": "smp.tickstar.com",
"documents": [{ "label": "Peppol BIS Billing 3.0 — Invoice", "kind": "invoice", "billing3": true }, …],
"endpoint": { "transportProfile": "peppol-transport-as4-v2_0", "expiration": "2030-01-30T00:00:00Z" } },
{ "id": "9925:BE0403170701", "status": "registered", … }
],
"advice": [{ "level": "ok", "text": "Can receive Peppol BIS Billing 3.0 invoices. Send to 0208:0403170701 …" }]
}verdict is receivable, partial (on Peppol, but not for BIS Billing 3.0 invoices), not_registered or unknown (the network could not be reached — retry). Try it without a key on the Peppol ID lookup.
Check a Belgian client list (0208 and 9925)
POST/api/public/v1/peppol/belgium-list
Send up to 25 Belgian enterprise or VAT numbers. Each company is checked live under both Belgian Peppol IDs — 0208 (enterprise number) and 9925 (VAT number) — and gets a coverage of both, only_0208, only_9925, neither or unknown (the network didn’t answer — retry), with the risk and fix in lang en, nl or fr. One call per valid company, charged all or nothing; unreadable numbers are free and listed under invalid. Split longer lists into several requests.
curl -X POST "https://peppol-invoice-compass.lovable.app/api/public/v1/peppol/belgium-list" \
-H "Authorization: Bearer $INVOICEVERIFY_KEY" \
-H "Content-Type: application/json" \
-d '{"lang": "nl", "companies": ["BE0403170701", {"id": "0462.920.226", "name": "Klant NV"}]}'{
"summary": { "checked": 2, "both": 1, "only0208": 1, "only9925": 0, "neither": 0, "unknown": 0, "invalid": 0 },
"results": [
{ "input": "0462.920.226", "name": "Klant NV", "enterpriseNumber": "0462.920.226", "vatNumber": "BE0462920226",
"coverage": "only_0208", "receivableUnder0208": true, "receivableUnder9925": false,
"sendTo": "0208:0462920226",
"ids": { "0208": { "id": "0208:0462920226", "status": "receivable", "serviceProvider": "smp.example.eu" },
"9925": { "id": "9925:BE0462920226", "status": "not_registered", "serviceProvider": null } },
"result": "Alleen onder 0208 (ondernemingsnummer)",
"risk": "Leveranciers waarvan de software Belgische klanten op btw-nummer adresseert …",
"fix": ["Vraag het bedrijf … om ook 9925:BE0462920226 te registreren …", "Laat leveranciers tot dan naar 0208:0462920226 sturen."],
"notes": [], "checkedAt": "2026-10-29T10:00:00.000Z" },
…
],
"invalid": []
}If a request needs more calls than you have left, it returns 402 with needed and available. In the browser, the same check is available for whole lists on the client list check.
Rule reference (free)
GET/api/public/v1/rules/BR-CO-15?lang=nl
Every rule ID returned by the validator, with the technical description and a plain-language explanation, typical causes and fix steps in English, Dutch or French. No API key needed. GET /v1/rules lists all rules.
Usage (free)
GET/api/public/v1/usage
{
"period": { "start": "2026-10-01T00:00:00.000Z", "end": "2026-11-01T00:00:00.000Z" },
"callsThisMonth": 1284,
"includedCalls": 1000,
"includedRemaining": 0,
"packCreditsRemaining": 1716,
"packs": [{ "pack": "starter", "remaining": 1716, "expiresAt": "2027-09-12T08:14:00.000Z" }],
"rateLimitPerMinute": 120
}Errors
Errors use one shape:
{ "error": { "code": "quota_exhausted", "message": "You have used your 100 included calls this month …" } }| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_body / empty_body / invalid_format | The body or a parameter is not in the expected shape |
| 401 | missing_api_key / invalid_api_key | No key, a malformed key or a revoked key |
| 402 | quota_exhausted | Monthly allowance used up and no pack credits left |
| 413 | payload_too_large | XML over 5 MB or request over 10 MB |
| 415 | unsupported_media_type | Use application/xml, application/json or application/pdf |
| 422 | unreadable_invoice / invalid_invoice_data / unrecognised_identifier | The document, data or identifier could not be processed |
| 429 | rate_limited | More than 120 metered requests in the last minute |