invowerk Validation API
The Validation API from invowerk — 1 operation(s) for validation.
The Validation API from invowerk — 1 operation(s) for validation.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/invowerk-dev:invowerk-dev-validation-api"
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: invowerk Validation API
description: 'German e-invoices: create ZUGFeRD PDFs and XRechnung, check ZUGFeRD/Factur-X, XRechnung (KoSIT) and Peppol BIS. Free tool, API, MCP.
Base URL: `https://api.invowerk.dev`.
### Authentication
Send your key in the `X-API-Key` header. Calls without a key run anonymously at
a lower rate limit per IP address.
### Rate limits
Every response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and
`X-RateLimit-Reset` (seconds until the window resets). Over-budget calls return
`429` with a `Retry-After` header. Check your live budget at `GET /v1/me`.
### Pagination
List endpoints return a keyset envelope: `{"items": [...], "next_cursor": "..."}`.
Pass `next_cursor` back as the `?cursor=` query param for the next page; it is
`null` on the last page.
### Errors
Errors are RFC 9457 `problem+json` with a
stable machine-readable **`code`** (e.g. `rate_limited`, `credits_exhausted`,
`validation_error`). Branch on `code`. The `title` and `detail` text may change.
### Quickstart
```sh
curl -H "X-API-Key: $API_KEY" https://api.invowerk.dev/v1/me
```'
version: '1'
servers:
- url: https://api.invowerk.dev
tags:
- name: Validation
paths:
/v1/validate:
post:
tags:
- Validation
summary: Check an e-invoice
description: 'Check an e-invoice against EN 16931 and the German rules.
Send an XML invoice (UBL or CII, e.g. XRechnung) or a ZUGFeRD / Factur-X PDF as
the raw request body or as a multipart file part, at most 20 MB. A PDF may have
at most 500 pages and 16 attachments. invowerk detects the format from the
bytes, not from the Content-Type. A larger body gets HTTP 413. Otherwise the
answer is always HTTP 200, because a failed check is a result, not an error.
`valid` is the result, `findings` lists each problem, `e_rechnung_de` maps the
result onto the BMF categories and `evidence` identifies the checked file.
`hints` are extra observations that never change `valid`. For XRechnung the
KoSIT validator decides `valid`. If it is unreachable, invowerk''s own check
with the same rules decides, `verdict_source` is `local` and the
`X-EInvoice-Degraded` header is set (`official-lane-unavailable`;
`pdfa-lane-unavailable` when the PDF/A check did not answer; comma-separated).
`disagreement` is true when both checks ran and differ. For a PDF,
`pdfa_verdict` is the PDF/A check with veraPDF. `?explain=true` adds the
explanation for rules that have one, in `lang` (`de` or `en`). An XML file
with more than 100,000 elements gets HTTP 413. HTTP 429 means every check
slot is busy (callers without an API key share one), HTTP 504 that the check
ran longer than 20 s. Costs 1 credit.
**Credits:** 1 per call.'
operationId: validate_invoice
security:
- APIKeyHeader: []
parameters:
- name: explain
in: query
required: false
schema:
type: boolean
default: false
title: Explain
- name: lang
in: query
required: false
schema:
type: string
default: de
title: Lang
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/ValidationResponse'
'422':
description: Validation Error
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
requestBody:
required: true
description: 'The invoice: XML (UBL or CII, e.g. XRechnung) or a ZUGFeRD / Factur-X PDF, as the raw body or as a multipart file part. At most 20 MB.'
content:
multipart/form-data:
schema:
type: object
properties:
file:
type: string
contentMediaType: application/octet-stream
description: The invoice file (XML or PDF).
required:
- file
application/xml:
schema:
type: string
application/pdf:
schema:
type: string
contentMediaType: application/pdf
x-connector:
kind: action
sample_file: /app-static/demo/beispiel-xrechnung.xml
x-credits-cost: 1
components:
schemas:
Problem:
description: The RFC 9457 error body. Every error response uses it.
properties:
type:
default: about:blank
description: Problem type URI (RFC 9457).
title: Type
type: string
title:
description: Short human-readable summary of the status.
title: Title
type: string
status:
description: The HTTP status code, repeated in the body.
title: Status
type: integer
detail:
description: What went wrong with this request.
title: Detail
type: string
instance:
description: The request path that produced the error.
title: Instance
type: string
code:
description: Stable machine-readable error code. Branch on this, not on the text.
title: Code
type: string
request_id:
anyOf:
- type: string
- type: 'null'
default: null
description: Correlation id for this request; quote it when asking for support.
title: Request Id
errors:
anyOf:
- items:
additionalProperties: true
type: object
type: array
- type: 'null'
default: null
description: Per-field validation failures. Present on 422 only.
title: Errors
upgrade_url:
anyOf:
- type: string
- type: 'null'
default: null
description: Where to add credits or move to a larger plan. Present on 402/403/429.
title: Upgrade Url
required:
- title
- status
- detail
- instance
- code
title: Problem
type: object
ErrorClasses:
properties:
format:
items:
$ref: '#/components/schemas/Finding'
type: array
title: Format
description: 'Problems with the file or XML structure: unknown format, malformed XML, a forbidden DOCTYPE, XML schema (XSD) errors.'
business_rules:
items:
$ref: '#/components/schemas/Finding'
type: array
title: Business Rules
description: Failed rules of the applied rule set (EN 16931, XRechnung, ZUGFeRD / Factur-X or Peppol BIS).
carrier:
items:
$ref: '#/components/schemas/Finding'
type: array
title: Carrier
description: 'Problems with the PDF of a ZUGFeRD / Factur-X invoice: PDF/A conformance and how the XML is embedded (BR-HYBRID-* rules). Always empty for an XML file.'
content:
$ref: '#/components/schemas/ContentClass'
description: Always the not_machine_checkable reminder. Never contains findings.
type: object
required:
- content
title: ErrorClasses
description: 'The findings grouped by where the problem is. ``findings`` has the same
findings as one list.'
ContentClass:
properties:
status:
type: string
title: Status
description: Always 'not_machine_checkable'.
default: not_machine_checkable
note:
type: string
title: Note
description: 'Fixed note: a format check cannot judge whether the content is correct.'
type: object
required:
- note
title: ContentClass
description: 'Reminder that the content of an invoice cannot be checked by machine.
It never contains findings. A clean result means the invoice is well-formed
and follows the rules. It does not mean that prices, amounts or the
delivered service are correct.'
Finding:
properties:
rule_id:
type: string
title: Rule Id
description: Code of the rule that failed, e.g. 'BR-DE-21', or 'XSD' for a schema error.
error_class:
type: string
title: Error Class
description: 'Where the problem is: ''format'' (file or XML structure), ''business_rules'' (a rule of the applied rule set) or ''carrier'' (the PDF around the XML).'
severity:
type: string
title: Severity
description: '''fatal'' (the invoice fails the check), ''warning'' or ''information''.'
xpath:
anyOf:
- type: string
- type: 'null'
title: Xpath
description: Where the problem is in the XML (an XPath or a schema line), if known.
message:
type: string
title: Message
description: The message of the failed rule or check.
explain_url:
anyOf:
- type: string
- type: 'null'
title: Explain Url
description: Path of the explanation page /codes/<CODE> (/en/codes/<CODE> in English), if invowerk has one.
explain:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
title: Explain
description: Explanation of the rule (meaning, causes, fixes) in the requested language. Set only with ?explain=true and if invowerk has an entry for the code.
printed_by:
anyOf:
- type: string
- type: 'null'
title: Printed By
description: 'Set only on ''no_embedded_xml'': the browser or printer that saved the PDF through a print function, for example ''Firefox'', ''Chrome'' or ''Microsoft Print to PDF''. Printing drops the embedded invoice XML, so the original file may still be an e-invoice.'
type: object
required:
- rule_id
- error_class
- severity
- message
title: Finding
description: 'One problem found in the invoice.
``error_class`` says where the problem is: in the file or XML structure
(``format``), in a rule of the applied rule set (``business_rules``) or in
the PDF around the invoice data (``carrier``). ``explain_url`` links the
explanation page for the code, in ``lang``. ``explain`` holds the explanation
itself when you call with ``?explain=true``. ``printed_by`` names the
browser or printer when a ``no_embedded_xml`` PDF was saved through a print
function.'
ERechnungDe:
properties:
status:
type: string
title: Status
description: '''e_rechnung'' (no errors), ''e_rechnung_mit_fehlern'' (rule errors), ''keine_e_rechnung'' (not a structured e-invoice) or ''nicht_pruefbar'' (the file could not be checked).'
reason:
type: string
title: Reason
description: Why this status applies, in German, naming the finding.
basis:
anyOf:
- type: string
- type: 'null'
title: Basis
description: The BMF letter or UStAE passage for the category; null for 'nicht_pruefbar'.
note:
type: string
title: Note
description: 'Fixed German note, the same on every answer: invowerk checks the technical format, not whether content and amounts are correct or what tax consequences an error has.'
type: object
required:
- status
- reason
- note
title: ERechnungDe
description: 'The result mapped onto the categories of the BMF letter of 15.10.2025
(Germany).
It describes the format check only. It is not a tax assessment (see
``note``).'
ScenarioInfo:
properties:
config:
type: string
title: Config
description: '''kosit-xrechnung'' (the KoSIT configuration for XRechnung and EN 16931) or ''zugferd'' (the FeRD rules for ZUGFeRD / Factur-X).'
name:
type: string
title: Name
description: The scenario name as the configuration states it.
type: object
required:
- config
- name
title: ScenarioInfo
description: 'The rule set that was applied: its configuration and scenario.'
Evidence:
properties:
document_sha256:
type: string
title: Document Sha256
description: Hex SHA-256 of the exact bytes you sent (compare with `sha256sum <file>`).
document_bytes:
type: integer
title: Document Bytes
description: Size in bytes of what you sent.
checked_at:
type: string
title: Checked At
description: UTC time of the check, ISO 8601 to the second, e.g. '2026-09-24T10:15:00Z'.
validator:
$ref: '#/components/schemas/ValidatorVersions'
description: The software versions.
type: object
required:
- document_sha256
- document_bytes
- checked_at
- validator
title: Evidence
description: 'Which file was checked, when, and with which software. Keep it with a
saved report: the SHA-256 ties the report to one file.'
HintInfo:
properties:
code:
type: string
title: Code
description: The hint code, e.g. 'IW-IBAN'.
severity:
type: string
title: Severity
description: '''warning'' or ''information''. A hint is never fatal.'
message:
type: string
title: Message
description: The observation, in the requested language.
bt_refs:
items:
type: string
type: array
title: Bt Refs
description: The EN 16931 Business Terms the hint is about.
explain_url:
anyOf:
- type: string
- type: 'null'
title: Explain Url
description: Path of the page /codes/<CODE> (/en/… in English) that explains the hint.
type: object
required:
- code
- severity
- message
title: HintInfo
description: 'One hint: an extra observation by invowerk. Hints never change the result.'
ValidationResponse:
properties:
valid:
type: boolean
title: Valid
description: The overall result. For the XML, the KoSIT validator decides when it answered (verdict_source 'official'); otherwise invowerk's own check decides (no fatal finding). A fatal problem with the PDF of a ZUGFeRD / Factur-X invoice always makes it false.
verdict_source:
type: string
title: Verdict Source
description: '''official'' when the KoSIT validator decided the XML result, ''local'' when invowerk''s own check did (ZUGFeRD or Peppol rules, or KoSIT did not answer).'
local_verdict:
type: string
title: Local Verdict
description: 'invowerk''s own result, ''valid'' or ''invalid'': ''invalid'' when any format, business rule or PDF finding is fatal.'
disagreement:
type: boolean
title: Disagreement
description: True when the KoSIT validator and invowerk's own check reach different results for the XML. The KoSIT result decides; the findings explain the difference.
lane:
type: string
title: Lane
description: '''official'' when invowerk asked the KoSIT validator (even if it did not answer), else ''local''.'
official_verdict:
anyOf:
- type: string
- type: 'null'
title: Official Verdict
description: 'The KoSIT validator''s result: ''valid'', ''invalid'' or ''unavailable'' (it did not answer). Null when it was not asked, e.g. for ZUGFeRD or Peppol.'
scenario:
anyOf:
- $ref: '#/components/schemas/ScenarioInfo'
- type: 'null'
description: The rule set that was applied, or null when no rule set matched.
pdfa_verdict:
anyOf:
- type: string
- type: 'null'
title: Pdfa Verdict
description: 'PDF/A check of a ZUGFeRD / Factur-X PDF with veraPDF: ''valid'', ''invalid'' or ''unavailable''. Null when not checked (always for XML). Null does not mean the PDF is fine.'
detection:
additionalProperties: true
type: object
title: Detection
description: 'What invowerk detected from the bytes: kind, syntax, flavor, customization_id, is_hybrid_pdf. For a PDF it describes the embedded XML.'
errors:
$ref: '#/components/schemas/ErrorClasses'
description: The findings grouped by where the problem is.
findings:
items:
$ref: '#/components/schemas/Finding'
type: array
title: Findings
description: All format, business rule and PDF findings as one ordered list.
rulesets:
additionalProperties:
type: string
type: object
title: Rulesets
description: 'The rule set versions used for this check, as {name: version}.'
evidence:
$ref: '#/components/schemas/Evidence'
description: SHA-256 and size of the file, time of the check, software versions.
e_rechnung_de:
$ref: '#/components/schemas/ERechnungDe'
description: The result mapped onto the e-invoice categories of the German BMF. Describes the format only, not a tax assessment.
hints:
items:
$ref: '#/components/schemas/HintInfo'
type: array
title: Hints
description: Extra observations by invowerk (IW-* codes), e.g. a wrong IBAN check digit. They never change valid, verdict_source or e_rechnung_de.
type: object
required:
- valid
- verdict_source
- local_verdict
- disagreement
- lane
- detection
- errors
- evidence
- e_rechnung_de
title: ValidationResponse
description: 'The result of POST /v1/validate. Always HTTP 200: a failed check is data,
not an error.'
ValidatorVersions:
properties:
invowerk:
type: string
title: Invowerk
description: The invowerk version, or 'unknown'.
appkit:
type: string
title: Appkit
description: The version of the platform invowerk runs on, or 'unknown'.
type: object
required:
- invowerk
- appkit
title: ValidatorVersions
description: The software versions that produced the result.
securitySchemes:
APIKeyHeader:
type: apiKey
in: header
name: X-API-Key