Policy Behavior
Flagged Review Configuration
flagged-review sends each response to a second model for review before your application gets it. The reviewer can allow, block, or escalate the response, or return an edited version. In escalate mode, the control skips the review model and escalates every response to a person.
The control reviews every response that reaches it, not only responses that another control flagged. Each review sends the input and output to the reviewer and can add a second model charge.
Syntax
The reviewer must use a different provider name and endpoint host from the primary provider. With provider_isolation: true, changing only the model or the key is not enough. For example, the OpenAI reviewer below cannot review a primary request that went to api.openai.com.
Export VERDICTAN_OPENAI_REVIEW_KEY in the terminal that starts the gateway, and add these sections to a complete configuration:
policies:
chain:
- prompt-injection
- flagged-review
- audit-logger
policy:
prompt-injection:
embedding_threshold: 0.6
response:
action: block
flagged-review:
mode: judge
provider:
name: review-llm
endpoint: https://api.openai.com/v1/chat/completions
model: your-openai-review-model
secret_key_ref:
env: VERDICTAN_OPENAI_REVIEW_KEY
timeout_ms: 5000
recursion_depth_max: 1
provider_isolation: true
rationale_capture: trueSend requests with stream: false. A streaming request through a chain with flagged-review fails with streaming.policy_cannot_enforce.
Fields
| Field | Type | Default | Description |
|---|---|---|---|
mode | string | judge | judge, review_and_return, audit_only, or escalate |
provider.name | string | flagged-review | Provider name stored in review records |
provider.endpoint | string | https://api.openai.com/v1/chat/completions | OpenAI-compatible Chat Completions endpoint |
provider.model | string | gpt-5.4-mini | Review model |
provider.secret_key_ref | object | Environment variable with the review credential. env only. | |
provider.timeout_ms | integer, 100 to 60000 | 5000 | Timeout of the review call |
recursion_depth_max | integer, 1 to 8 | 1 | Maximum review depth for one request |
provider_isolation | boolean | true | Rejects a reviewer with the same provider name or endpoint host |
rationale_capture | boolean | true | Stores the rationale of the reviewer |
prompt_template | string | Built-in | Review prompt with {input}, {output}, {reason_code}, and {mode} |
In escalate mode, provider.name defaults to human_escalation and provider.model to manual_review. The runtime also reads provider.id, but lint accepts only provider.name.
Review modes
| Mode | What happens |
|---|---|
judge | The reviewer's verdict applies to the response. |
review_and_return | With an allow or warn verdict, the gateway returns the reviewer's reviewed_response. |
audit_only | The review is recorded. The verdict does not change. |
escalate | No review call. Every response escalates. |
Reviewer response
In the three review modes, the review model must return one JSON object:
| Field | Required | Description |
|---|---|---|
verdict | Yes | allow, warn, block, or escalate |
review_summary | No | Short summary for operators. Falls back to rationale. |
rationale | No | Short reason, stored when rationale_capture is true |
reviewed_response | In review_and_return with allow or warn | The revised response to return |
{
"verdict": "allow",
"review_summary": "The answer follows the requested format.",
"rationale": "No blocked content was found."
}The gateway reads verdict, not decision. The reason code is flagged_review.<verdict>.
Examples
Review and return with a custom prompt
policy:
flagged-review:
mode: review_and_return
provider:
name: gpt4-reviewer
model: your-openai-review-model
secret_key_ref:
env: VERDICTAN_OPENAI_REVIEW_KEY
timeout_ms: 8000
recursion_depth_max: 1
provider_isolation: true
rationale_capture: true
prompt_template: |
You are a safety reviewer.
## Input
{input}
## Output
{output}
## Reason
{reason_code}
Return ONLY valid JSON with these fields:
Choose one verdict: allow, warn, block, or escalate.
Example for an allowed revised answer:
{"verdict":"allow","review_summary":"Checked the answer",
"reviewed_response":"The revised answer", "rationale":"Reason"}Escalate without a review call
policy:
flagged-review:
mode: escalate
recursion_depth_max: 1
provider_isolation: trueEvery response gets an escalation result with reason code flagged_review.escalate.
Verify one review
- Apply the configuration to a test agent and send a short request with
stream: false. - Find
flagged_review.allow,flagged_review.block, orflagged_review.escalateand the review summary in the runtime event. - For
review_and_return, check that the returned text is the reviewer'sreviewed_response. - For an escalation, open Inbox and check the assignment. Use escalation routing to send it to a specific team.
A successful HTTP response does not prove that a review ran. Look for the review record, also in audit_only mode.
Limits and failure behavior
-
Every response that reaches the control is reviewed. An earlier
blockorescalatestops the chain before it. -
These failures produce an escalation result. In
audit_onlymode, the result is only recorded and the answer still goes out. Usejudgeif a failed review must stop the answer.Case Reason code Review depth is above recursion_depth_maxflagged_review.recursion_depth_exceededThe reviewer has the same provider name or endpoint host as the primary flagged_review.provider_isolation_failedThe reviewer returns an invalid response flagged_review.invalid_responseThe review call times out or fails flagged_review.timeout -
In
review_and_return, analloworwarnwithoutreviewed_responsecounts as an invalid response. -
Lint requires
recursion_depth_maxfrom 1 to 8. The runtime uses 1 for a lower value. -
The control does not run on streaming responses.
Next steps
- Escalation Routing Configuration: send escalations to a team or user.
- Resolve an Escalation: claim and resolve an escalation in the Inbox.