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

http://localhost:8001/v1

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

http://localhost:8001/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

limit

integer

200 (max 500)

Page size

offset

integer

0

Offset for pagination

search

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

trace

boolean

false

When true, the response includes a node-by-node execution trace (for debugging broken expressions) and the typed derivation_edges folded from it (T2-2 #679).

corpus_hash

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 ruleset_corpus_versions and the response corpus_hash echoes the pinned value; an unknown hash → 422.

audit

boolean

true

T2-7 (#680): when false, the evaluation is ephemeral — it writes no rule_evaluations audit row and stages no rules.evaluated event (the write-free dry-run path, ADR-027 §6).

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

limit

integer

50 (max 200)

Page size

offset

integer

0

Offset for pagination

search

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 (require_service_caller rejected the token)

404

Ruleset not found (GET /v1/rule-sets/{name}, POST /v1/evaluate)

422

POST /v1/evaluate?corpus_hash= names a corpus version this service never stored (CorpusUnavailable, T2-7 #680)

500

Internal error reading/parsing a JDM file or evaluating a ruleset

503

Transient overload — the DB connection pool was exhausted (PoolTimedOut) while loading a pinned corpus version; retryable, not a defect (#1296). Callers (e.g. canopy-medicaid /v1/determine) should retry rather than fail the determination.

Notes

  • Rulesets are auto-imported from the CANOPY_RULESETS_DIR directory on service startup

  • The evaluation endpoint runs on a dedicated OS thread to avoid blocking the async runtime

  • Audited evaluations (the audit=true default) record a rule_evaluations row + a rules.evaluated event, written best-effort off the request path since #1296 — so GET /v1/evaluations is 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

Edit this page · default