Approval Rules Configuration
Rules decide who must approve a purchase request, in what order, from the amount, cost center, category, supplier and other properties of the request. The same engine produces approval plans for purchase requests and for credit-card limit increases.
:::warning Author rules in the dashboard, not through the API
rule_content is a GoRules ZEN decision graph, not a hand-writable schema. It
is produced by the rule editor under Administration → Rules, which is the only
supported way to author one.
The API stores rule_content as opaque JSON and validates only that it is JSON.
A body that is well-formed JSON but not a valid ZEN graph is accepted by POST /rules and then matches nothing at evaluation time, with no error anywhere. A
rule that silently never fires on the surface that gates spend approval is the
worst failure mode this API has, so treat rule bodies as editor output that you
move around, never as something you compose.
:::
What the engine is
Evaluation is delegated to GoRules ZEN (gorules/zen-go).
A rule body is a JDM graph: a set of nodes, of which the interesting one is a
decision table. Each table has input columns (the conditions) and output
columns (the result). A row whose input cells all match contributes its output
cells.
For an approval rule the output column that matters is named approver
(receiver for order-confirmation tables). Its cell holds either a concrete user
code or one of the role selectors:
| Selector | Resolves to |
|---|---|
:cost_center_owner | The owner of record of the request's cost center |
:org_unit_leader | The leader, or deputy leader, of the relevant org unit |
:manager | The requester's direct manager |
:manager_chain[n] | The n-th manager up the requester's reporting line |
The selectors are symbolic on purpose: a rule referring to :cost_center_owner
always resolves to the current owner, so changing the owner does not require
touching the rule.
Rule entity
The wrapper around rule_content is a normal REST resource:
{
"name": "PR over 5k EUR, CFO sign-off",
"rule_type": "approval",
"priority": 100,
"rule_content": { "nodes": [ /* editor output */ ] },
"tags": ["procurement", "finance"]
}
| Field | Notes |
|---|---|
rule_type | One of approval, procurement, validation, pricing, order_confirmation, limit_increase. A limit_increase rule can never fire on an ordinary purchase request, and vice versa. |
priority | Lower number wins on tie-breaks. Defaults to 100. |
rule_content | Opaque JSON to the API; a ZEN graph to the engine. |
tags | Free-form labels, a text[] column. |
is_active | Not settable on create; it defaults to true. Change it through PUT /rules/{code}. |
PUT bumps version. DELETE is a soft delete.
Endpoints
GET /api/v1/rules
POST /api/v1/rules
GET /api/v1/rules/{code}
PUT /api/v1/rules/{code}
DELETE /api/v1/rules/{code}
POST /api/v1/rules/simulate
GET /api/v1/rules/executions
GET /rules/executions is the audit trail of evaluations.
Simulating
Exactly one of input or purchase_request_code must be set.
Prefer purchase_request_code: it builds the same input the production evaluation
would build from a real request, so it exercises the derivation as well as the
table. This is what the dashboard simulator uses.
{
"rule_type": "approval",
"purchase_request_code": "PR-1042"
}
Add rule_code alongside it to scope the simulation to one rule set instead of the
request's whole flow.
The response is a batch evaluation result carrying context, total_rules,
successful_rules, failed_rules, total_time_ms, derived_input, scope, and a
results array holding each rule's raw output. Note what it does not contain:
there is no resolved per-step approver list. To see the plan a request would get,
read the plan itself through the approvals endpoints after the request is created.
Behavior worth knowing
- Active rules apply to new plans only. In-flight approval plans are not re-evaluated when a rule changes.
- Inactive rules are skipped at evaluation but remain simulatable, which is how you validate a change before turning it on.
- Composition is over decision-table rows, not over a merge of separate rule documents. Two rules matching the same request each contribute their outputs; how those combine is the graph's business, not a documented merge order.
Related
- Purchase Request to PO for the consumer side: how a plan becomes approval tasks.
- Approver Delegation for what happens when an approver is away.
- API Basics for the response envelope and error model.