Inputs, variables, and TypeScript
Durable workflows do not have a separate variable language. Use ordinary
TypeScript locals and control flow inside run().
Typed inputs and outputs
Section titled “Typed inputs and outputs”Define run inputs and final outputs with schema. Supported values include
strings, booleans, finite numbers, safe integers, bigint, uint256,
int256, addresses, bytes, hashes, chain IDs, controlled-account IDs, arrays,
tuples, objects, enums, and discriminated unions.
Use label() and describe() to make browser run forms clear:
input: schema.object({ amount: schema .uint256() .label("Amount") .describe("Amount of ETH to transfer in wei"), note: schema.string().optional().describe("Optional note for this run"),})The browser humanises an unlabelled property name. A description appears as restrained field help, an optional field can be added or removed, and a default value is prefilled. Nested objects, tuples, arrays, and discriminated unions use the same schema recursively.
Integer fields include an inline unit calculator. For example, 0.1 ETH
produces the raw value 100000000000000000 for a uint256 field.
Run inputs contain literal values only. They do not reference other fields or
contain expressions. Calculate derived values inside run() with ordinary
TypeScript:
const total = input.amount * BigInt(input.count)The advanced JSON editor uses tagged wire values for integers and bytes. The
SDK converts them so a contract result or schema.uint256() input is a native
bigint in the program:
const total = input.pricePerItem * BigInt(input.count)const buffered = (total * 10_050n) / 10_000nDo not convert EVM integers to JavaScript number.
Ordinary scoping
Section titled “Ordinary scoping”Locals, closures, loops, and branches work normally because Cloudflare Workflows resumes the durable operation and txcast effects have stable keys:
const calls = []for (let index = 0; index < input.count; index += 1) { calls.push({ kind: "registry-abi" as const, contractKey: "1:0x1111111111111111111111111111111111111111", functionKey: "acquire(uint256)", args: [BigInt(index)], })}A key inside a loop must be unique and deterministic, for example
acquire-${index}. The runtime count is governed by run and payload limits,
not by an authoring-language loop bound.
Parallel effects
Section titled “Parallel effects”Sequential await, Promise.all, and Promise.allSettled are supported.
Runtime-sized parallel frontiers use one local const array, standalone
.push(effect) calls in the same lexical block, and one direct await or return
of Promise.all or Promise.allSettled.
Promise.race, Promise.any, direct promises/thenables, aliased or floating
effect arrays, duplicate keys, and unawaited effects are rejected.
Determinism and restricted APIs
Section titled “Determinism and restricted APIs”Workflow code may import the pinned @txcast/workflow-sdk and reviewed pure
viem utilities. It cannot use direct fetch, arbitrary npm packages,
filesystem, environment, clock, randomness, dynamic imports, eval, or
Function. Use a durable effect for every external observation or action.