Use state in OPA rules
OPA rules can compare a generated answer with the state your application supplied. Rego reads the answer at input.output and the state snapshot at input.state.
Use this for calculations, relationships with generated values or rules that benefit from Rego's richer logic. For a small set of choices whose availability depends on known state, schema conditions can enforce the restriction directly during generation.
Define the output, facts and rule
This example approves a duplicate-charge refund only when the order is paid and its amount is within the account's refund limit. Review remains an allowed fallback.
Save the three files:
{
"type": "object",
"properties": {
"action": {
"type": "string",
"enum": [
"approve",
"review"
],
"description": "Approve a substantiated duplicate charge when permitted; otherwise choose review."
},
"reason": {
"type": "string",
"enum": [
"duplicate_charge",
"manual_review"
],
"description": "The reason for the decision."
}
},
"required": [
"action",
"reason"
],
"additionalProperties": false,
"if": {
"properties": {
"action": {
"const": "approve"
}
},
"required": [
"action"
]
},
"then": {
"properties": {
"reason": {
"const": "duplicate_charge"
}
}
},
"else": {
"properties": {
"reason": {
"const": "manual_review"
}
}
}
}
{
"order": {
"paid": true,
"amount": 25
},
"refund_limit": 50
}
package refund
default allow := false
allow if {
input.output.action == "review"
}
allow if {
input.output.action == "approve"
input.state.order.paid == true
is_number(input.state.order.amount)
is_number(input.state.refund_limit)
input.state.order.amount <= input.state.refund_limit
}
The schema constrains the output shape and the relationship between action and reason. The Rego rule separately enforces the relationship with state. It has no schema eligibility extension, so the two approaches can be understood independently.
default allow := false denies answers unless an allow rule succeeds. The first rule permits review. The second permits approval only when the required facts are present and satisfy the comparisons. Missing state or nonnumeric amounts cannot satisfy the approval rule.
Send the policy and state
Before you run this example
Call https://api.blatent.ai from your server. Set your API key in the same terminal where you run the example:
export BLATENT_API_KEY='YOUR_SERVICE_KEY'
Python requires Python 3.10+ and HTTPX:
python -m pip install "httpx>=0.28,<1"
TypeScript requires Node.js 24+ and uses its built-in fetch. The cURL examples require curl and jq.
Save the displayed files and the selected client in one directory. Run the client there with python example.py, node example.ts or bash example.sh. Downloads use the example's name in place of example.
- Python
- TypeScript
- cURL
import json
import os
from pathlib import Path
import httpx
request = {
"input": "This is a substantiated duplicate charge. Approve the refund.",
"policy": {
"engine": "opa",
"entrypoint": "data.refund.allow",
"modules": {
"state-refund.rego": Path("state-refund.rego").read_text(encoding="utf-8"),
},
},
"policy_max_retries": 8,
"schema": json.loads(Path("state-policy.schema.json").read_text(encoding="utf-8")),
"state": json.loads(Path("refund-eligibility.state.json").read_text(encoding="utf-8")),
}
with httpx.Client(
base_url="https://api.blatent.ai",
headers={"Authorization": f"Bearer {os.environ['BLATENT_API_KEY']}"},
timeout=195,
) as client:
response = client.post("/v1/resolve", json=request)
response.raise_for_status()
print(json.dumps(response.json()["output"]))
import { readFileSync } from "node:fs";
const request = {
"input": "This is a substantiated duplicate charge. Approve the refund.",
"policy": {
"engine": "opa",
"entrypoint": "data.refund.allow",
"modules": {
"state-refund.rego": readFileSync("state-refund.rego", "utf8"),
},
},
"policy_max_retries": 8,
"schema": JSON.parse(readFileSync("state-policy.schema.json", "utf8")),
"state": JSON.parse(readFileSync("refund-eligibility.state.json", "utf8")),
};
const response = await fetch(
"https://api.blatent.ai" + "/v1/resolve",
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.BLATENT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(request),
signal: AbortSignal.timeout(195_000),
},
);
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(JSON.stringify((await response.json()).output));
set -eu
body=$(jq -n --argjson body '{"input":"This is a substantiated duplicate charge. Approve the refund.","policy":{"engine":"opa","entrypoint":"data.refund.allow","modules":{}},"policy_max_retries":8}' \
--slurpfile f0 'state-policy.schema.json' \
--slurpfile f1 'refund-eligibility.state.json' \
--rawfile f2 'state-refund.rego' \
'$body | setpath(["schema"]; $f0[0]) | setpath(["state"]; $f1[0]) | setpath(["policy","modules","state-refund.rego"]; $f2)')
curl --fail-with-body --silent --show-error \
"https://api.blatent.ai/v1/resolve" \
-H "Authorization: Bearer $BLATENT_API_KEY" \
-H 'Content-Type: application/json' \
--data-binary "$body"
Illustrative output: {"action":"approve","reason":"duplicate_charge"}.
If order.paid is false, the amount exceeds the limit, or state is omitted, the policy denies approval and allows review. A successful response must satisfy both the schema and policy. The service can search for an alternative within the configured budget; allowing a fallback does not guarantee it will find one. If that budget is exhausted, it returns 422 POLICY_SEARCH_EXHAUSTED.
Reference the right context
| Request value | Rego path |
|---|---|
state.order.paid | input.state.order.paid |
state.refund_limit | input.state.refund_limit |
Generated action | input.output.action |
| Original request input | input.input |
| Optional parameters | input.parameters |
| Application-supplied user context | input.user_context |
The state snapshot belongs to this request and is used for its policy checks. A schema or policy registered for reuse does not retain the previous request's state. Omitted optional context is absent from the policy input.
You may also combine schema eligibility with OPA: the schema narrows known choices, while Rego enforces additional relationships. OPA policies explains enforcement, and search budgets and feedback covers recovery settings. For permission claims, see permission context.