canopy-rules API Reference
On this page
Overview
Cross-link: canopy-rules Data Model (#419)
Manages JDM (JSON Decision Model) rulesets used by all program services for eligibility logic (ADR-003). Rulesets are loaded from disk on startup (not created over HTTP); the API provides read access to the loaded rulesets plus a generic evaluation endpoint that any service can call.
- Base URL
- Authentication
-
Bearer token (Keycloak RS256 JWT)
- Access
-
service-to-service — every endpoint requires a service-caller token (
require_service_caller); these are not interactive caseworker routes - Swagger UI
- Database
-
canopy_rules
Endpoints
GET /v1/rule-sets
List the rulesets known to the loader, with pagination and search.
Minimum role: service-caller
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
|
integer |
200 (max 500) |
Page size |
|
integer |
0 |
Offset for pagination |
|
string |
— |
Filter by ruleset name (substring match) |
Response (200): Array of RuleSetSummary (logical ruleset names only).
[
{ "name": "georgia-snap-eligibility" },
{ "name": "georgia-snap-deductions" }
]
GET /v1/rule-sets/{name}
Get a single ruleset’s JDM document by its logical name. Reads the backing JDM file from disk.
Minimum role: service-caller
Path parameters: name — logical ruleset name (the name field inside the JDM file).
Response (200): Raw JDM document (nodes/edges object) for the ruleset.
Response (404): Rule set not found.
POST /v1/evaluate
Evaluate a ruleset against input data. This is the primary endpoint called by program services during eligibility determination.
Minimum role: service-caller
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
|
boolean |
false |
When |
|
string |
— |
T2-7 (#680): pin the evaluation to a stored corpus version for replay. Omitted (or equal to the live corpus hash) evaluates the live corpus; a stored non-live hash replays that exact ruleset version from |
|
boolean |
true |
T2-7 (#680): when |
Request: EvaluateRequest
{
"rule_set_name": "georgia-snap-eligibility",
"context_type": "household",
"context_id": "uuid-of-household",
"input": {
"gross_monthly_income": 1500,
"household_size": 3,
"has_elderly_disabled": false
}
}
Response (200): EvaluateResponse — structured output from the ruleset’s output node, the wall-clock duration_ms, and (when trace=true) a node-by-node trace plus derivation_edges: a typed list of RuleFiring`s (the per-node `rule_ref + namespaced input fields + declared outputs with their values), folded from the trace joined to the parsed ruleset graph. The program services rewrite these into the determination snapshot’s derivation_graph (T2-2 #679, ADR-028 Amendment 2). Both fields are null when trace was not requested.
{
"output": { "eligible": true, "allotment": 535 },
"duration_ms": 4,
"trace": null,
"derivation_edges": null
}
Response (404): Rule set not found.
GET /v1/evaluations
List recent evaluations (audit trail).
Minimum role: service-caller
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
|
integer |
50 (max 200) |
Page size |
|
integer |
0 |
Offset for pagination |
|
string |
— |
Filter by ruleset name (substring match) |
Response (200): Array of RuleEvaluation objects (id, ruleset name, input, output, duration_ms, created_at, optional context type/id).
GET /v1/corpus
The live ruleset-corpus content hash (#1469; ADR-002 Amendment 1 D8). A bulk
cohort run resolves this once at enact time as the corpus half of its pinned
policy target; the value equals the corpus_hash every unpinned
POST /v1/evaluate response echoes, and is persisted in
ruleset_corpus_versions at boot so a later pinned replay always resolves.
Minimum role: service-caller
Response (200): CorpusInfo — { "corpus_hash": "<hex sha-256>" }.
Error Codes
| Code | Meaning |
|---|---|
401 |
Missing or invalid JWT |
403 |
Caller is not a service-caller ( |
404 |
Ruleset not found ( |
422 |
|
500 |
Internal error reading/parsing a JDM file or evaluating a ruleset |
503 |
Transient overload — the DB connection pool was exhausted ( |
Notes
-
Rulesets are auto-imported from the
CANOPY_RULESETS_DIRdirectory on service startup -
The evaluation endpoint runs on a dedicated OS thread to avoid blocking the async runtime
-
Audited evaluations (the
audit=truedefault) record arule_evaluationsrow + arules.evaluatedevent, written best-effort off the request path since #1296 — soGET /v1/evaluationsis eventually consistent, and the telemetry write can never exhaust the pool or fail an evaluation. This is internal service telemetry, not the compliance audit-of-record (ADR-019: auditors read canopy-eligibility per-case views) -
See ADR-003 for the design rationale