API Reference

Host Adapters

Host adapters

Host adapters turn provider-native cron or scheduled function entrypoints into a bounded runtime.sweep() call.

They are intentionally thin. Workflow semantics live in the runtime and store. Documentation and adapter development should prioritize partner environments: Cloudflare, Railway, and Netlify. Vercel remains an important compatibility target, but examples should not make it the default host.

Common behavior

Current packaged host adapters:

  • call materializeWorkflowSchedules
  • call runtime.sweep
  • default includeEvents to false
  • return a compact JSON summary
  • optionally include the full sweep result for debugging

Cloudflare

Install:

sh
pnpm add @tanstack/workflow-cloudflare
pnpm add @tanstack/workflow-cloudflare

createCloudflareWorkflowScheduledHandler

ts
import { createCloudflareWorkflowScheduledHandler } from '@tanstack/workflow-cloudflare'

export default {
  scheduled: createCloudflareWorkflowScheduledHandler({
    runtime: ({ env }) => createWorkflowRuntime(env),
    maxScheduledRuns: 25,
    maxTimers: 25,
    maxDurationMs: 25_000,
  }),
}
import { createCloudflareWorkflowScheduledHandler } from '@tanstack/workflow-cloudflare'

export default {
  scheduled: createCloudflareWorkflowScheduledHandler({
    runtime: ({ env }) => createWorkflowRuntime(env),
    maxScheduledRuns: 25,
    maxTimers: 25,
    maxDurationMs: 25_000,
  }),
}

Options:

OptionPurpose
runtimeRequired WorkflowRuntimeDefinition, or a function that creates one from controller, env, and ctx.
nowOptional timestamp provider. Defaults to controller.scheduledTime.
leaseOwnerString or function for worker identity.
limitShared fallback limit for schedules and timers.
maxScheduledRunsMaximum schedule buckets to start.
maxTimersMaximum timers to resume.
maxDurationMsSweep wall-clock budget.
leaseMsLease duration.
includeEventsWhether retained run results include events. Defaults to false.
maxEventsMaximum retained events per run result.
includeSweepResultInclude full sweep result in the returned object. Defaults to false.
materializeSchedulesSet false to skip schedule materialization.
cronLookbackMsLookback window for cron schedule materialization.

Configure Cloudflare Cron Triggers directly in wrangler.json or wrangler.toml. The adapter does not wrap that static platform config.

Railway

Install:

sh
pnpm add @tanstack/workflow-railway
pnpm add @tanstack/workflow-railway

createRailwayWorkflowCronCommand

ts
import { createRailwayWorkflowCronCommand } from '@tanstack/workflow-railway'

const sweep = createRailwayWorkflowCronCommand({
  runtime,
  maxScheduledRuns: 25,
  maxTimers: 25,
  maxDurationMs: 55_000,
  logSummary: true,
})

await sweep()
import { createRailwayWorkflowCronCommand } from '@tanstack/workflow-railway'

const sweep = createRailwayWorkflowCronCommand({
  runtime,
  maxScheduledRuns: 25,
  maxTimers: 25,
  maxDurationMs: 55_000,
  logSummary: true,
})

await sweep()

Options:

OptionPurpose
runtimeRequired WorkflowRuntimeDefinition.
nowOptional timestamp provider.
leaseOwnerString or function for worker identity.
limitShared fallback limit for schedules and timers.
maxScheduledRunsMaximum schedule buckets to start.
maxTimersMaximum timers to resume.
maxDurationMsSweep wall-clock budget.
leaseMsLease duration.
includeEventsWhether retained run results include events. Defaults to false.
maxEventsMaximum retained events per run result.
includeSweepResultInclude full sweep result in the returned object. Defaults to false.
materializeSchedulesSet false to skip schedule materialization.
cronLookbackMsLookback window for cron schedule materialization.
logSummarytrue to log JSON summary, or a function to receive the result.

Configure the Railway Cron Job directly in railway.toml or railway.json:

toml
[deploy]
startCommand = "pnpm workflow:sweep"
cronSchedule = "*/5 * * * *"
restartPolicyType = "NEVER"
[deploy]
startCommand = "pnpm workflow:sweep"
cronSchedule = "*/5 * * * *"
restartPolicyType = "NEVER"

Netlify

Install:

sh
pnpm add @tanstack/workflow-netlify
pnpm add @tanstack/workflow-netlify

createNetlifyWorkflowSweepHandler

ts
import { createNetlifyWorkflowSweepHandler } from '@tanstack/workflow-netlify'

export default createNetlifyWorkflowSweepHandler({
  runtime,
  maxScheduledRuns: 25,
  maxTimers: 25,
  maxDurationMs: 25_000,
})
import { createNetlifyWorkflowSweepHandler } from '@tanstack/workflow-netlify'

export default createNetlifyWorkflowSweepHandler({
  runtime,
  maxScheduledRuns: 25,
  maxTimers: 25,
  maxDurationMs: 25_000,
})

Options:

OptionPurpose
runtimeRequired WorkflowRuntimeDefinition.
nowOptional timestamp provider.
leaseOwnerString or function for worker identity.
limitShared fallback limit for schedules and timers.
maxScheduledRunsMaximum schedule buckets to start.
maxTimersMaximum timers to resume.
maxDurationMsSweep wall-clock budget.
leaseMsLease duration.
includeEventsWhether retained run results include events. Defaults to false.
maxEventsMaximum retained events per run result.
includeSweepResultInclude full sweep result in JSON response. Defaults to false.
materializeSchedulesSet false to skip schedule materialization.
cronLookbackMsLookback window for cron schedule materialization.

Export the Netlify Scheduled Function config object directly. Netlify needs that object to be statically visible:

ts
export const config = {
  schedule: '*/5 * * * *',
}
export const config = {
  schedule: '*/5 * * * *',
}

The Netlify adapter only wakes the runtime. Apply the store adapter's package-owned migration during deploy/setup; do not define Workflow's workflow_* tables in application schema files unless you are intentionally building typed admin access.

Vercel

Install:

sh
pnpm add @tanstack/workflow-vercel
pnpm add @tanstack/workflow-vercel

createVercelWorkflowSweepHandler

ts
import { createVercelWorkflowSweepHandler } from '@tanstack/workflow-vercel'

export const GET = createVercelWorkflowSweepHandler({
  runtime,
  cronSecret: process.env.CRON_SECRET,
  maxScheduledRuns: 25,
  maxTimers: 25,
  maxDurationMs: 55_000,
})
import { createVercelWorkflowSweepHandler } from '@tanstack/workflow-vercel'

export const GET = createVercelWorkflowSweepHandler({
  runtime,
  cronSecret: process.env.CRON_SECRET,
  maxScheduledRuns: 25,
  maxTimers: 25,
  maxDurationMs: 55_000,
})

Options:

OptionPurpose
runtimeRequired WorkflowRuntimeDefinition.
nowOptional timestamp provider.
leaseOwnerString or function for worker identity.
limitShared fallback limit for schedules and timers.
maxScheduledRunsMaximum schedule buckets to start.
maxTimersMaximum timers to resume.
maxDurationMsSweep wall-clock budget.
leaseMsLease duration.
includeEventsWhether retained run results include events. Defaults to false.
maxEventsMaximum retained events per run result.
includeSweepResultInclude full sweep result in JSON response. Defaults to false.
materializeSchedulesSet false to skip schedule materialization.
cronLookbackMsLookback window for cron schedule materialization.
cronSecretExpected Authorization: Bearer <secret> value.

Configure Vercel Cron directly in vercel.json:

json
{
  "$schema": "https://openapi.vercel.sh/vercel.json",
  "crons": [{ "path": "/api/workflow/sweep", "schedule": "*/5 * * * *" }]
}
{
  "$schema": "https://openapi.vercel.sh/vercel.json",
  "crons": [{ "path": "/api/workflow/sweep", "schedule": "*/5 * * * *" }]
}

The Vercel adapter only wakes the runtime. Apply the store adapter's package-owned migration during deploy/setup; route code owns host config, while Workflow owns the runtime persistence schema.

Response shape

Successful adapters return:

ts
interface SweepResponse {
  ok: true
  now: number
  leaseOwner: string
  materialized: ReadonlyArray<MaterializedWorkflowSchedule>
  summary: {
    materialized: number
    scheduled: Partial<Record<WorkflowRuntimeRunResultKind, number>>
    timers: Partial<Record<WorkflowRuntimeRunResultKind, number>>
    eventCount: number
    returnedEventCount: number
  }
  deadlineReached: boolean
  remainingMayExist: boolean
  sweep?: WorkflowRuntimeSweepResult
}
interface SweepResponse {
  ok: true
  now: number
  leaseOwner: string
  materialized: ReadonlyArray<MaterializedWorkflowSchedule>
  summary: {
    materialized: number
    scheduled: Partial<Record<WorkflowRuntimeRunResultKind, number>>
    timers: Partial<Record<WorkflowRuntimeRunResultKind, number>>
    eventCount: number
    returnedEventCount: number
  }
  deadlineReached: boolean
  remainingMayExist: boolean
  sweep?: WorkflowRuntimeSweepResult
}

Use the summary for normal logs and monitoring. Include the full sweep only for debugging.

Re-exported schedule helpers

Both adapters re-export:

ts
materializeWorkflowSchedules
materializeWorkflowSchedules

and the related materialization types from @tanstack/workflow-runtime.