API Documentation

Learn how to integrate InvoiceHub validation into your application.

Authentication

All API requests require an API key. Include it in the Authorization header:

Authorization: Bearer ih_live_xxxxxxxxxxxxxxxxxxxxxx

Get your API key from the dashboard.

Validate Invoice

POST /api/v1/validate

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.

Request Headers

Content-Type: application/xml
Authorization: Bearer <api_key>

Request Body

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>

Response (200 OK - Valid)

{
  "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
    }
  }
}

Response (422 Unprocessable Entity - Invalid)

{
  "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": []
  }
}

Generate Invoice

POST /api/v1/generate

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.

Request Body (application/json)

{
  "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" }
}

Response (200 OK)

{
  "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 Invoice

POST /api/v1/convert

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.

Request Body (application/json)

{
  "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.

Response (200 OK)

{
  "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": [] }
}

Response (422 — target validation failed)

{
  "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.

Supported Formats & Roadmap

Query GET /api/v1/formats for the live list, or see the full format reference and worked conversion examples. Current status:

FormatStatus
OASIS UBL 2.1 Invoice & CreditNoteGenerally 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.

MCP server (for AI agents)

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 →

OpenAPI & SDKs

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-client

Code Examples

cURL

curl -X POST https://api.invoicehub.dev/api/v1/validate \
  -H "Authorization: Bearer ih_live_xxx" \
  -H "Content-Type: application/xml" \
  -d @invoice.xml

JavaScript

const 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);

Python

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'])

Usage & Limits

/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: 2000
X-Usage-Documents-Remaining: 1847
X-Usage-Period-Reset: 2026-09-01T00:00:00Z

See 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.

Error Handling

401 Unauthorized

Invalid or missing API key. Check your authentication header.

403 Forbidden

The requested format isn't available on your current plan. The error names the format and the minimum plan required.

422 Unprocessable Entity

Invoice validation failed. Check the errors array in the response. Never consumes quota.

402 Payment Required

Monthly document quota exhausted. The response includes your plan, limit, period reset time, and an upgrade link.

429 Too Many Requests

Abuse-prevention burst limit exceeded (600 req/min per key, all plans). Check Retry-After for when to retry.

500 Internal Server Error

Unexpected error. Contact support if this persists.

Need help?

Check our pricing page for support options.

Contact us and we'll get back to you.