Ruleset versioning
How Beliq versions its validation rules, how to choose the ruleset that runs with channels and the Beliq-Ruleset header, and how changes are announced.
Beliq judges each invoice it validates against pinned rule packs and schemas: Schematron and XSD bundles, carrying the authority’s own rules where the badge is Authority-checked. Those artifacts have versions, and they change over time. Two independent version axes decide which rules apply to your document:
- The spec axis: the standard the document itself declares (for example XRechnung 3.0, via its
CustomizationID). Beliq detects this from the document; it is not something you set. - The ruleset axis: the specific version of the rule artifacts Beliq bundles to check that spec. This page is about the ruleset axis: how it is versioned, how you choose which version runs, and how changes are announced.
For the exact artifacts and versions in force right now, see Validation artifacts.
Ruleset channels
A channel is a symbolic name for “which ruleset version to run, per format”, so you never hard-code version numbers. There are two:
| Channel | What it selects |
|---|---|
latest |
The current ruleset for each format. This is the default, used when a request sends no Beliq-Ruleset header. |
previous |
The ruleset retained before the most recent breaking change, per format. Retention is gated on breaking changes (see How rulesets change), so a format with no breaking bump behind it has nothing to retain: there previous resolves to latest and the response flags rulesetFellBack. Factur-X (1.08) and the two France CTC overlays (1.3.1) resolve it to the packs they served before the 2026-09-10 bump. Peppol BIS Billing 3.0 and XRechnung fall back: Peppol’s retained 3.0.20, and XRechnung’s retained Schematron 2.5.0 with the 2026-01-31 severity table, are superseded, and only an exact pin still reaches them. |
The selectable channels are also listed by the keyless GET /v1/rulesets endpoint, in its channels array.
The format names in that previous row move on dates nobody pushes a commit for, so read them from the API rather than from this page: each rulesets[] entry carries previousChannel[], the version previous resolves to for that format today and, where it resolves to nothing, why.
Choosing which ruleset runs
Ruleset selection resolves with a fixed precedence, highest first:
- The
Beliq-Rulesetrequest header (per call): a channel, or an exact per-format pin. - Your organisation’s default channel.
- The system default,
latest.
Organisation default
In the dashboard, Settings → Default ruleset channel sets the channel every API key in your organisation validates against when a request sends no Beliq-Ruleset header. Only owners and admins can change it. This is the org-wide baseline; leave it on latest unless you have a specific reason to pin.
It takes a channel (latest or previous) and nothing else. An exact version pin is a per-request header only; see Pinning an exact version for why.
Per request
Send a Beliq-Ruleset header with the channel name to override the org default for a single call:
curl -X POST "https://api.beliq.eu/v1/validate?format=auto" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/xml" \
-H "Beliq-Ruleset: previous" \
--data-binary @invoice.xml
The header is effective on POST /v1/validate only. /v1/generate, /v1/convert, and /v1/parse accept it but ignore it: they always run the current bundled artifacts and do not perform Schematron verdict selection. A ruleset change still reaches a conversion: POST /v1/convert validates the XML it returns, always against latest, whatever the header or your organisation default says. So a ruleset bump can change the outcome of a conversion: a request that answered 200 before the bump can answer 422 CONVERSION_OUTPUT_INVALID after it. To see which artifact versions those routes used, read the X-Version-Block response header (compact JSON with the same version fields as the validate body).
Pinning an exact version
Instead of a channel, the header takes a comma-separated list of exact per-format pins, written <artifactKey>:<version>:
curl -X POST "https://api.beliq.eu/v1/validate?format=auto" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/xml" \
-H "Beliq-Ruleset: facturx_schematron:1.08" \
--data-binary @invoice.xml
The separator is a colon. A pin is not a key=value assignment, and one written that way is rejected with a 400 naming the expected shape.
A few things worth knowing before you pin:
- A pin and a channel are mutually exclusive. Send one or the other, not both.
- Artifact keys come from
GET /v1/rulesets: theversionKeyon each entry ofrulesets[], and thekeyon each entry ofartifacts[]. They are stable identifiers likefacturx_schematronorxrechnung_schematron, not format names. - Up to 32 pins per request, and naming the same key twice with different versions is an error.
- The XRechnung pair is pinned as one. KoSIT releases
xrechnung_schematronand the validator configuration behindxrechnung_severity_overridesin one bundle, so a pin of either also runs the other at the version of the same bundle, and the response lists both. Pins of the two from different bundles return 422: KoSIT never released that combination. - An unavailable version returns 422, with the versions that are available listed in the error message. Retention is bounded, so a version you pinned today will eventually stop being served. See
rulesetSunsetsOnbelow. - A pin is per request only. The organisation default takes a channel, not a pin, so pinning an exact version across every call means sending the header every time. That is deliberate: a channel keeps meaning something after the next bump, whereas an org-wide exact pin would quietly stop resolving the day that version retires.
Prefer previous if what you want is “keep today’s rules while I adapt”: it needs no version numbers and survives the next bump. Reach for a pin when you need one specific artifact version, for example to re-run a document under the rules that judged it earlier. A pin fixes the rule artifacts, not the engine code that runs them, so a pinned re-run can still differ from the original verdict when a correction has landed in between. A pin is also the only thing that keeps working once an authority makes the successor mandatory: see When the authority supersedes a retained ruleset.
Seeing which ruleset ran
Every response tells you exactly what was applied:
| Field / header | What it reports |
|---|---|
Beliq-Ruleset-Resolved (response header) |
The ruleset that was actually applied to the request. |
rulesetChannel (body) |
The channel applied, latest or previous, present when a channel was selected via the header, or when your org default is previous. |
rulesetFellBack (body) |
true when previous was requested and the format had no retained ruleset for the channel to reach, so the request fell back to latest. Either nothing is retained, or what is retained has been superseded. Present only alongside rulesetChannel. |
rulesetSunsetsOn (body) |
The date the retained ruleset serving this request stops being served, when one did. Retention is bounded, so a pin is not permanent: after this date an exact pin returns 422. It bounds the pin, not the channel, which can stop reaching the version earlier. |
For the concrete artifact versions and checksums that judged your document, the response also carries the per-format version fields and the rulesetSha256 fingerprint. See Validation artifacts → API response metadata and Verify a hash.
How rulesets change
Beliq does not auto-update validation artifacts: every version bump is reviewed and tested before rollout. Changes that move an artifact version fall into two classes:
- Safe: a false-positive fix, a rule relaxation, a code-list addition, a refactor. A document that passed before still passes. Safe changes roll straight into
latest, with no retainedprevious. - Breaking: a change that can make a previously-valid document newly invalid. Breaking changes are announced on the day they take effect at the latest, and the outgoing ruleset stays selectable for at least 6 months from that day, so you can pin to it while you adapt.
A third kind of change moves no version at all:
- Correction: Beliq was applying a ruleset already in force differently from the authority that publishes it, and the fix moves Beliq’s verdict onto the authority’s answer. A document that passed before can fail afterwards. Corrections roll straight into
latest, with no notice period and no retainedprevious, because there is no earlier ruleset to select, only an earlier defect. They are published on the changelog asFixed. Because no version moves, an exact pin does not shield a verdict from a correction.
The public record of what changed and when is the Validation artifacts → Version changelog. Beliq also runs a nightly content-SHA drift check against every pinned artifact and opens a tracking issue if an authority re-publishes an artifact without bumping its version.
Beliq announces a bump ahead of the day it takes effect where the authority’s own timetable allows one, and there is no guaranteed period: OpenPEPPOL made BIS Billing 3.0.21 mandatory 89 days after publishing it, and KoSIT ships XRechnung Schematron with no advance notice at all. What is guaranteed is the 6 months on the way out. When a bump is announced ahead, it is machine-readable: GET /v1/rulesets lists it under scheduledChanges with the version it moves to, the date it takes effect, and the date the outgoing ruleset stops being served at all. rulesets[] keeps reporting the outgoing version until the date, because that is what your documents are still judged against. Every artifact that judges a publicly-carried claim is listed, including the overlays that run without being any format’s versionKey, so polling the endpoint is enough to find out ahead of the date rather than on it.
When the authority supersedes a retained ruleset
Retention has two halves and they do not always end on the same day.
An authority can make a new ruleset version mandatory on its network, at which point a document built to the version before it no longer clears, whatever Beliq retains. From that date previous stops reaching the retained version: a channel asks Beliq to choose a ruleset for you, and Beliq will not choose one the receiving corner already rejects. Requests on previous resolve latest and set rulesetFellBack: true.
The exact pin is unaffected and keeps resolving until the version’s retirement date. That is the point of the distinction: a pin names a version, so it is the right tool for re-running a document under the superseded rules after the network has moved on, which is a thing you may legitimately need to do. A channel is the wrong tool for it.
Two cases are in force today. OpenPEPPOL made Peppol BIS Billing Schematron 3.0.21 mandatory on 2026-08-17, so previous no longer reaches 3.0.20, while peppol_bis_billing_schematron:3.0.20 stays pinnable until 2027-03-04. KoSIT dates the XRechnung bundle of 2026-08-31, which holds Schematron 2.6.0 and its severity table, valid from 2026-08-31, so previous no longer reaches Schematron 2.5.0 and the 2026-01-31 severity table, while a pin of xrechnung_schematron:2.5.0 or of xrechnung_severity_overrides:2026-01-31 reaches the retained pair until 2027-06-11. FNFE-MPE publishes no mandatory date for a Factur-X or France CTC pack, so Factur-X and the France CTC overlays keep resolving previous to their retained packs for the full window.
Related
- Validation artifacts: the exact artifacts, versions, sources, and checksums in force now.
- Verify a hash: reproduce
rulesetSha256and cross-check the ruleset against the public catalog.