---
title: "Replay stored decisions under a candidate policy"
method: POST
path: "/policy/simulate"
---

# Replay stored decisions under a candidate policy

`POST /policy/simulate`

## Request

- Base URL: `https://getsanction.com/api/v1`
- URL: `https://getsanction.com/api/v1/policy/simulate`
- Auth: API key in header `x-mgmt-key`

Retro-simulation: POST a partial candidate policy (dollars, same shape as the policy update) and a range up to 92 days (defaults to the last 7); every stored decision in the window re-runs through the pure ladders with the candidate overlaid on its recorded context, and the response reports what flips — totals by effect, approved-spend delta, and the changed decisions with before/after codes. Read + compute only: nothing is persisted, debited, or escalated. mode=as_recorded (SIM-1, default) holds budget counters as the engine saw them; mode=sequential (SIM-2) replays in chronological order threading each agent's approved spend forward, so an early would-denial frees budget for a later request. Owner-only.

## Request body

- object
  - `wallet_id` string, required
  - `from` string, date
  - `to` string, date
  - `mode` 'as_recorded' | 'sequential': as_recorded (default, SIM-1): each decision replays with its recorded budget state. sequential (SIM-2): replays in chronological order threading each agent's approved spend forward, so an early would-denial frees budget for a later request.
  - `policy` SimulatePolicyCandidate, required: Partial candidate policy, dollars — the policy-update field names, minus wallet_id. Fields the simulation cannot honor are echoed back as ignored_fields.
    - `daily_spend_budget_usd` number
    - `monthly_spend_budget_usd` number, nullable
    - `per_transaction_max_usd` number
    - `auto_approve_under_usd` number
    - `escalate_over_usd` number
    - `allowed_categories` string[]
    - `blocked_categories` string[]
    - `capability_rules` object[]: Ordered capability governance rules (CAP-1): namespaced prefix-glob patterns with block → allow-list → escalate precedence.
      - `pattern` string, required: e.g. skill:install:*, api:github.com/*
      - `effect` 'block' | 'allow' | 'escalate', required
    - `tool_conditions` object[]: Context-conditional tool rules (COND-1): a closed vocabulary, exactly one predicate per rule. The restriction is ACTIVE when the condition holds — outside_hours_utc: [start, end) UTC hours (wrapping midnight allowed), or after_model_calls_today: N persisted model calls by this agent today. Effects are restrictive only; allow-list membership is never conditional.
      - `pattern` string, required: Prefix-glob tool pattern, e.g. deploy.*, *
      - `effect` 'block' | 'escalate', required
      - `when` object, required: Exactly one of:
        - `outside_hours_utc` integer[]
        - `after_model_calls_today` integer

## Response `200`

Simulation report

- object
  - `wallet_id` string
  - `from` string, date
  - `to` string, date
  - `state` 'as_recorded' | 'sequential': Honesty envelope. as_recorded: counters held as the engine saw them, cascade not modeled. sequential: approved spend threaded forward in time (daily/monthly reset at UTC boundaries); subtree/pool caps not yet threaded.
  - `note` string
  - `applied_fields` string[]
  - `ignored_fields` string[]: Provided candidate fields the simulation cannot honor.
  - `totals` object
    - `was` SimulateEffectCounts
      - `allow` integer
      - `escalate` integer
      - `deny` integer
    - `would` SimulateEffectCounts
      - `allow` integer
      - `escalate` integer
      - `deny` integer
  - `approved_spend_usd` object
    - `was` number
    - `would` number
  - `counts` object
    - `considered` integer
    - `simulated` integer
    - `changed` integer
    - `out_of_scope` integer
    - `unreplayable` integer
  - `changes` object[]: Flipped decisions (first 100).
    - `id` string
    - `at` string, date-time
    - `agent` string
    - `ladder` 'spend' | 'capability'
    - `action` string
    - `merchant` string
    - `amount_usd` number
    - `final_status` string: What actually happened, incl. human approvals.
    - `was` SimulateOutcome
      - `effect` 'allow' | 'escalate' | 'deny'
      - `code` string, nullable
    - `would` SimulateOutcome
      - `effect` 'allow' | 'escalate' | 'deny'
      - `code` string, nullable
  - `truncated` boolean

## Other responses

- `400`: Invalid range, malformed body, or no simulatable fields provided
- `401`: Management key required

---

[API](https://skmtc.dev/getsanction/apis/sanction-agent-governance-api.md) · [All operations](https://skmtc.dev/getsanction/apis/sanction-agent-governance-api/llms.txt) · [OpenAPI document](https://skmtc.dev/getsanction/apis/sanction-agent-governance-api/revisions/c645a9e4b72d?raw)
