POST /v1/workflows/analyze-funds
Analyze the fund requirements of an inline workflow definition without saving or executing it. Same response shape as the workflow-scoped variant, with `workflowId` empty. Read-only — does not consume CU.
POST
/v1/workflows/analyze-funds
Analyze the fund requirements of an inline workflow definition without saving or executing it. Same response shape as the workflow-scoped variant, with `workflowId` empty. Read-only — does not consume CU.
Request Body required
Inline workflow definition
application/jsonOne of:
Option 1
Option 2
defaultWalletAddress
string
DefaultWalletAddress overrides the wallet used when a node has no
explicit connector or payload wallet field. When empty, the org's
default wallet is used (same fallback as the saved-workflow path).
definition
object
REQUIRED
Definition is the inline workflow graph to analyze. Same shape as a
saved workflow's definition. Unlike the ephemeral-run endpoint there
is no trigger restriction — analysis is read-only and method-agnostic.
blockExpansions
object
Set on run snapshots only (not workflow DB)
inputSchema
object[]
Array of:
description
string
key
string
required
boolean
type
string
"string", "number", "boolean", "object", "array"
nodes
object
REQUIRED
sensitivePropKeys
string[]
Legacy: kept for old runs; no longer populated for new workflows
Array of:
triggerNodeIds
string[]
TriggerNodeIDs lists the node IDs that are trigger (root) nodes. Every
workflow declares this — single-trigger workflows ship ["root"] (the
legacy node id), multi-trigger workflows list every trigger node id.
Treating single-trigger as a forest-of-1 removes the two-path branching
throughout the BE + FE; older rows without the field are backfilled by
migration 000282 and the field-missing path stays as a read-side safety
net (see FindTriggerNodeIDs) but is no longer exercised by saves.
"root" is also a runtime alias for "the trigger that fired this run" —
{{root.X}} variable references resolve to the firing trigger regardless
of which trigger fired. Don't repurpose the literal "root" as a trigger
id on a multi-trigger workflow.
Array of:
variableDefs
object[]
VariableDefs is a snapshot of the workflow's declared variables at run
creation time. The canonical source lives on the workflows row
(Workflow.VariableDefs column). Snapshotted into the run definition
so the worker can resolve {{$vars.x}} lookups and route variable-action
writes to the right scope without an extra DB round trip.
Array of:
default
unknown
description
string
lifetime
string
Enum:
persist, resetname
string
type
string
Enum:
number, text, boolean, list, objectResponses
200
OK
application/jsoncode
integer
data
object
balancesFetched
boolean
requirements
object[]
Array of:
amountIsHumanReadable
boolean
amountIsPartial
boolean
AmountIsPartial means RequiredAmount understates the true total for a reason
OTHER than a runtime expression: an operand was dropped, or the amount is
per-iteration inside a loop. Floor semantics apply (the number is a lower
bound) but NO claim about a later step computing the remainder is true.
amountRuntimeDetermined
boolean
AmountRuntimeDetermined is true when the leg's amount is wired to a runtime
expression ({{...}}) rather than a literal, and could not be constant-folded.
The leg's IDENTITY (chain + token) is fully resolved — only the SIZE is unknown
until the workflow runs. Distinct from Unresolved, which means the identity
itself could not be determined.
When multiple legs sharing the same dedup key (wallet + chain + token)
are merged (addOrMerge), this flag is true if ANY contributing leg was
runtime-determined — even when another contributing leg supplied a
literal amount. It is never cleared just because a literal amount is
also present.
FLOOR SEMANTICS: when this is true AND RequiredAmount is non-empty, the
two fields are BOTH meaningful together: RequiredAmount is the sum of
only the literal contributing legs — a known LOWER BOUND on the true
requirement, not the total (the runtime-determined leg's own
contribution is never invented). Treat RequiredAmount as "at least X"
in this state. isInsufficient MAY still be computed against this floor
and MAY be true — a balance below the floor is a genuine shortfall. A
balance at or above the floor is UNKNOWN sufficiency, not proven
sufficient.
amountUnit
string
AmountUnit declares the unit of the amount when the analyzer KNOWS it,
rather than inferring it from the string's shape.
"raw" — smallest units (wei-like). ONLY the constant-fold sets this: it
computes the integer itself, so it is the one producer that knows.
"" — unknown. Consumers MUST print the value verbatim and MUST NOT
rescale it by decimals.
Deliberately has no "human" value yet. Action payloads don't carry unit
metadata, so claiming "human" would be the same guess in a new coat.
balance
object
amount
string
raw units (wei)
amountIsPartial
boolean
AmountIsPartial mirrors funds.Requirement.AmountIsPartial: true when
RequiredAmount understates the true total for a reason OTHER than a
runtime expression — a dropped operand on a unit mismatch, or a
per-iteration amount inside a loop. Same FLOOR semantics as
AmountRuntimeDetermined (the amount is a known lower bound, and
IsInsufficient=true against it is a genuine shortfall), but unlike that
flag there is no runtime expression that a later step resolves — no
claim is made about anything computing the remainder.
amountRuntimeDetermined
boolean
AmountRuntimeDetermined mirrors funds.Requirement.AmountRuntimeDetermined:
true when at least one contributing leg's amount is wired to a runtime
expression and could not be resolved statically.
When this is true and the requirement has no known amount at all,
IsInsufficient stays false (nothing to compare against — "unknown", not
"known and sufficient").
When this is true AND a RequiredAmount IS present (merged with a
literal-amount leg), that amount is only a known FLOOR — the literal
legs' sum, not the true total — and IsInsufficient IS computed against
it. IsInsufficient=true there is a genuine shortfall. But
IsInsufficient=false is still NOT proof of sufficiency: it only means
the balance clears the known floor, not the (partially unknown) total.
Consumers must key off AmountRuntimeDetermined AND AmountIsPartial, not
IsInsufficient alone, to know whether "sufficient" can be asserted —
both flags carry the identical FLOOR semantics described above.
amountUsd
number
chainId
integer
decimals
integer
isInsufficient
boolean
amount < requiredAmount
isLow
boolean
gas only: balance below LowGasThreshold
symbol
string
tokenAddress
string
walletAddress
string
chainId
integer
chainName
string
decimals
integer
isNative
boolean
nodeId
string
nodeName
string
nodeType
string
reason
string
receivedDisplay
object
ReceivedDisplay is DISPLAY-ONLY context describing what the SAME node
receives, attached only when this sent leg has no RequiredAmount of its
own. See the ReceivedLegDisplay doc for the boundary it must not cross.
amount
string
Amount is the received leg's resolved amount: a literal from the
payload, or the exact constant-fold of an upstream literal-only
convert-to-wei node. Never estimated.
amountUnit
string
AmountUnit declares the unit of the amount when the analyzer KNOWS it,
rather than inferring it from the string's shape.
"raw" — smallest units (wei-like). ONLY the constant-fold sets this: it
computes the integer itself, so it is the one producer that knows.
"" — unknown. Consumers MUST print the value verbatim and MUST NOT
rescale it by decimals.
Deliberately has no "human" value yet. Action payloads don't carry unit
metadata, so claiming "human" would be the same guess in a new coat.
chainId
integer
chainName
string
decimals
integer
isHumanReadable
boolean
IsHumanReadable mirrors Requirement.AmountIsHumanReadable for Amount:
true for values like "0.5", false for raw smallest-unit integers.
symbol
string
requiredAmount
string
source
string
Enum:
requiredTokens, fundsMovement.sent, fundsMovement.received, requiresGassymbol
string
tokenAddress
string
unresolved
boolean
unresolvedReason
string
Enum:
chainId, tokenAddress, parseError, templatewalletAddress
string
unresolved
object[]
Array of:
amountIsHumanReadable
boolean
amountIsPartial
boolean
AmountIsPartial means RequiredAmount understates the true total for a reason
OTHER than a runtime expression: an operand was dropped, or the amount is
per-iteration inside a loop. Floor semantics apply (the number is a lower
bound) but NO claim about a later step computing the remainder is true.
amountRuntimeDetermined
boolean
AmountRuntimeDetermined is true when the leg's amount is wired to a runtime
expression ({{...}}) rather than a literal, and could not be constant-folded.
The leg's IDENTITY (chain + token) is fully resolved — only the SIZE is unknown
until the workflow runs. Distinct from Unresolved, which means the identity
itself could not be determined.
When multiple legs sharing the same dedup key (wallet + chain + token)
are merged (addOrMerge), this flag is true if ANY contributing leg was
runtime-determined — even when another contributing leg supplied a
literal amount. It is never cleared just because a literal amount is
also present.
FLOOR SEMANTICS: when this is true AND RequiredAmount is non-empty, the
two fields are BOTH meaningful together: RequiredAmount is the sum of
only the literal contributing legs — a known LOWER BOUND on the true
requirement, not the total (the runtime-determined leg's own
contribution is never invented). Treat RequiredAmount as "at least X"
in this state. isInsufficient MAY still be computed against this floor
and MAY be true — a balance below the floor is a genuine shortfall. A
balance at or above the floor is UNKNOWN sufficiency, not proven
sufficient.
amountUnit
string
AmountUnit declares the unit of the amount when the analyzer KNOWS it,
rather than inferring it from the string's shape.
"raw" — smallest units (wei-like). ONLY the constant-fold sets this: it
computes the integer itself, so it is the one producer that knows.
"" — unknown. Consumers MUST print the value verbatim and MUST NOT
rescale it by decimals.
Deliberately has no "human" value yet. Action payloads don't carry unit
metadata, so claiming "human" would be the same guess in a new coat.
chainId
integer
chainName
string
decimals
integer
isNative
boolean
nodeId
string
nodeName
string
nodeType
string
reason
string
receivedDisplay
object
ReceivedDisplay is DISPLAY-ONLY context describing what the SAME node
receives, attached only when this sent leg has no RequiredAmount of its
own. See the ReceivedLegDisplay doc for the boundary it must not cross.
amount
string
Amount is the received leg's resolved amount: a literal from the
payload, or the exact constant-fold of an upstream literal-only
convert-to-wei node. Never estimated.
amountUnit
string
AmountUnit declares the unit of the amount when the analyzer KNOWS it,
rather than inferring it from the string's shape.
"raw" — smallest units (wei-like). ONLY the constant-fold sets this: it
computes the integer itself, so it is the one producer that knows.
"" — unknown. Consumers MUST print the value verbatim and MUST NOT
rescale it by decimals.
Deliberately has no "human" value yet. Action payloads don't carry unit
metadata, so claiming "human" would be the same guess in a new coat.
chainId
integer
chainName
string
decimals
integer
isHumanReadable
boolean
IsHumanReadable mirrors Requirement.AmountIsHumanReadable for Amount:
true for values like "0.5", false for raw smallest-unit integers.
symbol
string
requiredAmount
string
source
string
Enum:
requiredTokens, fundsMovement.sent, fundsMovement.received, requiresGassymbol
string
tokenAddress
string
unresolved
boolean
unresolvedReason
string
Enum:
chainId, tokenAddress, parseError, templatewalletAddress
string
workflowId
string
workflowVersion
integer
message
string
requestId
string
400
Invalid definition
403
Missing org context
curl -X POST 'https://api.example.com/v1/workflows/analyze-funds' \ -H 'Authorization: Bearer YOUR_API_TOKEN' \ -H 'Content-Type: application/json' \ -d '{}'
const response = await fetch('https://api.example.com/v1/workflows/analyze-funds', { method: 'POST', headers: { "Authorization": "Bearer YOUR_API_TOKEN", "Content-Type": "application/json" }, body: JSON.stringify({})});const data = await response.json();console.log(data);
import requestsheaders = { 'Authorization': 'Bearer YOUR_API_TOKEN'}response = requests.post('https://api.example.com/v1/workflows/analyze-funds', headers=headers, json={})print(response.json())
package mainimport ( "fmt" "io" "net/http" "strings")func main() { body := strings.NewReader(`{}`) req, _ := http.NewRequest("POST", "https://api.example.com/v1/workflows/analyze-funds", body) req.Header.Set("Authorization", "Bearer YOUR_API_TOKEN") req.Header.Set("Content-Type", "application/json") resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() result, _ := io.ReadAll(resp.Body) fmt.Println(string(result))}
200
Response
{ "code": 200, "data": { "balancesFetched": true, "requirements": [ { "amountIsHumanReadable": true, "amountIsPartial": true, "amountRuntimeDetermined": true, "amountUnit": "<string>", "balance": { "amount": "<string>", "amountIsPartial": true, "amountRuntimeDetermined": true, "amountUsd": 123, "chainId": 123, "decimals": 123, "isInsufficient": true, "isLow": true, "symbol": "<string>", "tokenAddress": "<string>", "walletAddress": "<string>" }, "chainId": 123, "chainName": "<string>", "decimals": 123, "isNative": true, "nodeId": "<string>", "nodeName": "<string>", "nodeType": "<string>", "reason": "<string>", "receivedDisplay": { "amount": "<string>", "amountUnit": "<string>", "chainId": 123, "chainName": "<string>", "decimals": 123, "isHumanReadable": true, "symbol": "<string>" }, "requiredAmount": "<string>", "source": "requiredTokens", "symbol": "<string>", "tokenAddress": "<string>", "unresolved": true, "unresolvedReason": "chainId", "walletAddress": "<string>" } ], "unresolved": [ { "amountIsHumanReadable": true, "amountIsPartial": true, "amountRuntimeDetermined": true, "amountUnit": "<string>", "chainId": 123, "chainName": "<string>", "decimals": 123, "isNative": true, "nodeId": "<string>", "nodeName": "<string>", "nodeType": "<string>", "reason": "<string>", "receivedDisplay": { "amount": "<string>", "amountUnit": "<string>", "chainId": 123, "chainName": "<string>", "decimals": 123, "isHumanReadable": true, "symbol": "<string>" }, "requiredAmount": "<string>", "source": "requiredTokens", "symbol": "<string>", "tokenAddress": "<string>", "unresolved": true, "unresolvedReason": "chainId", "walletAddress": "<string>" } ], "workflowId": "<string>", "workflowVersion": 123 }, "message": "success", "requestId": "abc-123"}
API Playground
Try this endpoint
POST
/v1/workflows/analyze-funds
