eliDocs
API Reference

POST /v1/validate

POST/v1/validate

Validate an e-invoice document — EN 16931 / CIUS Schematron for UBL and CII, Italy FatturaPA XSD-only for invoice XML, or Italy SDI file messaggio XSD-only when applicable.

Upload an invoice file (XML or PDF) and receive a validation result.

For UBL and CII documents in the EN 16931 family, checks include:

  1. XML structure (XSD)
  2. EN 16931 business rules (Schematron)
  3. Country- or network-specific rules when applicable — XRechnung for Germany, Peppol BIS Billing 3.0 for cross-border / Peppol Network invoices

For Italy FatturaPA XML (Agenzia namespaces: FatturaElettronica / FatturaElettronicaSemplificata), Beliq validates against the published W3C XML Schema graph bundled in the engine. That path is XSD only (no EN 16931 Schematron; no BR-* rule IDs). Additional normative constraints from the Italian technical specification (tables / regole) are not implemented as code in this release.

For Italy SDI file messaggio XML (namespace http://www.fatturapa.gov.it/sdi/messaggi/v1.0, roots such as NotificaScarto, RicevutaConsegna, … per MessaggiTypes_v1.1), Beliq validates against the authority Messaggi XSD vendored in the engine. Use format=sdi_messaggio or rely on auto-detect when the root element matches. This is interchange notification / receipt structural validation only — not FatturaPA invoice-body validation and not a substitute for SDI transport (which remains outside Beliq; SDI submission is not implemented).

For Spain Facturae XML (MINECO namespace http://www.facturae.gob.es/formato/Versiones/Facturaev3_2_2.xml, root Facturae), Beliq validates against the published Facturae 3.2.2 XSD bundled in the engine. Use format=facturae or rely on auto-detect. That path is XSD only (no EN 16931 Schematron; no BR-* rule IDs). VeriFactu / FACe submission and XAdES signing remain outside Beliq (BYOC).

For Slovenia e-SLOG 2.0 XML (namespace urn:eslog:2.00, root Invoice wrapping M_INVOIC), Beliq validates against the GZS/ePOS e-SLOG 2.0 XSD bundled in the engine. Use format=eslog or rely on auto-detect. That path is XSD only (no EN 16931 Schematron; no BR-* rule IDs). UJP submission, e-route provider services, and XML-DSig signing remain outside Beliq (BYOC).

Quick copy examples

Quick copy examples

Validate XML:

validate-xml.sh
curl -X POST "https://api.beliq.eu/v1/validate?format=auto" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/xml" \
  --data-binary @invoice.xml
import { readFile } from 'node:fs/promises';

import { Beliq } from '@beliq/sdk';

const beliq = new Beliq({ apiKey: process.env.BELIQ_API_KEY! });

const result = await beliq.validate(await readFile('invoice.xml'), { format: 'auto' });

console.log(result.valid, result.format, result.schematronVersion);
for (const issue of result.errors) console.log(issue.ruleId, issue.message);
import os
from pathlib import Path

from beliq import Beliq

beliq = Beliq(api_key=os.environ["BELIQ_API_KEY"])

result = beliq.validate(Path("invoice.xml").read_bytes(), format="auto")

print(result.valid, result.format, result.schematron_version)
for issue in result.errors:
    print(issue.rule_id, issue.message)

Validate PDF:

validate-pdf.sh
curl -X POST https://api.beliq.eu/v1/validate \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @invoice.pdf
import { readFile } from 'node:fs/promises';

import { Beliq } from '@beliq/sdk';

const beliq = new Beliq({ apiKey: process.env.BELIQ_API_KEY! });

const result = await beliq.validate(await readFile('invoice.pdf'));

console.log(result.valid, result.format, result.schematronVersion);
for (const issue of result.errors) console.log(issue.ruleId, issue.message);
import os
from pathlib import Path

from beliq import Beliq

beliq = Beliq(api_key=os.environ["BELIQ_API_KEY"])

result = beliq.validate(Path("invoice.pdf").read_bytes())

print(result.valid, result.format, result.schematron_version)
for issue in result.errors:
    print(issue.rule_id, issue.message)
Request

Request

POST /v1/validate
Content-Type: application/xml | application/octet-stream
Authorization: Bearer <api-key>

Headers

Header Required Description
Authorization required Bearer <api-key> or use X-API-Key header
Content-Type required application/xml for XML input, application/octet-stream for PDF or binary input
Beliq-Ruleset optional Pin the validation ruleset for this request to a channel (latest or previous), overriding your org’s default channel. The applied ruleset is echoed in the Beliq-Ruleset-Resolved response header. See Ruleset versioning.

Query parameters

Parameter Type Required Description
format string optional Format hint: auto, cii, ubl, fatturapa, facturae (Spain Facturae 3.2.2 XML — XSD only), eslog (Slovenia e-SLOG 2.0 XML, XSD only), or sdi_messaggio (Italy SDI file messaggio XML — XSD only, not a FatturaPA invoice). When set to auto (default), the engine detects the format automatically (including FatturaPA, Facturae, e-SLOG, and SDI messaggi when the root element matches).

Request body

The raw XML or PDF file content as the request body. Do not JSON-encode it.

validate.sh
curl -X POST "https://api.beliq.eu/v1/validate?format=auto" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/xml" \
  --data-binary @invoice.xml
import { readFile } from 'node:fs/promises';

import { Beliq } from '@beliq/sdk';

const beliq = new Beliq({ apiKey: process.env.BELIQ_API_KEY! });

const result = await beliq.validate(await readFile('invoice.xml'), { format: 'auto' });

console.log(result.valid, result.format, result.schematronVersion);
for (const issue of result.errors) console.log(issue.ruleId, issue.message);
import os
from pathlib import Path

from beliq import Beliq

beliq = Beliq(api_key=os.environ["BELIQ_API_KEY"])

result = beliq.validate(Path("invoice.xml").read_bytes(), format="auto")

print(result.valid, result.format, result.schematron_version)
for issue in result.errors:
    print(issue.rule_id, issue.message)
Response

Response

Alongside the JSON body below, the response carries the verdict, the detected syntax and profile, and the ruleset seal as headers. See the Response headers reference.

Success (200)

{
  "success": true,
  "data": {
    "valid": true,
    "format": "cii",
    "profileDetected": "XRechnung",
    "schematronVersion": "1.3.16",
    "ciusVersion": "XRechnung-2.6.0",
    "errors": [],
    "warnings": []
  }
}

Validation result object

Field Type Description
valid boolean true if the invoice passes all validation stages with no fatal/error-level violations
format string Detected format: cii, ubl, fatturapa, facturae, eslog, sdi_messaggio, or unknown
profileDetected string? Detected profile label when applicable (e.g. xrechnung, peppol, romania-ro-cius, netherlands-nlcius, en16931, Italy FatturaPA labels such as italy-fatturapa-ordinaria-fpr12, Spain Facturae label spain-facturae-v3-2-2, Slovenia e-SLOG label slovenia-eslog-v2-0, or messaggio labels such as italy-sdi-notifica-scarto)
sha256 string Lowercase-hex SHA-256 of the exact request body validated. Reproduce with sha256sum on your file. Always present.
rulesetSha256 string? Single fingerprint of the rule artifacts that judged the document, the syntax schema it was checked against included. Present whenever a ruleset ran, which includes a document the syntax schema rejected; reconstructable from rulesetArtifacts.
rulesetArtifacts object[]? The component rows behind rulesetSha256, { key, version, fileSha256 } each. For an EN 16931 document one row is the syntax schema: cii_xsd, cii_xsd_d22b or ubl_xsd, or a Factur-X profile’s own schema under facturx_schematron. See Verify a hash for the reconstruction and catalog cross-check.
driftCheckedAt string? ISO 8601 UTC instant reporting when the pinned artifacts that judged the document were last confirmed to still match their upstream authority sources (the nightly drift check). A conservative lower bound: “verified at least as recently as this instant.” Present alongside rulesetSha256 only when every pinned artifact cleared a clean, matching check; omitted otherwise. See When the ruleset was last drift-checked.
rulesetChannel string? The ruleset channel applied to this request (latest or previous), present when a channel was selected via the Beliq-Ruleset header or when your org default is previous. See Ruleset versioning.
rulesetFellBack boolean? true when previous was requested and nothing the request resolved had a retained ruleset for the channel to reach, so the request ran exactly what latest would have run. Two things leave an artifact with nothing to reach: it retains no prior ruleset at all, or the one it retains has been superseded, meaning the authority’s own mandatory date for the successor has passed and Beliq stops picking the older ruleset on your behalf. An exact pin still reaches a superseded version until its sunset date. One document resolves several rule artifacts and previous reaches each out of what that artifact still offers, so a request can run a retained artifact beside one that fell back. That is not a fallback: this field, rulesetChannel, rulesetSunsetsOn and Beliq-Ruleset-Resolved each collapse the whole resolution to one answer, and rulesetArtifacts is what names the version every artifact actually ran at. Present only alongside rulesetChannel.
rulesetSunsetsOn string? ISO date (YYYY-MM-DD) the retained ruleset that served this request stops being served. Present only when a retained ruleset actually ran, so never on a default request; the earliest date when several ran. After it an exact pin returns 422, so treat it as your deadline to migrate off the pin. It bounds the pin, not the channel: previous stops reaching the version on this date or on the authority’s mandatory date for the successor, whichever comes first. See Ruleset versioning.
schematronVersion string? EN 16931 Schematron version used (e.g. "1.3.16"). Omitted for FatturaPA, Facturae, e-SLOG, and SDI file messaggio (XSD-only validation).
ciusVersion string? CIUS Schematron version used (e.g. "XRechnung-2.6.0" or "PeppolBIS-Billing-3.0.21")
peppolVersion string? Peppol BIS Billing 3.0 CIUS version used. Only present when profileDetected === "peppol"; mirrors ciusVersion for Peppol-specific consumers
romaniaRoCiusVersion string? Romania CIUS-RO Schematron version used. Only present when profileDetected === "romania-ro-cius"; mirrors ciusVersion for Romania-specific consumers. See Romania format reference.
netherlandsNlciusVersion string? Netherlands NLCIUS (SI-UBL 2.0) Schematron version used. Only present when profileDetected === "netherlands-nlcius"; mirrors ciusVersion for NL-specific consumers. See Netherlands NLCIUS format reference.
italyFatturapaXsdBundle string? Present for FatturaPA invoice XML: human-readable label of the authority XSD bundle used (no Schematron).
italyFatturapaRuntimeVersion string? Present for FatturaPA invoice XML: the pinned bundle version.
italySdiMessaggiXsdBundle string? Present for SDI file messaggio XML: human-readable label of the MessaggiTypes XSD bundle (not FatturaPA invoices).
italySdiMessaggiXsdVersion string? Present for SDI file messaggio XML: the pinned bundle version.
spainFacturaeXsdBundle string? Present for Facturae invoice XML: human-readable label of the authority XSD bundle used (no Schematron).
spainFacturaeRuntimeVersion string? Present for Facturae invoice XML: the pinned bundle version.
sloveniaEslogXsdBundle string? Present for e-SLOG XML: human-readable label of the authority XSD bundle used (no Schematron).
sloveniaEslogRuntimeVersion string? Present for e-SLOG XML: the pinned bundle version.
errors ValidationError[] Violations at fatal or error severity
warnings ValidationError[] Violations at warning or info severity

Validation error object

Field Type Description
ruleId string Official business rule ID (e.g. BR-01, BR-DE-15, PEPPOL-EN16931-R001), or a Beliq advisory ID (see Advisory findings)
severity string fatal, error, warning, or info
location string? XPath location of the violation in the source XML
message string Human-readable description of the violation

Peppol BIS Billing 3.0 example

When the input declares the Peppol BIS Billing 3.0 customization, Beliq runs the OpenPeppol Schematron in addition to EN 16931 and reports the Peppol version explicitly:

{
  "success": true,
  "data": {
    "valid": true,
    "format": "ubl",
    "profileDetected": "peppol",
    "schematronVersion": "1.3.16",
    "ciusVersion": "PeppolBIS-Billing-3.0.21",
    "peppolVersion": "PeppolBIS-Billing-3.0.21",
    "errors": [],
    "warnings": []
  }
}

Example with validation errors

{
  "success": true,
  "data": {
    "valid": false,
    "format": "cii",
    "profileDetected": "XRechnung",
    "schematronVersion": "1.3.16",
    "ciusVersion": "XRechnung-2.6.0",
    "errors": [
      {
        "ruleId": "BR-01",
        "severity": "error",
        "location": "/rsm:CrossIndustryInvoice/rsm:ExchangedDocumentContext",
        "message": "An Invoice shall have a Specification identifier (BT-24)."
      },
      {
        "ruleId": "BR-DE-15",
        "severity": "error",
        "location": "/rsm:CrossIndustryInvoice",
        "message": "An XRechnung invoice must contain a buyer reference (BT-10)."
      }
    ],
    "warnings": [
      {
        "ruleId": "BR-CL-01",
        "severity": "warning",
        "location": "/rsm:CrossIndustryInvoice/...",
        "message": "The currency code should be a valid ISO 4217 code."
      }
    ]
  }
}
Advisory findings

Advisory findings

A validation artifact does not always implement every rule its standard’s text carries. Where a rule is in the normative text, is absent from every artifact that judged your document, and is still enforced by other validators your invoice may reach, Beliq reports the violation as an advisory: severity: "info", in warnings.

An advisory never changes valid. That flag reflects only the artifacts that actually ran, which rulesetArtifacts names and rulesetSha256 fingerprints, so a document is never failed here by a rule the pinned artifacts do not carry. Advisory IDs are deliberately not official rule IDs, so you can tell them apart: they read <STANDARD>_<RULE>_NOT_ENFORCED.

One rule qualifies today.

Advisory ID Rule What it means
EN16931_BR_CO_25_NOT_ENFORCED BR-CO-25, EN 16931-1 § 6.13.7 Amount due for payment (BT-115) is positive but neither the payment due date (BT-9) nor the payment terms (BT-20) is present. Removed from the EN 16931 Schematron in 1.3.16, still enforced by the Mustangproject Factur-X packs, the FNFE-MPE France EXTENDED-CTC-FR overlay and the Romanian RO-CIUS overlay.
{
  "ruleId": "EN16931_BR_CO_25_NOT_ENFORCED",
  "severity": "info",
  "location": "/rsm:CrossIndustryInvoice",
  "message": "Amount due for payment (BT-115) is positive but neither the Payment due date (BT-9) nor the Payment terms (BT-20) is present. ..."
}

Supplying a payment due date or payment terms clears it. Credit notes are out of scope for this rule and never draw the advisory.

You will not see the advisory when an artifact that ran already enforces the rule. A Factur-X document in the BASIC, BASIC-WL or EXTENDED profile is judged by a pack that carries BR-CO-25, so the violation arrives as an ordinary error from that pack instead, and the same happens for every document once an artifact version reinstates the rule. For the full background, see Validation artifacts.

Response verifiability

Response verifiability

Every response carries two hashes so you can confirm the result yourself: sha256 (the exact bytes you sent) and rulesetSha256 (the rule artifacts that judged them), plus the rulesetArtifacts rows the ruleset hash is built from. For an EN 16931 document those rows include the syntax schema, because a document the schema rejects is judged by the schema alone and no business rule runs. Responses may also carry driftCheckedAt, a UTC timestamp reporting when those artifacts were last confirmed against their upstream authority sources.

{
  "success": true,
  "data": {
    "valid": false,
    "format": "ubl",
    "profileDetected": "netherlands-nlcius",
    "sha256": "8b508d3648dbee66ca6798e6bc812842204974328fd626e3d9cdf60c98782cd1",
    "rulesetSha256": "9f539954095415c7d9e8f1fb8459afe532d3ce3395fdd626d0e2fa73a4e4a887",
    "rulesetArtifacts": [
      { "key": "en16931_ubl_schematron", "version": "1.3.16", "fileSha256": "39f9d282867f1a49e7708d9e29a53da89643e1ee56f10cec1ebcf1277595fcbd" },
      { "key": "netherlands_nlcius_schematron", "version": "2.0.3.13", "fileSha256": "73da3db0b8cf6a37a8ad17051ac1603a83a695e2fa066820803aa1283a06e140" },
      { "key": "ubl_xsd", "version": "2.1", "fileSha256": "40fae8cb436f3a9506d7acce65ba162caef3b0bed4d5cbc0992b2153ded4edf4" }
    ],
    "driftCheckedAt": "2026-07-03T04:12:07Z"
  }
}

sha256sum your file to match sha256; sort the rulesetArtifacts "<key>@<version>=<fileSha256>" lines, join with newlines, and SHA-256 to reproduce rulesetSha256; then cross-check each component against GET /v1/rulesets. driftCheckedAt, when present, reports how recently that ruleset was drift-checked against its authority sources. Full walkthrough: Verify a hash.

Error responses

Error responses

HTTP Status Error Code When
400 VALIDATION_ERROR Request body is empty; its Content-Type is not a raw XML or PDF body; the PDF carries no embedded invoice XML (not a ZUGFeRD / Factur-X hybrid); or the Beliq-Ruleset header is malformed
413 VALIDATION_ERROR Request body is larger than this endpoint’s 1 MB size limit. The cap is enforced before the body is read, so nothing was validated and no quota unit is spent
415 VALIDATION_ERROR The Content-Type is not one this route accepts (application/xml, text/xml, application/octet-stream, application/pdf). The free public demo is narrower: XML only, and PDF bytes are refused even when labelled as XML
422 VALIDATION_ERROR A well-formed Beliq-Ruleset pin names a ruleset version or artifact that is no longer served
503 ENGINE_UNAVAILABLE The validation engine is temporarily unavailable

400 example

{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request body must contain an XML or PDF file"
  }
}
Notes

Notes

  • valid: false is not an API error. It means validation ran successfully and found rule violations.
  • format tells you which syntax family was detected (cii, ubl, fatturapa, facturae, eslog, or sdi_messaggio).
  • Rule IDs follow the official naming conventions where Schematron is used: BR-* for EN 16931 core rules, BR-DE-* for XRechnung rules, PEPPOL-* for Peppol-specific rules. FatturaPA, Facturae, e-SLOG, and SDI file messaggio XSD violations use ruleId: "XSD" (structural). Messaggio validation does not imply FatturaPA invoice conformance or SDI operational interchange.
  • A document in no syntax Beliq recognises comes back as a normal validation result, not an error: HTTP 200, valid: false, format: "unknown", and a single fatal finding with ruleId: "UNSUPPORTED_FORMAT". Match on errors[].ruleId, not on error.code, which is absent on a 200.
  • See the validation artifacts page for details on which Schematron versions are currently in use.