Skip to content

Search is only available in production builds. Try building and previewing the site to test it out locally.

Jobs

Generally Available

Jobs add durable execution on top of Functions. A job is a function that survives crashes, retries on failure, checkpoints progress across steps, and can sleep for hours or days without holding a worker.

Use Jobs for order processing, nightly reports, webhook handlers that need retries, CRM sync orchestration, and any work that outlives a single HTTP request. Functions handle request/response; Jobs handle workflows.

Four trigger types ship at v0: cron (scheduled), webhook (external HTTP), event (internal pub-sub), and manual (API call). The TypeScript SDK (@reactor/jobs-sdk) provides ctx.step(), ctx.state, ctx.emit(), and ctx.sleep() for checkpointed execution.

  • Checkpointed stepsctx.step(name, fn) caches output; replays skip completed steps on retry.
  • Durable state — Per-run key-value store persists across steps and retries.
  • Event pub-subctx.emit(topic, payload) triggers downstream jobs.
  • Durable sleep — Release the worker; wake up and resume from the same step.
  • Retry with backoff — Exponential or linear backoff; failed runs land in a dead-letter queue.
  • Concurrency control — Per-job semaphores prevent resource exhaustion.
  • Built-in Data clientctx.data queries user tables with internal auth.

Create a function with a job manifest, register the job, add a cron trigger, and trigger manually.

functions/process-order/code/index.ts:

import { JobContext } from '@reactor/jobs-sdk';
export default {
async fetch(request: Request, _env: Env, ctx: JobContext): Promise<Response> {
const payload = await request.json();
const order = await ctx.step('validate', async () => validateOrder(payload));
await ctx.step('charge', async () => chargeCustomer(order));
await ctx.emit('order.processed', { orderId: order.id });
return Response.json({ success: true, orderId: order.id });
},
};
Terminal window
reactor functions create process-order --runtime bun
reactor functions deploy process-order --dir ./functions/process-order
reactor functions promote process-order
reactor jobs create process-order --function process-order
reactor jobs triggers create process-order --kind cron --schedule "0 * * * *"
reactor jobs trigger process-order --payload '{"orderId":"ord_123"}'

Webhook triggers accept unauthenticated HTTP POSTs to a stable token URL.

Terminal window
reactor jobs triggers create process-order --kind webhook
# Returns ingress URL: /jobs/v1/webhooks/{token}

Sleep releases the worker and resumes execution after the duration.

await ctx.step('send-welcome', async () => sendWelcomeEmail(user));
await ctx.sleep('cooling-off', '24h');
await ctx.step('follow-up', async () => sendFollowUp(user));

On wake, completed steps return cached output; execution continues after the sleep() call.

Terminal window
reactor jobs runs list process-order --status failed
reactor jobs dlq list process-order
reactor jobs dlq retry process-order dlq_01HZ...
[jobs]
worker_count = 4
scheduler_interval_ms = 1000
default_timeout_ms = 600000 # 10 minutes
max_timeout_ms = 3600000 # 1 hour
webhook_secret = "your-webhook-secret"
[jobs.functions]
url = "http://localhost:8004"
api_key = "internal-functions-api-key"
[jobs.data]
url = "http://localhost:8002"
api_key = "internal-data-api-key"

Job manifest fields (inside function bundle):

{
"job": {
"triggers": [
{ "kind": "cron", "schedule": "0 */6 * * *" },
{ "kind": "event", "topic": "order.created" },
{ "kind": "webhook" }
],
"retry": {
"maxAttempts": 3,
"backoff": "exponential",
"initialDelayMs": 1000,
"maxDelayMs": 60000
},
"maxConcurrency": 5,
"timeoutMs": 600000
}
}
LimitDefaultNotes
Default job timeout10 minutesOverridable per job
Max job timeout1 hourServer cap
Default max attempts3Configurable per job
Worker count4Parallel run execution
Scheduler poll interval1 secondCron, events, sleep wakeups
Max concurrency per job10Returns 429 when exceeded
Queue backendPostgres (SKIP LOCKED)Redis adapter in v0.2

Cron schedule examples:

ScheduleMeaning
0 * * * *Every hour
0 0 * * *Daily at midnight UTC
0 9 * * 1-5Weekdays at 09:00 UTC
*/15 * * * *Every 15 minutes
MethodPathDescription
POST/jobs/v1/{name}/triggerManual trigger
POST/jobs/v1/webhooks/{token}Webhook trigger
GET/jobs/v1/_admin/jobs/{name}/runsList runs
POST/jobs/v1/_admin/jobs/{name}/runs/{id}/cancelCancel run
GET/jobs/v1/_admin/jobs/{name}/logsStream logs (SSE)

The worker may have crashed mid-execution. Cancel and retry, or wait for timeout. Check function logs for the associated run_id.

Terminal window
reactor jobs runs get process-order run_01HZ...
reactor jobs runs cancel process-order run_01HZ...
reactor jobs runs retry process-order run_01HZ...

Ensure step names are stable strings. Renaming a step breaks the cache key. Step functions must be idempotent when they do run (cache miss).

Too many concurrent runs for this job. Increase maxConcurrency or queue triggers across time.

Token may be invalid or expired. Recreate the webhook trigger to get a new ingress URL.

Verify the emitting job uses the exact topic string. Event matching is case-sensitive. Check _reactor_jobs.events for unconsumed events via admin API.