Trigger functions
Trigger tasks from your backend:
| Function | What it does |
|---|
tasks.trigger() | Triggers a single task run and returns a handle. |
tasks.batchTrigger() | Triggers a single task multiple times in a batch. |
batch.trigger() | Triggers multiple different tasks in a single batch. |
Trigger tasks from inside another task:
| Function | What it does |
|---|
yourTask.trigger() | Triggers a task and returns a handle. Does not wait. |
yourTask.batchTrigger() | Triggers a task multiple times and returns handles. Does not wait. |
yourTask.triggerAndWait() | Triggers a task and waits for the result before continuing. |
yourTask.batchTriggerAndWait() | Triggers a task multiple times and waits for all results. |
batch.triggerAndWait() | Triggers multiple different tasks and waits for all results. |
batch.triggerByTask() | Like batch.trigger() but accepts task instances. |
batch.triggerByTaskAndWait() | Like batch.triggerByTask() but waits for all results. |
Triggering from your backend
When triggering from your backend, set the TRIGGER_SECRET_KEY environment variable. You can find it on the API keys page in the dashboard.
tasks.trigger()
Triggers a single run without needing to import the task. Use a type-only import to get full type-checking:
import { tasks } from "@trigger.dev/sdk";
import type { emailSequence } from "~/trigger/emails";
// 👆 type-only import
export async function POST(request: Request) {
const data = await request.json();
const handle = await tasks.trigger<typeof emailSequence>("email-sequence", {
to: data.email,
name: data.name,
});
return Response.json(handle);
}
Pass options as a third argument:
import { tasks } from "@trigger.dev/sdk";
import type { emailSequence } from "~/trigger/emails";
export async function POST(request: Request) {
const data = await request.json();
const handle = await tasks.trigger<typeof emailSequence>(
"email-sequence",
{ to: data.email, name: data.name },
{ delay: "1h" } // 👈 options
);
return Response.json(handle);
}
tasks.batchTrigger()
Triggers multiple runs of a single task at once:
import { tasks } from "@trigger.dev/sdk";
import type { emailSequence } from "~/trigger/emails";
export async function POST(request: Request) {
const data = await request.json();
const batchHandle = await tasks.batchTrigger<typeof emailSequence>(
"email-sequence",
data.users.map((u) => ({ payload: { to: u.email, name: u.name } }))
);
return Response.json(batchHandle);
}
Pass per-item options inline, or batch-level options as a third argument:
import { tasks } from "@trigger.dev/sdk";
import type { emailSequence } from "~/trigger/emails";
const batchHandle = await tasks.batchTrigger<typeof emailSequence>(
"email-sequence",
data.users.map((u) => ({
payload: { to: u.email, name: u.name },
options: { delay: "1h" }, // 👈 per-item options
}))
);
batch.trigger()
Triggers multiple different tasks at once:
import { batch } from "@trigger.dev/sdk";
import type { myTask1, myTask2 } from "~/trigger/myTasks";
export async function POST(request: Request) {
const data = await request.json();
const result = await batch.trigger<typeof myTask1 | typeof myTask2>([
{ id: "my-task-1", payload: { some: data.some } },
{ id: "my-task-2", payload: { other: data.other } },
]);
return Response.json(result);
}
Triggering from inside another task
Use these functions when inside a task run. They allow waiting for results and avoid importing task code into your backend bundle.
yourTask.trigger()
Triggers a single run and returns a handle:
import { task, runs } from "@trigger.dev/sdk";
import { myOtherTask } from "~/trigger/my-other-task";
export const myTask = task({
id: "my-task",
run: async (payload: string) => {
const handle = await myOtherTask.trigger({ foo: "some data" });
const run = await runs.retrieve(handle);
console.log("Triggered run status", run.status);
},
});
If you need to call trigger() in a loop, use batchTrigger() instead — it is more efficient and
can trigger up to 1,000 runs in a single call.
yourTask.batchTrigger()
Triggers multiple runs of the same task:
import { task, batch } from "@trigger.dev/sdk";
import { myOtherTask } from "~/trigger/my-other-task";
export const myTask = task({
id: "my-task",
run: async (payload: string) => {
const batchHandle = await myOtherTask.batchTrigger([
{ payload: "item1" },
{ payload: "item2" },
]);
const batchResult = await batch.retrieve(batchHandle.id);
},
});
yourTask.triggerAndWait()
Triggers a task and pauses the current run until the child task completes:
import { task } from "@trigger.dev/sdk";
import { childTask } from "~/trigger/child";
export const parentTask = task({
id: "parent-task",
run: async (payload: string) => {
const result = await childTask.triggerAndWait("some-data");
if (result.ok) {
console.log("Child output", result.output);
} else {
console.error("Child error", result.error);
}
},
});
Use .unwrap() to throw automatically if the child task fails:
import { task, SubtaskUnwrapError } from "@trigger.dev/sdk";
import { childTask } from "~/trigger/child";
export const parentTask = task({
id: "parent-task",
run: async (payload: string) => {
try {
const output = await childTask.triggerAndWait("some-data").unwrap();
console.log("Output", output);
} catch (error) {
if (error instanceof SubtaskUnwrapError) {
console.error("Child task failed", {
runId: error.runId,
taskId: error.taskId,
cause: error.cause,
});
}
}
},
});
Do not use triggerAndWait inside Promise.all(). Use batchTriggerAndWait() instead.
This method can only be used inside a task. It will throw if called outside one.
yourTask.batchTriggerAndWait()
Triggers multiple runs in parallel and waits for all of them to finish:
import { task } from "@trigger.dev/sdk";
import { childTask } from "~/trigger/child";
export const batchParentTask = task({
id: "batch-parent-task",
run: async (payload: string) => {
const results = await childTask.batchTriggerAndWait([
{ payload: "item1" },
{ payload: "item2" },
{ payload: "item3" },
]);
for (const run of results.runs) {
if (run.ok) {
console.log("Run succeeded", run.output);
} else {
console.error("Run failed", run.error);
}
}
},
});
batch.triggerAndWait()
Triggers multiple different tasks and waits for all results:
import { batch, task } from "@trigger.dev/sdk";
import { childTask1, childTask2 } from "~/trigger/children";
export const parentTask = task({
id: "parent-task",
run: async (payload: string) => {
const results = await batch.triggerAndWait<typeof childTask1 | typeof childTask2>([
{ id: "child-task-1", payload: { foo: "World" } },
{ id: "child-task-2", payload: { bar: 42 } },
]);
for (const result of results.runs) {
if (result.ok) {
switch (result.taskIdentifier) {
case "child-task-1":
console.log("Task 1 output", result.output); // typed as string
break;
case "child-task-2":
console.log("Task 2 output", result.output); // typed as number
break;
}
}
}
},
});
batch.triggerByTask()
Like batch.trigger() but accepts task instances directly for better type inference:
import { batch, task, runs } from "@trigger.dev/sdk";
import { childTask1, childTask2 } from "~/trigger/children";
export const parentTask = task({
id: "parent-task",
run: async (payload: string) => {
const results = await batch.triggerByTask([
{ task: childTask1, payload: { foo: "World" } },
{ task: childTask2, payload: { bar: 42 } },
]);
// results.runs is a tuple — index access is fully typed
const run1 = await runs.retrieve(results.runs[0]);
const run2 = await runs.retrieve(results.runs[1]);
},
});
batch.triggerByTaskAndWait()
Like batch.triggerByTask() but waits for all runs to complete:
import { batch, task } from "@trigger.dev/sdk";
import { childTask1, childTask2 } from "~/trigger/children";
export const parentTask = task({
id: "parent-task",
run: async (payload: string) => {
const {
runs: [run1, run2],
} = await batch.triggerByTaskAndWait([
{ task: childTask1, payload: { foo: "World" } },
{ task: childTask2, payload: { bar: 42 } },
]);
if (run1.ok) {
console.log("Task 1 output", run1.output);
}
if (run2.ok) {
console.log("Task 2 output", run2.output);
}
},
});
Triggering from your frontend
If you want to trigger a task from a frontend application, use our React hooks.
Options
All trigger functions accept an options object as the last argument.
delay
Trigger now but run later:
await myTask.trigger({ some: "data" }, { delay: "1h" });
await myTask.trigger({ some: "data" }, { delay: "88s" });
await myTask.trigger({ some: "data" }, { delay: "2024-12-01T00:00:00" });
await myTask.trigger({ some: "data" }, { delay: new Date(Date.now() + 1000 * 60 * 60) });
Delayed runs show as Delayed in the dashboard. You can cancel or reschedule them:
import { runs } from "@trigger.dev/sdk";
await runs.cancel("run_1234");
await runs.reschedule("run_1234", { delay: "1h" });
ttl
Automatically expire a run if it has not started within the given time:
await myTask.trigger({ some: "data" }, { ttl: "1h" });
await myTask.trigger({ some: "data" }, { ttl: 3600 }); // seconds
Dev runs automatically get a 10-minute TTL. On Trigger.dev Cloud, staging and production runs
receive a maximum TTL of 14 days.
idempotencyKey
Ensures a task is only triggered once with the same key — useful inside tasks that may be retried:
import { idempotencyKeys, task } from "@trigger.dev/sdk";
export const myTask = task({
id: "my-task",
retry: { maxAttempts: 4 },
run: async (payload: any) => {
const idempotencyKey = await idempotencyKeys.create("my-task-key");
await childTask.trigger(payload, { idempotencyKey });
},
});
debounce
Consolidate multiple trigger calls into a single delayed run:
// First trigger creates a run delayed by 5 seconds
await myTask.trigger({ some: "data" }, { debounce: { key: "user-123", delay: "5s" } });
// If triggered again within 5 seconds, the existing run is pushed later
await myTask.trigger({ updated: "data" }, { debounce: { key: "user-123", delay: "5s" } });
// The run only executes after 5 seconds of inactivity (first payload wins by default)
Use mode: "trailing" to execute with the last payload instead:
await myTask.trigger(
{ count: 2 },
{ debounce: { key: "user-123", delay: "5s", mode: "trailing" } }
);
Set maxDelay to guarantee execution even with continuous triggers:
await summarizeChat.trigger(
{ conversationId: "123" },
{
debounce: {
key: "conversation-123",
delay: "10s",
maxDelay: "5m", // Always run within 5 minutes of first trigger
},
}
);
queue
Override the task’s default queue when triggering:
await generatePullRequest.trigger(data, {
queue: {
name: "main-branch",
concurrencyLimit: 10,
},
});
concurrencyKey
Create a separate queue for each value of the key — useful for per-user concurrency:
await generatePullRequest.trigger(data, {
queue: { name: "free-users", concurrencyLimit: 1 },
concurrencyKey: data.userId,
});
maxAttempts
Override the task’s retry limit for this specific run:
await myTask.trigger({ some: "data" }, { maxAttempts: 1 }); // no retries
machine
Override the machine preset for this run:
await yourTask.trigger(payload, { machine: "large-1x" });
region
Override the execution region for this run:
await yourTask.trigger(payload, { region: "eu-central-1" });
Streaming batch triggering
This feature requires SDK 4.3.1+.
Pass an AsyncIterable or ReadableStream to any batch trigger function to generate items lazily without loading them all into memory:
import { task } from "@trigger.dev/sdk";
import { myOtherTask } from "~/trigger/my-other-task";
export const myTask = task({
id: "my-task",
run: async (payload: { userIds: string[] }) => {
async function* generateItems() {
for (const userId of payload.userIds) {
yield { payload: { userId } };
}
}
const batchHandle = await myOtherTask.batchTrigger(generateItems());
return { batchId: batchHandle.batchId };
},
});
Handling batch trigger errors
When a batch trigger fails, the SDK throws a BatchTriggerError:
| Property | Type | Description |
|---|
isRateLimited | boolean | true if caused by rate limiting. |
retryAfterMs | number | undefined | Milliseconds until the rate limit resets. |
phase | "create" | "stream" | Which phase failed. |
batchId | string | undefined | The batch ID if it was created before failure. |
itemCount | number | Number of items attempted. |
import { task, BatchTriggerError } from "@trigger.dev/sdk";
import { childTask } from "./child-task";
export const parentTask = task({
id: "parent-task",
run: async (payload: { userIds: string[] }) => {
const items = payload.userIds.map((userId) => ({ payload: { userId } }));
try {
const batchHandle = await childTask.batchTrigger(items);
return { batchId: batchHandle.batchId };
} catch (error) {
if (error instanceof BatchTriggerError && error.isRateLimited) {
// Re-throw to let the task retry naturally
throw error;
}
throw error;
}
},
});
Large payloads
Payloads must be under 10MB. Payloads over 512KB are automatically stored in object storage and downloaded when the task runs. Task outputs are limited to 100MB.
For payloads above 10MB, upload to your own storage and pass a URL:
import { myTask } from "./trigger/myTasks";
// Upload to S3 and get a presigned URL
const presignedUrl = await getSignedUrl(s3Client, new GetObjectCommand({ Bucket, Key }), {
expiresIn: 3600,
});
const handle = await myTask.trigger({ url: presignedUrl });