---
title: "Simulate a rule"
method: POST
path: "/v1/rules/{rule_id}/simulate"
tags: ["Rules"]
---

# Simulate a rule

`POST /v1/rules/{rule_id}/simulate`

## Request

- Base URL: `https://api.scrip.dev`
- URL: `https://api.scrip.dev/v1/rules/{rule_id}/simulate`
- Auth: HTTP bearer

Dry-run a rule against sample event data. No ledger changes occur.

## Path parameters

- `rule_id` string, uuid, required

## Request body

- SimulateRuleRequest: Simulates a saved rule against an event and a participant state. Nothing is saved.
  - `diagnostics` boolean: Diagnostics opts into clause values, missing paths, short-circuit decisions, active-window and reachability outcomes. The normal response remains compact.
  - `event` object: Sample event data to evaluate against the rule. Numbers with a magnitude greater than 2^53 are rejected to prevent loss of precision. Supply exactly one of `event` or `event_id`.
  - `event_id` string, uuid: ID of a stored event to test. Uses its original data and timestamp, and the rule version recorded for that event when available.
  - `event_timestamp` string, date-time: When the sample event occurred. Used to check whether it falls within the rule's active window. Cannot be used with `event_id`, which uses the stored timestamp.
  - `participant_id` string, uuid: A real participant whose current tags, counters, attributes and tiers Scrip loads as the simulation's participant context, the same fields a live rule sees. Send this or `participant_state`, not both. The simulation reads state and writes nothing.
  - `participant_state` object: Simulated participant state (tags, counters, attributes). Conditions read it as `participant.counter.<name>`, `participant.tag.<name>` and `participant.attribute.<name>`, which fall back to safe defaults, or with `get(participant.counters, ...)`. Counter values can be JSON numbers or decimal strings, and Scrip evaluates both as numbers. Send this or `participant_id`, not both.

## Response `200`

Simulation result

- SimulateRuleResponse: The result of a rule simulation: the rule, the evaluation and any warnings.
  - `evaluation` SimulateEvaluation: The outcome of a simulated rule: whether it matched and the actions it would take.
    - `diagnostics` SimulateConditionDiagnostics: Why a simulated rule matched or did not: the rule's active window, its condition and whether it was reached.
      - `active_window` SimulateDiagnosticActiveWindow: Whether the event fell inside the rule's active dates.
        - `active` boolean: Whether the event time fell inside the rule's active dates.
        - `active_from` string, date-time: The earliest event time the rule applies to.
        - `active_to` string, date-time: The event time the rule stops applying.
        - `event_timestamp` string, date-time: The event time Scrip checked against the dates.
        - `outcome` string: How the active dates affected the result.
      - `cel` ConditionDiagnostic: An explanation of how one rule condition evaluated, clause by clause. Scrip returns it only when you ask for diagnostics and does not store it.
        - `clauses` ConditionDiagnosticClause[]: The condition split into clauses, each listed before its children.
          - `default` ConditionDiagnosticDefault: A default value that Scrip applied to a missing event field while it evaluated a clause.
            - `applied` boolean: Whether Scrip used the default.
            - `note` string: A short explanation of the default.
            - `redacted` boolean: Whether Scrip hid the value because it can contain customer data.
            - `source` string: Where the default came from.
            - `value` unknown
          - `depth` integer: How many clauses deep this clause is. The top clause is 0.
          - `error` ConditionDiagnosticError: An error raised while Scrip evaluated a condition.
            - `kind` string: The kind of error.
            - `message` string: What went wrong.
          - `expression` string: The text of the clause.
          - `id` integer: The number of the clause in this list.
          - `kind` string: What kind of clause this is.
          - `parent_id` integer: The number of the clause that contains this one. The top clause has none.
          - `path` string: The field the clause reads, when it reads one.
          - `redacted` boolean: Whether Scrip hid the value because it can contain customer data.
          - `status` 'EVALUATED' | 'ABSENT' | 'ERROR' | 'SHORT_CIRCUITED' | 'NOT_EVALUATED': How the clause evaluated.
          - `truncated` boolean: Whether Scrip shortened the expression text.
          - `value` unknown
          - `value_type` string: The CEL type of the value, such as `bool` or `null`.
        - `error` ConditionDiagnosticError: An error raised while Scrip evaluated a condition.
          - `kind` string: The kind of error.
          - `message` string: What went wrong.
        - `missing_paths` ConditionDiagnosticMissingPath[]: The fields the condition read that were not there.
          - `handled_by` string: What kept the missing field from failing the condition, such as a default or a check for the field.
          - `kind` string: Whether the field belongs to the event, the participant or the program.
          - `path` string: The field that was missing.
          - `redacted` boolean: Whether Scrip hid details because they can contain customer data.
          - `suggestion` string: How to fix the condition.
        - `result` string: What the condition produced: a boolean, an error, or no value.
        - `short_circuits` ConditionDiagnosticShortCircuit[]: The clauses Scrip skipped because an earlier clause already decided the result.
          - `expression` string: The text of the clause that decided the result.
          - `operator` string: The operator that decided the result early, `&&` or `||`.
          - `reason` string: Why Scrip skipped the clause.
          - `redacted` boolean: Whether Scrip hid details because they can contain customer data.
          - `skipped_expression` string: The text of the clause Scrip did not evaluate.
        - `truncated` boolean: Whether Scrip left out clauses because the condition is large.
        - `value` boolean: The boolean the condition produced. It is absent when the condition raised an error.
      - `final_result` string: The result of the rule: it matched, did not match, or was not evaluated.
      - `reachability` SimulateDiagnosticReachability: Whether an earlier rule stopped evaluation before this rule ran.
        - `reached` boolean: Whether Scrip got to this rule.
        - `reason` string: Why Scrip did or did not get to the rule.
        - `stopped_by_rule_id` string: The earlier rule that stopped evaluation, when one did.
      - `rule_snapshot` SimulateDiagnosticRuleSnapshot: Which version of the rule the simulation used.
        - `rule_history_id` string: The id of the earlier version of the rule, when the simulation used one.
        - `source` string: Whether the simulation used the saved rule, a draft or an earlier version.
      - `state_context` SimulateDiagnosticStateContext: Which participant and program state the simulation read. A replay of an old event reads the current state, not the state at the time.
        - `participant` string: Which participant state the simulation read.
        - `program` string: Which program state the simulation read.
    - `matched` boolean: Whether the condition evaluated to true
    - `reason` string: Error detail when `status` is CONDITION_FAILED
    - `results` object[]: What each action would do, including calculated amounts, counter values, and asset symbols. Present only when matched is true. An explicit target adds resolved_target with external_id, participant_id, program_id, or group_id. Static participant IDs use participant_id. Omitted for the default event participant. Asset actions report from_bucket and/or to_bucket. CREDIT adds value; DEBIT and FORFEIT remove it. HOLD moves AVAILABLE to HELD; RELEASE moves HELD to AVAILABLE. VOID_PENDING returns HELD value to AVAILABLE at its source: the program wallet for PREFUNDED assets, or system issuance for UNLIMITED assets. CREDIT, HOLD, RELEASE, and VOID_PENDING also report reference_id when set. CREDIT reports expires_at and matures_at as full RFC 3339 timestamps, keeping fractional seconds. Durations count from the request time. A CREDIT to AVAILABLE with future matures_at uses DEFERRED. For CREDIT, settles_reference: true means a reference_id was supplied with AVAILABLE (the default). Execution settles pending rewards for that reference, or issues a new credit if none exist. Simulation does not read lots or change the ledger, so it cannot confirm pending rewards or select lots. See https://docs.scrip.dev/api-reference/rules/simulate-a-rule for the result fields by action.
    - `status` 'EVALUATED' | 'CONDITION_FAILED': Whether the condition ran: EVALUATED when it produced a result, CONDITION_FAILED when the CEL expression errored.
  - `rule` SimulateRuleInfo: The rule that a simulation tested.
    - `condition` string: CEL expression that was evaluated
    - `id` string, uuid: Unique identifier for the rule
    - `name` string: The name people see for the rule that was simulated.
    - `order` integer: Evaluation sequence position
    - `stop_after_match` boolean: Whether this rule stops subsequent evaluation in its containing set on match; other sets continue
  - `warnings` RuleWarning[]: Advisories about the simulation context. For example, `missing_participant_context` means the condition reads participant state but the request sent neither `participant_id` nor `participant_state`, so the condition ran against empty defaults.
    - `client_ref` string: The `client_ref` of the rule, when the rule is new.
    - `code` string: What the warning is about. On a rule: `unknown_state_key` (a key no rule writes), `deprecated_alias`, or `possible_unmarked_expression` (a literal that looks like CEL without `${{ }}`). On a simulation: `missing_participant_context`. On a configuration preview, also `stop_after_match_has_no_following_rule`, `unconditional_stop_shadows_following_rules`, or `later_rule_set_retained_empty`.
    - `key` string: The referenced key, as written in the condition.
    - `kind` string: Kind of reference: `counter`, `tag`, `attribute`, or `alias`.
    - `message` string: Human-readable explanation (includes the suggestion when one exists).
    - `rule_id` string: Rule identity is populated for complete-configuration validation. The single-rule authoring surfaces omit these fields.
    - `rule_set_id` string: The id of the rule set that holds the rule.
    - `scope` string: Where the referenced state belongs: `participant`, `program`, or `group`.
    - `suggestion` string: Closest known key, when a near-match exists. Empty otherwise.

## Other responses

- `400`: Invalid request (code: validation_error | invalid_json)
- `401`: The API key is missing or not valid. See [Authentication](https://docs.scrip.dev/authentication).
- `403`: The caller is not allowed to do this.
- `404`: The rule, the participant_id participant, or the event_id event was not found (code: rule_not_found | participant_not_found | event_not_found)
- `429`: Too many requests. Wait the number of seconds in the `Retry-After` header, then retry. See [Limits and durations](https://docs.scrip.dev/limits#rate-limits).
- `500`: Something failed on Scrip's side. Retry the request, and quote `request_id` if it keeps failing. [Errors](https://docs.scrip.dev/errors) lists every code, and the [API introduction](https://docs.scrip.dev/api-reference/introduction) describes the error format.

---

[API](https://skmtc.dev/scrip/apis/scrip-api.md) · [All operations](https://skmtc.dev/scrip/apis/scrip-api/llms.txt) · [OpenAPI document](https://skmtc.dev/scrip/apis/scrip-api/revisions/c52d35da8d97?raw)
