POST /api/v1/guardrails
Create a guardrail rule.Request Body
Example
Response
GET /api/v1/guardrails
List all guardrails for the tenant.Response
Returns an array of guardrail objects.PUT /api/v1/guardrails
Update a guardrail by key. Supports optimistic concurrency viarowVersion.
Request Body
DELETE /api/v1/guardrails
Soft-delete a guardrail by key.Query Parameters
Response 200
Enforcement at decision time
Active guardrails run duringPOST /api/v1/recommend. The stage executes
once per request, at rank-node entry — so ranking and the result limit
operate on the survivors, and expressions can reference offer.score. Flows
without a rank node fall back to running the stage at the response node.
Guardrails are independent of contact policies: setting skipContactPolicy
on a flow does not skip guardrails.
Expression semantics
TheexpressionAst describes when the rule fires — i.e. when the
candidate matches the constraint the guardrail is guarding against. A leaf
condition is { field, operator, value }; conditions combine with all (AND),
any (OR), and not.
Operators:
eq, neq, gt, gte, lt, lte, in, not_in, contains,
starts_with, exists.
What firing does
Rules whose
status is not "active" are skipped.
Fail-open on load errors
If the guardrail rule lookup fails entirely, the request continues unfiltered with a loud error log — a rules-load outage never takes down the decision hot path. The active rule list is cached for 300 seconds and invalidated automatically on every guardrail create, update, or delete, so edits take effect on the next request.Sub-flows
Each sub-flow execution (call_flow / extension_point) runs its own guardrail
stage. Candidates returned by a sub-flow are already filtered within that
sub-flow’s run.
Debug trace
Withdebug: true on the Recommend request, debugTrace.afterGuardrails is the
real post-guardrail candidate count, and debugTrace.guardrailReasons[] lists
each failed evaluation: