curl --request POST \
--url https://api.scrip.dev/v1/rules/{id}/simulate \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"diagnostics": true,
"event": {
"amount": 75,
"type": "purchase"
},
"event_id": "550e8400-e29b-41d4-a716-446655440004",
"event_timestamp": "2026-07-21T12:00:00Z",
"participant_id": "550e8400-e29b-41d4-a716-446655440000",
"participant_state": {
"attributes": {
"region": "US"
},
"counters": {
"purchase_count": 9
},
"tags": [
"vip"
]
}
}
'import requests
url = "https://api.scrip.dev/v1/rules/{id}/simulate"
payload = {
"diagnostics": True,
"event": {
"amount": 75,
"type": "purchase"
},
"event_id": "550e8400-e29b-41d4-a716-446655440004",
"event_timestamp": "2026-07-21T12:00:00Z",
"participant_id": "550e8400-e29b-41d4-a716-446655440000",
"participant_state": {
"attributes": { "region": "US" },
"counters": { "purchase_count": 9 },
"tags": ["vip"]
}
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
diagnostics: true,
event: {amount: 75, type: 'purchase'},
event_id: '550e8400-e29b-41d4-a716-446655440004',
event_timestamp: '2026-07-21T12:00:00Z',
participant_id: '550e8400-e29b-41d4-a716-446655440000',
participant_state: {attributes: {region: 'US'}, counters: {purchase_count: 9}, tags: ['vip']}
})
};
fetch('https://api.scrip.dev/v1/rules/{id}/simulate', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.scrip.dev/v1/rules/{id}/simulate",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'diagnostics' => true,
'event' => [
'amount' => 75,
'type' => 'purchase'
],
'event_id' => '550e8400-e29b-41d4-a716-446655440004',
'event_timestamp' => '2026-07-21T12:00:00Z',
'participant_id' => '550e8400-e29b-41d4-a716-446655440000',
'participant_state' => [
'attributes' => [
'region' => 'US'
],
'counters' => [
'purchase_count' => 9
],
'tags' => [
'vip'
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.scrip.dev/v1/rules/{id}/simulate"
payload := strings.NewReader("{\n \"diagnostics\": true,\n \"event\": {\n \"amount\": 75,\n \"type\": \"purchase\"\n },\n \"event_id\": \"550e8400-e29b-41d4-a716-446655440004\",\n \"event_timestamp\": \"2026-07-21T12:00:00Z\",\n \"participant_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"participant_state\": {\n \"attributes\": {\n \"region\": \"US\"\n },\n \"counters\": {\n \"purchase_count\": 9\n },\n \"tags\": [\n \"vip\"\n ]\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.scrip.dev/v1/rules/{id}/simulate")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"diagnostics\": true,\n \"event\": {\n \"amount\": 75,\n \"type\": \"purchase\"\n },\n \"event_id\": \"550e8400-e29b-41d4-a716-446655440004\",\n \"event_timestamp\": \"2026-07-21T12:00:00Z\",\n \"participant_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"participant_state\": {\n \"attributes\": {\n \"region\": \"US\"\n },\n \"counters\": {\n \"purchase_count\": 9\n },\n \"tags\": [\n \"vip\"\n ]\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.scrip.dev/v1/rules/{id}/simulate")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"diagnostics\": true,\n \"event\": {\n \"amount\": 75,\n \"type\": \"purchase\"\n },\n \"event_id\": \"550e8400-e29b-41d4-a716-446655440004\",\n \"event_timestamp\": \"2026-07-21T12:00:00Z\",\n \"participant_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"participant_state\": {\n \"attributes\": {\n \"region\": \"US\"\n },\n \"counters\": {\n \"purchase_count\": 9\n },\n \"tags\": [\n \"vip\"\n ]\n }\n}"
response = http.request(request)
puts response.read_body{
"evaluation": {
"diagnostics": {
"active_window": {
"active": true,
"active_from": "2023-11-07T05:31:56Z",
"active_to": "2023-11-07T05:31:56Z",
"event_timestamp": "2023-11-07T05:31:56Z",
"outcome": "<string>"
},
"cel": {
"clauses": [
{
"default": {
"applied": true,
"note": "<string>",
"redacted": true,
"source": "<string>",
"value": "<unknown>"
},
"depth": 123,
"error": {
"kind": "<string>",
"message": "<string>"
},
"expression": "<string>",
"id": 123,
"kind": "<string>",
"parent_id": 123,
"path": "<string>",
"redacted": true,
"status": "<string>",
"truncated": true,
"value": "<unknown>",
"value_type": "<string>"
}
],
"error": {
"kind": "<string>",
"message": "<string>"
},
"missing_paths": [
{
"handled_by": "<string>",
"kind": "<string>",
"path": "<string>",
"redacted": true,
"suggestion": "<string>"
}
],
"result": "<string>",
"short_circuits": [
{
"expression": "<string>",
"operator": "<string>",
"reason": "<string>",
"redacted": true,
"skipped_expression": "<string>"
}
],
"truncated": true,
"value": true
},
"final_result": "<string>",
"reachability": {
"reached": true,
"reason": "<string>",
"stopped_by_rule_id": "<string>"
},
"rule_snapshot": {
"rule_history_id": "<string>",
"source": "<string>"
},
"state_context": {
"participant": "<string>",
"program": "<string>"
}
},
"matched": true,
"reason": "no such key: amount",
"results": [
{
"action": {
"amount": "${{ event.amount * 10 }}",
"asset_id": "550e8400-e29b-41d4-a716-446655440002",
"type": "CREDIT"
},
"result": {
"amount": "750",
"asset_symbol": "POINTS",
"description": "Credit 750 POINTS to participant",
"to_bucket": "AVAILABLE"
}
}
],
"status": "evaluated"
},
"rule": {
"condition": "event.type == 'purchase' && event.amount > 0",
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Purchase Reward",
"order": 1,
"stop_after_match": false
},
"warnings": [
{
"client_ref": "<string>",
"code": "unknown_state_key",
"key": "purchaseCnt",
"kind": "counter",
"message": "unknown counter \"purchaseCnt\" — no rule in this program writes it; did you mean \"purchase_count\"?",
"rule_id": "<string>",
"rule_set_id": "<string>",
"scope": "participant",
"suggestion": "purchase_count"
}
]
}{
"code": "bad_request",
"details": {
"expected": "<string>",
"field": "<string>",
"fields": [
{
"expected": "<unknown>",
"field": "<string>",
"message": "This field is required",
"reason": "required",
"received": "<string>"
}
],
"reason": "invalid",
"received": "<string>"
},
"message": "Invalid request parameters"
}{
"code": "unauthorized",
"details": {
"expected": "<string>",
"field": "<string>",
"fields": [
{
"expected": "<unknown>",
"field": "<string>",
"message": "This field is required",
"reason": "required",
"received": "<string>"
}
],
"reason": "invalid",
"received": "<string>"
},
"message": "Missing or invalid credentials"
}{
"code": "forbidden",
"details": {
"expected": "<string>",
"field": "<string>",
"fields": [
{
"expected": "<unknown>",
"field": "<string>",
"message": "This field is required",
"reason": "required",
"received": "<string>"
}
],
"reason": "invalid",
"received": "<string>"
},
"message": "Insufficient permissions for this action"
}{
"code": "not_found",
"details": {
"expected": "<string>",
"field": "<string>",
"fields": [
{
"expected": "<unknown>",
"field": "<string>",
"message": "This field is required",
"reason": "required",
"received": "<string>"
}
],
"reason": "invalid",
"received": "<string>"
},
"message": "Resource not found"
}{
"code": "unsupported_media_type",
"details": {
"expected": "<string>",
"field": "<string>",
"fields": [
{
"expected": "<unknown>",
"field": "<string>",
"message": "This field is required",
"reason": "required",
"received": "<string>"
}
],
"reason": "invalid",
"received": "<string>"
},
"message": "Content-Type must be application/json"
}{
"code": "internal_error",
"message": "An internal error occurred"
}Simulate a rule
Dry-run a rule against sample event data. No ledger changes occur.
curl --request POST \
--url https://api.scrip.dev/v1/rules/{id}/simulate \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"diagnostics": true,
"event": {
"amount": 75,
"type": "purchase"
},
"event_id": "550e8400-e29b-41d4-a716-446655440004",
"event_timestamp": "2026-07-21T12:00:00Z",
"participant_id": "550e8400-e29b-41d4-a716-446655440000",
"participant_state": {
"attributes": {
"region": "US"
},
"counters": {
"purchase_count": 9
},
"tags": [
"vip"
]
}
}
'import requests
url = "https://api.scrip.dev/v1/rules/{id}/simulate"
payload = {
"diagnostics": True,
"event": {
"amount": 75,
"type": "purchase"
},
"event_id": "550e8400-e29b-41d4-a716-446655440004",
"event_timestamp": "2026-07-21T12:00:00Z",
"participant_id": "550e8400-e29b-41d4-a716-446655440000",
"participant_state": {
"attributes": { "region": "US" },
"counters": { "purchase_count": 9 },
"tags": ["vip"]
}
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
diagnostics: true,
event: {amount: 75, type: 'purchase'},
event_id: '550e8400-e29b-41d4-a716-446655440004',
event_timestamp: '2026-07-21T12:00:00Z',
participant_id: '550e8400-e29b-41d4-a716-446655440000',
participant_state: {attributes: {region: 'US'}, counters: {purchase_count: 9}, tags: ['vip']}
})
};
fetch('https://api.scrip.dev/v1/rules/{id}/simulate', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.scrip.dev/v1/rules/{id}/simulate",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'diagnostics' => true,
'event' => [
'amount' => 75,
'type' => 'purchase'
],
'event_id' => '550e8400-e29b-41d4-a716-446655440004',
'event_timestamp' => '2026-07-21T12:00:00Z',
'participant_id' => '550e8400-e29b-41d4-a716-446655440000',
'participant_state' => [
'attributes' => [
'region' => 'US'
],
'counters' => [
'purchase_count' => 9
],
'tags' => [
'vip'
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.scrip.dev/v1/rules/{id}/simulate"
payload := strings.NewReader("{\n \"diagnostics\": true,\n \"event\": {\n \"amount\": 75,\n \"type\": \"purchase\"\n },\n \"event_id\": \"550e8400-e29b-41d4-a716-446655440004\",\n \"event_timestamp\": \"2026-07-21T12:00:00Z\",\n \"participant_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"participant_state\": {\n \"attributes\": {\n \"region\": \"US\"\n },\n \"counters\": {\n \"purchase_count\": 9\n },\n \"tags\": [\n \"vip\"\n ]\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.scrip.dev/v1/rules/{id}/simulate")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"diagnostics\": true,\n \"event\": {\n \"amount\": 75,\n \"type\": \"purchase\"\n },\n \"event_id\": \"550e8400-e29b-41d4-a716-446655440004\",\n \"event_timestamp\": \"2026-07-21T12:00:00Z\",\n \"participant_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"participant_state\": {\n \"attributes\": {\n \"region\": \"US\"\n },\n \"counters\": {\n \"purchase_count\": 9\n },\n \"tags\": [\n \"vip\"\n ]\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.scrip.dev/v1/rules/{id}/simulate")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"diagnostics\": true,\n \"event\": {\n \"amount\": 75,\n \"type\": \"purchase\"\n },\n \"event_id\": \"550e8400-e29b-41d4-a716-446655440004\",\n \"event_timestamp\": \"2026-07-21T12:00:00Z\",\n \"participant_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"participant_state\": {\n \"attributes\": {\n \"region\": \"US\"\n },\n \"counters\": {\n \"purchase_count\": 9\n },\n \"tags\": [\n \"vip\"\n ]\n }\n}"
response = http.request(request)
puts response.read_body{
"evaluation": {
"diagnostics": {
"active_window": {
"active": true,
"active_from": "2023-11-07T05:31:56Z",
"active_to": "2023-11-07T05:31:56Z",
"event_timestamp": "2023-11-07T05:31:56Z",
"outcome": "<string>"
},
"cel": {
"clauses": [
{
"default": {
"applied": true,
"note": "<string>",
"redacted": true,
"source": "<string>",
"value": "<unknown>"
},
"depth": 123,
"error": {
"kind": "<string>",
"message": "<string>"
},
"expression": "<string>",
"id": 123,
"kind": "<string>",
"parent_id": 123,
"path": "<string>",
"redacted": true,
"status": "<string>",
"truncated": true,
"value": "<unknown>",
"value_type": "<string>"
}
],
"error": {
"kind": "<string>",
"message": "<string>"
},
"missing_paths": [
{
"handled_by": "<string>",
"kind": "<string>",
"path": "<string>",
"redacted": true,
"suggestion": "<string>"
}
],
"result": "<string>",
"short_circuits": [
{
"expression": "<string>",
"operator": "<string>",
"reason": "<string>",
"redacted": true,
"skipped_expression": "<string>"
}
],
"truncated": true,
"value": true
},
"final_result": "<string>",
"reachability": {
"reached": true,
"reason": "<string>",
"stopped_by_rule_id": "<string>"
},
"rule_snapshot": {
"rule_history_id": "<string>",
"source": "<string>"
},
"state_context": {
"participant": "<string>",
"program": "<string>"
}
},
"matched": true,
"reason": "no such key: amount",
"results": [
{
"action": {
"amount": "${{ event.amount * 10 }}",
"asset_id": "550e8400-e29b-41d4-a716-446655440002",
"type": "CREDIT"
},
"result": {
"amount": "750",
"asset_symbol": "POINTS",
"description": "Credit 750 POINTS to participant",
"to_bucket": "AVAILABLE"
}
}
],
"status": "evaluated"
},
"rule": {
"condition": "event.type == 'purchase' && event.amount > 0",
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Purchase Reward",
"order": 1,
"stop_after_match": false
},
"warnings": [
{
"client_ref": "<string>",
"code": "unknown_state_key",
"key": "purchaseCnt",
"kind": "counter",
"message": "unknown counter \"purchaseCnt\" — no rule in this program writes it; did you mean \"purchase_count\"?",
"rule_id": "<string>",
"rule_set_id": "<string>",
"scope": "participant",
"suggestion": "purchase_count"
}
]
}{
"code": "bad_request",
"details": {
"expected": "<string>",
"field": "<string>",
"fields": [
{
"expected": "<unknown>",
"field": "<string>",
"message": "This field is required",
"reason": "required",
"received": "<string>"
}
],
"reason": "invalid",
"received": "<string>"
},
"message": "Invalid request parameters"
}{
"code": "unauthorized",
"details": {
"expected": "<string>",
"field": "<string>",
"fields": [
{
"expected": "<unknown>",
"field": "<string>",
"message": "This field is required",
"reason": "required",
"received": "<string>"
}
],
"reason": "invalid",
"received": "<string>"
},
"message": "Missing or invalid credentials"
}{
"code": "forbidden",
"details": {
"expected": "<string>",
"field": "<string>",
"fields": [
{
"expected": "<unknown>",
"field": "<string>",
"message": "This field is required",
"reason": "required",
"received": "<string>"
}
],
"reason": "invalid",
"received": "<string>"
},
"message": "Insufficient permissions for this action"
}{
"code": "not_found",
"details": {
"expected": "<string>",
"field": "<string>",
"fields": [
{
"expected": "<unknown>",
"field": "<string>",
"message": "This field is required",
"reason": "required",
"received": "<string>"
}
],
"reason": "invalid",
"received": "<string>"
},
"message": "Resource not found"
}{
"code": "unsupported_media_type",
"details": {
"expected": "<string>",
"field": "<string>",
"fields": [
{
"expected": "<unknown>",
"field": "<string>",
"message": "This field is required",
"reason": "required",
"received": "<string>"
}
],
"reason": "invalid",
"received": "<string>"
},
"message": "Content-Type must be application/json"
}{
"code": "internal_error",
"message": "An internal error occurred"
}stop_after_match behavior. See Testing multiple rule sets for the full-program test boundary.
The event field is required and should mirror a real event payload. As with real ingestion, numeric values whose magnitude exceeds 2^53 are rejected with 400 amount_precision_exceeded; send very large values as strings.
Participant context comes from one of two mutually exclusive sources (supplying both returns a 400):
participant_id: loads a real participant’s current state and ledger balances, soparticipant.balance.<symbol>conditions evaluate against actual values. Returns404if the participant doesn’t exist. Still a dry run: nothing is locked or written.participant_state: caller-supplied mock state (tags,counters,attributes, optionallytiersandbalances). Counter values may be JSON numbers or decimal strings; both are evaluated numerically.
0, no tags, attributes, tiers, or balances), and the response carries a missing_participant_context warning if the condition reads participant state. Balance comparisons never match without balances.
The response pairs the rule’s metadata (rule) with the simulation outcome (evaluation): matched, status (evaluated, or condition_failed with a reason), and per-action results when matched. Result fields vary by action type; state projections (current_value, would_add, and similar) are included only when the action targets the event participant.
| Action type | Result fields |
|---|---|
CREDIT | amount, asset_symbol, to_bucket, reference_id, expires_at, matures_at, settles_reference, description |
DEBIT, FORFEIT | amount, asset_symbol, from_bucket, description |
HOLD, RELEASE | amount, asset_symbol, from_bucket, to_bucket, reference_id, description |
VOID_HOLD | amount (only when the action sets amount), asset_symbol, from_bucket, to_bucket, reference_id, description |
COUNTER | current_value, projected_value, value, description |
TAG | current_tags, would_add, description |
UNTAG | current_tags, would_remove, description |
SET_ATTRIBUTE | current_value, would_change, description |
SET_TIER | description |
SCHEDULE_EVENT | event_data, description |
BROADCAST | event_data, description |
target names a recipient also returns resolved_target, which shows who the action would reach: the resolved external_id or participant_id for a dynamic target, participant_id for a static participant id, program_id for the program, or group_id for a group. If the target can’t be resolved, the action’s error field says why.
Asset action results show where the value would go. from_bucket is the bucket the action takes value from, and to_bucket is the bucket it adds value to. Both buckets belong to the action’s target, except for VOID_HOLD. A VOID_HOLD returns the value to where it came from, so its to_bucket is AVAILABLE on the program wallet for a PREFUNDED asset, or on system issuance for an UNLIMITED asset.
For a partial VOID_HOLD, amount is the rewards the void would cancel, rounded to the asset’s scale. Use it to check the amount before the rule runs. Simulation does not read pending rewards, so amount is not capped at what the participant has pending. Without amount, the result has no amount field because the void cancels all pending rewards for the reference.
A CREDIT with a reference_id and no bucket, or "bucket": "AVAILABLE", is a settlement, so its result shows "settles_reference": true. The flag does not mean pending rewards exist for that reference. When the CREDIT runs, it settles the pending rewards for the reference. If there are none, it credits the full amount as new rewards. A settlement CREDIT lands in DEFERRED when its matures_at is in the future.
expires_at and matures_at are full timestamps, including fractional seconds. A duration such as 30d counts from the time of the request, so a live run of the rule computes a later timestamp.
Simulation uses balances from participant_id or participant_state to evaluate the condition and any action expression that reads them. The actions do not run against the ledger or look at lots, so a result does not show whether pending rewards exist or which lots an action would use. A rule configuration simulation shows that in lot_diffs.
SCHEDULE_EVENT and BROADCAST are not executed, but their event_data templates are resolved, so a bad template surfaces here before the rule ever fires on a live event. Amount constraints match production: an asset action whose amount resolves to zero, negative, or beyond the precision-safe range fails in its result (COUNTER deltas may still be negative).
To check CEL syntax without an existing rule, use the validate endpoint; to dry-run an unsaved rule, use simulate a draft rule.
Authorizations
API key passed in the X-API-Key header.
Path Parameters
Rule ID
Body
Sample event and optional participant state
Diagnostics opts into clause values, missing paths, short-circuit decisions, active-window and reachability outcomes. The normal response remains compact.
true
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.
{ "amount": 75, "type": "purchase" }
ID of a stored event to test. Uses its original data and timestamp, and the rule version recorded for that event when available.
"550e8400-e29b-41d4-a716-446655440004"
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.
"2026-07-21T12:00:00Z"
ParticipantID optionally identifies a real participant whose current state (tags, counters, attributes, tiers — the same fields production rule evaluation sees) is loaded as the simulation's participant context. Mutually exclusive with participant_state. The simulation remains a dry run: no state is read with locks and nothing is written.
"550e8400-e29b-41d4-a716-446655440000"
ParticipantState is optional simulated participant state (tags, counters, attributes). Conditions read it via the dot-access shorthand (participant.counter/tag/attribute., with safe defaults) or the get(participant.counters, ...) form, matching production evaluation. Counter values may be JSON numbers or decimal strings (the wire format returned by the participant state endpoints); both are evaluated numerically. Mutually exclusive with participant_id.
Show child attributes
Show child attributes
{ "attributes": { "region": "US" }, "counters": { "purchase_count": 9 }, "tags": ["vip"] }
Response
Simulation result
Evaluation contains the simulation results
Show child attributes
Show child attributes
Rule contains metadata about the rule that was simulated
Show child attributes
Show child attributes
Non-blocking advisories about the simulation context — e.g. missing_participant_context when the rule's condition reads participant state but the request supplied neither participant_id nor participant_state, so the condition evaluated against empty defaults.
Show child attributes
Show child attributes