What It Is
Machine-Readable Legislation transforms laws, regulations, and policies from unstructured text
(PDFs, legal documents) into structured, semantic, executable formats that software systems can understand,
query, and automatically enforce. Rules as Code (RaC) takes this further: legislation is
drafted using controlled vocabularies linked to OWL ontologies, then exposed as queryable APIs.
Core Vision: Instead of humans reading 200-page laws to determine eligibility for a benefit
or permit, AI agents query a Rules API: POST /rules/check-eligibility {"citizen": {...}, "benefit": "housing_allowance"}
→ Response: {"eligible": true, "reason": "Income < €30k AND resident_of_municipality"}
This is the missing link in digital government: We have APIs for data, but not for the rules
that govern how data should be used. Machine-readable legislation enables:
- Automated eligibility checking (benefits, permits, licenses)
- Proactive compliance (systems prevent violations before they occur)
- Explainable decisions (AI cites specific law sections in responses)
- Rapid policy iteration (update rules → systems adapt instantly)
Technical Architecture
Legislation Drafting Stack
| Layer |
Technology |
Purpose |
| Controlled Vocabularies |
SKOS (sanastot.suomi.fi) |
Standardized terms with URIs (e.g., "housing_allowance" → unique ID) |
| Ontologies |
OWL (tietomallit.suomi.fi) |
Semantic relationships (e.g., "Citizen subClassOf Person", "hasIncome range xsd:decimal") |
| Rule Language |
SWRL, RIF, DMN (Decision Model Notation), or custom DSL |
Express legal logic in machine-executable format |
| Rule Engine |
Drools, OpenFisca, Blawx, or custom (Rete algorithm) |
Execute rules against input data → infer conclusions |
| Legislative Markup |
Akoma Ntoso XML, LegalDocML |
Structure legal documents (sections, articles, amendments) |
| Version Control |
Git (for rules), Finlex API (for Finnish laws) |
Track amendments, temporal validity ("rule valid 2024-01-01 to 2025-12-31") |
| Rules API Gateway |
REST/GraphQL + OpenAPI spec |
Expose rules as queryable endpoints |
Rules as Code Workflow
┌──────────────────────────────────────────────────────────────────────┐
│ Legislation Lifecycle │
└──────────────────────────────────────────────────────────────────────┘
1. DRAFTING (Legislative Authors)
┌────────────────────────────────┐
│ Legal Text (Natural Language) │
│ + Controlled Vocabulary Tags │
│ + Semantic Annotations (OWL) │
└────────────┬───────────────────┘
│
2. FORMALIZATION (Legal Engineers + AI)
┌────────────▼───────────────────┐
│ Extract Rules: │
│ IF income < €30,000 │
│ AND resident_of_municipality │
│ THEN eligible(housing_allow) │
└────────────┬───────────────────┘
│
3. ENCODING (Rules as Code)
┌────────────▼───────────────────┐
│ SWRL / DMN / DSL: │
│ Citizen(?c) ^ │
│ hasIncome(?c, ?i) ^ │
│ lessThan(?i, 30000) ^ │
│ residesIn(?c, ?m) -> │
│ eligible(?c, HousingAllowance)│
└────────────┬───────────────────┘
│
4. VALIDATION (Automated Testing)
┌────────────▼───────────────────┐
│ Test Cases: │
│ Input: {income: 25000, ...} │
│ Expected: eligible = true │
│ Result: ✓ Pass │
└────────────┬───────────────────┘
│
5. PUBLICATION (Rules API)
┌────────────▼───────────────────┐
│ POST /api/rules/check │
│ {"citizen": {...}} │
│ Response: {"eligible": true} │
└────────────┬───────────────────┘
│
6. CONSUMPTION (Systems + AI Agents)
┌────────────▼───────────────────────────┐
│ Kela System: Queries rules API │
│ AI Agent: "Is person X eligible?" │
│ Mobile App: Real-time eligibility │
└────────────────────────────────────────┘
Amendment Flow:
Parliament passes amendment → Rules updated in Git → CI/CD → API updated → All systems get new rules automatically
Controlled Vocabularies & Ontologies
Why Controlled Vocabularies?
Legal text is ambiguous. "Resident" could mean: domicile, registered address, physical presence, or tax residence.
Controlled vocabularies assign unique URIs to each concept, linked to OWL ontologies
that define precise semantics.
Example: Housing Allowance Ontology
# OWL Ontology (Turtle syntax)
@prefix ex: .
@prefix owl: .
@prefix xsd: .
# Classes
ex:Citizen a owl:Class ;
rdfs:label "Citizen"@en, "Kansalainen"@fi ;
rdfs:comment "Person with Finnish residency status" .
ex:HousingAllowance a owl:Class ;
rdfs:label "Housing Allowance"@en, "Asumistuki"@fi ;
rdfs:comment "Social benefit for low-income residents" .
ex:Municipality a owl:Class ;
rdfs:label "Municipality"@en, "Kunta"@fi .
# Properties
ex:hasIncome a owl:DatatypeProperty ;
rdfs:domain ex:Citizen ;
rdfs:range xsd:decimal ;
rdfs:label "annual income in euros" .
ex:residesIn a owl:ObjectProperty ;
rdfs:domain ex:Citizen ;
rdfs:range ex:Municipality ;
rdfs:label "has registered address in" .
ex:hasHousingCost a owl:DatatypeProperty ;
rdfs:domain ex:Citizen ;
rdfs:range xsd:decimal ;
rdfs:label "monthly housing costs in euros" .
# Rules (SWRL - Semantic Web Rule Language)
[HousingAllowanceEligibility:
(?c rdf:type ex:Citizen)
(?c ex:hasIncome ?income)
lessThan(?income, 30000)
(?c ex:residesIn ?m)
(?m rdf:type ex:Municipality)
->
(?c ex:eligibleFor ex:HousingAllowance)
]
Integration with Finnish Interoperability Platform
| Platform Component |
Rules as Code Use |
| sanastot.suomi.fi (SKOS vocabularies) |
Provides standardized URIs for legal terms used in rules |
| tietomallit.suomi.fi (OWL models) |
Defines semantic structure referenced by rules (classes, properties, constraints) |
| koodistot.suomi.fi (Code lists) |
Enumerations used in rules (e.g., benefit types, permit categories) |
Key Insight: By drafting legislation using terms from these registries, rules automatically
inherit semantic definitions. When a term's definition updates in tietomallit.suomi.fi, all rules referencing
it can be validated for consistency.
Rules API Design
Two Types of Rules
| Type |
Source |
Authority |
Examples |
| Legislative Rules |
Acts of Parliament, Government decrees |
Legal force (binding) |
Benefit eligibility, tax rates, permit requirements |
| Organizational Policy Rules |
Agency policies, internal guidelines |
Administrative (discretionary) |
Priority scoring, risk assessment, service standards |
API Endpoints
# OpenAPI Specification (excerpt)
paths:
/rules/check-eligibility:
post:
summary: Check eligibility for a benefit or service
operationId: checkEligibility
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
ruleId:
type: string
description: URI of rule (e.g., "housing-allowance-2024")
context:
type: object
description: Input data (citizen attributes, application data)
properties:
income:
type: number
residency:
type: string
housingCost:
type: number
responses:
'200':
description: Eligibility determination
content:
application/json:
schema:
type: object
properties:
eligible:
type: boolean
reason:
type: string
description: Human-readable explanation
citations:
type: array
items:
type: object
properties:
law:
type: string
description: "Law identifier (e.g., 'Social Security Act §15')"
url:
type: string
description: "Finlex URL to law section"
/rules/query:
post:
summary: Query rules by criteria (e.g., "all rules affecting companies in industry X")
# ... (similar structure)
/rules/validate-compliance:
post:
summary: Check if an action complies with applicable rules
# ... (e.g., "Can company X export product Y to country Z?")
/rules/calculate:
post:
summary: Calculate amounts (taxes, benefits, fees) based on rules
# ... (e.g., "Calculate child benefit for family with 3 children, income €45k")
Example API Call: Housing Allowance Eligibility
# Request
POST /api/rules/check-eligibility
Content-Type: application/json
{
"ruleId": "https://tietomallit.suomi.fi/housing-allowance/eligibility-2024",
"context": {
"citizen": {
"id": "010190-900C",
"income": 25000,
"residency": "Helsinki",
"housingCost": 800
}
}
}
# Response
{
"eligible": true,
"confidence": 1.0,
"reason": "Income (€25,000) is below threshold (€30,000) AND registered resident of Helsinki",
"citations": [
{
"law": "Asumistukilaki 408/2015 §3",
"section": "Income limits",
"url": "https://finlex.fi/fi/laki/ajantasa/2015/20150408#L1P3"
}
],
"calculatedAmount": 450.50,
"unit": "EUR/month",
"validFrom": "2024-01-01",
"validTo": "2024-12-31"
}
Real-World Use Cases
1. Automated Benefit Eligibility (Kela)
Scenario: Citizen applies for housing allowance via mobile app.
Traditional Process:
- Kela officer reads 200-page Act (Asumistukilaki)
- Manually checks income, residency, family size against tables
- Calculates benefit amount using formula (error-prone)
- Decision in 2-4 weeks
With Rules API:
- App queries:
POST /rules/check-eligibility {"ruleId": "housing-allowance", "context": {...}}
- Rules API evaluates → returns:
{"eligible": true, "amount": 450.50, "reason": "..."}
- Instant decision (no human review needed for simple cases)
- Officer reviews only edge cases flagged by AI
Benefit: 95% of applications auto-processed, decision time: 1 hour → 5 minutes
2. Permit Pre-Validation (Construction)
Scenario: Company designs building, wants to know if it complies before applying for permit.
Traditional: Submit permit application → wait 6 weeks → get rejection ("violates zoning rule §12")
With Rules API:
- BIM software queries:
POST /rules/validate-compliance {"ruleId": "building-code-2024", "context": {"buildingHeight": 25, "zone": "residential", ...}}
- Rules API:
{"compliant": false, "violations": ["Height exceeds 20m limit for residential zones (§12)"]}
- Architect adjusts design → re-queries →
{"compliant": true}
- Submits permit application with pre-validated design
Benefit: 80% fewer rejections, 50% faster permit processing
3. Export Control Automation (Tulli)
Scenario: Company exports electronics to Russia (hypothetical, complex sanctions).
Traditional: Read 500 pages of EU/UN sanctions lists, dual-use goods regulations → manual determination
With Rules API:
- ERP queries:
POST /rules/validate-compliance {"ruleId": "export-control-2024", "context": {"product": "HS-8542.31", "destination": "RU", "endUser": "..."}}
- Rules API checks: Product classification, sanctions lists, dual-use regulations, license requirements
- Response:
{"allowed": false, "reason": "Product on EU dual-use list, license required for Russia"}
- System prevents shipment, suggests alternative markets or license application
Benefit: Zero violations, automatic compliance in real-time
4. Tax Calculation (Vero)
Scenario: Freelancer calculates quarterly VAT.
Traditional: Read tax law → fill 20-field form → hope calculation is correct
With Rules API:
- Accounting software:
POST /rules/calculate {"ruleId": "vat-2024", "context": {"revenue": 50000, "expenses": 10000, "vatRate": "standard"}}
- Rules API:
{"vatOwed": 9600, "calculation": "...", "dueDate": "2024-04-15"}
- Auto-generates pre-filled tax return
Benefit: 99% accuracy, zero manual calculation errors
5. Proactive Compliance (AI Agent)
Scenario: AI agent monitors company operations for regulatory compliance.
Process:
- AI subscribes to event stream: "EmployeeHired", "ProductShipped", "ContractSigned"
- For each event, queries Rules API:
POST /rules/validate-compliance {...}
- If violation detected:
{"compliant": false, "violation": "Employee count >50 requires works council (§25)"}
- AI alerts management: "Action required: Form works council within 30 days"
Benefit: Proactive compliance (violations prevented before they occur)
Implementation Considerations
Legal Engineering Challenges
| Challenge |
Solution Approach |
| Ambiguity in Legal Text |
Use controlled vocabularies from drafting stage, disambiguate with ontologies |
| Discretionary Rules |
Express as probability scores or flag for human review (e.g., "case officer may grant exception") |
| Temporal Validity |
Version rules in Git, tag with effective dates, API returns rule valid for query timestamp |
| Conflicting Rules |
Ontology reasoning detects conflicts, flag for legislative review |
| Privacy |
Rules API doesn't store input data (stateless), audit log tracks queries only |
| Explainability |
API returns reasoning trace + law citations (GDPR "right to explanation") |
Technology Stack Recommendation
| Component |
Options |
Rationale |
| Rule Engine |
OpenFisca, Drools, or custom (Rete algorithm) |
OpenFisca: Tax/benefit focus, Python-based, used by France Drools: Enterprise Java, complex rule management |
| Ontology Store |
Apache Jena, Ontotext GraphDB, Stardog |
SPARQL query support, reasoning capabilities, scalable |
| Legislative Markup |
Akoma Ntoso XML, LegalDocML |
International standard for parliamentary documents |
| Version Control |
Git (rules as code), semantic versioning |
Track amendments, branching for draft legislation |
| API Framework |
FastAPI (Python), Spring Boot (Java) |
REST + GraphQL support, OpenAPI auto-generation |
Critical Constraint: Rules as Code is not the law itself—it's an interpretation.
In case of conflict, legal text prevails. Best practice: Treat RaC as "reference implementation" with legal text
as authoritative source. Courts may review rule logic for correctness.
Real-World Implementations
International Leaders
| Country/Initiative |
Approach |
Status |
| New Zealand - Better Rules |
Draft legislation in decision tables → automated eligibility tools |
Production (rates rebates, student allowances) |
| France - OpenFisca |
Tax & benefit rules as Python code, public API |
Production (used by gov services + startups) |
| Australia - Digital Service Standard |
Machine-consumable legislation (pilot), API-first mandates |
Pilot programs |
| Canada - Rules as Code |
CRA (tax agency) experimenting with rule formalization |
Research phase |
| EU - ELI (European Legislation Identifier) |
URIs for all EU laws, machine-readable metadata |
Adopted (not full RaC, but foundation) |
Deployment Roadmap (Finland)
| Phase |
Timeframe |
Deliverables |
| Foundation |
2025-2026 |
Pilot: 5 high-volume benefit rules (housing allow, child benefit, etc.) formalized + API |
| Expansion |
2026-2027 |
20+ rules online, legislative drafting tools with controlled vocab support |
| Integration |
2027-2028 |
Rules API standard for all new legislation, AI agents consume rules |
| Maturity |
2028+ |
100+ rules, proactive compliance agents, cross-border rule interop (EU) |