Cron
0 */6 * * *One day
Any standard cron expression. The console reads it back in plain words, so you can check "At minute 0 past every 6th hour" before you save.
Write a TypeScript task that reads the chain and returns the calls to send. Flow runs it on a cron, an interval or a webhook, sends the calls from a Safe you own, and handles the gas.
$ npm install -g @thyme-labs/cli| Fired | Executable | Trigger | Result | Transaction |
|---|---|---|---|---|
| 21:13:00 | harvest-rewards | cron* * * * * | Included in 93,626,991 | 0xb96c…919c |
| 21:12:42 | sync-oracle | intervalevery 30s | Included in 93,626,982 | 0x3b59…7d2c |
| 21:12:27 | top-up-relayer | webhookPOST /hooks/… | Included in 93,626,975 | 0x1df8…2ab0 |
| 21:12:12 | sync-oracle | intervalevery 30s | Included in 93,626,967 | 0xfe97…c844 |
Your code decides whether to act and which calls to make. Flow handles the schedule, the signing and the gas.
import { defineTask, z } from '@thyme-labs/sdk'
import { encodeFunctionData, parseAbi } from 'viem'
const abi = parseAbi([
'function harvest()',
'function pendingRewards() view returns (uint256)',
])
export default defineTask({
schema: z.object({
vault: z.address(),
minRewards: z.coerce.bigint(),
}),
async run(ctx) {
const { vault, minRewards } = ctx.args
const pending = await ctx.client.readContract({
address: vault, abi, functionName: 'pendingRewards',
})
// Skip this run: not worth the gas yet.
if (pending < minRewards) {
return { canExec: false, message: `${pending} pending` }
}
return {
canExec: true,
calls: [{
to: vault,
data: encodeFunctionData({ abi, functionName: 'harvest' }),
}],
}
},
})Every executable has one schedule. Webhooks and Execute now in the console fire extra runs without touching it.
0 */6 * * *One day
Any standard cron expression. The console reads it back in plain words, so you can check "At minute 0 past every 6th hour" before you save.
every 30sSix minutes
From every 30 seconds up to once a day, with an optional start time. Useful for polling state that has no event to listen for.
POST /hooks/…Whenever you call it
Up to ten named, secret URLs per executable. Post an empty body to use the saved arguments, or a JSON body that replaces them for that run.
curl -X POST "$HARVEST_WEBHOOK_URL" \
-H 'content-type: application/json' \
-d '{ "vault": "0x8f3C…a21E", "minRewards": "5000000000000000000" }'The body is checked against the same schema as your task's arguments. Fast runs answer with their result; slower ones return 202 and a URL to collect it from.
Reacting to an on-chain event? Poll for it on an interval and return canExec: false when there is nothing to do. Read about triggers.
The runtime
A task is plain TypeScript. Check live chain state, use its saved arguments, then return the contract calls Flow should execute. A skipped run sends no transaction.
Explore the task contextctx.argsDefine a schema once. The console and webhooks validate arguments against it.
ctx.clientRead contract state inside your task before deciding which calls to return.
ctx.secretsAttach only the values this executable needs, without adding them to your source.
canExec: falseNo call to send. Try again at the next trigger.Run history
Each execution has a status and a record in the console. Follow it from trigger to transaction, or inspect why it skipped or failed.
A schedule, webhook or manual run starts the executable.
The task reads the chain and either skips or returns calls.
Flow sends the approved calls using the selected profile.
The run records its receipt, transaction hash and outcome.
Skipped and failed runs end before confirmation and remain visible in run history.
Give Flow permission to execute the calls your task returns. Keep ownership and the ability to remove that permission.
Read about profilesPermission map
Who controls the account
Your wallet owns the Safe. Flow receives a scoped executor role for the calls you allow. You can revoke that role from the Safe.
During profile setup, the console checks deployed contract code against pinned hashes before it asks for your signature.
Bind project secrets in the console. Flow passes their values to the runtime when the executable runs.
Pricing
Choose a plan for your workspace. Paid plans add capacity, mainnet access and metered usage above the included compute units.
Try scheduled work on testnets.
Put recurring work into production.
Room for higher volume execution.
Free is testnet only. Mainnet gas is charged separately; plan limits and current pricing are shown in the console before you subscribe.
Start here
$ npm install -g @thyme-labs/cliMake your first scheduled executable in the console, or work through the quickstart from your editor.