Stage0
Stage0GO / NO_GO / DEFERProduction docs

Stage0 authorization API docs

Stage0 is a runtime authorization layer for AI agents. Send a proposed action first, receive a GO / NO_GO / DEFER decision plus an ALLOW / DENY / DEFER verdict, then let your own system decide whether execution should continue. For loop protection, you can either send runtime counters in `context` or reuse a stable `run_id` and let Stage0 persist runtime state across checks.

This page stays intentionally plain. The goal is to make the contract easy to learn without losing the production details you need for a safe integration.

What Stage0 actually does

Stage0 evaluates goal, success criteria, constraints, tools, side effects, and plan context before execution begins. It is not an executor, scheduler, or prompt optimizer. Treat it as the authorization checkpoint between model intent and real-world action.

Good use cases

  • Guard expensive agent actions
  • Block risky production changes
  • Filter vague or low-value tasks

What Stage0 is not for

  • It does not replace your business logic
  • It does not execute tools for you
  • It does not guarantee downstream model correctness

Quickstart

The shortest path is simple: create an API key, call `/check`, read the decision, and only continue when both Stage0 and your own application checks agree. For loop-heavy agents, either attach counters such as `current_iteration` and `cumulative_cost_usd`, or keep the same `run_id` so Stage0 can maintain that state server-side.

  1. 1Create an account and generate an API key.
  2. 2Send `x-api-key` on every protected request.
  3. 3Call `/check` with a complete task proposal.
  4. 4Only continue when Stage0 returns `decision = GO` and your own checks also pass.
curl --globoff -X POST "https://api.signalpulse.org/check" \
  -H "Content-Type: application/json" \
  -H "x-api-key: <YOUR_API_KEY>" \
  --data-raw '{"goal":"test","success_criteria":["ok"],"constraints":[],"tools":[],"side_effects":[],"pro":false}'

Framework quickstarts

Call Stage0 before each real tool execution, treat `verdict` as the authorization signal, and keep the final side effect in your own runtime.

Contract sample

OpenAI Agents SDK

Wrap the last hop before tool execution with a Stage0 authorization call. The reliable boundary is the tool call, not the model text.

  • Build the payload from goal, tool name, side effects, and runtime context.
  • Stop execution unless Stage0 returns `verdict = ALLOW`.
  • Persist `request_id` and `policy_version` with your own task log.
Show code sample
import os
import requests

STAGE0_URL = os.environ.get("STAGE0_BASE_URL", "<YOUR_STAGE0_BASE_URL>")
STAGE0_API_KEY = os.environ["STAGE0_API_KEY"]


def authorize_or_raise(goal: str, tool_name: str, side_effects: list[str], context: dict) -> dict:
    payload = {
        "goal": goal,
        "tools": [tool_name],
        "side_effects": side_effects,
        "constraints": ["require human approval for destructive actions"],
        "context": context,
    }
    response = requests.post(
        f"{STAGE0_URL}/check",
        headers={
            "x-api-key": STAGE0_API_KEY,
            "content-type": "application/json",
        },
        json=payload,
        timeout=5,
    )
    response.raise_for_status()
    decision = response.json()
    if decision["verdict"] != "ALLOW":
        raise RuntimeError(f"Stage0 blocked tool call: {decision}")
    return decision

# Call authorize_or_raise(...) immediately before each tool execution.

Contract sample

LangGraph

Insert a dedicated gate node before your tool node and route on the Stage0 verdict so the authorization step is visible in the graph itself.

  • Write the Stage0 response back into graph state.
  • Route `ALLOW` to tools, `DEFER` to human review, and `DENY` to a blocked path.
  • Include `run_id`, iteration counters, and cost state in `context`.
Show code sample
import requests
from typing import TypedDict
from langgraph.graph import StateGraph, END

STAGE0_URL = "<YOUR_STAGE0_BASE_URL>"
STAGE0_API_KEY = "<YOUR_API_KEY>"


class AgentState(TypedDict, total=False):
    goal: str
    tool_name: str
    side_effects: list[str]
    context: dict
    stage0: dict


def stage0_gate(state: AgentState) -> AgentState:
    response = requests.post(
        f"{STAGE0_URL}/check",
        headers={"x-api-key": STAGE0_API_KEY},
        json={
            "goal": state["goal"],
            "tools": [state["tool_name"]],
            "side_effects": state.get("side_effects", []),
            "context": state.get("context", {}),
        },
        timeout=5,
    )
    response.raise_for_status()
    return {"stage0": response.json()}


def route_after_gate(state: AgentState) -> str:
    verdict = state["stage0"]["verdict"]
    if verdict == "ALLOW":
        return "tools"
    if verdict == "DEFER":
        return "human_review"
    return "blocked"


graph = StateGraph(AgentState)
graph.add_node("stage0_gate", stage0_gate)
graph.add_conditional_edges("stage0_gate", route_after_gate)
graph.add_edge("blocked", END)

Contract sample

MCP server / internal tools

Put Stage0 inside the server-side tool handler. This is the cleanest authorization boundary for MCP and private control-plane actions.

  • Do not let the model alone decide whether a tool should run.
  • Return the structured Stage0 response when a request is blocked or deferred.
  • Use `actor_role`, `approval_status`, and `environment` in tool context.
Show code sample
const STAGE0_URL = "<YOUR_STAGE0_BASE_URL>";
const STAGE0_API_KEY = process.env.STAGE0_API_KEY;

async function stage0Check(payload) {
  const response = await fetch(`${STAGE0_URL}/check`, {
    method: "POST",
    headers: {
      "content-type": "application/json",
      "x-api-key": STAGE0_API_KEY,
    },
    body: JSON.stringify(payload),
  });

  if (!response.ok) {
    throw new Error(`Stage0 request failed: ${response.status}`);
  }

  return response.json();
}

server.tool("delete_customer", async (args) => {
  const decision = await stage0Check({
    goal: "Delete a customer record after support confirmation",
    tools: ["delete_customer"],
    side_effects: ["data deletion"],
    constraints: ["approval required", "business-hours only"],
    context: {
      actor_role: args.actorRole,
      approval_status: args.approvalStatus,
      environment: args.environment,
    },
  });

  if (decision.verdict !== "ALLOW") {
    return {
      content: [{ type: "text", text: JSON.stringify(decision, null, 2) }],
      isError: true,
    };
  }

  return performDelete(args);
});

Authentication and plan behavior

Stage0 uses `x-api-key`. Quota, analytics depth, and response fields depend on the plan behind the key. Free/Starter plans omit `value_risk`, `waste_risk`, `value_findings`, and `cost_estimate`. Free also omits `guardrails`. Pro/Enterprise return the full response.

Do not expose API keys in the browser bundle.
Use Pro when you want cost tracking, Usage Analytics, and richer decision insights.
Rotate or delete keys that are no longer needed.

Endpoints

Design Contract

Read the current design contract and version information.

GET/stage0/design

Request

No request body.

Response

JSON payload describing the policy pack.

Common errors

  • 5xx: Stage0 runtime is unhealthy or unavailable.

Success example

{"version":"v1.0.0","positioning":"Runtime policy gate"}

Failure example

{"error":"service_unavailable"}

Health Check

Check runtime and policy pack health.

GET/health

Request

No request body.

Response

JSON: { ok: true, policy_pack_version: string, ... }

Common errors

  • 5xx: Stage0 runtime is unhealthy or unavailable.

Success example

{"ok":true,"policy_pack_version":"v1.0.0"}

Failure example

{"ok":false,"error":"runtime_unhealthy"}

Policy Check

Submit a task proposal and get the Stage0 decision.

POST/check

Request

JSON: { goal, success_criteria, constraints, tools, side_effects, pro } with header x-api-key. Note: pro: true requires a paid plan (Pro or Enterprise).

Response

JSON: { decision, verdict, issues, risk_score, guardrails, request_id, policy_version, decision_trace_summary, ... }

Common errors

  • 401: API key missing or invalid.
  • 403: API key disabled.
  • 402: Pro plan required (for pro: true requests) or monthly quota exceeded.
  • 422: Request validation failed.
  • 429: Rate limit exceeded (per-minute).
  • 500+: Stage0 server error.

Success example

{"decision":"GO","verdict":"ALLOW","issues":[],"risk_score":18,"high_risk":false,"value_risk":12,"waste_risk":8,"clarifying_questions":[],"guardrails":["read_only"],"guardrail_checks":{},"value_findings":["Low risk, clear success criteria"],"defer_questions":[],"request_id":"req_docs_sample","policy_pack_version":"2026-03-01","policy_version":"2026-03-01","timestamp":1735689600,"evaluated_at":1735689600,"decision_trace_summary":"Decision: GO (verdict: ALLOW). Risk score: 18.","cached":false,"meta":{"source":"snapshot","test_mode":false},"cost_estimate":{"currency":"USD","min":0.0124,"max":0.0268,"assumptions":["Pro plan","single evaluation"]}}

Failure example

{"error":"quota_exceeded","message":"Monthly quota exhausted."}

Decision semantics

A `200` HTTP status only means the request was processed. The real control signal is the decision inside the response body.

GO

Safe enough to continue, but your app still owns execution safety.

NO_GO

Do not run. The request crosses a policy boundary.

DEFER

Do not run yet. More context or a clearer request is needed.

ERROR

System error. Request could not be evaluated. Retry or check payload format.

Production behavior

These are the public runtime behaviors to design around when you move Stage0 into a governed workflow.

Integration pattern

Fail closed by default

If Stage0 is unavailable, returns an unknown verdict, or cannot evaluate policy, your runtime should stop the side effect instead of guessing.

  • Treat 5xx responses as authorization failures.
  • Do not continue on missing decision fields.
  • Reserve fail-open behavior for explicitly documented low-risk paths only.

Integration pattern

Timeouts and retries

Use a bounded timeout for `/check` and retry only when the underlying tool execution has not started yet.

  • Do not wait indefinitely for a decision before a high-risk action.
  • Retry network failures only before any side effect begins.
  • Resume repeated evaluations with the same `run_id` and updated context.

Integration pattern

Request traceability

Carry `request_id`, `X-Request-Id`, and `policy_version` into your own logs, approvals, and incident review records.

  • Use `request_id` as the application-level decision trace id.
  • Use `policy_version` to audit which policy revision made the decision.
  • Store both with downstream execution records.

Integration pattern

Quota and plan behavior

Stage0 enforces per-minute limits and monthly quotas per API key or usage group. These are runtime controls, not just billing counters.

  • `429` means rate limit exceeded and includes a retry hint.
  • `402` means plan or quota requirements blocked the request.
  • Separate keys or usage groups when workflows need different envelopes.

Integration pattern

Audit and enforcement responsibility

Stage0 produces the decision. Your runtime still owns final enforcement, final tool execution, and any compensating action if downstream work fails.

  • Persist the full Stage0 response with your task or approval record.
  • Keep final execution checks server-side.
  • Record approver identity and timing when human approval is involved.

Contract sample

Integration patterns

Three standard patterns cover most production authorization workflows. Choose based on where the side effect happens in your system.

  • Pre-tool check: Call Stage0 immediately before each tool invocation. Block if `verdict != ALLOW`.
  • Pre-webhook check: Gate external notifications, emails, and customer-visible actions. Require approval context for sensitive channels.
  • Pre-deploy check: Intercept CI/CD, migrations, and production changes. Enforce change windows, required roles, and rollback plans.
Show code sample
# Pre-tool pattern (pseudo-code)
def before_tool_execution(tool_name, context):
    decision = stage0.check(goal, tools=[tool_name], side_effects=..., context=context)
    if decision.verdict != "ALLOW":
        raise AuthorizationError(decision)
    return decision  # Continue to tool execution

# Pre-webhook pattern
async def send_webhook(url, payload, context):
    decision = await stage0.check(
        goal=f"Send webhook to {url}",
        side_effects=["external notification"],
        constraints=["approval required for customer-facing channels"],
        context={**context, "approval_status": context.get("approval_status")}
    )
    if decision.verdict != "ALLOW":
        log.deferred("webhook", url, decision.request_id)
        return
    await http_post(url, payload)

# Pre-deploy pattern
def deploy_service(environment, service, context):
    decision = stage0.check(
        goal=f"Deploy {service} to {environment}",
        side_effects=["deploy"],
        constraints=[
            "required_role:platform_admin",
            f"allowed_environment:staging,production",
            "rollback_plan_required"
        ],
        context={**context, "environment": environment}
    )
    if decision.verdict != "ALLOW":
        return decision  # Do not proceed with deployment
    return kubectl_apply(service, environment)

Contract sample

When fail-open is acceptable

Fail-open should be an explicit, documented exception. It is only appropriate for low-risk read-only operations where unavailability is less dangerous than incorrect authorization.

  • Read-only queries with no side effects may allow fail-open with explicit fallback.
  • Never fail-open for deletes, payments, deploys, webhooks, or any customer-visible action.
  • Document every fail-open path with a justification and a monitoring alert.
Show code sample
# Acceptable fail-open pattern (read-only only)
async def safe_read_with_fallback(query, context):
    try:
        decision = await stage0.check(
            goal=query,
            tools=["db"],
            side_effects=[],  # No side effects - read only
            constraints=["read-only"],
            context=context,
            timeout_seconds=5
        )
        if decision.verdict == "ALLOW":
            return execute_query(query)
        # DENY or DEFER: respect the decision
        return None
    except (TimeoutError, ServiceUnavailableError):
        # Fail-open fallback for READ-ONLY operations only
        # This must be documented and monitored
        log.warn("stage0_unavailable_read_fallback", query)
        return execute_query(query)  # Proceed with read

Contract sample

Retry boundary: before side effects only

Network retries are safe only before any irreversible action starts. Once a side effect has begun, retrying the `/check` call creates inconsistent state.

  • Side effects include: database writes, API calls, file modifications, webhooks, deploys.
  • If Stage0 succeeds but downstream execution fails, do not retry `/check` - handle the execution error instead.
  • Use idempotency keys and `run_id` to prevent duplicate actions on legitimate retries.
Show code sample
# SAFE: Retry before side effect begins
async def check_with_retry(payload, max_retries=3):
    for attempt in range(max_retries):
        try:
            return await stage0.check(payload, timeout_seconds=10)
        except NetworkError:
            if attempt == max_retries - 1:
                raise
            await sleep(2 ** attempt)  # Exponential backoff
    # If we reach here, check failed - side effect never started

# UNSAFE: Retry after side effect started (DO NOT DO THIS)
async def unsafe_pattern(goal, execute_fn):
    decision = await stage0.check(goal)
    if decision.verdict != "ALLOW":
        return
    result = execute_fn()  # Side effect starts here
    # If execution fails, DO NOT retry /check
    # The side effect may have partially completed
    # Handle execution errors separately, do not re-authorize

Runtime contract

Stage0 already supports approval evidence, actor roles, environment restrictions, and loop state. These fields should be treated as part of the contract.

Integration pattern

Approval evidence

When a workflow needs human review, pass approval state in `context` so Stage0 can distinguish approved runs from unapproved ones.

  • Supported fields include `approval_status`, `approved_by`, `approved_at`, and `approval_reason`.
  • Approval timeout constraints can be enforced with timestamp-aware checks.
  • Missing approval evidence should normally resolve to `DEFER`.

Integration pattern

Actor and role policy

Stage0 can compare the caller role in runtime context against a required role declared in constraints.

  • Use `actor_role`, `user_role`, or `requester_role` in `context`.
  • Use constraints such as `required_role:platform_admin`.
  • Missing or insufficient roles can defer or deny execution.

Integration pattern

Environment scope

High-risk actions should declare the target environment explicitly so Stage0 can block the wrong target before any tool runs.

  • Use `environment` or `target_environment` in `context`.
  • Use constraints such as `allowed_environment:staging,production`.
  • Missing environment data should be treated as incomplete authorization context.

Contract sample

Loop and cost controls

For long-running agents, provide loop counters and cost state so Stage0 can stop pathological runs before they become expensive or dangerous.

  • Use `run_id` to preserve state across repeated `/check` calls.
  • Pass `current_iteration`, `elapsed_seconds`, `current_tool`, `recent_tools`, and `cumulative_cost_usd`.
  • Constraints such as `max_iterations`, `timeout`, and `max_cost_usd` depend on runtime context being present.
Show code sample
{
  "goal": "Deploy the payments service to production",
  "tools": ["argo_cd", "slack"],
  "side_effects": ["deployment", "external notification"],
  "constraints": [
    "approval required",
    "required_role:platform_admin",
    "allowed_environment:staging,production",
    "max_iterations:3",
    "timeout:300s",
    "max_cost_usd:5"
  ],
  "context": {
    "run_id": "run_9f3d7f51",
    "actor_role": "platform_admin",
    "approval_status": "approved",
    "approved_by": "[email protected]",
    "approved_at": "2026-03-14T09:30:00Z",
    "environment": "production",
    "current_iteration": 1,
    "elapsed_seconds": 42,
    "current_tool": "argo_cd",
    "recent_tools": ["github_actions"],
    "cumulative_cost_usd": 0.18
  },
  "pro": true
}

Cost model

On Pro, `/check` can return `cost_estimate`. Treat it as an estimate for analytics and guardrails, not as a final billing line item.

  • Use min and max as a range, not a precise bill.
  • Decision cost and downstream execution cost are different numbers.
  • Missing cost data usually means non-Pro usage or older logs.
Estimated savings = blocked or deferred runs x average downstream run cost
  • Estimated savings = blocked or deferred runs x average downstream run cost.
  • Track successful runs, failed runs, and risky runs separately for a more honest model.
  • DEFER also saves money when it stops vague requests from triggering retries.

Common mistakes

Most integration failures come from thin payloads, wrong API keys, or treating HTTP success as policy success.

Do not send only goal if you already know tools, constraints, and side effects.
Do not treat DEFER as failure; it usually means your UI should collect more context.
Do not continue execution just because the transport succeeded.

Examples

Use these payloads and outputs as readable starting points. Copy them, edit them, and adapt them to your own agent runtime.

Response

{"decision":"GO","verdict":"ALLOW","issues":[],"risk_score":18,"high_risk":false,"value_risk":12,"waste_risk":8,"clarifying_questions":[],"guardrails":["read_only"],"guardrail_checks":{},"value_findings":["Low risk, clear success criteria"],"defer_questions":[],"request_id":"req_docs_sample","policy_pack_version":"2026-03-01","policy_version":"2026-03-01","timestamp":1735689600,"evaluated_at":1735689600,"decision_trace_summary":"Decision: GO (verdict: ALLOW). Risk score: 18.","cached":false,"meta":{"source":"snapshot","test_mode":false},"cost_estimate":{"currency":"USD","min":0.0124,"max":0.0268,"assumptions":["Pro plan","single evaluation"]}}

Examples

{"go":{"decision":"GO","verdict":"ALLOW"},"no_go":{"decision":"NO_GO","verdict":"DENY","issues":[{"code":"SIDE_EFFECTS_NEED_GUARDRAILS"}]},"defer":{"decision":"DEFER","verdict":"DEFER","issues":[{"code":"INSUFFICIENT_CONTEXT"}]},"error":{"decision":"ERROR","verdict":"DEFER","issues":[{"code":"INTERNAL_ERROR","message":"Request could not be evaluated."}]}}

Policy case library

en_safe_go

Summarize weekly KPI deltas and produce a concise report for stakeholders

ALLOW
Expected verdict: ALLOW
Show payload

Request body

{
  "goal": "Summarize weekly KPI deltas and produce a concise report for stakeholders",
  "success_criteria": [
    "Report includes top 3 root causes",
    "No external side effects are executed"
  ],
  "constraints": [
    "read-only",
    "sandbox"
  ],
  "tools": [
    "db"
  ],
  "side_effects": [],
  "pro": false
}

zh_high_risk_no_guardrails

直接連到 production 資料庫,刪除指定客戶資料後再發信通知對方

DENY
Expected verdict: DENY
Show payload

Request body

{
  "goal": "直接連到 production 資料庫,刪除指定客戶資料後再發信通知對方",
  "success_criteria": [
    "資料刪除與通知都完成"
  ],
  "constraints": [],
  "tools": [
    "db",
    "shell",
    "email"
  ],
  "side_effects": [
    "delete_customer_data",
    "send_external_email"
  ],
  "pro": true
}

Expected issue codes: SIDE_EFFECTS_NEED_GUARDRAILS

ja_guardrailed_deploy_go

ステージング環境に限定して設定差分を確認し、承認後にデプロイ手順を準備する

ALLOW
Expected verdict: ALLOW
Show payload

Request body

{
  "goal": "ステージング環境に限定して設定差分を確認し、承認後にデプロイ手順を準備する",
  "success_criteria": [
    "変更点が一覧化される"
  ],
  "constraints": [
    "approval_required",
    "dry-run",
    "staging-only"
  ],
  "tools": [
    "shell"
  ],
  "side_effects": [
    "deploy_preview"
  ],
  "context": {
    "approved": true,
    "approved_by": "ops-manager"
  },
  "pro": false
}

zh_low_value_defer_pro

幫我想一下

DEFER
Expected verdict: DEFER
Show payload

Request body

{
  "goal": "幫我想一下",
  "success_criteria": [
    "有結果"
  ],
  "constraints": [],
  "tools": [],
  "side_effects": [],
  "pro": true
}

Expected issue codes: UNCLEAR_VALUE_SIGNAL

zh_sensitive_tool_non_pro_go

讀取使用者資料表欄位結構並整理成內部文件

ALLOW
Expected verdict: ALLOW
Show payload

Request body

{
  "goal": "讀取使用者資料表欄位結構並整理成內部文件",
  "success_criteria": [
    "只輸出 schema 摘要"
  ],
  "constraints": [
    "read-only"
  ],
  "tools": [
    "db"
  ],
  "side_effects": [],
  "pro": false
}

en_structured_high_risk_defer

Deploy version 2.3.0 to production

DEFER
Expected verdict: DEFER
Show payload

Request body

{
  "goal": "Deploy version 2.3.0 to production",
  "success_criteria": [
    "Deployment completes without customer-visible outage"
  ],
  "constraints": [
    "approval_required",
    "dry-run",
    "require_mfa: true",
    "check_change_window: business_hours",
    "rollback_plan_required: true"
  ],
  "tools": [
    "shell"
  ],
  "side_effects": [
    "deploy",
    "service_impact"
  ],
  "context": {
    "current_time": "2026-03-15T12:00:00+00:00",
    "timezone": "UTC"
  },
  "pro": true
}

Expected issue codes: APPROVAL_REQUIRED_PENDING, MFA_VERIFICATION_REQUIRED, CHANGE_WINDOW_CLOSED, ROLLBACK_PLAN_REQUIRED

en_access_control_without_guardrails_deny

Grant temporary admin access to a production support engineer

DENY
Expected verdict: DENY
Show payload

Request body

{
  "goal": "Grant temporary admin access to a production support engineer",
  "success_criteria": [
    "Engineer receives admin access"
  ],
  "constraints": [],
  "tools": [
    "identity.admin"
  ],
  "side_effects": [
    "grant permissions",
    "create admin users"
  ],
  "pro": true
}

Expected issue codes: SIDE_EFFECTS_NEED_GUARDRAILS

en_internal_tools_sample_defer

Delete inactive users older than 2 years

DEFER
Expected verdict: DEFER
Show payload

Request body

{
  "goal": "Delete inactive users older than 2 years",
  "success_criteria": [
    "Users deleted"
  ],
  "constraints": [
    "role: admin",
    "require_confirmation: true",
    "max_rows_affected: 100",
    "dry_run_first: true"
  ],
  "tools": [
    "db.users.delete"
  ],
  "side_effects": [
    "destructive",
    "irreversible"
  ],
  "pro": true
}

Expected issue codes: ROLE_CONTEXT_MISSING, ACTION_CONFIRMATION_REQUIRED, DRY_RUN_REQUIRED, ROWS_AFFECTED_ESTIMATE_MISSING

en_internal_tools_authorized_go

Delete inactive users older than 2 years

ALLOW
Expected verdict: ALLOW
Show payload

Request body

{
  "goal": "Delete inactive users older than 2 years",
  "success_criteria": [
    "Users deleted"
  ],
  "constraints": [
    "role: admin",
    "require_confirmation: true",
    "max_rows_affected: 100",
    "dry_run_first: true",
    "read-only preview before execution"
  ],
  "tools": [
    "db.users.delete"
  ],
  "side_effects": [
    "destructive",
    "irreversible"
  ],
  "context": {
    "user_role": "admin",
    "confirmed": true,
    "dry_run_completed": true,
    "estimated_rows_affected": 40
  },
  "pro": true
}

en_internal_tools_role_denied

Delete inactive users older than 2 years

DENY
Expected verdict: DENY
Show payload

Request body

{
  "goal": "Delete inactive users older than 2 years",
  "success_criteria": [
    "Users deleted"
  ],
  "constraints": [
    "role: admin",
    "require_confirmation: true",
    "max_rows_affected: 100",
    "dry_run_first: true"
  ],
  "tools": [
    "db.users.delete"
  ],
  "side_effects": [
    "destructive",
    "irreversible"
  ],
  "context": {
    "user_role": "intern",
    "confirmed": true,
    "dry_run_completed": true,
    "estimated_rows_affected": 10
  },
  "pro": true
}

Expected issue codes: ROLE_NOT_AUTHORIZED

en_internal_tools_rows_limit_denied

Delete inactive users older than 2 years

DENY
Expected verdict: DENY
Show payload

Request body

{
  "goal": "Delete inactive users older than 2 years",
  "success_criteria": [
    "Users deleted"
  ],
  "constraints": [
    "role: admin",
    "require_confirmation: true",
    "max_rows_affected: 100",
    "dry_run_first: true"
  ],
  "tools": [
    "db.users.delete"
  ],
  "side_effects": [
    "destructive",
    "irreversible"
  ],
  "context": {
    "user_role": "admin",
    "confirmed": true,
    "dry_run_completed": true,
    "estimated_rows_affected": 250
  },
  "pro": true
}

Expected issue codes: ROWS_AFFECTED_LIMIT_EXCEEDED

en_internal_tools_query_cost_denied

Run a customer-level analytics query over the full production event table

DENY
Expected verdict: DENY
Show payload

Request body

{
  "goal": "Run a customer-level analytics query over the full production event table",
  "success_criteria": [
    "Query returns within policy"
  ],
  "constraints": [
    "read-only",
    "query_cost_limit: 500"
  ],
  "tools": [
    "db.analytics.query"
  ],
  "side_effects": [],
  "context": {
    "estimated_query_cost": 2500
  },
  "pro": true
}

Expected issue codes: QUERY_COST_LIMIT_EXCEEDED

en_internal_tools_pii_denied

Export customer emails and phone numbers to CSV

DENY
Expected verdict: DENY
Show payload

Request body

{
  "goal": "Export customer emails and phone numbers to CSV",
  "success_criteria": [
    "CSV exported"
  ],
  "constraints": [
    "read-only"
  ],
  "tools": [
    "db.customers.export"
  ],
  "side_effects": [
    "read"
  ],
  "pro": true
}

Expected issue codes: SENSITIVE_DATA_EXPOSURE_RISK

en_internal_tools_secret_denied

Read production API keys and secrets from config files

DENY
Expected verdict: DENY
Show payload

Request body

{
  "goal": "Read production API keys and secrets from config files",
  "success_criteria": [
    "Keys listed"
  ],
  "constraints": [
    "read-only"
  ],
  "tools": [
    "filesystem.read"
  ],
  "side_effects": [
    "read"
  ],
  "pro": true
}

Expected issue codes: SECRET_EXPOSURE_RISK

en_ai_saas_sample_no_false_pii_deny

Generate marketing copy for user's product

ALLOW
Expected verdict: ALLOW
Show payload

Request body

{
  "goal": "Generate marketing copy for user's product",
  "success_criteria": [
    "copy generated",
    "no_pii",
    "no_harmful_content"
  ],
  "constraints": [
    "max_output_length: 500",
    "content_policy: marketing",
    "user_quota_check: true",
    "no_competitor_mentions: true"
  ],
  "tools": [
    "ai.generate"
  ],
  "side_effects": [
    "read_only"
  ],
  "pro": false
}

Safe internal analysis task

{
  "goal": "Summarize weekly KPI deltas",
  "success_criteria": [
    "Report includes top 3 root causes"
  ],
  "constraints": [
    "read-only data access",
    "no external side effects"
  ],
  "tools": [
    "db"
  ],
  "side_effects": [],
  "pro": false
}
{
  "decision": "GO",
  "risk_score_range": "0-35"
}

External side effects without guardrails

{
  "goal": "Deploy and notify customers",
  "success_criteria": [
    "Deployment finished",
    "Users informed"
  ],
  "constraints": [
    "production change window"
  ],
  "tools": [
    "shell",
    "email"
  ],
  "side_effects": [
    "deploy",
    "email"
  ],
  "pro": true
}
{
  "decision": "NO_GO",
  "required_follow_up": "add approval and dry-run constraints"
}

Debug and playground

The playground below is local-only. It exists to help you learn the contract before wiring real backend calls.

Input payload

Simulated response

{"decision":"GO","verdict":"ALLOW","issues":[],"risk_score":18,"high_risk":false,"value_risk":12,"waste_risk":8,"clarifying_questions":[],"guardrails":["read_only"],"guardrail_checks":{},"value_findings":["Low risk, clear success criteria"],"defer_questions":[],"request_id":"req_docs_sample","policy_pack_version":"2026-03-01","policy_version":"2026-03-01","timestamp":1735689600,"evaluated_at":1735689600,"decision_trace_summary":"Decision: GO (verdict: ALLOW). Risk score: 18.","cached":false,"meta":{"source":"snapshot","test_mode":false},"cost_estimate":{"currency":"USD","min":0.0124,"max":0.0268,"assumptions":["Pro plan","single evaluation"]}}