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.
Current packaged host adapters:
Install:
pnpm add @tanstack/workflow-cloudflarepnpm add @tanstack/workflow-cloudflareimport { 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:
| Option | Purpose |
|---|---|
| runtime | Required WorkflowRuntimeDefinition, or a function that creates one from controller, env, and ctx. |
| now | Optional timestamp provider. Defaults to controller.scheduledTime. |
| leaseOwner | String or function for worker identity. |
| limit | Shared fallback limit for schedules and timers. |
| maxScheduledRuns | Maximum schedule buckets to start. |
| maxTimers | Maximum timers to resume. |
| maxDurationMs | Sweep wall-clock budget. |
| leaseMs | Lease duration. |
| includeEvents | Whether retained run results include events. Defaults to false. |
| maxEvents | Maximum retained events per run result. |
| includeSweepResult | Include full sweep result in the returned object. Defaults to false. |
| materializeSchedules | Set false to skip schedule materialization. |
| cronLookbackMs | Lookback 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.
Install:
pnpm add @tanstack/workflow-railwaypnpm add @tanstack/workflow-railwayimport { 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:
| Option | Purpose |
|---|---|
| runtime | Required WorkflowRuntimeDefinition. |
| now | Optional timestamp provider. |
| leaseOwner | String or function for worker identity. |
| limit | Shared fallback limit for schedules and timers. |
| maxScheduledRuns | Maximum schedule buckets to start. |
| maxTimers | Maximum timers to resume. |
| maxDurationMs | Sweep wall-clock budget. |
| leaseMs | Lease duration. |
| includeEvents | Whether retained run results include events. Defaults to false. |
| maxEvents | Maximum retained events per run result. |
| includeSweepResult | Include full sweep result in the returned object. Defaults to false. |
| materializeSchedules | Set false to skip schedule materialization. |
| cronLookbackMs | Lookback window for cron schedule materialization. |
| logSummary | true to log JSON summary, or a function to receive the result. |
Configure the Railway Cron Job directly in railway.toml or railway.json:
[deploy]
startCommand = "pnpm workflow:sweep"
cronSchedule = "*/5 * * * *"
restartPolicyType = "NEVER"[deploy]
startCommand = "pnpm workflow:sweep"
cronSchedule = "*/5 * * * *"
restartPolicyType = "NEVER"Install:
pnpm add @tanstack/workflow-netlifypnpm add @tanstack/workflow-netlifyimport { 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:
| Option | Purpose |
|---|---|
| runtime | Required WorkflowRuntimeDefinition. |
| now | Optional timestamp provider. |
| leaseOwner | String or function for worker identity. |
| limit | Shared fallback limit for schedules and timers. |
| maxScheduledRuns | Maximum schedule buckets to start. |
| maxTimers | Maximum timers to resume. |
| maxDurationMs | Sweep wall-clock budget. |
| leaseMs | Lease duration. |
| includeEvents | Whether retained run results include events. Defaults to false. |
| maxEvents | Maximum retained events per run result. |
| includeSweepResult | Include full sweep result in JSON response. Defaults to false. |
| materializeSchedules | Set false to skip schedule materialization. |
| cronLookbackMs | Lookback window for cron schedule materialization. |
Export the Netlify Scheduled Function config object directly. Netlify needs that object to be statically visible:
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.
Install:
pnpm add @tanstack/workflow-vercelpnpm add @tanstack/workflow-vercelimport { 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:
| Option | Purpose |
|---|---|
| runtime | Required WorkflowRuntimeDefinition. |
| now | Optional timestamp provider. |
| leaseOwner | String or function for worker identity. |
| limit | Shared fallback limit for schedules and timers. |
| maxScheduledRuns | Maximum schedule buckets to start. |
| maxTimers | Maximum timers to resume. |
| maxDurationMs | Sweep wall-clock budget. |
| leaseMs | Lease duration. |
| includeEvents | Whether retained run results include events. Defaults to false. |
| maxEvents | Maximum retained events per run result. |
| includeSweepResult | Include full sweep result in JSON response. Defaults to false. |
| materializeSchedules | Set false to skip schedule materialization. |
| cronLookbackMs | Lookback window for cron schedule materialization. |
| cronSecret | Expected Authorization: Bearer <secret> value. |
Configure Vercel Cron directly in vercel.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.
Successful adapters return:
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.
Both adapters re-export:
materializeWorkflowSchedulesmaterializeWorkflowSchedulesand the related materialization types from @tanstack/workflow-runtime.