Skip to main content
Tasks are the core primitive of Trigger.dev. They are async functions that run in the background, support retries, and can execute for as long as needed — no timeouts. There are different types of tasks:
  • Regular tasks — triggered manually from your backend or from another task.
  • Scheduled tasks — triggered on a recurring cron schedule. See Scheduled tasks.

Hello world task

Here is a minimal task definition:
/trigger/hello-world.ts
import { task } from "@trigger.dev/sdk";

export const helloWorld = task({
  // Unique identifier for this task
  id: "hello-world",
  // The run function is called when the task is triggered
  run: async (payload: { message: string }) => {
    console.log(payload.message);
  },
});
You can trigger this in two ways:
  1. From the dashboard using the Test feature.
  2. From your backend code using the SDK. See Triggering.
Here is how to trigger a single run from your backend:
Your backend code
import { helloWorld } from "./trigger/hello-world";

async function triggerHelloWorld() {
  const handle = await helloWorld.trigger({ message: "Hello world!" });
  console.log("Task is running with handle", handle.id);
}
You can also trigger a task from another task and wait for the result.

Defining a task

The task() function accepts a configuration object with the following fields.

id

A unique string identifier for your task. Used to trigger it, manage it, and view runs in the dashboard. It must be unique across your entire project.

run

Your custom logic. It is an async function that receives two arguments:
  1. Payload — the data passed when triggering the task.
  2. Context object — contains ctx (run context) and any output from the optional init function.
Anything you return from run becomes the task output. Returned values must be JSON-serializable.

retry options

Tasks are retried on uncaught errors. By default a task retries up to 3 times with exponential backoff.
/trigger/retry.ts
import { task } from "@trigger.dev/sdk";

export const taskWithRetries = task({
  id: "task-with-retries",
  retry: {
    maxAttempts: 10,
    factor: 1.8,
    minTimeoutInMs: 500,
    maxTimeoutInMs: 30_000,
    randomize: false,
  },
  run: async (payload: any, { ctx }) => {
    // ...
  },
});
For more information read the Errors & Retrying guide.

queue options

Queues control the concurrency of your tasks — how many can run at the same time.
/trigger/one-at-a-time.ts
import { task } from "@trigger.dev/sdk";

export const oneAtATime = task({
  id: "one-at-a-time",
  queue: {
    concurrencyLimit: 1,
  },
  run: async (payload: any, { ctx }) => {
    // ...
  },
});
For more information read the Concurrency & Queues guide.

machine options

Some tasks require more vCPUs or RAM. Specify machine requirements with the machine field.
/trigger/heavy-task.ts
import { task } from "@trigger.dev/sdk";

export const heavyTask = task({
  id: "heavy-task",
  machine: {
    preset: "large-1x", // 4 vCPU, 8 GB RAM
  },
  run: async (payload: any, { ctx }) => {
    // ...
  },
});

maxDuration option

By default, tasks can run indefinitely. Set maxDuration (in seconds) to stop a run that runs too long.
/trigger/long-task.ts
import { task } from "@trigger.dev/sdk";

export const longTask = task({
  id: "long-task",
  maxDuration: 300, // 5 minutes
  run: async (payload: any, { ctx }) => {
    // ...
  },
});

Global lifecycle hooks

When specifying global lifecycle hooks, we recommend placing them in an init.ts file at the root of your trigger directory.
You can register lifecycle hooks that run for every task in your project:
init.ts
import { tasks } from "@trigger.dev/sdk";

tasks.onStart(({ ctx, payload, task }) => {
  console.log("Run started", ctx.run);
});

tasks.onSuccess(({ ctx, output }) => {
  console.log("Run finished", ctx.run);
});

tasks.onFailure(({ ctx, error }) => {
  console.log("Run failed", ctx.run);
});

Lifecycle functions

middleware and locals

Task middleware runs at the top level, wrapping the entire task lifecycle including all hooks. Use the locals API to share data between middleware and hooks.
db.ts
import { locals, logger, tasks } from "@trigger.dev/sdk";

const DbLocal = locals.create<{ connect: () => Promise<void>; disconnect: () => Promise<void> }>("db");

export function getDb() {
  return locals.getOrThrow(DbLocal);
}

tasks.middleware("db", async ({ ctx, payload, next, task }) => {
  const db = locals.set(DbLocal, {
    connect: async () => logger.info("Connecting to the database"),
    disconnect: async () => logger.info("Disconnecting from the database"),
  });

  await db.connect();
  await next();
  await db.disconnect();
});

// Disconnect when the run is paused
tasks.onWait("db", async ({ ctx, payload, task }) => {
  const db = getDb();
  await db.disconnect();
});

// Reconnect when the run is resumed
tasks.onResume("db", async ({ ctx, payload, task }) => {
  const db = getDb();
  await db.connect();
});
Access the database client in your tasks using getDb():
import { task } from "@trigger.dev/sdk";
import { getDb } from "./db";

export const myTask = task({
  id: "my-task",
  run: async (payload: any, { ctx }) => {
    const db = getDb();
    await db.connect();
  },
});

onStartAttempt

Called before each run attempt. Useful for per-attempt setup, logging, or notifications.
/trigger/on-start-attempt.ts
import { task } from "@trigger.dev/sdk";

export const taskWithOnStartAttempt = task({
  id: "task-with-on-start-attempt",
  onStartAttempt: async ({ payload, ctx }) => {
    if (ctx.run.attempt.number === 1) {
      console.log("First attempt starting", ctx.run);
    }
  },
  run: async (payload: any, { ctx }) => {
    // ...
  },
});
Errors thrown in onStartAttempt will cause the attempt to fail.

onWait and onResume

Called when a run is paused at a waitpoint or resumed from one:
import { task, wait } from "@trigger.dev/sdk";

export const myTask = task({
  id: "my-task",
  onWait: async ({ wait }) => {
    console.log("Run paused", wait);
  },
  onResume: async ({ wait }) => {
    console.log("Run resumed", wait);
  },
  run: async (payload: any, { ctx }) => {
    console.log("Run started", ctx.run);
    await wait.for({ seconds: 10 });
    console.log("Run resumed after 10 seconds");
  },
});

onSuccess

Called when a run completes successfully. Useful for sending notifications or syncing state to your database.
/trigger/on-success.ts
import { task } from "@trigger.dev/sdk";

export const taskWithOnSuccess = task({
  id: "task-with-on-success",
  onSuccess: async ({ payload, output, ctx }) => {
    // send a notification, log, etc.
  },
  run: async (payload: any, { ctx }) => {
    // ...
  },
});
Errors thrown in onSuccess are ignored but visible in the dashboard.

onComplete

Called when a run finishes, regardless of whether it succeeded or failed:
/trigger/on-complete.ts
import { task } from "@trigger.dev/sdk";

export const taskWithOnComplete = task({
  id: "task-with-on-complete",
  onComplete: async ({ payload, result, ctx }) => {
    if (result.ok) {
      console.log("Run succeeded", result.output);
    } else {
      console.log("Run failed", result.error);
    }
  },
  run: async (payload: any, { ctx }) => {
    // ...
  },
});

onFailure

Called once all retry attempts are exhausted. Useful for alerts or cleanup.
/trigger/on-failure.ts
import { task } from "@trigger.dev/sdk";

export const taskWithOnFailure = task({
  id: "task-with-on-failure",
  onFailure: async ({ payload, error, ctx }) => {
    // send an alert, log the error, etc.
  },
  run: async (payload: any, { ctx }) => {
    // ...
  },
});
onFailure does not fire for Crashed, System failure, or Canceled run statuses.

catchError

A function called when an error is thrown in run, giving you control over retry behavior. Read more in the Errors & Retrying guide.

onCancel

Called when a run is canceled. Use this to clean up resources or save partial results.
import { task, tasks } from "@trigger.dev/sdk";
import { setTimeout } from "node:timers/promises";

export const cancelExampleTask = task({
  id: "cancel-example",
  run: async (payload: { message: string }, { signal }) => {
    try {
      // Pass the signal so this resolves early if canceled
      await setTimeout(10_000, undefined, { signal });
    } catch (error) {
      // Ignore abort error
    }

    return { message: "Hello, world!" };
  },
  onCancel: async ({ runPromise }) => {
    // Optionally await the run to access its output
    const output = await runPromise;
    console.log("Run canceled with output", output);
  },
});
onCancel only runs if the run is actively executing. Runs that are canceled while queued or suspended will not invoke onCancel.

Next steps

Triggering

Learn how to trigger your tasks from your backend or another task.

Errors & Retrying

Handle errors and configure retry behavior.

Scheduled tasks

Run tasks on a recurring cron schedule.

Concurrency & Queues

Control how many runs execute at once.

Build docs developers (and LLMs) love