> ## Documentation Index
> Fetch the complete documentation index at: https://anvil.thatdevguy.in/llms.txt
> Use this file to discover all available pages before exploring further.

# Triggered Agents: Fire Tasks on Webhooks and Events

> Use defineTrigger and TriggerRegistry to run background agent tasks in response to webhooks, queue messages, and other external events.

Triggered tasks are the event-driven counterpart to [scheduled agents](/background/scheduled-agents) — instead of firing on a cron clock, they fire when you call `fire(name, payload)`, typically from inside a webhook handler or queue consumer. Both run under the same tracing as request-driven agents.

## Defining a trigger

Create a `trigger.ts` file anywhere under a background-tasks directory (conventionally `server/triggers/`):

```ts server/triggers/order-created/trigger.ts theme={null}
import { defineTrigger } from 'anvil-sdk/schedule';

export default defineTrigger({
  name: 'order.created',
  run: async (ctx) => {
    const order = ctx.payload as { id: string; total: number };
    await notifyFulfillment(order);
  },
});
```

<ParamField body="name" type="string" required>
  Unique trigger name — this is the identifier you pass to `fire()`.
</ParamField>

<ParamField body="run" type="(ctx: TaskContext) => unknown | Promise<unknown>" required>
  The task body. Receives a `TaskContext` with `now` (when `fire` was called), `payload` (whatever you passed to `fire`), and `trace`.
</ParamField>

## Registering and firing triggers

```ts theme={null}
import { TriggerRegistry, loadBackgroundTasks } from 'anvil-sdk/schedule';
import { Tracer, SqliteTraceStore } from 'anvil-sdk/trace';

const tracer = new Tracer(await SqliteTraceStore.open('.anvil/traces.db'));
const { triggers } = await loadBackgroundTasks('server/triggers');

const registry = new TriggerRegistry({ tracer });
triggers.forEach((task) => registry.register(task));
```

Fire a trigger from any webhook handler or queue consumer:

```ts theme={null}
// server/routes/webhooks/orders/post.ts
import type { Context } from 'anvil-sdk';
import { registry } from '../../../triggers';

export default async function handler(ctx: Context) {
  const payload = await ctx.body();
  await registry.fire('order.created', payload);
  return { received: true };
}
```

### `TriggerRegistry` API

<ResponseField name="register(task)" type="(task: TriggerTask) => this">
  Register a trigger task. Chainable.
</ResponseField>

<ResponseField name="has(name)" type="(name: string) => boolean">
  Check whether a trigger with this name is registered.
</ResponseField>

<ResponseField name="fire(name, payload?, now?)" type="(name: string, payload?: unknown, now?: Date) => Promise<unknown>">
  Run the named trigger's `run` function, passing `payload` through `ctx.payload`. Returns whatever `run` returns. Throws if no trigger is registered under `name`.
</ResponseField>

Each `fire()` call opens a trace span named `trigger <name>`. If `run` throws, the span closes with status `error` and the error propagates back to the caller — unlike scheduled tasks, a triggered task's failure is not swallowed, since the caller (your webhook handler) usually needs to know it failed to return the right HTTP status.

<Note>
  `fire()` runs the trigger synchronously, in the same request that called it. For triggers backed by slow or unreliable side effects, queue the actual work inside `run` (or hand off to a job queue) rather than blocking the webhook response on it.
</Note>

## See also

* [Scheduled agents](/background/scheduled-agents) — the cron-driven counterpart
* [Multi-agent orchestration](/agents/orchestration) — call a registered `AgentRegistry` agent from inside a trigger's `run` function
* [Observability](/observability/tracing) — triggered runs appear in the same trace dashboard as request-driven agents

Triggered tasks are the event-driven counterpart to [scheduled agents](/background/scheduled-agents) — instead of firing on a cron clock, they fire when you call `fire(name, payload)`, typically from inside a webhook handler or queue consumer. Both run under the same tracing as request-driven agents.

## Defining a trigger

Create a `trigger.ts` file anywhere under a background-tasks directory (conventionally `server/triggers/`):

```ts server/triggers/order-created/trigger.ts theme={null}
import { defineTrigger } from 'anvil-sdk/schedule';

export default defineTrigger({
  name: 'order.created',
  run: async (ctx) => {
    const order = ctx.payload as { id: string; total: number };
    await notifyFulfillment(order);
  },
});
```

<ParamField body="name" type="string" required>
  Unique trigger name — this is the identifier you pass to `fire()`.
</ParamField>

<ParamField body="run" type="(ctx: TaskContext) => unknown | Promise<unknown>" required>
  The task body. Receives a `TaskContext` with `now` (when `fire` was called), `payload` (whatever you passed to `fire`), and `trace`.
</ParamField>

## Registering and firing triggers

```ts theme={null}
import { TriggerRegistry, loadBackgroundTasks } from 'anvil-sdk/schedule';
import { Tracer, SqliteTraceStore } from 'anvil-sdk/trace';

const tracer = new Tracer(await SqliteTraceStore.open('.anvil/traces.db'));
const { triggers } = await loadBackgroundTasks('server/triggers');

const registry = new TriggerRegistry({ tracer });
triggers.forEach((task) => registry.register(task));
```

Fire a trigger from any webhook handler or queue consumer:

```ts theme={null}
// server/routes/webhooks/orders/post.ts
import type { Context } from 'anvil-sdk';
import { registry } from '../../../triggers';

export default async function handler(ctx: Context) {
  const payload = await ctx.body();
  await registry.fire('order.created', payload);
  return { received: true };
}
```

### `TriggerRegistry` API

<ResponseField name="register(task)" type="(task: TriggerTask) => this">
  Register a trigger task. Chainable.
</ResponseField>

<ResponseField name="has(name)" type="(name: string) => boolean">
  Check whether a trigger with this name is registered.
</ResponseField>

<ResponseField name="fire(name, payload?, now?)" type="(name: string, payload?: unknown, now?: Date) => Promise<unknown>">
  Run the named trigger's `run` function, passing `payload` through `ctx.payload`. Returns whatever `run` returns. Throws if no trigger is registered under `name`.
</ResponseField>

Each `fire()` call opens a trace span named `trigger <name>`. If `run` throws, the span closes with status `error` and the error propagates back to the caller — unlike scheduled tasks, a triggered task's failure is not swallowed, since the caller (your webhook handler) usually needs to know it failed to return the right HTTP status.

<Note>
  `fire()` runs the trigger synchronously, in the same request that called it. For triggers backed by slow or unreliable side effects, queue the actual work inside `run` (or hand off to a job queue) rather than blocking the webhook response on it.
</Note>

## See also

* [Scheduled agents](/background/scheduled-agents) — the cron-driven counterpart
* [Multi-agent orchestration](/agents/orchestration) — call a registered `AgentRegistry` agent from inside a trigger's `run` function
* [Observability](/observability/tracing) — triggered runs appear in the same trace dashboard as request-driven agents
