{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/invowerk-dev/json-schema/invowerk-dev-validation-response-schema.json",
  "title": "ValidationResponse",
  "description": "The result of POST /v1/validate. Always HTTP 200: a failed check is data,\nnot an error.",
  "x-generated": "2026-11-12",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/invowerk-dev-openapi.yml#/components/schemas/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": "#/$defs/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": "#/$defs/ErrorClasses",
      "description": "The findings grouped by where the problem is."
    },
    "findings": {
      "items": {
        "$ref": "#/$defs/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": "#/$defs/Evidence",
      "description": "SHA-256 and size of the file, time of the check, software versions."
    },
    "e_rechnung_de": {
      "$ref": "#/$defs/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": "#/$defs/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"
  ],
  "$defs": {
    "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.\n\nIt never contains findings. A clean result means the invoice is well-formed\nand follows the rules. It does not mean that prices, amounts or the\ndelivered service are correct."
    },
    "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\n(Germany).\n\nIt describes the format check only. It is not a tax assessment (see\n``note``)."
    },
    "ErrorClasses": {
      "properties": {
        "format": {
          "items": {
            "$ref": "#/$defs/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": "#/$defs/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": "#/$defs/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": "#/$defs/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\nfindings as one list."
    },
    "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": "#/$defs/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\nsaved report: the SHA-256 ties the report to one file."
    },
    "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.\n\n``error_class`` says where the problem is: in the file or XML structure\n(``format``), in a rule of the applied rule set (``business_rules``) or in\nthe PDF around the invoice data (``carrier``). ``explain_url`` links the\nexplanation page for the code, in ``lang``. ``explain`` holds the explanation\nitself when you call with ``?explain=true``. ``printed_by`` names the\nbrowser or printer when a ``no_embedded_xml`` PDF was saved through a print\nfunction."
    },
    "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."
    },
    "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."
    },
    "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."
    }
  }
}