---
title: "Prepare a Manual workflow for one owner-signed batch"
method: POST
path: "/workflows:prepare"
tags: ["Workflows"]
---

# Prepare a Manual workflow for one owner-signed batch

`POST /workflows:prepare`

## Request

- Base URL: `https://api.avaprotocol.org/api/v1`
- URL: `https://api.avaprotocol.org/api/v1/workflows:prepare`
- Auth: HTTP bearer

Resolve existing read/check nodes, capture the terminal ContractWrite
calls, and build/price one MA v2 batch without signing or broadcasting.
Requires the concrete owner's Bearer JWT and a registered supported
source wallet. Current on-chain owner, implementation and gas funding
are checked independently of the stored wallet association.
Derived wallets require the verified factory personal_sign build
0x000000000000c5A9089039570Dd36455b5C07383. An EOA source must already be
delegated to the pinned SMA-7702 1.0.0 implementation, registered as
kind=eoa_7702, and enabled by eoa_7702_execute on Sepolia or Base.
EOA operations use sender=owner and omit factory deployment fields.
Delegation is a separate explicit wallet action; prepare never installs
delegation or a session grant. Both account kinds use personal_sign of
the 32 raw userOpHash bytes, owner nonce entity 0/global option 1.
A canonical delegated EOA can also sign for its derived smart wallet.
Up to 32 calls; no native value, write Loop or post-write nodes.
Unsigned preparations are capped at 64 per owner. Submitted, uncertain,
rejected, and unsent records do not consume that cap. A gateway with
owner execution disabled returns 403 OWNER_EXECUTION_DISABLED;
preparing again does not enable it. Other precondition failures return
412 and ask for a fresh prepare. Ordinary Auto/session and simulate
behavior is unchanged.

## Request body

- PrepareWorkflowRequest: A Manual definition with read/check nodes and exactly one terminal ContractWrite. The initial owner path supports one configured chain, MA v2 derived wallets with an EOA fallback owner, and zero native value. No persistent/Auto workflow or session grant is created. Requires gateway owner_execution_enabled: true (default false).
  - `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.
  - `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.
          - `reserveGas` boolean: Owner preparation only. Treat amount as a captured balance cap and deduct the signed maximum gas cost; sponsored operations reserve zero. This node must be terminal.
          - `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[]
          - `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`

Priced operation awaiting an owner signature; no transaction has been submitted.

- PrepareWorkflowResponse
  - `execution` Execution, required
    - `id` string, required: ULID identifier (26-char Crockford base32).
    - `workflowId` string, required: ULID identifier (26-char Crockford base32).
    - `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.
    - `index` integer: 0-based run counter within the workflow.
    - `startAt` integer, required
    - `endAt` integer
    - `status` 'pending' | 'waiting' | 'success' | 'failed' | 'error', required: Outcome of an execution. `pending` is in-flight; `waiting` is suspended mid-workflow at an `await` node, durably parked until a signal arrives (a human approve/reject or an operator-observed chain event) or the wait times out — non-terminal, like `pending`, but distinguishable so a client can show "awaiting approval"; `success` is full success; `failed` is logical failure (e.g., a node returned an error, or a wait timed out); `error` is a system / infrastructure failure (e.g., RPC unreachable).
    - `error` string
    - `steps` ExecutionStep[]
      - `id` string, required
      - `type` string, required
      - `name` string
      - `success` boolean, required
      - `error` string
      - `errorCode` string
      - `errorDetails` object: Structured refusal fields. BUDGET_USED_UP has token/needed/remaining; BUDGET_ENDED has validUntil.
        - `token` string: Lowercase or checksummed hex EOA / contract address.
        - `needed` string
        - `remaining` string
        - `validUntil` integer
      - `log` string
      - `inputs` string[]
      - `config` unknown
      - `metadata` unknown
      - `executionContext` unknown
      - `output` unknown
      - `gasUsed` string: Gas units consumed (decimal string). Empty if unavailable.
      - `gasPrice` string: Gas price in wei per unit (decimal string).
      - `totalGasCost` string: gasUsed × gasPrice in wei (decimal string).
      - `startAt` integer
      - `endAt` integer
    - `executionFee` Fee
      - `amount` string, required: Decimal numeric value, encoded as a string for big-int safety.
      - `unit` 'USD' | 'WEI' | 'PERCENTAGE', required
    - `cogs` NodeCOGS[]: Per-node actual costs (gas, external API).
      - `nodeId` string, required
      - `costType` 'gas' | 'externalApi' | 'walletCreation', required
      - `fee` Fee, required
        - `amount` string, required: Decimal numeric value, encoded as a string for big-int safety.
        - `unit` 'USD' | 'WEI' | 'PERCENTAGE', required
      - `gasUnits` string: Gas units (for `gas` cost type only).
    - `valueFee` ValueFee
      - `fee` Fee, required
        - `amount` string, required: Decimal numeric value, encoded as a string for big-int safety.
        - `unit` 'USD' | 'WEI' | 'PERCENTAGE', required
      - `tier` 'unspecified' | 'tier1' | 'tier2' | 'tier3', required: Pricing group for value-capture fees.
      - `valueBase` string: What the percentage applies to (e.g., `input_token_value`).
      - `classificationMethod` 'ruleBased' | 'llm'
      - `confidence` number, float
      - `reason` string
    - `summary` ExecutionSummary: Studio's summary-step reply, present once the run has ended. Absent on older runs and while the run is still in progress.
      - `subject` string: Report title.
      - `body` object: Structured report. Its fields include the summary line, executions, errors, notice, and step counts.
    - `authorization` SessionAuthorization: Opt-in simulate verdict. Returned only when `authorizationMode` is `report`. It does not replace step errors from anything other than grant coverage.
      - `status` 'covered' | 'no_grant' | 'not_covered' | 'cap_too_low' | 'expires_too_soon' | 'cap_needs_input' | 'target_unresolved', required: `covered` — the usable grant already allows this workflow, or the workflow moves no funds. `no_grant` — fund-moving steps and no usable grant, including a grant made for a session signer the gateway no longer uses. `not_covered` — a planned call or native recipient is outside the grant. `cap_too_low` — a sized cap or native budget is short. `expires_too_soon` — the grant has already expired, or it ends before this workflow's window. A workflow with no end still reports this when the grant's own expiry has passed. `cap_needs_input` — a spend amount is not a fixed number, so the caller must choose the cap. `target_unresolved` — a fund-moving target could not be read from the stored workflow (a loop over a previous step, not a settings list). Choosing a spend cap does not name that target. One observed iteration does not clear this. Pause the automation, or name it in `dropTaskIds`.
      - `policyId` string: The runner's current usable grant, when there is one.
      - `missingActions` AllowedAction[]
        - `target` string, required: Lowercase or checksummed hex EOA / contract address.
        - `selectors` string[], required: 4-byte function selectors permitted on the target.
      - `missingTokens` EthereumAddress[]: Budget mode only. Spending tokens that need a new budget row.
      - `needsUntil` integer: Budget mode only. The recurring workflow end that exceeds the budget end, in Unix milliseconds.
      - `required` SessionPolicyNeed: Permissions one workflow still needs. Workflow mode sizes the remaining runs. Budget mode reports one run and never sizes the wallet budget from this need. Cap amounts are in the token's smallest unit.
        - `allowedActions` AllowedAction[]
          - `target` string, required: Lowercase or checksummed hex EOA / contract address.
          - `selectors` string[], required: 4-byte function selectors permitted on the target.
        - `erc20SpendCaps` Erc20SpendCap[]
          - `token` string, required: Lowercase or checksummed hex EOA / contract address.
          - `amount` string, required: Total cap in the token's smallest unit (decimal string, no reset).
        - `nativeRecipients` EthereumAddress[]
        - `nativeSpendCap` NativeSpendCap
          - `amount` string, required: Cumulative native-token cap in wei (decimal string, no reset). Enforced on-chain by NativeTokenLimitModule. Self-funded UserOps also decrement this cap by gas; sponsored UserOps decrement only the ETH value sent. When the chain does not sponsor gas, prepare sizes headroom for those UserOps into this amount: the steady-op ceiling times a per-chain fee assumption, plus one install. That headroom can also be sent as value to the allowed recipients.
        - `nativeGasWei` string: Wei of `nativeSpendCap` that is self-funded gas rather than ETH this workflow sends. Steady UserOps only. Prepare adds one install for the wallet when the grant is not yet applied. Omitted when this workflow's gas was not priced.
        - `validUntil` integer: Absolute unix milliseconds the grant must last through. 0 when the window is unknown.
      - `detail` string
  - `userOperation` OwnerUserOperationV07, required: Finalized ERC-4337 v0.7 bundler-wire operation. Quantity fields are hex strings, never JSON numbers. Signature is empty (0x) until continuation; no operation field can be supplied or changed by submitExecution.
    - `sender` string, required: Lowercase or checksummed hex EOA / contract address.
    - `nonce` string, required: Arbitrary-length hex-encoded byte string.
    - `callData` string, required: Arbitrary-length hex-encoded byte string.
    - `callGasLimit` string, required: Arbitrary-length hex-encoded byte string.
    - `verificationGasLimit` string, required: Arbitrary-length hex-encoded byte string.
    - `preVerificationGas` string, required: Arbitrary-length hex-encoded byte string.
    - `maxFeePerGas` string, required: Arbitrary-length hex-encoded byte string.
    - `maxPriorityFeePerGas` string, required: Arbitrary-length hex-encoded byte string.
    - `factory` string: Lowercase or checksummed hex EOA / contract address.
    - `factoryData` string: Arbitrary-length hex-encoded byte string.
    - `paymaster` string: Lowercase or checksummed hex EOA / contract address.
    - `paymasterData` string: Arbitrary-length hex-encoded byte string.
    - `paymasterVerificationGasLimit` string: Arbitrary-length hex-encoded byte string.
    - `paymasterPostOpGasLimit` string: Arbitrary-length hex-encoded byte string.
    - `signature` string, required: Arbitrary-length hex-encoded byte string.
  - `userOpHash` string, required: 32-byte hex hash (UserOp hash, transaction hash).
  - `owner` string, required: Lowercase or checksummed hex EOA / contract address.
  - `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.
  - `entryPoint` string, required: Lowercase or checksummed hex EOA / contract address.
  - `signingMethod` 'personal_sign', required: Sign the 32 raw bytes of userOpHash, with the EIP-191 prefix applied once; ethers signMessage(getBytes(userOpHash)). Offered only for the verified alchemy.sma-bytecode.1.0.0 implementation.
  - `expiresAt` integer, required: Gateway continuation deadline, Unix milliseconds (10 minutes). This is not an on-chain signature expiry or revocation mechanism.
  - `writeNodeId` string, required
  - `calls` UserOpInnerCall[], required
    - `to` string, required: Lowercase or checksummed hex EOA / contract address.
    - `value` string, required: Native value in wei (decimal string).
    - `selector` string, required: 4-byte function selector of `data` (`0x00000000` for a value-only call).
    - `data` string, required: Arbitrary-length hex-encoded byte string.
  - `gasFunding` 'source' | 'sponsored', required: Final funding choice; sponsorship refusal never silently charges the source.
  - `maxGasCostWei` string, required: Signed maximum gas cost in decimal wei, before any paymaster sponsorship.

## Other responses

- `400`: Request validation failed.
- `401`: Missing or invalid bearer token.
- `403`: Authenticated but not permitted.
- `412`: Checks, source compatibility, funding or pricing failed; refresh the preparation.
- `429`: Rate limit exceeded. Inspect `X-RateLimit-Reset` for the next allowed request time and `Retry-After` for the seconds to wait.

## Changes

- **2026-11-04** `6b505ebcf3f7`: 24 info
  - added the new optional request property `nodes/items/oneOf[#/components/schemas/ETHTransferNode]/allOf[subschema #1]/config/reserveGas`
  - added the optional property `availableWei` to the response with the `400` status
  - added the optional property `availableWei` to the response with the `401` status
  - added the optional property `availableWei` to the response with the `403` status
  - …20 more
- **2026-10-28** `4ebf07931311`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/avaprotocol/apis/ava-protocol-avs-api/changes/workflows:prepare/post.md)

---

[API](https://skmtc.dev/avaprotocol/apis/ava-protocol-avs-api.md) - [All operations](https://skmtc.dev/avaprotocol/apis/ava-protocol-avs-api/llms.txt) - [OpenAPI document](https://skmtc.dev/avaprotocol/apis/ava-protocol-avs-api/revisions/6b505ebcf3f7?raw)
