Learn how to integrate InvoiceHub validation into your application.
All API requests require an API key. Include it in the Authorization header:
Authorization: Bearer ih_live_xxxxxxxxxxxxxxxxxxxxxxGet your API key from the dashboard.
Validate a UBL 2.1 invoice against the official CEN EN 16931 Schematron (v1.3.16). Returns structured errors and warnings mapped to the official BR-* rule identifiers.
No API key yet? Try POST /api/v1/playground (unauthenticated, rate-limited) or the live playground on the home page.
Content-Type: application/xmlAuthorization: Bearer <api_key>Raw XML invoice document
<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
<cbc:CustomizationID>urn:cen.eu:en16931:2017</cbc:CustomizationID>
<cbc:ID>INV-001</cbc:ID>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
<!-- ... -->
</Invoice>{
"timestamp": "2026-06-17T10:30:00Z",
"requestId": "0f3c…",
"performance": { "duration_ms": 5 },
"report": {
"valid": true,
"format": "UBL_2.1",
"profile": "EN 16931 (CustomizationID: urn:cen.eu:en16931:2017)",
"rulesetVersion": "EN16931-1.3.16",
"assertionsEvaluated": 211,
"errors": [],
"warnings": [],
"metadata": {
"invoiceNumber": "INV-2026-001",
"currency": "EUR",
"payableAmount": 180.6
}
}
}{
"timestamp": "2026-06-17T10:30:00Z",
"requestId": "0f3c…",
"performance": { "duration_ms": 4 },
"report": {
"valid": false,
"format": "UBL_2.1",
"rulesetVersion": "EN16931-1.3.16",
"assertionsEvaluated": 187,
"errors": [
{
"ruleId": "BR-CO-15",
"severity": "CRITICAL",
"message": "Invoice total amount with VAT must equal total without VAT + total VAT.",
"path": "Invoice.cac:LegalMonetaryTotal.cbc:TaxInclusiveAmount"
}
],
"warnings": []
}
}Build an EN 16931-conformant UBL 2.1 invoice from structured JSON. Every monetary total is computed for you, and the generated document is validated against the official Schematron before it is returned — if it would not pass, you get the validation report (HTTP 422) instead of an invalid document. We never hand back XML we have not checked.
{
"invoiceNumber": "INV-2026-001",
"issueDate": "2026-06-19",
"dueDate": "2026-07-19",
"currency": "EUR",
"seller": {
"name": "Acme Trading GmbH",
"vatId": "DE123456789",
"address": { "street": "Hauptstraße 1", "city": "Berlin", "postalZone": "10115", "country": "DE" }
},
"buyer": {
"name": "Buyer Industries SARL",
"address": { "city": "Paris", "postalZone": "75001", "country": "FR" }
},
"lines": [
{ "description": "Consulting services", "quantity": 10, "unitPrice": 100, "vatRate": 19, "unitCode": "HUR" }
],
"payment": { "iban": "DE89370400440532013000" }
}{
"requestId": "0f3c…",
"performance": { "duration_ms": 6 },
"xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Invoice …>…</Invoice>",
"report": { "valid": true, "format": "UBL_2.1", "rulesetVersion": "EN16931-1.3.16", "assertionsEvaluated": 156, "errors": [], "warnings": [] }
}Standard-rated lines (VAT category S) require avatRate and the seller vatId (BR-S-2). Exempt and reverse-charge categories require an exemptionReason.
Convert an invoice between any two supported formats via the shared canonical model: the source document is parsed into every EN 16931 business term, then re-serialized as the target. The result is validated against the target format's own official rules before it is returned — same discipline as /generate, never a document we have not checked. See worked examples for every conversion pair.
{
"from": "UBL_2.1",
"to": "UN_CEFACT_CII",
"document": "<Invoice …>…</Invoice>"
}document is raw text for XML formats (UBL, CII, XRechnung), or base64 for the binary Factur-X format — same encoding on the way out.
{
"requestId": "0f3c…",
"performance": { "duration_ms": 9 },
"document": "<rsm:CrossIndustryInvoice …>…</rsm:CrossIndustryInvoice>",
"encoding": "utf8",
"report": { "valid": true, "format": "UN_CEFACT_CII", "rulesetVersion": "EN16931-1.3.16", "assertionsEvaluated": 63, "errors": [], "warnings": [] }
}{
"document": "…",
"encoding": "utf8",
"report": { "valid": false, "errors": [{ "ruleId": "BR-DE-6", "severity": "CRITICAL",
"message": "[BR-DE-6] Seller contact telephone number (BT-42) must be provided." }] }
}A 403 names the required plan when the target format isn't available on your current tier (e.g. converting to anything beyond UBL 2.1 requires Pro or above). A 400 means the source document or format id couldn't be parsed at all — check from and to against the live format list.
Query GET /api/v1/formats for the live list, or see the full format reference and worked conversion examples. Current status:
| Format | Status |
|---|---|
| OASIS UBL 2.1 Invoice & CreditNote | Generally available |
| Official CEN EN 16931 Schematron (v1.3.16) | Generally available |
| UN/CEFACT Cross Industry Invoice (D16B) | Generally available |
| XRechnung 3.0 (UBL) | Generally available |
| XRechnung 3.0 (CII) | Generally available |
| Factur-X / ZUGFeRD (PDF/A-3, EN 16931 profile) | Generally available |
UBL 2.1 is validated with the official CEN EN 16931 Schematron artifacts (v1.3.16). XSD schema validation and additional syntaxes are in progress.
InvoiceHub ships a hosted Model Context Protocol server so AI agents can validate, generate, and convert EU e-invoices directly — no install, no API key needed for validation or UBL generation.
Read the MCP docs →The full API is described by an OpenAPI 3.1 document. Import it into Postman or Insomnia, or generate a typed client in your language with tools likeopenapi-generator,Speakeasy orFern.
npx @openapitools/openapi-generator-cli generate \
-i https://api.invoicehub.dev/api/v1/openapi.json \
-g typescript-fetch -o ./invoicehub-clientcurl -X POST https://api.invoicehub.dev/api/v1/validate \
-H "Authorization: Bearer ih_live_xxx" \
-H "Content-Type: application/xml" \
-d @invoice.xmlconst response = await fetch('https://api.invoicehub.dev/api/v1/validate', {
method: 'POST',
headers: {
'Authorization': 'Bearer ih_live_xxx',
'Content-Type': 'application/xml'
},
body: xmlContent
});
const result = await response.json();
console.log(result.report);import requests
response = requests.post(
'https://api.invoicehub.dev/api/v1/validate',
headers={
'Authorization': 'Bearer ih_live_xxx',
'Content-Type': 'application/xml'
},
data=xml_content
)
result = response.json()
print(result['report'])/validate is unlimited on every plan, forever — it is never metered against a quota. /generate is metered by documents produced per month, reset on the first of each UTC calendar month. A document that fails its own validation (HTTP 422) never counts against your quota — you are only ever charged for a document that actually validated.
Every metered response includes:
X-Usage-Documents-Limit: 2000X-Usage-Documents-Remaining: 1847X-Usage-Period-Reset: 2026-09-01T00:00:00ZSee pricing for the documents/month included on each plan. On top of this, a flat abuse-prevention limit of 600 requests/minute per key applies equally to every plan — it is not a pricing lever, just a burst guard.
Invalid or missing API key. Check your authentication header.
The requested format isn't available on your current plan. The error names the format and the minimum plan required.
Invoice validation failed. Check the errors array in the response. Never consumes quota.
Monthly document quota exhausted. The response includes your plan, limit, period reset time, and an upgrade link.
Abuse-prevention burst limit exceeded (600 req/min per key, all plans). Check Retry-After for when to retry.
Unexpected error. Contact support if this persists.
Check our pricing page for support options.
Contact us and we'll get back to you.