Projects & artifacts
Decisions and workflows are both artifacts — same storage shape, same
CRUD surface, distinguished by kind. Every route below is tenant-scoped
(403 with no tenant claim) and grouped under a project path segment,
which is just a label within the tenant (see
Tenancy).
Artifact shape
{
"id": "art_9f2c...",
"tenant": "acme",
"project": "lending",
"kind": "decision",
"name": "loan_risk",
"model": { "...": "the decision or workflow JSON" }
}
Decisions
| Method & path | Description |
|---|---|
POST /projects/{project}/decisions | Create. Body: { "name": "...", "model": { ... } } |
GET /projects/{project}/decisions | List all decisions in the project |
GET /projects/{project}/decisions/{artifactId} | Get one |
PUT /projects/{project}/decisions/{artifactId} | Replace name + model in place |
DELETE /projects/{project}/decisions/{artifactId} | Delete; audited as decision.deleted |
POST /projects/{project}/decisions/{artifactId}/simulate | Run the saved decision against a body of raw inputs |
curl -sS -X POST "$RULEFLOW_API/api/projects/lending/decisions" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{ "name": "loan_risk", "model": { "id": "loan_risk", "inputs": [...], "outputs": [...], "nodes": [...] } }'
{ "id": "art_9f2c...", "tenant": "acme", "project": "lending", "kind": "decision", "name": "loan_risk", "model": { "...": "..." } }
Simulate takes the raw input object as the body (not wrapped):
curl -sS -X POST "$RULEFLOW_API/api/projects/lending/decisions/art_9f2c.../simulate" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{ "credit_score": 780, "debt_to_income": 0.2 }'
A successful simulate (2xx from the engine) records one decision_simulation
unit of metered usage — see Usage & quotas.
Workflows
| Method & path | Description |
|---|---|
POST /projects/{project}/workflows | Create. Body: { "name": "...", "model": { ... } } |
GET /projects/{project}/workflows | List |
PUT /projects/{project}/workflows/{artifactId} | Replace in place |
DELETE /projects/{project}/workflows/{artifactId} | Delete; audited as workflow.deleted |
POST /projects/{project}/workflows/{artifactId}/compile | Compile the saved workflow to ASL (read-only, not persisted) |
There is no GET .../workflows/{artifactId} single-item route — list and
filter client-side, or track ids from create/list responses.
Ad-hoc engine operations
Not persisted; useful while authoring before you save anything:
| Method & path | Forwards to |
|---|---|
POST /engine/validate | /v1/validate |
POST /engine/simulate | /v1/simulate |
POST /engine/compile-workflow | /v1/compile-workflow |
POST /engine/diff | /v1/diff |
These relay the request body verbatim to the engine service and return its
response verbatim (status code included) — a 503 means no engine is
configured for this deployment.
Storage quota
If the deployment enforces MaxArtifactBytesPerTenant, POST (create only —
not update) checks the tenant’s total stored artifact bytes before writing
and returns 429 if the new artifact would exceed it:
{ "error": "artifact storage quota exceeded (max 5000000 bytes per tenant)" }
Audit
Audit entries are recorded on writes (deletes explicitly; create/update audit entries are recorded by the release/deploy flows, not per-artifact edit). Read them tenant- or project-scoped:
| Method & path | Description |
|---|---|
GET /projects/{project}/audit | This project’s audit entries |
GET /audit?project=&action=&actor=&from=&to=&limit= | Tenant-wide, filterable (project omitted spans every project) |
GET /audit/summary | Aggregate counts (by action, actor, day) over the same filters |
{ "id": "aud_1a2b...", "tenant": "acme", "project": "lending", "action": "release.created", "actor": "alice@acme.com", "detail": "v1 (3 item(s))" }