Automation MCP Server Features Blog Pricing Contact
POST /v1/validate/zugferd

ZUGFeRD Validation REST API for Germany

Validate German ZUGFeRD hybrid invoices, PDF/A-3 with embedded UN/CEFACT CII XML, against the official FeRD per-profile XSD and Schematron rule set. The conformance profile is read from the embedded XML (MINIMUM, BASIC WL, BASIC, EN 16931, EXTENDED) and the matching rules are applied, returned as structured JSON in under two seconds.

The InvoiceXML ZUGFeRD validation API runs the same checks DATEV, SAP, Lexware, and German ERP systems apply before importing an invoice, every rule violation reported with rule ID, severity, and XPath location. Legacy zugferd.de 2p0 specification identifiers are accepted alongside the current factur-x.eu identifiers.

Bash
curl -X POST https://api.invoicexml.com/v1/validate/zugferd \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "[email protected]"
200 OK · Application/JSON
{
  "valid": true,
  "data": {
    "profile": "en16931",
    "customizationId": "urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:en16931"
  },
  "errors": [],
  "warnings": []
}

Try it live, no API key required

This runs the same POST /v1/validate/zugferd endpoint your integration will call. Drop any ZUGFeRD PDF to see the full validation response.

Drop your ZUGFeRD PDF here

or browse files to upload

POST /v1/validate/zugferd · Accepted format: PDF · All versions 1.0–2.4 · Max 20 MB

Upload any ZUGFeRD 2.x or Factur-X PDF and receive a structured JSON compliance report. The API finds the embedded XML under any spec attachment name (factur-x.xml, zugferd-invoice.xml, or xrechnung.xml), detects the conformance profile, and validates against that profile official XSD and Schematron rules. The applied profile and the declared identifier are reported in data.profile and data.customizationId.
Validation report

Not just pass or fail. A full ZUGFeRD compliance report.

Most validators stop at an accept or reject verdict and leave you to decode raw Schematron output before an invoice can move into DATEV, SAP, or Lexware. The InvoiceXML ZUGFeRD API returns a complete report array instead: one row for every EN 16931 Business Term it found in the embedded CII XML, each with the extracted value and a pass or fail verdict. It is exactly the data a German ERP import-readiness check needs, with zero CII XML parsing on your side. The example below is a ZUGFeRD 2.3.2 invoice from a German supplier, declared at the EN 16931 profile.

ZUGFeRD Compliance Report

rechnung-zugferd.pdf · ZUGFeRD 2.3.2 · EN 16931 · Muller Handels GmbH

162 terms 160 passed 2 failed
Status Business Term Field Value
BT-1 Invoice number invoiceNumber INV-2026-001
BT-5 Invoice currency currency EUR
BT-27 Seller name seller.name Muller Handels GmbH
BT-31 Seller VAT identifier seller.vatIdentifier DE283849123
BT-118 VAT category code vatBreakdowns[0].categoryCode Reverse charge declared, but the VAT rate is not 0% (BR-AE-05).
BT-112 Invoice total with VAT totals.grandTotalAmount 1190.00 EUR
BT-110 Invoice total VAT amount totals.taxTotalAmount Does not match the sum of the VAT breakdown (BR-CO-14).

Rendered from the report array. Each row is one object: code, name, section, path, value, isValid, and error.

DATEV and SAP import-readiness checks

Group the rows by the section field and confirm at a glance whether a ZUGFeRD invoice carries every Business Term DATEV, SAP, and Lexware expect on import. Since Germany made receiving e-invoices mandatory in January 2025, screen incoming supplier files before they enter your Buchhaltung, instead of discovering a silent import failure after the fact.

Inline form highlighting

The path on every report row, and the fields array on every friendly error, map directly to your invoice form inputs. Mark the failing field red and show the message beside it, so a German trading partner fixes the exact value that failed before resending.

GoBD-compliant archiving

Store the report alongside each archived ZUGFeRD invoice as machine-readable proof that the compliance check ran. When a tax auditor queries a document under GoBD retention rules years later, the term-by-term evidence is already on file.

Validate ZUGFeRD REST API Request

A single API call handles XML extraction, profile detection, and all validation layers automatically:

Request
$ curl -X POST https://api.invoicexml.com/v1/validate/zugferd \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "[email protected]"

You only send the file. The API detects the ZUGFeRD version and profile automatically and returns the detected values.

Validate ZUGFeRD API Response

Both valid and invalid invoices return HTTP 200. The response includes the detected ZUGFeRD version and profile read directly from the embedded XML, so you know exactly which version was validated, not which version you assumed you were sending.

Validation errors are returned in two formats: raw Schematron rule codes for programmatic handling in your pipeline, and a friendly message array with plain-language descriptions you can render directly in a dashboard, notification, or no-code workflow step.

Each friendly error also carries btCodes and a fields array, the dotted JSON paths the error maps to. A front-end form can read fields to highlight the exact input that failed, for example lines[1].vatInformation.rate, and show the message right beside it. Every response, valid or not, also includes a report array: one row per EN 16931 Business Term with its value and a pass or fail flag, ready to render as a compliance table.

Valid ZUGFeRD Response

Response, valid invoice
// 200 OK
{
  "valid": true,
  "detail": "Your invoice is ZUGFeRD, Factur-X and EN16931 compliant",
  "data": {
    "schemaValid": true,
    "schematronValid": true,
    "conformanceLevel": "EN16931",
    "version": "2.3.2",
    "hasEmbeddedXml": true,
    "profile": "en16931",
    "customizationId": "urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:en16931"
  },
  "errors": [],
  "warnings": [],
  "report": [
    { "code": "BT-1", "name": "Invoice number", "section": "header", "path": "invoiceNumber", "value": "RE-2026-0042", "isValid": true, "errors": [], "warnings": [] },
    { "code": "BT-27", "name": "Seller name", "section": "seller", "path": "seller.name", "value": "Muller Handels GmbH", "isValid": true, "errors": [], "warnings": [] },
    { "code": "BT-31", "name": "Seller VAT identifier", "section": "seller", "path": "seller.vatIdentifier", "value": "DE283849123", "isValid": true, "errors": [], "warnings": [] }
    // one row per EN 16931 business term, see the compliance report section below
  ]
}

Invalid ZUGFeRD Response

Response, invalid invoice
// 200 OK
{
  "valid": false,
  "detail": "Validation failed with 3 error(s)",
  "data": {
    "schemaValid": true,
    "schematronValid": false,
    "conformanceLevel": "EN16931",
    "version": "2.3.2",
    "hasEmbeddedXml": true,
    "profile": "en16931",
    "customizationId": "urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:en16931"
  },
  "errors": [
    {
      "rule": "BR-AE-05",
      "layer": "en16931",
      "line": 2,
      "message": "When using Reverse charge VAT, the VAT rate must be set to 0%.",
      "btCodes": ["BT-152"],
      "fields": ["lines[1].vatInformation.rate"],
      "raw": "[BR-AE-05] EN16931: In an invoice line where VAT category code is Reverse charge, the VAT rate shall be 0 (at /*:CrossIndustryInvoice/*:SupplyChainTradeTransaction/*:IncludedSupplyChainTradeLineItem[2])." },
    {
      "rule": "BR-AE-09",
      "layer": "en16931",
      "line": null,
      "message": "The total VAT amount for the Reverse charge breakdown must be 0.",
      "btCodes": ["BT-117"],
      "fields": ["vatBreakdowns[0].taxAmount"],
      "raw": "[BR-AE-09] EN16931: The VAT category tax amount in a VAT breakdown with category code Reverse charge shall be 0 (at /*:CrossIndustryInvoice/*:SupplyChainTradeTransaction/*:ApplicableHeaderTradeSettlement/*:ApplicableTradeTax[1])." },
    {
      "rule": "BR-CO-14",
      "layer": "en16931",
      "line": null,
      "message": "The invoice total VAT amount does not match the sum of VAT breakdown.",
      "btCodes": ["BT-110"],
      "fields": ["totals.taxTotalAmount"],
      "raw": "[BR-CO-14] EN16931: Invoice total VAT amount must equal the sum of VAT category tax amounts (at /*:CrossIndustryInvoice/*:SupplyChainTradeTransaction/*:ApplicableHeaderTradeSettlement)." }
  ],
  "warnings": [],
  "report": [
    // full field-by-field report, see the compliance report section below
    { "code": "BT-110", "name": "Invoice total VAT amount", "section": "totals", "path": "totals.taxTotalAmount", "value": "0.00", "isValid": false, "errors": ["The invoice total VAT amount does not match the sum of VAT breakdown."], "warnings": [] }
  ]
}

The ZUGFeRD profile ladder, validated against the right rule set automatically

ZUGFeRD defines five conformance profiles that climb from a payment-data minimum to a full structured invoice: MINIMUM and BASIC WL carry header and total data only (they are accounting aids, not legally complete invoices on their own), BASIC adds line items, EN 16931 is the European semantic core that satisfies the German mandate, and EXTENDED layers on additional fields for complex B2B trade. Each profile is identified by the specification identifier inside the embedded CII XML, not by the filename, and each ships its own Schematron rule set from FeRD. The API reads that identifier, applies only the rules that profile requires, and returns the applied profile in data.profile so you always know whether a supplier sent MINIMUM, BASIC, EN 16931, or EXTENDED. Legacy zugferd.de 2p0 identifiers from older toolchains are mapped to the same profile rules, so a 2016-era ZUGFeRD 2.0 file and a current ZUGFeRD 2.3.2 file are both validated correctly without any version flag on your side.

FeRD versions, maintained for you

We track FeRD so your integration never falls out of compliance

FeRD (Forum elektronische Rechnung Deutschland) publishes ZUGFeRD as a sequence of versioned releases (2.0, 2.1, 2.2, 2.3, 2.3.2, 2.4 and onward), each shipping a new XSD and per-profile Schematron with an official effective date. When a release lands, InvoiceXML loads the new artifacts and switches validation to them on that exact effective date, automatically. Your integration keeps calling the same endpoint and is always validating against the rules currently in force in Germany.

Switched on the effective date

Each FeRD release carries an effective date. We stage the updated XSD and Schematron in advance and cut validation over on that date. No window where you are checking against retired rules, and no manual coordination with a German regulatory calendar.

Zero changes on your side

No standards-body monitoring, no artifact downloads, no library upgrade, no redeploy, no maintenance release. This removes the 0.3 to 0.5 FTE that a German tax and e-invoicing team otherwise spends keeping a validator current, and the audit risk of an integration silently going stale the day FeRD changes a rule.

The self-hosted alternative, without the upkeep

Self-hosted open-source validators like Mustang or the KoSIT validator put the entire update cycle on you: pull each new FeRD artifact, re-test, redeploy, and hope nothing was missed before the German mandate deadline. With InvoiceXML the current rule set is the default and the only thing you call.

What the ZUGFeRD validation API checks

Version Detection and XML Extraction

Extracts the embedded zugferd-invoice.xml attachment from the PDF/A-3 container and detects the ZUGFeRD version automatically. ZUGFeRD 1.0 (root element CrossIndustryDocument) and ZUGFeRD 2.x (root element CrossIndustryInvoice) use different namespace structures and different validation artefacts, the API routes each to the correct pipeline without any version parameter required.

CII XSD Schema

Validates the extracted CII XML against the UN/CEFACT CII D16B XSD schema. Catches malformed XML, missing required elements, incorrect data types, and namespace errors. For ZUGFeRD 1.0 files, the legacy CrossIndustryDocument schema is applied. For 2.x files, the current CrossIndustryInvoice D16B schema is used.

EN 16931 Schematron

Validates all 200+ EN 16931 business rules (BR-xx codes) against the embedded CII XML. Covers arithmetic rules (BR-CO-14: VAT totals must balance), VAT category rules (BR-AE-05: reverse charge rate must be zero), party rules (BR-01: specification identifier required), and payment rules. The profile declared in the specification identifier determines which rules are mandatory vs optional.

PDF/A-3b and ERP Import Conditions

Checks the outer PDF container for the conditions that DATEV, SAP, Lexware, and compatible German ERP systems verify on import: embedded fonts, ICC colour profile, XMP metadata with the correct ZUGFeRD namespace and version declaration, AFRelationship set to Alternative, and the embedded file named exactly zugferd-invoice.xml. A file that fails any of these is silently treated as a plain PDF on import, no error, no structured data.

Response schema

Both valid and invalid invoices return HTTP 200. The data.profile and data.customizationId fields confirm exactly which conformance profile was validated and what the document declared, useful when processing invoices from multiple suppliers who send different profiles. Create/convert endpoints return HTTP 400 with the same errors structure on validation failure.

Field Type Description
Always present
validbooleanPrimary flag to branch on. true when the file passes all CII, Schematron, and PDF/A-3b checks.
detailstringHuman-readable summary, e.g. "Your invoice is ZUGFeRD compliant…" or "Validation failed with 2 error(s)".
data.conformanceLevelstringDetected EN 16931 profile, e.g. EN16931, EXTENDED, BASIC.
data.profilestring?Conformance profile whose rules were applied: minimum, basic-wl, basic, en16931, extended or xrechnung. Read from the specification identifier in the embedded XML, never assumed from the filename. Null when the identifier has no dedicated rule set.
data.customizationIdstring?The specification identifier the document declared, verbatim, e.g. urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:en16931. Null when the embedded XML carries no identifier.
data.specificationIdentifierstringFull specification identifier URI from the embedded XML, e.g. urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:en16931.
data.schemaValidbooleanCII XSD schema check passed for the detected version.
data.schematronValidbooleanEN 16931 Schematron business rules check passed.
Findings (errors and warnings)
errorsobject[]Flat array of finding objects, one per blocking rule violation. Empty array on a valid invoice.
warningsobject[]Flat array of finding objects for non-blocking advisories. May contain entries on a valid invoice.
[].rulestringRule identifier, e.g. BR-AE-05, BR-CO-14.
[].layerstringValidation layer the finding came from: schema (CII XSD), en16931 (EN 16931 base rules), or the per-profile Schematron rule set.
[].lineint?Invoice line item number where the violation occurred, or null for document-level findings.
[].messagestringHuman-readable, plain-language description suitable for end users and no-code notification workflows.
[].btCodesstring[]EN 16931 Business Term codes the rule references, e.g. ["BT-110"].
[].fieldsstring[]Dotted JSON paths the finding maps to, e.g. ["totals.taxTotalAmount"]. Use them to highlight the exact form input that failed.
[].rawstringVerbatim technical validator output, including the rule code and the XPath context, for programmatic handling and logging.
Compliance report (always present)
reportobject[]One row per EN 16931 Business Term found on the invoice, with its value and a pass/fail flag. null if the report could not be built.
report[].codestringBusiness Term code, e.g. BT-1.
report[].namestringHuman-readable term name, e.g. Invoice number.
report[].sectionstringGrouping for display: header, seller, buyer, lines, payment, totals, or tax.
report[].pathstringDotted JSON path to the field, e.g. lines[0].priceDetails.netPrice.
report[].valuestring?Current value of the term, or null when absent.
report[].isValidbooleanfalse when a validation error references this term, otherwise true.
report[].errorsstring[]All error messages referencing this Business Term. Empty array when the term has no errors.
report[].warningsstring[]All warning messages referencing this Business Term. Empty array when the term has no warnings.

Integrate ZUGFeRD validation into your workflow

REST API

Call POST /v1/validate/zugferd from any language. No parameters beyond the file, profile detection is automatic. Returns structured JSON in under 2 seconds. Use valid to gate ERP import pipelines and data.profile to log which conformance profile your suppliers are sending.

API docs

Zapier / Make / n8n

Screen incoming ZUGFeRD invoices from suppliers before they reach your accounting system. Route non-compliant files to a Slack channel or email with the friendly errors[].message values so your team knows exactly what to ask the supplier to fix, before the file causes a silent DATEV import failure.

Automation integrations

CI/CD Pipeline

Validate ZUGFeRD output on every build of your invoice generation pipeline. Because the API tracks the FeRD release cadence for you, the rules update on each version's effective date with no change to your pipeline. When ZUGFeRD 2.4 made service period dates mandatory and deprecated NIL elements, integrations on InvoiceXML simply began enforcing it on the effective date. Your pipeline catches regressions before non-compliant invoices reach your German customers.

View cURL example
Just need to create or validate one invoice?

Skip the API. Use getzugferd.com

Our online companion tool. Create and validate ZUGFeRD invoices in your browser, no integration, no API key, no code. Perfect for freelancers, small businesses, and one-off invoices.

Open getzugferd.com

Frequently Asked Questions

Which ZUGFeRD versions does the API validate?

The API validates ZUGFeRD 2.x and Factur-X documents using the official Factur-X 1.0.8 artifacts, which accept both the current factur-x.eu and the legacy zugferd.de 2p0 profile identifiers. The conformance profile (MINIMUM, BASIC WL, BASIC, EN 16931, EXTENDED) is detected automatically from the embedded XML and the matching per-profile schema and rules are applied. ZUGFeRD 1.0 documents use the retired CrossIndustryDocument format and are reported as schema-invalid with a clear finding, re-issue them as ZUGFeRD 2.x.

Does validation check the PDF/A-3b container as well as the embedded XML?

The validation targets the embedded XML, which is what the business rules apply to: the PDF container is parsed, the embedded invoice is located under any spec attachment name (factur-x.xml, zugferd-invoice.xml, or xrechnung.xml), and the XML is validated against its declared profile rules. Full PDF/A-3b container conformance (fonts, colour profiles, XMP) is not part of this check, so pair the API with a PDF/A validator if your archive also enforces the container level. A PDF whose attachment is UBL instead of CII gets a clear PDF-EMBED-SYNTAX finding.

Why does my ZUGFeRD file pass the XML check but fail on import into DATEV or SAP?

DATEV and SAP perform import checks beyond EN 16931 Schematron validation, like the XMP metadata block and the AFRelationship declaration. The InvoiceXML validation API catches the most common rejection cause, an XML payload that breaks the declared profile rules, and tells you exactly which rule failed and at which XPath. For container-level checks (XMP, AFRelationship, PDF/A-3b conformance), pair it with a PDF/A validator in the same pipeline.

What happens when FeRD publishes a new ZUGFeRD version?

Nothing changes on your side. FeRD (Forum elektronische Rechnung Deutschland) ships each ZUGFeRD release (2.0, 2.1, 2.2, 2.3, 2.3.2, 2.4 and onward) as an updated XSD plus per-profile Schematron with an official effective date. InvoiceXML loads the new artifacts and switches validation to them on that effective date automatically. Your integration keeps calling POST /v1/validate/zugferd and is always checking against the currently-effective German rule set, with no tracking of FeRD announcements, no library upgrade, and no redeploy. This removes the 0.3 to 0.5 FTE compliance-maintenance burden that self-hosted validators like Mustang or the KoSIT validator place on your team, and the risk that an integration silently falls out of compliance the day the rules change.

Is ZUGFeRD the same as Factur-X, and does the API cover Germany's e-invoicing mandate?

ZUGFeRD and Factur-X are technically the same hybrid format (PDF/A-3 carrying an embedded UN/CEFACT CII XML), ZUGFeRD is the German branding maintained by FeRD and Factur-X the French branding. A file produced under either name validates here. For Germany's mandate, receiving structured e-invoices has been mandatory since January 2025 and issuing phases in from 2027. A ZUGFeRD file at the EN 16931 profile or above carries the structured XML the mandate requires, and this API confirms the embedded XML conforms to the currently-effective EN 16931 rules before you transmit or archive it under GoBD.

Start free today

Ready to automate your invoices?

Validate, convert and embed compliant e-invoices through one API. Start your 30-day free trial. No credit card required.

GDPR Compliant No credit card required Setup in minutes
Peppol UBL
Factur-X
EN 16931
142 / 142 passed
Compliant
PDF/A-3 embedded