Overview
All paid REST API endpoints live under the versioned path /api/v1. For interactive manual checks without an API key, use our free browser validator (up to 5 checks per UTC day). The examples below use the shell variables $BELEGBRIDGE_URL and $BELEGBRIDGE_API_KEY.
- Memory-only processing: uploaded invoices are processed exclusively in memory and discarded as soon as the response has been sent. Nothing is written to disk, a database or logs.
- Content types: responses are
application/json; charset=utf-8unless stated otherwise. The visualizer can also return HTML or PDF (see Visualize). - Versioning: breaking changes are only introduced under a new path (e.g.
/api/v2). New optional fields may be added tov1responses at any time – please ignore unknown fields.
export BELEGBRIDGE_URL="<address-of-this-website>"
export BELEGBRIDGE_API_KEY="bb_live_..."
Authentication
All REST API endpoints under /api/v1/* require an API key in the Authorization: Bearer bb_live_... header and never set cookies. REST API access is included in the Pro and Agency plans. Compare plans on Pricing.
Authorization: Bearer bb_live_...
Requests without a valid API key receive 401 Unauthorized with WWW-Authenticate: Bearer realm="BelegBridge API":
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="BelegBridge API"
Content-Type: application/json; charset=utf-8
{
"valid": false,
"legal_ustg14_compliant": false,
"status": "UNAUTHORIZED",
"message": "Missing or invalid API key. Pass 'Authorization: Bearer bb_live_...' — REST API access is included in the Pro and Agency plans (see /#pricing).",
"pricing_url": "/#pricing"
}
Early access REST API keys for the Pro and Agency plans are currently issued to early-access members. Reserve your key via the waitlist modal below.
Rate limits
Public endpoints are protected by a per-client token bucket. The client is identified by a salted hash of its IP address; the salt rotates daily, so raw IP addresses are never stored – neither in memory nor in logs.
When the limit is exceeded, the API responds with 429 Too Many Requests and the header Retry-After: 60. Wait at least the indicated number of seconds before retrying.
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content-Type: application/json; charset=utf-8
{
"valid": false,
"legal_ustg14_compliant": false,
"status": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded for public validation endpoint. Please try again shortly."
}
Validate an invoice
POST /api/v1/validate
Detects the format, checks the XML against the official KoSIT validator (EN 16931 and XRechnung business rules) and – for PDFs – checks PDF/A-3 conformance. Every finding is translated into German and English with a concrete fix.
Request
multipart/form-datawith the file in the fieldfile(the field namesinvoiceanddocumentare accepted as well), or- the raw file as request body with
Content-Type: application/xml,application/pdforapplication/octet-stream. - Maximum size: 15 MB. Larger requests are rejected with
413.
Supported inputs:
- UN/CEFACT Cross Industry Invoice (CII) – XRechnung CII, ZUGFeRD 2.x / Factur-X XML
- OASIS UBL 2.1 Invoice and CreditNote – XRechnung UBL, Peppol BIS Billing 3.0
- Hybrid PDF/A-3 files with an embedded
factur-x.xml,xrechnung.xmlorzugferd-invoice.xmlattachment (ZUGFeRD 2.x / Factur-X)
# multipart upload
curl -sS -X POST "$BELEGBRIDGE_URL/api/v1/validate" \
-H "Authorization: Bearer $BELEGBRIDGE_API_KEY" \
-F "file=@rechnung.xml"
# raw body (PDF)
curl -sS -X POST "$BELEGBRIDGE_URL/api/v1/validate" \
-H "Authorization: Bearer $BELEGBRIDGE_API_KEY" \
-H "Content-Type: application/pdf" \
--data-binary "@rechnung.pdf"
Response fields
| Field | Type | Description |
|---|---|---|
valid | boolean | Overall verdict (see rules below). |
legal_ustg14_compliant | boolean | true only if the document is valid and uses a profile that qualifies as an e-invoice under § 14 UStG (German VAT Act). |
status | string, optional | Machine-readable status for special cases, e.g. INVALID_NOT_EINVOICE for a plain PDF without XML data. |
message | string, optional | Human-readable explanation accompanying status. |
detected_container | string | XML | PDF_A3 | INVALID_NOT_EINVOICE |
detected_syntax | string | UN_CEFACT_CII | OASIS_UBL_2_1_INVOICE | OASIS_UBL_2_1_CREDIT_NOTE |
detected_profile | string | XRECHNUNG_3_0_2, XRECHNUNG_LEGACY, EN16931, EXTENDED, BASIC, PEPPOL_BIS_3_0, BASIC_WL, MINIMUM, ZUGFERD_1_0_LEGACY, UNKNOWN – see Profiles |
document_type | string | 380_INVOICE, 381_CREDIT_NOTE, 384_CORRECTED_INVOICE, 389_SELF_BILLED_INVOICE, 326_PARTIAL_INVOICE |
pdfa_compliant | boolean | Result of the PDF/A-3 conformance check (only meaningful for PDF_A3). |
summary.errors_count | integer | Number of FATAL violations. |
summary.warnings_count | integer | Number of WARNING findings. |
violations[] | array | All findings; empty array if there are none. |
violations[].rule_id | string | Rule ID exactly as reported by the validator, e.g. BR-DE-15, BR-CO-15, PEPPOL-EN16931-R008. |
violations[].severity | string | FATAL | WARNING |
violations[].bt_reference | string | Affected business term or group (e.g. BT-10, BG-6), if known. |
violations[].xpath | string | Location in the XML document. |
violations[].raw_message | string | Original message from the validator. |
violations[].title_de, title_en | string | Short, understandable title in German and English. |
violations[].fix_de, fix_en | string | Concrete fix in German and English. |
When is an invoice valid?
valid is true only if all of the following apply:
- there are zero
FATALviolations, - the KoSIT validation report recommends acceptance,
- the detected profile is allowed under § 14 UStG (see Profiles), and
- for PDFs: the file conforms to PDF/A-3.
Warnings are reported for information and to improve data quality – they never make an invoice invalid.
Example: invoice without buyer reference (BT-10) and seller contact (BG-6)
{
"valid": false,
"legal_ustg14_compliant": false,
"detected_container": "XML",
"detected_syntax": "UN_CEFACT_CII",
"detected_profile": "XRECHNUNG_3_0_2",
"document_type": "380_INVOICE",
"pdfa_compliant": false,
"summary": {
"errors_count": 2,
"warnings_count": 0
},
"violations": [
{
"rule_id": "BR-DE-2",
"severity": "FATAL",
"bt_reference": "BG-6",
"xpath": "/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty",
"raw_message": "[BR-DE-2] Die Gruppe \"SELLER CONTACT\" (BG-6) muss übermittelt werden.",
"title_de": "Kontaktdaten des Verkäufers (BG-6) fehlen",
"title_en": "Seller contact details (BG-6) are missing",
"fix_de": "Ergänzen Sie Ansprechpartner (BT-41), Telefonnummer (BT-42) und E-Mail-Adresse (BT-43) des Verkäufers.",
"fix_en": "Add the seller's contact person (BT-41), phone number (BT-42) and email address (BT-43)."
},
{
"rule_id": "BR-DE-15",
"severity": "FATAL",
"bt_reference": "BT-10",
"xpath": "/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement",
"raw_message": "[BR-DE-15] Das Element \"Buyer reference\" (BT-10) muss übermittelt werden.",
"title_de": "Käuferreferenz bzw. Leitweg-ID (BT-10) fehlt",
"title_en": "Buyer reference / Leitweg-ID (BT-10) is missing",
"fix_de": "Tragen Sie die Leitweg-ID des öffentlichen Auftraggebers oder eine andere Käuferreferenz in BT-10 ein.",
"fix_en": "Enter the public-sector routing ID (Leitweg-ID) or another buyer reference in BT-10."
}
]
}
Titles and fixes are maintained editorially and may be refined over time; rely on rule_id and severity for automated processing.
Profiles & § 14 UStG
The field detected_profile tells you which specification the invoice claims to follow. Only profiles that contain the full EN 16931 data set (including invoice lines) qualify as e-invoices under § 14 UStG (German VAT Act).
detected_profile | Meaning | § 14 UStG compliant |
|---|---|---|
XRECHNUNG_3_0_2 | XRechnung 3.0.x (German CIUS of EN 16931) | Yes |
XRECHNUNG_LEGACY | Older XRechnung version (before 3.0); validated against the current rules | Depends on the validation result |
EN16931 | ZUGFeRD 2.x / Factur-X profile EN 16931 (Comfort) | Yes |
EXTENDED | ZUGFeRD 2.x / Factur-X profile EXTENDED | Yes |
BASIC | ZUGFeRD 2.x / Factur-X profile BASIC | Yes |
PEPPOL_BIS_3_0 | Peppol BIS Billing 3.0 (validated against the EN 16931 core rules) | Yes |
BASIC_WL | ZUGFeRD / Factur-X BASIC WL – no invoice lines | No |
MINIMUM | ZUGFeRD / Factur-X MINIMUM – no invoice lines | No |
ZUGFERD_1_0_LEGACY | ZUGFeRD 1.0 – predates EN 16931 | No |
UNKNOWN | Specification identifier not recognized | No |
Visualize an invoice
POST /api/v1/visualize
Turns an XRechnung or ZUGFeRD invoice into a unified, human-readable view and recalculates the totals. Accepts the same request formats and size limit as /api/v1/validate.
Parameters
| Parameter | Values | Description |
|---|---|---|
format | json | html | pdf | Query parameter. Default: json. HTMX requests receive HTML. |
lang | de | en | Language of labels in the HTML/PDF view. Query parameter or multipart field. |
# JSON
curl -sS -X POST "$BELEGBRIDGE_URL/api/v1/visualize?format=json&lang=de" \
-H "Authorization: Bearer $BELEGBRIDGE_API_KEY" \
-F "file=@rechnung.xml"
# PDF download
curl -sS -X POST "$BELEGBRIDGE_URL/api/v1/visualize?format=pdf&lang=de" \
-H "Authorization: Bearer $BELEGBRIDGE_API_KEY" \
-F "file=@rechnung.xml" \
-o invoice-preview.pdf
JSON response
| Field | Description |
|---|---|
valid_math | true if all recalculated totals match the invoice. |
syntax | Detected syntax (same values as detected_syntax). |
profile_level | Detected profile (same values as detected_profile). |
source_container | XML | ZUGFERD_PDF_A3 |
invoice | Unified invoice view: invoice_number, issue_date, type_code, currency, buyer_reference, seller{…}, buyer{…}, payment{means_code, iban, bic, payment_terms}, lines[], taxes[], totals{…}, penny_math{…}. |
invoice.totals | line_total, tax_basis_total, tax_total, grand_total, due_payable as decimal strings, each with an integer counterpart in cents (line_total_cents, …). |
invoice.penny_math | valid, br_co_10_valid, br_co_13_valid, br_co_14_valid, br_co_15_valid and – only when there are mismatches – discrepancies[]. |
html_preview | Ready-to-embed HTML fragment of the invoice. |
Example (shortened):
{
"valid_math": true,
"syntax": "UN_CEFACT_CII",
"profile_level": "XRECHNUNG_3_0_2",
"source_container": "XML",
"invoice": {
"invoice_number": "RE-2026-0042",
"issue_date": "2026-11-02",
"type_code": "380",
"currency": "EUR",
"buyer_reference": "04011000-12345-67",
"seller": { "name": "Muster Webagentur", "city": "Berlin", "country_code": "DE" },
"buyer": { "name": "Beispielkunde AG", "city": "Hamburg", "country_code": "DE" },
"payment": { "means_code": "58", "iban": "DE02120300000000202051", "payment_terms": "14 Tage netto" },
"lines": [
{ "id": "1", "name": "Webentwicklung", "quantity": "10", "unit_code": "HUR", "net_price": "95.00", "line_net_amount": "950.00", "line_net_cents": 95000, "vat_category": "S", "vat_rate": "19" }
],
"taxes": [
{ "category_code": "S", "rate": "19", "taxable_amount": "950.00", "taxable_cents": 95000, "tax_amount": "180.50", "tax_cents": 18050 }
],
"totals": {
"line_total": "950.00", "line_total_cents": 95000,
"tax_basis_total": "950.00", "tax_basis_total_cents": 95000,
"tax_total": "180.50", "tax_total_cents": 18050,
"grand_total": "1130.50", "grand_total_cents": 113050,
"due_payable": "1130.50", "due_payable_cents": 113050
},
"penny_math": {
"valid": true,
"br_co_10_valid": true,
"br_co_13_valid": true,
"br_co_14_valid": true,
"br_co_15_valid": true
}
},
"html_preview": "<div class=\"bb-invoice\">…</div>"
}
All totals are recalculated with integer cents – never with floating-point numbers – following the EN 16931 rules BR-CO-10 (sum of line net amounts), BR-CO-13 (tax basis), BR-CO-14 (VAT total), BR-CO-15 (grand total), BR-CO-16 (amount due with rounding) and BR-CO-17 (VAT per category). A difference of a single cent is reported.
Errors & status codes
Note: an invoice with validation errors is not an HTTP error – it is returned with 200 OK and "valid": false. HTTP errors indicate that the request itself could not be processed. They share this body:
{
"valid": false,
"legal_ustg14_compliant": false,
"status": "UNSUPPORTED_FORMAT",
"message": "…"
}
| HTTP | status | Meaning |
|---|---|---|
| 400 | INVALID_REQUEST | Malformed request, e.g. multipart without a file part. |
| 400 | EMPTY_PAYLOAD | The uploaded file is empty. |
| 400 | UNSUPPORTED_FORMAT | Neither a supported XML invoice nor a PDF. |
| 400 | UNSAFE_XML_DECLARATION | The XML contains <!DOCTYPE or <!ENTITY and was rejected (XXE protection). |
| 401 | UNAUTHORIZED | Missing or invalid Authorization: Bearer API key (see Authentication). |
| 405 | – | Method not allowed; the Allow header lists the permitted method. |
| 413 | PAYLOAD_TOO_LARGE | The request exceeds 15 MB or the embedded PDF decompression limit. |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests; see Rate limits. |
| 502 | SIDECAR_VALIDATION_ERROR | The validation service could not process the document. Retry later. |
| 503 | SIDECAR_BUSY | Validation concurrency limit reached; retry after Retry-After: 5 seconds. |
Security & privacy
- XXE protection: XML documents containing
<!DOCTYPEor<!ENTITYdeclarations are rejected before parsing. - Size limit: requests are capped at 15 MB.
- Memory-only: invoice contents are never written to disk, a database or logs.
- No cookies, no tracking: neither the website nor the API sets cookies.
- Strict Content Security Policy: all scripts, styles and fonts are served from our own domain – no external CDNs.
More details: privacy policy and data processing agreement (AVV).
Planned endpoints
The following endpoints are part of the paid bridge and are not available yet. Paths and schemas may still change.
| Endpoint | Purpose | Status |
|---|---|---|
POST /api/v1/generate | Create EN 16931 XML and a ZUGFeRD 2.3 PDF/A-3 from JSON (API key required). | Planned |
POST /api/v1/webhooks/stripe/{seller_profile_id} | Stripe webhook endpoint per seller profile: turns paid Stripe invoices into compliant ZUGFeRD invoices. | Planned |
| DATEV dispatch | Automatic forwarding of validated invoices to the buyer and your DATEV inbox. | Planned |