The JSON schema every rulework must conform to: its fields, operations and vocabulary, and how versioned schema URLs work.
The law format is defined by a JSON Schema. Every rulework in the corpus must conform to this schema.
Everything below the version history is generated from the released schema itself, so this page and the contract cannot disagree. Where a field has a concept page, the table links to it.
This page owns the field-level detail, down to which operands each operation takes. How a rulework is put together, and why, is walked through on one real article in Law Format.
The current schema version is v0.8.6.
Schema URLs use immutable git tags to guarantee reproducibility. The format is:
The tag schema-vX.Y.Z is created when a schema version is released. Using tags instead of refs/heads/main ensures that the schema a version of a rulework references can never change underneath it. See RFC-013 for the rationale.
This table is the single source of truth for which schema version introduced which construct. RFCs that add a construct reference this table rather than asserting a version independently.
| Version | Introduces | RFC |
|---|---|---|
| v0.8.6 | A REDUCTIE may read the executograms of a stream of its cell (source.executograms), as it reads decisions; combine: ONLY gives the one result a FOREACH filter leaves, null when it leaves none and an error when it leaves more. A file valid under v0.8.5 is valid under v0.8.6 unchanged | RFC-051 |
| v0.8.5 | A REDUCTIE may fill an open term with implements: the engine runs it for that term like any implementation, also in a nested call. It still has no hooks, overrides, execution.produces or open_terms. A file valid under v0.8.4 is valid under v0.8.5 unchanged | RFC-048 |
| v0.8.4 | source.article: an input calls the decision of an article, named as <regulation>#<article> or by a $-reference to a value that holds one; a $-reference to a decision value calls that decision anew, with its own fields and findings. Without output the input is a decision value {grondslag, fields, findings, outputs}, with it one output of the decision. source.parameters_from: objects merged in order into the parameters of that call, with parameters on top; only with article, and article stands apart from regulation, decisions and endpoint. The field type decision for such a value. derived on the decision item of produces: outputs that are data derived for the decision, computed at its stages but left out of the decision value. Additive: a file valid under v0.8.3 is valid under v0.8.4 unchanged | RFC-050 |
| v0.8.3 | BEZWAAR as a kind of submission, next to AANVRAAG: in produces.submission.kind, as the type of a list item and in a hook’s applies_to.submission. The article that decides on an objection establishes it, and the Awb hooks onto it. Additive: a file valid under v0.8.2 is valid under v0.8.3 unchanged | RFC-050 |
| v0.8.2 | regulatory_layer: REDUCTIE: no regulation, but a reduction of a cell over its own chronicle (RFC-022 §4), kept with the cell and not in the corpus of regulations. source.decisions (<regulation>#<article>): the decisions taken under that article, as a list with one row per decision. It stands alone, without regulation, output, parameters or endpoint. Only a REDUCTIE reads it, only a data source answers it, and a REDUCTIE does not act on law (no implements, hooks, overrides, execution.produces or open_terms). Additive: a file valid under v0.8.1 is valid under v0.8.2 unchanged | RFC-048 |
| v0.8.1 | execution.produces may be a list of produced items instead of one object, such as an application and the decision on it. Each item has a type: a kind of submission (AANVRAAG) or a legal character (BESCHIKKING), one item per type. parameters names the article’s parameters an item contains, outputs the outputs it yields; an item without outputs yields the outputs no other item lists. A submission names the article whose decision it asks for with requests (<regulation>#<article>). A decision may carry decision_type and procedure_id. In a staged execution the hooks on a submission fire at the stage named after its type (AANVRAAG), that stage computes the submission’s outputs, and every other stage computes the decision’s. Descriptions: an open term with decided_per_case_by that no implementation fills takes the authority’s judgement for the case from the execution. The object form is unchanged, so a file valid under v0.8.0 is valid under v0.8.1 unchanged | RFC-047 |
| v0.8.0 | produces.submission (kind: AANVRAAG): the article establishes an application. produces.moment: the moment that counts in law, with a legal_basis citation of the provision behind it. A hook applies to a decision (legal_character) or to a submission (submission, optionally narrowed with established_by), not both. Additive: a file valid under v0.7.1 is valid under v0.8.0 unchanged | RFC-045 |
| v0.7.1 | Descriptions only: six field descriptions said the Rust law-model drops or cannot read a field it does read (endpoint, requires, references, resolve and legal_basis on an action, and the preamble’s machine_readable); they now say what the model and the engine do with it. No keyword that affects validation changed, so a file valid under v0.7.0 is valid under v0.7.1 unchanged | RFC-040 |
| v0.7.0 | placement on articles: the chapter, title, division and paragraph that enclose it, with their headings. markings replaces untranslatables (a flag on an article that is otherwise worked out, with resolution, resolved_by and target), declares records a document property an article establishes, and enables is removed. open_terms gains expected_source, decided_per_case_by and a closed delegation_type. An override can state voids: true with a legal_text_excerpt. Operations DATE_PART and START_OF. Not additive: a file carrying untranslatables or enables does not validate under v0.7.0 and goes through law-migrate | RFC-031, RFC-032, RFC-027 |
| v0.6.0 | No new constructs. Fills in the description of every field that lacked one, rewrites 39 that were already there, and adds examples, so the schema documents itself and this page can render it. Both keywords are annotation-only, so a file valid under v0.5.9 is valid under v0.6.0 unchanged | RFC-040 |
| v0.5.9 | RECHTSPOSITIE added to produces.legal_character and to the hooks.applies_to filter: a legal position of a person that arises by operation of law, without a besluit and without an aanvraag | RFC-038 |
| v0.5.8 | nullable on parameters, inputs and outputs: whether null (absence) is a legitimate value of the field; defaults to false, and the validator and the engine hold a law to it. Also corrects the description of required on execution parameters: it defaults to true, and an omitted required: false parameter is an unknown value, not an error | RFC-036 |
| v0.5.7 | FOREACH operation (iteration over a variable-length collection) | RFC-016 |
| v0.5.6 | Quantity unit labels (euro, ratio, percentage) and structured definitions constants carrying type/type_spec | RFC-023 |
| v0.5.5 | ROUND, CEIL, FLOOR rounding operations (explicit statutory rounding) | RFC-024 |
| v0.5.4 | DATE_DIFF operation; date operands for the comparison operators | RFC-021 |
| v0.5.3 | valid_to (law end date) | RFC-019 |
| v0.5.2 | annotation-schema.json for stand-off notes | RFC-005, RFC-018 |
| v0.5.1 | Tag-based immutable schema URLs; refinements within the v0.5.x line | RFC-013 |
| v0.5.0 | hooks, overrides (reactive execution); procedure, procedure_id (Awb lifecycle); Woo support | RFC-007, RFC-008 |
| v0.4.0 | open_terms, implements (IoC); legal_character; date and array value types | RFC-003 |
| v0.3.2 | SUBTRACT_DATE split into its own date operation with a unit (days, months, years) | |
| v0.3.1 | Structured references on articles (bwb_id, artikel, lid, onderdeel, …) for runtime cross-reference resolution | |
| v0.3.0 | Typed operation definitions (arithmetic, logical, comparison, conditional, switch) with semantic operands and legal_basis, replacing the single untyped operation | RFC-004 |
| v0.2.0 | Initial public schema: regulatory_layer, competent_authority, execution.produces, cross-law references (source) | RFC-001, RFC-002 |
Multi-organization execution (RFC-009) reuses competent_authority (v0.2.0) and adds no schema construct of its own. Absent and unknown values (RFC-036) add one construct, the nullable flag of v0.5.8, and otherwise fix what the engine does with existing constructs over a missing value; v0.5.8 also corrects the wording of required, whose text said “defaults to false” while the engine has always read an absent flag as required.
The annotation schema (annotation-schema.json) versions independently of the law schema: it is republished only when it changes. It was introduced at schema/v0.5.2/ and last republished at schema/v0.5.3/, which is the version the validators embed.
Validate ruleworks against the schema:
Each version of a rulework is one file: the regulation as it reads from one date. These 21 keys sit at the top level; articles carries the text and, where a provision has been translated, its machine_readable section.
namestringpublication_datestring^[0-9]{4}-[0-9]{2}-[0-9]{2}$.
valid_fromstring^([0-9]{4}-[0-9]{2}-[0-9]{2}|#.+)$.
valid_tostring^[0-9]{4}-[0-9]{2}-[0-9]{2}$.
regulatory_layerstringdelegation_type, not by this field alone. REDUCTIE: not a regulation but a cell's reduction over its own chronicle (RFC-022 §4). Such a rulework is kept with its cell, not in the corpus of regulations, and is the only one that may read its chronicle: decisions (source.decisions) and, since v0.8.6, executograms (source.executograms, RFC-051); it may fill an open term of an article (implements, since v0.8.5), and the engine then runs it for that term like any implementation, also in a nested call; beyond that it does not act on law (no hooks or overrides), takes no decisions (no execution.produces) and delegates nothing (no open_terms).urlstringvalid_from.bwb_idstring^BWBR[0-9]{7}$.
celex_nummerstringelistringtractatenblad_idstringunts_nummerstringgemeente_codestring^GM[0-9]{4}$.
provincie_codestring^PV[0-9]{2}$.
waterschap_codestring^WS[0-9]{4}$.
officiele_titelstringstcrt_idstringorganisationstringlegal_basisarray of objectlegal_basis a field or action carries, which cites a provision far more precisely.preambleobjectarticlesarray of objectmachine_readable is what makes one executable.procedurearray of objectWhich identifier a file must carry depends on what kind of instrument it is. A national law is identified by its BWB number, an EU regulation by its CELEX number, a municipal ordinance by the CBS municipality code plus its official title. The schema states this as conditional requirements:
A REDUCTIE is no regulation but a cell's reduction over its own chronicle (RFC-048). It needs no identifier, and its articles take no decision, delegate nothing and do not act on law. It is the only layer that may read the decisions taken under an article (source.decisions).
legal_basis[]The articles of higher law that authorize this regulation. Not the same shape as the legal basis a field or action carries: this one names a law by law_id and nothing finer than an article.
law_idstringarticlestringdescriptionstringpreambleThe aanhef, the passage before Article 1 that names the authority the regulation is made under. It carries no operative norm of its own, so it rarely computes anything.
textstringurlstringmachine_readableMachine readable sectionprocedure[]AWB-defined procedure lifecycles for administrative decisions (RFC-008).
idstringdefaultbooleanapplies_toobjectproduces.stagesarray of objectprocedure[].stages[]The lifecycle stages in order, from the application to the final decision. Each names itself and what it needs before it can complete.
namestringdescriptionstringrequiresarray of objectEach entry of articles is one provision: its number, its verbatim text, and the URL it was taken from. machine_readable is present only where the provision has been translated, which for most of the corpus is not yet the case. An article without it is carried as text and nothing more.
numberstringtextstringurlstringmachine_readableMachine readable sectionreferencesarray of objectplacementobjectarticles[].references[]Structured references to other laws and articles. The Rust law-model reads and keeps them, but nothing resolves these at runtime today.
idstringbwb_idstring^BWBR[0-9]{7}$.
artikelstringlidstringonderdeelstringhoofdstukstringparagraafstringafdelingstringarticles[].placementWhere this article sits in the document: the containers that enclose it, each with its number and its opschrift. The opschrift is condensed legal classification written by the legislator, and it decides questions the article text alone cannot answer. Awb 3:9 only makes sense once you can see it sits in 'Afdeling 3.3 Advisering', because the adviseur definition of 3:5 lid 1 is scoped to that division. Absent for an article that no container encloses, which is normal in a short law.
boekobjectdeelobjecthoofdstukobjecttiteldeelobjectafdelingobjectparagraafobjectAn article carries this when it has been translated. It holds the executable part (execution) and the annotations around it: what the article delegates, what it implements, which authority decides, and what could not be translated.
endpointstringcompetent_authoritystring or objectrequiresarray of objectopen_termsarray of objectimplementsarray of objectdefinitionsobjectnaam: 123) or the optionally-structured form carrying a type and type_spec so the constant can declare its quantity-kind (RFC-023): naam: {value: 123, type: amount, type_spec: {unit: eurocent}}.executionobjecthooksarray of objectoverridesarray of objectmarkingsarray of objectdeclaresarray of objectmachine_readable.requires[]Other provisions this article depends on. The Rust law-model reads the structure below; the engine does not act on it at runtime.
articlestringlawstringregelingstringkoninklijk_besluitstringvaluesarray of stringmachine_readable.open_terms[]Abstract values that this article leaves open and that an implementing regulation or implementing policy fills at a lower level. Any regulatory layer can declare open_terms; a law typically has required: true (no default), lower layers often provide defaults that can be refined further down. This is the single place for an open norm, whether the article names who fills it ('bij ministeriele regeling') or leaves that open ('redelijkerwijs'); in the second case delegated_to and delegation_type are simply absent and any competent authority may fill it through implementing policy. Whether the filling regulation is currently in the corpus is a state of the corpus and not a property of the law, so it is not recorded here.
idstringdescriptionstringtypestringrequiredbooleantrue.
delegated_tostringdelegation_typestringlegal_basisstringdefaultobjectimplements. It applies in two cases: no implementing regulation was found, and an implementation ran but returned null for this case.expected_sourcestringdecided_per_case_bystringmachine_readable.implements[]Declares that this article fills open terms from a higher-level law. Maps to the 'Gelet op' clause in Dutch legislation. One article can implement multiple open terms from different laws.
lawstringarticlestringopen_termstringgelet_opstringmachine_readable.hooks[]Hook declarations: this article fires when matching lifecycle events occur (RFC-007).
hook_pointstringapplies_toanymachine_readable.overrides[]Override declarations: this article replaces another article's output (RFC-007, lex specialis).
lawstring$id of the law whose output is being replaced: lex specialis written down. An override from another law fires only when that law is the one being executed, so with no such context it does not apply (RFC-007). An override on an output of its own law applies whatever the contextual law is, because an exclusion in the same law is not a claim about the asking law (RFC-041). The engine does not check that the override is legally valid.articlestringoutputstringvoidsbooleanlegal_text_excerptstringmarkings already do. A closed vocabulary was considered and rejected: it would make the agent translate the statute into a category, which is the interpretive step this format exists to avoid. A quotation only has to be copied, and a check can hold it against the article's own text.machine_readable.markings[]Constructs in this article that the format itself cannot express. A marking is a flag on an article that is otherwise worked out: it names the one thing that does not fit and leaves everything that does fit standing. A marking that empties an article is a defect, not a translation. Keep it strictly apart from open_terms, which say the opposite: the language can express this fine, the content is filled elsewhere. Replaces untranslatables and norm_gaps from earlier schema versions.
aboutstringreasonstringresolutionstringresolved_bystringtargetarray of stringaccepted and the untranslatable modes of RFC-012, which do not read this field; whether a marking should ever block only the values it names is an open question in RFC-031.legal_text_excerptstringacceptedbooleanfalse.
machine_readable.declares[]Document properties this article establishes. A citation title, a commencement date or a scope in time is not a calculation, and it is not nothing either: it fixes a value the rest of the corpus and every trace depend on. Article 51 of the Awir says what that law is called, and a trace that names the law is quoting that article. Recording it here makes the article accounted for and lets a check hold the document header against the provision that decides it.
propertystringvalueanyapplies_fromstringhooks[].applies_toWhich decisions (legal_character, optionally decision_type and stage) or which submissions (submission, optionally established_by, RFC-045) of other articles this hook fires on. A hook names one of the two.
legal_characterstringtype in the list form (RFC-047).decision_typestringstagestringsubmissionstringtype in the list form (RFC-047). A hook names either legal_character or submission (RFC-045). For an article that follows a procedure, a staged execution fires the hook at the stage named after the kind (RFC-047).established_byArticle reference<regulation>#<article>) establishes. Without it the hook applies to every submission of the kind.The executable core. Values come in as parameters andinput, actions compute, and outputdeclares what the article yields.
producesobject or array of Produced itemparametersarray of Parameter fieldinputarray of Input fieldsource.outputarray of Output fieldactionsarray of Actionexecution.producesWhat executing this article yields in legal terms. One object for an article that yields one thing, the form most laws use. A list when the article yields more than one thing, such as an application and the decision on it, each item with its own type, parameters and outputs (RFC-047).
legal_characterstringdecision_typestringGEEN_BESLUIT is the value for an article that establishes a fact rather than deciding anything.procedure_idstringsubmissionobjectmomentobjectexecution.produces[]Several things the article yields, each its own item with a type, such as an application (AANVRAAG) and the decision on it (BESCHIKKING), with the parameters and outputs that belong to each (RFC-047).
typestringrequestsArticle reference<regulation>#<article>, also when that is this article (Awb 1:3 lid 3, Awb 4:2 lid 1 onder c). The target is that article's decision: the item with a decision type in the list form, or the article itself in the object form with a legal_character. The loader checks a target in the same law; a target in another law is checked when it is used (RFC-047).parametersarray of stringoutputsarray of stringderived. Every name must be an output of the article; the loader checks it. Every output belongs to exactly one item, under outputs or, for data derived for the decision, under derived (RFC-047, RFC-050). In a staged execution, the stage named after a submission's type computes that submission's outputs, and every other stage those of the decision with the data derived for it.derivedarray of stringsource.article) leaves them out of the decision value. Derived data is listed, never inferred: an output that no item lists under outputs or derived is refused, so a forgotten or misspelt output fails instead of turning into derived data. Every name must be an output of the article and belongs to no other item; the loader checks both.decision_typestringprocedure_idstringmomentobjectOne assignment to one output. It either states a literal valueor computes one with an operation, never both.
outputstringvalueOperation valueoperation, that operation wins and this field is read as one of its operands.operationOperation typevalue, this wins and that field is read as an operand.valuesarray of Operation valueprecisionintegerresolveobjectlegal_basisLegal basisaction.resolveLook up the value in whichever regulation implements it. The Rust law-model reads it, but the engine does not execute it yet; no law in the corpus uses it.
typestringoutputstringmatchobjectParameters, inputs and outputs share one shape and add to it. Note the singular spelling: the keys are input and output, not their plurals.
Every parameter, input and output carries these. The three kinds below repeat them, so each section is the complete list for that kind.
namestring$name. This is the name another law asks for when it reads this value through source, so renaming an output is a change other laws can see.descriptionstringtypestringamount is counted in is declared separately, in type_spec.unit; units are opt-in per law, and an un-annotated value has unit unknown and is never checked (RFC-023). A decision is a decision value {grondslag, fields, findings, outputs}: the article that decided (<regulation>#<article>), the parameters that article was given and declares, its findings per case (the open terms it leaves to an authority per case, decided_per_case_by, under their ids), and the outputs of its decision item (RFC-050). A value built from a recorded decision may also carry taken_on: the article that established the submission the decision was taken on.nullablebooleannull (absence: the register or the law says there is none) is a legitimate value of this field. Defaults to false: a non-nullable input may never receive null from a data source, another law or a caller, a non-nullable output may never evaluate to null, and a non-nullable parameter may never be passed null. On a required parameter the flag means the same at the top level and across laws: a nullable one is run with the null and decides on it, a non-nullable one is a caller error at the top level and skips the call across laws. Unknown (a value nobody has) is not governed by this flag. RFC-036.type_specobjecttemporalobjectlegal_basisLegal basisfield.type_specAdditional type specifications for numeric and temporal values.
unitstringratio (0-1) and percentage (0-100) are distinct labels; any division by 100 is an explicit value operation, never implied by the label.precisionintegerminnumbermaxnumberfield.temporalHow the value relates to time: whether it is measured at a moment or over a period, and which date it is anchored to (RFC-001). Declared in the schema but not yet read by the engine, and unused in the corpus.
typestringperiod_typestringreferencestring or Variable referenceA value the caller passes in, rather than one the law looks up or derives. Adds required, which defaults to true; an omitted required: false parameter resolves to an unknown value rather than an error (RFC-036).
namestring$name. This is the name another law asks for when it reads this value through source, so renaming an output is a change other laws can see.descriptionstringtypestringamount is counted in is declared separately, in type_spec.unit; units are opt-in per law, and an un-annotated value has unit unknown and is never checked (RFC-023). A decision is a decision value {grondslag, fields, findings, outputs}: the article that decided (<regulation>#<article>), the parameters that article was given and declares, its findings per case (the open terms it leaves to an authority per case, decided_per_case_by, under their ids), and the outputs of its decision item (RFC-050). A value built from a recorded decision may also carry taken_on: the article that established the submission the decision was taken on.nullablebooleannull (absence: the register or the law says there is none) is a legitimate value of this field. Defaults to false: a non-nullable input may never receive null from a data source, another law or a caller, a non-nullable output may never evaluate to null, and a non-nullable parameter may never be passed null. On a required parameter the flag means the same at the top level and across laws: a nullable one is run with the null and decides on it, a non-nullable one is a caller error at the top level and skips the call across laws. Unknown (a value nobody has) is not governed by this flag. RFC-036.type_specobjecttemporalobjectlegal_basisLegal basisrequiredbooleanrequired: false that the caller omits resolves to an unknown value that names the parameter (RFC-036), so a law can declare application-form fields another law never passes.A value the law looks up elsewhere: another law's output, or a fact from a register. Adds source, which is required and says where the value comes from (RFC-007).
namestring$name. This is the name another law asks for when it reads this value through source, so renaming an output is a change other laws can see.descriptionstringtypestringamount is counted in is declared separately, in type_spec.unit; units are opt-in per law, and an un-annotated value has unit unknown and is never checked (RFC-023). A decision is a decision value {grondslag, fields, findings, outputs}: the article that decided (<regulation>#<article>), the parameters that article was given and declares, its findings per case (the open terms it leaves to an authority per case, decided_per_case_by, under their ids), and the outputs of its decision item (RFC-050). A value built from a recorded decision may also carry taken_on: the article that established the submission the decision was taken on.nullablebooleannull (absence: the register or the law says there is none) is a legitimate value of this field. Defaults to false: a non-nullable input may never receive null from a data source, another law or a caller, a non-nullable output may never evaluate to null, and a non-nullable parameter may never be passed null. On a required parameter the flag means the same at the top level and across laws: a nullable one is run with the null and decides on it, a non-nullable one is a caller error at the top level and skips the call across laws. Unknown (a value nobody has) is not governed by this flag. RFC-036.type_specobjecttemporalobjectlegal_basisLegal basissourceobjectinput[].sourceReference to where this input comes from.
regulationstringoutputstringparametersobject$-reference to a value in scope, as in bsn: $bsn.descriptionstringendpointstringdecisionsArticle reference<regulation>#<article>: a list with one row per decision, holding the inputs and outputs of the decision item of that article. Only a REDUCTIE rulework reads it, and only a data source (or the caller, as a parameter) answers it: its cell binds the decisions in its chronicle; no regulation is executed for it, so it stands alone, without regulation, output, parameters or endpoint. Without a source that answers it the input is an error, never an empty list.executogramsstring$id (RFC-051): a list with one row per executogram, holding the fields the stream declares and the executogram's id. An executogram records a fact, not a decision, such as where a school is established; its stream is declared with the cell, not in a regulation. Only a REDUCTIE rulework reads it, and only a data source (or the caller, as a parameter) answers it: its cell binds the executograms in its chronicle; no regulation is executed for it, so it stands alone, without regulation, output, parameters, endpoint or decisions. Without a source that answers it the input is an error, never an empty list.^[a-z][a-z0-9_]*$.
articleArticle reference or Variable reference<regulation>#<article>, or a $-reference to a value that holds one, such as the grondslag of a decision value, or that is a decision value itself. A decision value is called anew: the engine runs its grondslag with its own fields as parameters and its own findings. Without output the engine runs the decision item of that article at stage BESLUIT, and the input is a decision value {grondslag, fields, findings, outputs}. With output, the input is that one output of the decision item. The article is the one in force on the calculation date; one that is not in force, or that takes no decision, is an error at run time. The parameters are the fields of a decision value, then those of parameters_from, with parameters on top; a name the article does not declare is dropped, and the trace names it, unless it is the id of an open term the article leaves to an authority per case (decided_per_case_by): that is the finding for this case, kept under findings, not fields. A finding the caller of the engine gives for the execution, apart from the parameters, wins over one the call passes, and an implementation of the term still wins over both (RFC-047). It stands apart from regulation, decisions and endpoint.parameters_fromarray of Variable referencearticle as parameters, merged in order: a later one wins over an earlier one, and parameters wins over all of them. Each is an object, such as the fields or findings of a decision value, or null, which adds nothing; any other value is an error at run time. Only with article.A value the article yields. Adds nothing to the common fields, and that is the point: every output is callable from another law through source, which asks for it by name (RFC-007). Callable is not the same as presentable, and what a portal offers a citizen is derived from produces, not from a flag on the output (RFC-038).
namestring$name. This is the name another law asks for when it reads this value through source, so renaming an output is a change other laws can see.descriptionstringtypestringamount is counted in is declared separately, in type_spec.unit; units are opt-in per law, and an un-annotated value has unit unknown and is never checked (RFC-023). A decision is a decision value {grondslag, fields, findings, outputs}: the article that decided (<regulation>#<article>), the parameters that article was given and declares, its findings per case (the open terms it leaves to an authority per case, decided_per_case_by, under their ids), and the outputs of its decision item (RFC-050). A value built from a recorded decision may also carry taken_on: the article that established the submission the decision was taken on.nullablebooleannull (absence: the register or the law says there is none) is a legitimate value of this field. Defaults to false: a non-nullable input may never receive null from a data source, another law or a caller, a non-nullable output may never evaluate to null, and a non-nullable parameter may never be passed null. On a required parameter the flag means the same at the top level and across laws: a nullable one is run with the null and decides on it, a non-nullable one is a caller error at the top level and skips the call across laws. Unknown (a value nobody has) is not governed by this flag. RFC-036.type_specobjecttemporalobjectlegal_basisLegal basisA citation back to the provision a value or a step rests on. Any field, any action and any operation may carry one; it is what lets an execution trace name the article behind each number rather than only the number.
lawstringbwb_idstring^BWBR[0-9]{7}$.
articlestringparagraphstringsentencestringurlstringjuriconnectstringz=/g= parameters; most references in the corpus carry them, and the pattern does not require them.^jci1\.3:c:BWBR[0-9]{7}(&[a-zA-Z_]+=.+)*$.
explanationstringAn article of a regulation, <regulation>#<article>, without lid <n>. The article holds letters, digits, :, ., _, - and spaces (Kieswet G 1, Awb 4:13), and starts and ends with a letter or digit.
^[a-z0-9_]+#[A-Za-z0-9]([A-Za-z0-9:._ -]*[A-Za-z0-9])?$Variable reference starting with $ (e.g., $standaardpremie, $referencedate.year). Supports dot notation for property access.
^\$[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$An action either states a literal value or computes one with anoperation. There are 16 forms. Each is a closed object: the operation key picks the form and the schema rejects any other combination of keys, so a malformed action fails validation rather than executing as something unintended.
Wherever an operation below takes a value, three things may stand there:
Arithmetic operations that compute numeric values.
valuesarray of Operation valuelegal_basisLegal basisRounding operations (RFC-024). Round the single operand to precision decimal places, in the value's own unit (RFC-023). Rounding is never implicit - a law that rounds must say so here. ROUND is half-up (rekenkundig, the Hoge Raad default); CEIL rounds up (naar boven); FLOOR rounds down (naar beneden / afkapping).
valueOperation valueprecisionintegerlegal_basisLegal basisLogical operations that combine boolean conditions.
conditionsarray of Operation valuelegal_basisLegal basisComparison operations that compare values. Operands may be numbers or ISO 8601 dates (YYYY-MM-DD); when both operands are dates they are compared chronologically (RFC-021). value is required: all five of these compare two operands, and a comparison with only a subject compares against nothing. It was optional, and the Rust model's untagged fallback turned such an operation into a literal object without an error, a warning or a line in the trace, so the execution carried on and produced an answer that looked complete.
subjectOperation valuevalueOperation valuelegal_basisLegal basisLogical NOT - negates a boolean value.
valueOperation valuelegal_basisLegal basisConditional operation with multiple cases evaluated in order.
casesarray of objectwhen holds decides the value. Order is significant, so a narrower case belongs before a broader one.defaultOperation valuenullable. A case whose condition is unknown takes neither its branch nor the default.legal_basisLegal basisIF.cases[]Cases evaluated in order; the first whose when holds decides the value. Order is significant, so a narrower case belongs before a broader one.
whenOperation valuethenOperation valueMembership test - checks if subject is in a list.
subjectOperation valuevalueOperation valuevalues instead to write the list inline.valuesarray of Operation valuelegal_basisLegal basisConstruct an array from items.
itemsarray of Operation valuelegal_basisLegal basisIterate over a collection, evaluating an expression per element (RFC-016).
collectionOperation valueasstring^[a-z_][a-z0-9_]*$.
filterOperation valuebodyOperation valuecombinestringONLY (since v0.8.6) gives the one result the filter leaves, of any type, such as the record in force on a day: null when it leaves none, an error at run time when it leaves more than one.legal_basisLegal basisCalculate age in complete years between two dates.
date_of_birthOperation valuereference_dateOperation valuelegal_basisLegal basisAdd years, months, weeks, and/or days to a date. Applied coarsest-to-finest: years → months → weeks → days. Month/year additions use the Dutch legal 'corresponding numbered day' rule: the day is clamped to the last day of the target month (e.g., Jan 31 + 1 month = Feb 28).
dateOperation valueyearsOperation valuemonthsOperation valueweeksOperation valuedaysOperation valuelegal_basisLegal basisConstruct a date from year, month, day components.
yearOperation valuemonthOperation valuedayOperation valuelegal_basisLegal basisGet the day of the week (0=Monday, 6=Sunday).
dateOperation valuelegal_basisLegal basisSigned difference between two dates, expressed in a chosen unit. Positive when 'to' is on or after 'from'. Months and years count complete calendar units (RFC-021).
fromOperation valuetoOperation valueinstring or Variable referencelegal_basisLegal basisRead one calendar component out of a date. The inverse of DATE, so 'in' ranges over exactly the components DATE takes. Returns an integer: the year number, the month number (1-12) or the day of the month (1-31). A month number is only meaningful within one year — comparing two month numbers across a year boundary is almost always wrong. For the weekday, use DAY_OF_WEEK.
dateOperation valueinstringlegal_basisLegal basisTruncate a date down to the start of the calendar unit it falls in: 1 January of its year, or the first day of its month. Returns a date (ISO 8601 YYYY-MM-DD). For 'the first day of the month following', truncate first and add afterwards — DATE_ADD over START_OF — so the day-clamping of DATE_ADD never comes into play and the first of the month yields the next first.
dateOperation valueinstringlegal_basisLegal basisThe closed value sets a law picks from. These are the terms the rest of the documentation uses, so each one links to where it is explained.
regulatory_layerType of legal instrument (regelgevingslaag). It fixes which identifier the file must carry, and it ranks candidates when several regulations implement the same open term. Which layer may implement a term is set by that term's delegation_type, not by this field alone. REDUCTIE: not a regulation but a cell's reduction over its own chronicle (RFC-022 §4). Such a rulework is kept with its cell, not in the corpus of regulations, and is the only one that may read its chronicle: decisions (source.decisions) and, since v0.8.6, executograms (source.executograms, RFC-051); it may fill an open term of an article (implements, since v0.8.5), and the engine then runs it for that term like any implementation, also in a nested call; beyond that it does not act on law (no hooks or overrides), takes no decisions (no execution.produces) and delegates nothing (no open_terms).
machine_readable.execution.produces.legal_characterLegal character of the output. BESCHIKKING: a besluit not of general scope (Awb art. 1:3 lid 2). RECHTSPOSITIE: a legal position of a person that arises by operation of law, without a besluit and without an aanvraag (voting rights, majority, nationality).
machine_readable.execution.produces.decision_typeThe kind of decision this output is, within its legal character. GEEN_BESLUIT is the value for an article that establishes a fact rather than deciding anything.
type / type_spec.unitValue type. What an amount is counted in is declared separately, in type_spec.unit; units are opt-in per law, and an un-annotated value has unit unknown and is never checked (RFC-023). A decision is a decision value {grondslag, fields, findings, outputs}: the article that decided (<regulation>#<article>), the parameters that article was given and declares, its findings per case (the open terms it leaves to an authority per case, decided_per_case_by, under their ids), and the outputs of its decision item (RFC-050). A value built from a recorded decision may also carry taken_on: the article that established the submission the decision was taken on.
type_spec.unitUnit of measurement for the value (RFC-023). A label, never a computational constraint: tagging a value never changes it. ratio (0-1) and percentage (0-100) are distinct labels; any division by 100 is an explicit value operation, never implied by the label.
The sections above are generated from the released schema at build time, so this page cannot describe a schema other than the released one. That guard is about the version, not the wording: a description that has fallen behind what the engine does is still a description this page will render faithfully. RFC-040 sets out why the schema carries its own documentation and how the generated page is held to it.
The version history is not generated: it records what a version introduced and why, which is a judgement no schema can state about itself.
The schema is JSON Schema draft-07. It uses none of the constructs that changed in later drafts, so it would migrate to 2020-12 mechanically; the one keyword that would be worth having is deprecated, which arrived in 2019-09. That is a decision for its own RFC, since the schema is a contract other engines read (RFC-013) and draft-07 is the most widely implemented dialect.
One consequence of draft-07 shows up here. Where a field is a $ref to another definition, a description written next to that $ref is formally ignored: draft-07 says a $ref replaces its siblings. This page reads those descriptions anyway, falling back to the target’s, so nothing is lost in what you see. An editor that follows the specification strictly will not show them.
An exploration into transparent, executable legislation, and one of the three projects in the starting selection of the Nederlandse Digitale Dienst.
GitHub repository
How it works
Stay informed
Roadmap (Dutch)
Documentation
Research
Nederlandse Digitale Dienst
Ministry of Economic Affairs and Climate Policy