Skip to content
Thyme Docs

defineTask

Export defineTask({ schema, run, ...callbacks }) as your module's default export. The CLI and cloud runtime load that default export.

function defineTask<TSchema extends z.ZodType>(
  definition: TaskDefinition<TSchema>,
): TaskDefinition<TSchema>
 
interface TaskDefinition<TSchema extends z.ZodType> {
  schema: TSchema
  run: (ctx: ThymeContext<z.infer<TSchema>>) => Promise<TaskResult>
  onSuccess?: (ctx: ThymeContext<z.infer<TSchema>>, tx: SuccessPayload) => Promise<void> | void
  onSkip?: (ctx: ThymeContext<z.infer<TSchema>>, info: SkipPayload) => Promise<void> | void
  onError?: (ctx: ThymeContext<z.infer<TSchema>>, info: ErrorPayload) => Promise<void> | void
  onFail?: (ctx: ThymeContext<z.infer<TSchema>>, info: FailPayload) => Promise<void> | void
}

Only schema and run are required. The optional lifecycle callbacks have been available since SDK 0.5.0.

Validation and inference

Before every handler, defineTask calls schema.safeParseAsync(ctx.args). The handler receives the parsed output: defaults are applied, coercions and transforms run, addresses are checksummed, and async refinements are awaited.

functions/balance-monitor/index.ts
import { defineTask, z } from '@thyme-labs/sdk'
 
export default defineTask({
  schema: z.object({
    thresholdWei: z.coerce.bigint().nonnegative(),
    label: z.string().default('balance-monitor'),
  }),
  async run(ctx) {
    // thresholdWei is bigint; JSON input supplies it as a decimal string.
    const balance = await ctx.client.getBalance({ address: ctx.account })
    ctx.logger.info(`${ctx.args.label}: ${balance.toString()} wei`)
    return {
      canExec: false,
      message: balance < ctx.args.thresholdWei ? 'Below threshold' : 'Above threshold',
    }
  },
})
functions/balance-monitor/args.json
{ "thresholdWei": "100000000000000000" }

Invalid inputs throw an error beginning Invalid task arguments: before the handler body runs. Each issue includes its field path. A callback also validates its arguments, so invalid args can prevent onError itself from entering.

The returned task wraps the handlers; it is not an identity function. The validated context delegates to the original context, preserving a lazy client and the shared storage object. Mutate ctx.storage in place so those writes are available to the runtime.

Decisions and effects

Return { canExec: false, message } to skip, or { canExec: true, calls } to request execution. A successful run does not mean the calls are confirmed; use onSuccess for the confirmed outcome.

There is no wallet client on ctx. Use the public client for reads, encode calldata with viem, and return call descriptors. Callback return values do not enqueue additional calls.

The newest scaffold from thyme new starts with canExec: false until you implement your action. This is suitable for verifying local configuration before enabling a scheduled executable.