Documentation
Introduction
The E-Invoice API turns a plain JSON description of an invoice into a standards-compliant electronic invoice — and back again. One canonical model feeds every supported format, so adding XRechnung CII alongside XRechnung UBL is a parameter change, not a rewrite.
The base URL for this instance is https://api.invoapi.de. Every response carries an X-Request-ID header that also appears in error bodies.
What “valid” means here
status: "unsupported_validation" with valid: null rather than reporting an unchecked document as valid. Technical conformance is not by itself a statement about tax or legal acceptance.Quickstart
Send an invoice, get the XML. Replace the placeholder key with one from your dashboard; everything else is the complete request.
curl -X POST https://api.invoapi.de/api/v1/generate \
-H "Authorization: Bearer inv_test_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"format": "xrechnung",
"syntax": "ubl",
"version": "3.0.2",
"invoice": {
"invoice_number": "RE-2026-0001",
"issue_date": "2026-08-20",
"due_date": "2026-09-19",
"currency": "EUR",
"buyer_reference": "04011000-12345-34",
"seller": {
"name": "Muster GmbH",
"street": "Musterstraße 1",
"postal_code": "10115",
"city": "Berlin",
"country_code": "DE",
"vat_id": "DE123456789",
"email": "rechnung@muster.de",
"phone": "+49 30 1234567"
},
"buyer": {
"name": "Beispiel GmbH",
"street": "Teststraße 10",
"postal_code": "20095",
"city": "Hamburg",
"country_code": "DE",
"vat_id": "DE987654321",
"email": "kreditoren@beispiel.de"
},
"items": [
{
"name": "Softwareentwicklung",
"quantity": 10,
"unit": "HUR",
"unit_price": 100,
"vat_rate": 19
}
],
"payment": {
"means_code": "58",
"iban": "DE02120300000000202051",
"terms": "Zahlbar innerhalb von 30 Tagen ohne Abzug."
}
}
}'The response contains the generated document, the recomputed totals and the full validation report. Try it live in the playground.
Authentication
The invoicing endpoints require an API key. Create one in the dashboard and send it as a bearer token:
# Every invoicing endpoint expects your key
curl -X POST https://api.invoapi.de/api/v1/generate \
-H "Authorization: Bearer inv_test_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "format": "xrechnung", "invoice": { … } }'
# Without one you get 401 UNAUTHORIZED
{ "error": { "code": "UNAUTHORIZED", "message": "This endpoint requires an API key. …" } }X-API-Key is accepted as an alternative for clients that cannot set the Authorization header. A key is shown once when it is created and stored only as a hash — if you lose it, create a new one and revoke the old.
GET /api/v1/health, GET /api/v1/formats and GET /api/v1/formats/{format} stay public: they describe the service rather than doing work for you. Signed-in browser sessions may also use the endpoints, which is how the generator, validator and converter pages work.
Generate invoice
POST /api/v1/generate
Accepts a canonical invoice and returns a document in the requested format. Totals and the VAT breakdown are always recomputed server-side, so the result satisfies BR-CO-10 through BR-CO-17 by construction — omit totals and taxes from your request.
Parameters
| Field | Type | Description |
|---|---|---|
format | string | xrechnung · zugferd · en16931 · ubl · cii · peppol |
syntax | string | ubl or cii. Defaults to the format's primary syntax. |
version | string | Standard version. Defaults to the registry default. |
profile | string | ZUGFeRD conformance level, e.g. en16931. |
output | string | xml (default) or pdf for a hybrid PDF/A-3. |
validate | boolean | Run the real validator on the result. Default true. |
invoice | object | The canonical invoice. Required. |
Validate invoice
POST /api/v1/validate
Accepts multipart/form-data with a file field, a raw application/xml body, or a JSON wrapper with xml, content_base64 or a canonical invoice. Hybrid ZUGFeRD PDFs have their embedded XML extracted automatically.
# Upload a file
curl -X POST https://api.invoapi.de/api/v1/validate \
-F "file=@invoice.xml"
# Or post the XML directly
curl -X POST https://api.invoapi.de/api/v1/validate \
-H "Content-Type: application/xml" \
--data-binary @invoice.xml{
"status": "invalid",
"valid": false,
"detected_format": "xrechnung",
"syntax": "ubl",
"version": "3.0.2",
"result": {
"summary": { "errors": 1, "warnings": 0, "infos": 1 },
"issues": [
{
"severity": "error",
"rule": "BR-DE-15",
"path": "/Invoice[1]",
"message": "Das Element \"Buyer reference\" (BT-10) muss übermittelt werden.",
"explanation": "XRechnung requires a buyer reference (BT-10). For German public sector buyers this is the Leitweg-ID that routes the invoice to the right agency.",
"suggestion": "Set `buyer_reference` to the Leitweg-ID your customer gave you.",
"field": "buyer_reference",
"business_term": "BT-10",
"rule_set": "Schematron rules for Invoice - CIUS XRechnung (UBL)"
}
],
"coverage_note": "Validated against the XRechnung 3.0.2 rules (KoSIT Schematron) and the CEN EN 16931 rules, plus the UBL 2.1 XML Schema. Technical conformance with these rules is not by itself a statement about tax or legal acceptance."
}
}When validation cannot run
If the document is unrecognised, or the validation engine is unreachable, you get an explicit non-answer instead of a guess:
{
"status": "unsupported_validation",
"valid": null,
"result": {
"reason": "The document could not be identified as a supported e-invoice format, so no rule set applies to it."
}
}Convert invoice
POST /api/v1/convert
Supply exactly one source — invoice, xml or content_base64 — and a target of json, xml, html or pdf. Semantics the target cannot represent come back in warnings.
# XRechnung XML -> canonical JSON
curl -X POST https://api.invoapi.de/api/v1/convert \
-H "Content-Type: application/json" \
-d '{ "xml": "<Invoice …>", "target": "json" }'
# Canonical JSON -> XRechnung CII
curl -X POST https://api.invoapi.de/api/v1/convert \
-H "Content-Type: application/json" \
-d '{ "invoice": { … }, "target": "xml", "format": "xrechnung", "syntax": "cii" }'Detect format
POST /api/v1/detect
Identifies a document without validating it. Unrecognised documents come back as "format": "unknown" — never a guess.
curl -X POST https://api.invoapi.de/api/v1/detect \
-F "file=@invoice.pdf"{
"format": "zugferd",
"syntax": "cii",
"version": "2.5.2",
"confidence": 0.95,
"profile": "en16931",
"container": "pdf",
"embedded_filename": "factur-x.xml",
"evidence": [
"CustomizationID (BT-24) is 'urn:cen.eu:en16931:2017'",
"CII XML embedded in a PDF container as 'factur-x.xml' — this is a hybrid ZUGFeRD / Factur-X invoice"
]
}Render invoice
POST /api/v1/render
Returns the document itself with the matching content type — not a JSON envelope — so it can go straight into an iframe or a file.
# HTML
curl -X POST https://api.invoapi.de/api/v1/render \
-H "Content-Type: application/xml" \
--data-binary @invoice.xml > invoice.html
# PDF
curl -X POST "https://api.invoapi.de/api/v1/render?output=pdf" \
-H "Content-Type: application/xml" \
--data-binary @invoice.xml > invoice.pdfCalculate totals
POST /api/v1/calculate
Runs the VAT engine without producing a document. Useful for showing a running total in your own UI without reimplementing EN 16931 rounding — note that 3 × 19.99 is exactly 59.97 here, not 59.970000000000006.
curl -X POST https://api.invoapi.de/api/v1/calculate \
-H "Content-Type: application/json" \
-d '{ "invoice": { "invoice_number": "T-1", "issue_date": "2026-08-20",
"seller": { "name": "A", "country_code": "DE" },
"buyer": { "name": "B", "country_code": "DE" },
"items": [ { "name": "X", "quantity": 3, "unit_price": 19.99, "vat_rate": 7 } ] } }'{
"totals": {
"net": "59.97",
"tax": "4.20",
"gross": "64.17",
"payable": "64.17",
"currency": "EUR"
},
"breakdown": [
{ "category": "S", "rate": "7.00", "taxable_amount": "59.97", "tax_amount": "4.20" }
],
"line_amounts": { "1": "59.97" }
}XRechnung
The German CIUS of EN 16931, mandatory for invoices to German public sector buyers. Its most commonly hit extra rule is BR-DE-15: a buyer reference (BT-10) is required, and for public buyers that is the Leitweg-ID. If you omit buyer_reference, generation falls back to the invoice number to satisfy BR-DE-15 and returns a warning — pass the Leitweg-ID explicitly for public-sector buyers.
- Version
- 3.0.2
- Syntax
- UBL 2.1 and UN/CEFACT CII D16B
- Validation
- Official KoSIT Validator 1.6.3 with validator-configuration-xrechnung (XRechnung 3.0.2 Schematron 2.5.0 + CEN EN 16931 1.3.15).
- Source
- xeinkauf.de
ZUGFeRD / Factur-X
A hybrid invoice: a human readable PDF with the structured XML embedded as factur-x.xml. Profiles EN16931, XRECHNUNG, BASIC and EXTENDED are implemented; MINIMUM and BASIC WL return CAPABILITY_NOT_SUPPORTED rather than a partial document.
- Version
- 2.5.2
- Syntax
- CII D16B, optionally inside a PDF/A-3 container
- Validation
- CEN EN 16931 CII Schematron; the XRECHNUNG profile additionally runs the XRechnung rules. FeRD's profile-specific Schematron is not redistributable, so profile-only rules are not checked and PDF/A-3 conformance of the container is not verified.
- Source
- www.ferd-net.de
EN 16931
The European semantic core model that every other format on this list narrows. All BT/BG business terms in the canonical model map to it directly.
- Version
- 2017 / A1
- Syntax
- UBL 2.1 and CII D16B
- Validation
- CEN EN 16931 Schematron 1.3.15 plus the matching XML Schema.
- Source
- ec.europa.eu
UBL 2.1
OASIS Universal Business Language. The generator emits elements in the exact order the XSD sequence requires, which is the most common cause of hand-built UBL failing schema validation.
- Version
- 2.1
- Syntax
- Invoice and CreditNote
- Validation
- UBL 2.1 XML Schema plus the CEN EN 16931 Schematron.
- Source
- docs.oasis-open.org
UN/CEFACT CII
The syntax underneath ZUGFeRD and the CII flavour of XRechnung. Structured as three trade sections — agreement, delivery and settlement — rather than UBL's flatter layout.
- Version
- D16B
- Syntax
- CrossIndustryInvoice
- Validation
- CII D16B XML Schema plus the CEN EN 16931 CII Schematron.
- Source
- unece.org
Peppol BIS Billing 3.0
Document creation, parsing, conversion and validation only. No Access Point transmission is implemented — nothing is sent to the Peppol network. Note that Peppol rejects the 'EM' e-mail address scheme, so electronic addresses are mapped to a routable ISO 6523 scheme where one can be derived.
- Version
- 3.0
- Syntax
- UBL 2.1
- Validation
- CEN EN 16931 UBL Schematron plus the official PEPPOL-EN16931-UBL rules (peppol-bis-invoice-3 v3.0.20), compiled from source at setup time.
- Source
- docs.peppol.eu
Invoice model
One shape covers every format. Fields carry their EN 16931 business term so the mapping back to the standard is always traceable. camelCase keys are accepted too.
The example below uses the compact shorthand: items (also lines), the line's unit, price and vat, and flat address keys on a party. The canonical names — line_items, unit_code, unit_price, vat_rate and a nested address object — are what the OpenAPI schema lists, and both forms are accepted. Unknown keys are rejected, so a typo fails loudly instead of being ignored.
{
"invoice_number": "RE-2026-0001", // BT-1 required
"type": "invoice", // "invoice" | "credit_note"
"issue_date": "2026-08-20", // BT-2 required
"due_date": "2026-09-19", // BT-9
"currency": "EUR", // BT-5
"buyer_reference": "04011000-12345", // BT-10 required by XRechnung
"purchase_order_reference": "PO-77", // BT-13
"seller": { // BG-4 required
"name": "Muster GmbH", // BT-27
"street": "Musterstraße 1", // BT-35
"postal_code": "10115", // BT-38
"city": "Berlin", // BT-37
"country_code": "DE", // BT-40
"vat_id": "DE123456789", // BT-31
"email": "rechnung@muster.de", // BT-43
"phone": "+49 30 1234567" // BT-42
},
"buyer": { /* BG-7, same shape */ },
"items": [{ // BG-25 at least one
"name": "Softwareentwicklung", // BT-153
"quantity": 10, // BT-129
"unit": "HUR", // BT-130 UN/ECE Rec 20
"unit_price": 100, // BT-146
"vat_category": "S", // BT-151 S|Z|E|AE|K|G|O
"vat_rate": 19, // BT-152
"discount": 0 // BT-136
}],
"allowances": [{ "amount": 50, "reason": "Discount", "vat_rate": 19 }],
"charges": [{ "is_charge": true, "amount": 12.5, "reason": "Shipping", "vat_rate": 19 }],
"payment": {
"means_code": "58", // BT-81 UNTDID 4461
"iban": "DE02120300000000202051", // BT-84
"bic": "BYLADEM1001", // BT-86
"terms": "Zahlbar innerhalb von 30 Tagen." // BT-20
},
"notes": [{ "text": "Thank you." }] // BT-22
}
// Omit "totals" and "taxes" — the VAT engine computes both and its
// values are what end up in the document.Errors
Every failure uses the same envelope.
{
"error": {
"code": "INVALID_INVOICE",
"message": "The invoice contains validation errors.",
"details": [
{
"field": "buyer_reference",
"rule": "BR-DE-15",
"message": "Buyer reference is required."
}
],
"request_id": "req_2f1c8a94b0d3e5f6a7b8c9d0"
}
}Error codes
| Code | HTTP | Meaning |
|---|---|---|
BAD_REQUEST | 400 | The request shape is wrong or a required field is missing. |
INVALID_INVOICE | 422 | The canonical invoice is structurally or semantically invalid. |
INVALID_XML | 400 | The document is not well-formed, or contains a DTD / entity declaration. |
UNSUPPORTED_FORMAT | 400 | The requested format is not one this service implements. |
UNSUPPORTED_SYNTAX | 400 | The format cannot be expressed in the requested syntax. |
UNSUPPORTED_VERSION | 400 | The requested standard version is not supported. |
CAPABILITY_NOT_SUPPORTED | 400 | The operation is genuinely not implemented for this target — it is never faked. |
CONVERSION_FAILED | 422 | The document could not be mapped onto the canonical model. |
DETECTION_FAILED | 422 | The container could not be inspected, e.g. a password protected PDF. |
RENDERING_FAILED | 422 | The invoice could not be rendered as HTML or PDF. |
PAYLOAD_TOO_LARGE | 413 | The request body exceeds the configured upload limit. |
UNSUPPORTED_MEDIA_TYPE | 415 | The uploaded file type is not accepted. |
VALIDATOR_UNAVAILABLE | 503 | The validation engine could not be reached, so no verdict is reported. |
VALIDATION_TIMEOUT | 504 | Validation exceeded the configured timeout. |
GENERATION_NOT_SCHEMA_VALID | 500 | A document this service generated failed its own XML Schema. That is our defect, not your data — it is reported instead of being returned as a valid invoice. |
INTERNAL_ERROR | 500 | An unexpected failure. The request_id identifies it in the service logs. |
API reference
The complete machine readable specification is served as OpenAPI 3.1. Import it into your own client, an SDK generator or any API tool.
Service
| Method | Path | Summary |
|---|---|---|
| GET | A proof of work for use without an account | |
| GET | Service health | |
| POST | Send a contact enquiry |
Formats
| Method | Path | Summary |
|---|---|---|
| GET | List supported formats | |
| GET | Describe one format |
Invoicing
| Method | Path | Summary |
|---|---|---|
| POST | Generate an e-invoice | |
| POST | Validate an e-invoice | |
| POST | Detect the format of a document | |
| POST | Convert between formats | |
| POST | Render an invoice as a readable document | |
| POST | Parse a document into canonical JSON | |
| POST | Calculate totals and VAT |
Account
| Method | Path | Summary |
|---|---|---|
| POST | Create an account | |
| POST | Sign in | |
| POST | Sign out | |
| GET | Where you are signed in | |
| DELETE | End one session | |
| POST | Sign out everywhere else | |
| POST | Switch the active organization | |
| GET | Current session | |
| POST | Change your password | |
| POST | Delete your account and its data | |
| PATCH | Update your account | |
| GET | One organization | |
| PATCH | Rename an organization | |
| GET | Organization members | |
| GET | List API keys | |
| POST | Create an API key | |
| DELETE | Revoke an API key | |
| DELETE | Remove a revoked API key from the list | |
| GET | Usage for an organization | |
| GET | Request history, newest first | |
| GET | Audit events, newest first | |
| GET | The current subscription | |
| POST | Start a subscription | |
| POST | Manage the subscription | |
| GET | Describe a pending authorization | |
| POST | Approve a pending authorization | |
| GET | Programs connected through the browser flow | |
| DELETE | Disconnect a program | |
| POST | Request a password reset link | |
| POST | Set a new password with a reset link | |
| POST | Request an e-mail address change | |
| POST | Confirm the address an account was opened with | |
| POST | Send the confirmation link again | |
| POST | Confirm a new e-mail address |

