---
title: "Simulate a workflow without persisting it"
method: POST
path: "/workflows:simulate"
tags: ["Workflows"]
---

# Simulate a workflow without persisting it

`POST /workflows:simulate`

Run a workflow definition end-to-end against the engine (Tenderly
simulation for chain-writing nodes) and return the full Execution.
Nothing is persisted.

## Request body

- SimulateWorkflowRequest
  - `chainId` integer — Numeric chain ID (e.g. 11155111 for Sepolia, 8453 for Base). On chain-aware trigger/node configs this is required and must be a configured chain; on query/filter params it is optional.
  - `trigger` union, required
    - object
      - `type` 'manual', required
      - `config` ManualTriggerConfig, required — User-initiated trigger; no chain context.
        - `data` unknown
        - `headers` object — HTTP headers (for webhook testing).
        - `pathParams` object — Path parameters (for webhook testing).
        - `lang` 'javascript' | 'json' | 'graphql' | 'handlebars', required — Language/format of an inline payload (e.g., custom code source, manual trigger data). Mirrors the proto `Lang` enum minus the `LANG_` prefix. Wire values are lowercase.
    - object
      - `type` 'fixedTime', required
      - `config` FixedTimeTriggerConfig, required — Fires at one or more absolute Unix-epoch milliseconds.
        - `epochs` integer[], required
    - object
      - `type` 'cron', required
      - `config` CronTriggerConfig, required — Fires on one or more cron schedules.
        - `schedules` string[], required
        - `timezone` string — IANA timezone (e.g., `UTC`, `America/New_York`). Default UTC.
    - object
      - `type` 'block', required
      - `config` BlockTriggerConfig, required — Fires every N blocks on the target chain.
        - `interval` integer, required — Fire every N blocks.
        - `chainId` integer, required — Numeric chain ID (e.g. 11155111 for Sepolia, 8453 for Base). On chain-aware trigger/node configs this is required and must be a configured chain; on query/filter params it is optional.
    - object
      - `type` 'event', required
      - `config` EventTriggerConfig, required — Fires when matching on-chain events are observed.
        - `queries` EventTriggerQuery[], required
          - `addresses` EthereumAddress[] — Contract addresses to filter events from. Empty matches any contract.
          - `topics` string[] — Topic filters (`topics[0]` is the event signature, `topics[1..]` are indexed parameter values). `null` means wildcard at that position.
          - `maxEventsPerBlock` integer — Safety ceiling per query per block. Exceeded → task cancelled.
          - `contractAbi` unknown[] — Contract ABI entries (JSON form) for event decoding.
            - unknown
          - `conditions` EventCondition[] — Filters applied to decoded event data.
            - `fieldName` string, required
            - `operator` 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' | 'contains', required
            - `value` string, required — Value to compare against, encoded as a string. The operator parses it according to `fieldType` (e.g. `int256` / `uint256` → big.Int, `address` → checksummed hex, `bool` → "true"/"false"). Matches the proto `EventCondition.value`, which is also a string.
            - `fieldType` string
          - `methodCalls` EventMethodCall[] — Method calls used to enrich decoded event data (e.g., `decimals`).
            - `methodName` string, required
            - `callData` string — Arbitrary-length hex-encoded byte string.
            - `applyToFields` string[]
            - `methodParams` string[]
        - `cooldownSeconds` integer — Seconds to wait after a fire before allowing the same task to trigger again. Default 300. 0 disables cooldown.
        - `chainId` integer, required — Numeric chain ID (e.g. 11155111 for Sepolia, 8453 for Base). On chain-aware trigger/node configs this is required and must be a configured chain; on query/filter params it is optional.
  - `nodes` Node[], required
    - union
      - object
        - `type` 'ethTransfer', required
        - `config` ETHTransferNodeConfig, required
          - `destination` string, required — Lowercase or checksummed hex EOA / contract address.
          - `amount` string, required — Amount in wei (decimal string for big-int safety). Special value `max` withdraws the entire balance.
          - `chainId` integer, required — Numeric chain ID (e.g. 11155111 for Sepolia, 8453 for Base). On chain-aware trigger/node configs this is required and must be a configured chain; on query/filter params it is optional.
      - object
        - `type` 'contractWrite', required
        - `config` ContractWriteNodeConfig, required
          - `contractAddress` string, required — Lowercase or checksummed hex EOA / contract address.
          - `callData` string — Arbitrary-length hex-encoded byte string.
          - `contractAbi` unknown[]
            - unknown
          - `methodCalls` MethodCall[]
            - `methodName` string, required
            - `callData` string — Arbitrary-length hex-encoded byte string.
            - `contractAddress` string — Lowercase or checksummed hex EOA / contract address.
            - `applyToFields` string[]
            - `methodParams` string[]
          - `isSimulated` boolean — When true, use Tenderly simulation instead of sending a real UserOp.
          - `value` string — ETH value to send with the call (wei, decimal string).
          - `gasLimit` string — Custom gas limit (decimal string).
          - `chainId` integer, required — Numeric chain ID (e.g. 11155111 for Sepolia, 8453 for Base). On chain-aware trigger/node configs this is required and must be a configured chain; on query/filter params it is optional.
      - object
        - `type` 'contractRead', required
        - `config` ContractReadNodeConfig, required
          - `contractAddress` string, required — Lowercase or checksummed hex EOA / contract address.
          - `contractAbi` unknown[]
            - unknown
          - `methodCalls` MethodCall[]
            - `methodName` string, required
            - `callData` string — Arbitrary-length hex-encoded byte string.
            - `contractAddress` string — Lowercase or checksummed hex EOA / contract address.
            - `applyToFields` string[]
            - `methodParams` string[]
          - `chainId` integer, required — Numeric chain ID (e.g. 11155111 for Sepolia, 8453 for Base). On chain-aware trigger/node configs this is required and must be a configured chain; on query/filter params it is optional.
      - object
        - `type` 'graphqlQuery', required
        - `config` GraphQLQueryNodeConfig, required
          - `url` string, uri, required
          - `query` string, required
          - `variables` object
      - object
        - `type` 'restApi', required
        - `config` RestAPINodeConfig, required
          - `url` string, uri, required
          - `method` 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS', required
          - `headers` object
          - `body` string
          - `options` object — Generic options bag for backend features on terminal RestAPI nodes. `summarize: true` opts a SendGrid /v3/mail/send or Telegram /sendMessage node into the aggregator's context-memory summarizer, which composes a subject + HTML body from the workflow's execution context and injects them into the outgoing request. Without this field set, the aggregator falls back to the deterministic summarizer (no LLM polish).
            - `summarize` boolean — When true on a terminal SendGrid or Telegram node, ComposeSummarySmart runs at execution time and fills in the empty content.value / text slot with an AI-generated body. No-op on non-notification URLs.
      - object
        - `type` 'customCode', required
        - `config` CustomCodeNodeConfig, required
          - `lang` 'javascript' | 'json' | 'graphql' | 'handlebars', required — Language/format of an inline payload (e.g., custom code source, manual trigger data). Mirrors the proto `Lang` enum minus the `LANG_` prefix. Wire values are lowercase.
          - `source` string, required
      - object
        - `type` 'branch', required
        - `config` BranchNodeConfig, required
          - `conditions` BranchCondition[], required
            - `id` string, required
            - `type` 'if' | 'elseIf' | 'else'
            - `expression` string, required — JavaScript-evaluated boolean expression.
      - object
        - `type` 'filter', required
        - `config` FilterNodeConfig, required
          - `inputVariable` string, required — Template path for the source array (e.g., `{{custom_code1.data}}`).
          - `expression` string, required — JavaScript predicate evaluated per item.
      - object
        - `type` 'loop', required
        - `config` LoopNodeConfig, required — Iterates over an input array, running an inner Node per item. The runner node is one of the chain-aware or chain-agnostic node types; a chain-aware runner must specify its own required `chainId` (there is no inheritance from the loop or workflow).
          - `inputVariable` string, required — Template path for the iterable (e.g., `{{settings.addressList}}`).
          - `iterVar` string — Name of the per-iteration variable (defaults to `value`).
          - `runner` Node, required — recursive
      - object
        - `type` 'balance', required
        - `config` BalanceNodeConfig, required
          - `address` string, required — Lowercase or checksummed hex EOA / contract address.
          - `chain` string, required — Chain name or numeric ID (e.g., `ethereum`, `base`, `1`, `8453`).
          - `includeSpam` boolean
          - `includeZeroBalances` boolean
          - `minUsdValueCents` integer — Filter out tokens with USD value below this many cents.
          - `tokenAddresses` EthereumAddress[] — Restrict to these tokens. Empty = fetch all.
      - object
        - `type` 'await', required
        - `config` AwaitNodeConfig, required — Pauses the workflow until a wake arrives (durable execution). Two mutually exclusive flavors: the external-signal flavor (human approval — set `channel`, e.g. a Telegram approve/reject), or the chain-event flavor (cross-chain — set `chainEvent` to pause until an operator observes that on-chain event, e.g. a bridge arrival on another chain). Exactly one flavor must be configured.
          - `channel` 'telegram' | 'api' — External-signal flavor — signal channel: `telegram` or `api`.
          - `approvers` string[] — External-signal flavor — authorized approver identities. Empty = the workflow owner. NOTE (v1): not yet enforced — the signal endpoint authorizes by workflow ownership only, so the owner can always approve regardless of this list. Delegated-approver enforcement (Telegram binding) is a follow-up; do not rely on this field for security yet.
          - `prompt` string — External-signal flavor — message shown to the approver.
          - `chainEvent` EventTriggerConfig — Fires when matching on-chain events are observed.
            - `queries` EventTriggerQuery[], required
              - …
            - `cooldownSeconds` integer — Seconds to wait after a fire before allowing the same task to trigger again. Default 300. 0 disables cooldown.
            - `chainId` integer, required — Numeric chain ID (e.g. 11155111 for Sepolia, 8453 for Base). On chain-aware trigger/node configs this is required and must be a configured chain; on query/filter params it is optional.
          - `timeoutSeconds` integer — Safety bound; 0 = server default (the wait is never unbounded).
  - `edges` Edge[]
    - `id` string, required
    - `source` string, required — Node or trigger ID where this edge starts.
    - `target` string, required — Node ID where this edge ends.
  - `inputVariables` InputVariables, required — Free-form key-value bag of values used to resolve `{{variable.path}}` template references inside trigger and node configs. Conventional well-known keys: `settings.runner` (smart wallet address), `settings.chainId` (chain id). camelCase keys; back-compat support for snake_case keys exists during the migration window.

## Response `200`

Simulated execution.

- object

## Other responses

- `400` — Request validation failed.
- `401` — Missing or invalid bearer token.

---

[API](https://skmtc.net/avaprotocol/apis/ava-protocol-avs-api.md) · [All operations](https://skmtc.net/avaprotocol/apis/ava-protocol-avs-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/avaprotocol/ava-protocol-avs-api/versions/231d563f2c9f/schema)
