---
title: "Triggering"
description: "Tasks need to be triggered to run."
---

There are currently four ways you can trigger any task from your own code:

| Function                         | Where does this work? | What it does                                                                                                                       |
| -------------------------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `yourTask.trigger()`             | Anywhere              | Triggers a task and gets a handle you can use to monitor and manage the run. It does not wait for the result.                      |
| `yourTask.batchTrigger()`        | Anywhere              | Triggers a task multiple times and gets a handle you can use to monitor and manage the runs. It does not wait for the results.     |
| `yourTask.triggerAndWait()`      | Inside a task         | Triggers a task and then waits until it's complete. You get the result data to continue with.                                      |
| `yourTask.batchTriggerAndWait()` | Inside a task         | Triggers a task multiple times in parallel and then waits until they're all complete. You get the resulting data to continue with. |

Additionally, [scheduled tasks](/v3/tasks-scheduled) get automatically triggered on their schedule and [webhooks](/v3/tasks-webhooks) when receiving a webhook.

## Scheduled tasks

You should attach one or more schedules to your `schedules.task()` to trigger it on a recurring schedule. [Read the scheduled tasks docs](/v3/tasks-scheduled).

## From outside of a task

You can trigger any task from your backend code, using either `trigger()` or `batchTrigger()`.

<Note>
  Do not trigger tasks directly from your frontend. If you do, you will leak your private
  Trigger.dev API key to the world.
</Note>

You can use Next.js Server Actions but [you need to be careful with bundling](#next-js-server-actions).

### Authentication

When you trigger a task from your backend code, you need to set the `TRIGGER_SECRET_KEY` environment variable. You can find the value on the API keys page in the Trigger.dev dashboard. [More info on API keys](/v3/apikeys).

### trigger()

Triggers a single run of a task with the payload you pass in, and any options you specify. It does NOT wait for the result, you cannot do that from outside a task.

<CodeGroup>

```ts Next.js API route
import { emailSequence } from "~/trigger/emails";

//app/email/route.ts
export async function POST(request: Request) {
  //get the JSON from the request
  const data = await request.json();

  //trigger your task
  const handle = await emailSequence.trigger({ to: data.email, name: data.name });

  //return a success response with the handle
  return Response.json(handle);
}
```

```ts Remix
import { emailSequence } from "~/trigger/emails";

export async function action({ request, params }: ActionFunctionArgs) {
  if (request.method.toUpperCase() !== "POST") {
    return json("Method Not Allowed", { status: 405 });
  }

  //get the JSON from the request
  const data = await request.json();

  //trigger your task
  const handle = await emailSequence.trigger({ to: data.email, name: data.name });

  //return a success response with the handle
  return json(handle);
}
```

</CodeGroup>

### batchTrigger()

Triggers multiples runs of a task with the payloads you pass in, and any options you specify. It does NOT wait for the results, you cannot do that from outside a task.

<CodeGroup>

```ts Next.js API route
import { emailSequence } from "~/trigger/emails";

//app/email/route.ts
export async function POST(request: Request) {
  //get the JSON from the request
  const data = await request.json();

  //batch trigger your task
  const batchHandle = await emailSequence.batchTrigger(
    data.users.map((u) => ({ payload: { to: u.email, name: u.name } }))
  );

  //return a success response with the handle
  return Response.json(batchHandle);
}
```

```ts Remix
import { emailSequence } from "~/trigger/emails";

export async function action({ request, params }: ActionFunctionArgs) {
  if (request.method.toUpperCase() !== "POST") {
    return json("Method Not Allowed", { status: 405 });
  }

  //get the JSON from the request
  const data = await request.json();

  //batch trigger your task
  const batchHandle = await emailSequence.batchTrigger(
    data.users.map((u) => ({ payload: { to: u.email, name: u.name } }))
  );

  //return a success response with the handle
  return json(batchHandle);
}
```

</CodeGroup>

## From inside a task

You can trigger tasks from other tasks using `trigger()` or `batchTrigger()`. You can also trigger and wait for the result of triggered tasks using `triggerAndWait()` and `batchTriggerAndWait()`. This is a powerful way to build complex tasks.

### trigger()

This works the same as from outside a task. You call it and you get a handle back, but it does not wait for the result.

```ts /trigger/my-task.ts
import { myOtherTask } from "~/trigger/my-other-task";

export const myTask = task({
  id: "my-task",
  run: async (payload: string) => {
    const handle = await myOtherTask.trigger("some data");

    //...do other stuff
  },
});
```

### batchTrigger()

This works the same as from outside a task. You call it and you get a handle back, but it does not wait for the results.

```ts /trigger/my-task.ts
import { myOtherTask } from "~/trigger/my-other-task";

export const myTask = task({
  id: "my-task",
  run: async (payload: string) => {
    const batchHandle = await myOtherTask.batchTrigger([{ payload: "some data" }]);

    //...do other stuff
  },
});
```

### triggerAndWait()

This is where it gets interesting. You can trigger a task and then wait for the result. This is useful when you need to call a different task and then use the result to continue with your task.

<Accordion title="Don't use this in parallel, e.g. with `Promise.all()`">
  Instead, use `batchTriggerAndWait()` if you can, or a for loop if you can't.

To control concurrency using batch triggers, you can set `queue.concurrencyLimit` on the child task.

<CodeGroup>

```ts /trigger/batch.ts
export const batchTask = task({
  id: "batch-task",
  run: async (payload: string) => {
    const results = await childTask.batchTriggerAndWait([
      { payload: "item1" },
      { payload: "item2" },
    ]);
    console.log("Results", results);

    //...do stuff with the results
  },
});
```

```ts /trigger/loop.ts
export const loopTask = task({
  id: "loop-task",
  run: async (payload: string) => {
    //this will be slower than the batch version
    //as we have to resume the parent after each iteration
    for (let i = 0; i < 2; i++) {
      const result = await childTask.triggerAndWait(`item${i}`);
      console.log("Result", result);

      //...do stuff with the result
    }
  },
});
```

</CodeGroup>

</Accordion>

```ts /trigger/parent.ts
export const parentTask = task({
  id: "parent-task",
  run: async (payload: string) => {
    const result = await batchChildTask.triggerAndWait("some-data");
    console.log("Result", result);

    //...do stuff with the result
  },
});
```

### batchTriggerAndWait()

You can batch trigger a task and wait for all the results. This is useful for the fan-out pattern, where you need to call a task multiple times and then wait for all the results to continue with your task.

<Accordion title="Don't use this in parallel, e.g. with `Promise.all()`">
  Instead, pass in all items at once and set an appropriate `maxConcurrency`. Alternatively, use sequentially with a for loop.

To control concurrency, you can set `queue.concurrencyLimit` on the child task.

<CodeGroup>

```ts /trigger/batch.ts
export const batchTask = task({
  id: "batch-task",
  run: async (payload: string) => {
    const results = await childTask.batchTriggerAndWait([
      { payload: "item1" },
      { payload: "item2" },
    ]);
    console.log("Results", results);

    //...do stuff with the results
  },
});
```

```ts /trigger/loop.ts
export const loopTask = task({
  id: "loop-task",
  run: async (payload: string) => {
    //this will be slower than a single batchTriggerAndWait()
    //as we have to resume the parent after each iteration
    for (let i = 0; i < 2; i++) {
      const result = await childTask.batchTriggerAndWait([
        { payload: `itemA${i}` },
        { payload: `itemB${i}` },
      ]);
      console.log("Result", result);

      //...do stuff with the result
    }
  },
});
```

</CodeGroup>

</Accordion>

```ts /trigger/nested.ts
export const batchParentTask = task({
  id: "parent-task",
  run: async (payload: string) => {
    const results = await childTask.batchTriggerAndWait([
      { payload: "item4" },
      { payload: "item5" },
      { payload: "item6" },
    ]);
    console.log("Results", results);

    //...do stuff with the result
  },
});
```

## Next.js Server Actions

Server Actions allow you to call your backend code without creating API routes. This is very useful for triggering tasks but you need to be careful you don't accidentally bundle the Trigger.dev SDK into your frontend code.

If you see an error like this then you've bundled `@trigger.dev/sdk` into your frontend code:

```bash
Module build failed: UnhandledSchemeError: Reading from "node:crypto" is not handled by plugins (Unhandled scheme).
Module build failed: UnhandledSchemeError: Reading from "node:process" is not handled by plugins (Unhandled scheme).
Webpack supports "data:" and "file:" URIs by default.
You may need an additional plugin to handle "node:" URIs.
```

When you use server actions that use `@trigger.dev/sdk`:

- The file can't have any React components in it.
- The file should have `"use server"` on the first line.

Here's an example of how to do it with a component that calls the server action and the actions file:

<CodeGroup>

```tsx app/page.tsx
"use client";

import { create } from "@/app/actions";

export default function Home() {
  return (
    <main className="flex min-h-screen flex-col items-center justify-between p-24">
      <button
        onClick={async () => {
          const handle = await create();
          console.log(handle);
        }}
      >
        Create a thing
      </button>
    </main>
  );
}
```

```tsx app/actions.ts
"use server";

import { createAvatar } from "@/trigger/create-avatar";

export async function create() {
  try {
    const handle = await createAvatar.trigger({
      userImage: "http://...",
    });

    return { handle };
  } catch (error) {
    console.error(error);
    return {
      error: "something went wrong",
    };
  }
}
```

</CodeGroup>
