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

# Triggers

# Triggers

A **trigger** runs your agent without a user typing anything — a webhook fires, a cron window opens, an event is emitted in your own code, and Svantic spins up a session and hands the agent a prompt.

Triggers live on the **Agent**. Register them with `agent.add_triggers()` before calling `attach()` or `agent.start()`.

## The shape of a trigger

Every trigger has a `type`, a `prompt` template, and the fields its kind requires:

```typescript theme={null}
import type { RuntimeTrigger } from '@svantic/sdk';

const triggers: RuntimeTrigger[] = [
  /* … */
];
```

The three kinds — `webhook`, `schedule`, `emit` — are covered below. See the [`RuntimeTrigger` reference](../reference/attach#runtimetrigger) for every field.

## Prompt templates

Prompts are regular strings with `&#123;&#123;placeholders&#125;&#125;`:

```typescript theme={null}
prompt: 'A new order came in for {{amount}} from {{customer}}.'
```

At dispatch time, placeholders are replaced with values from the trigger's payload. For webhook triggers with nested payloads, use `payload_map` to extract values with dot-notation before interpolation:

```typescript theme={null}
{
  type: 'webhook',
  path: '/hooks/stripe',
  prompt: 'A payment of {{amount}} just cleared for {{customer}}.',
  payload_map: {
    amount: 'data.object.amount_total',
    customer: 'data.object.customer_email',
  },
}
```

Unknown placeholders are left as-is (`&#123;&#123;key&#125;&#125;`) so they're easy to spot in logs.

## Webhooks

`attach` mounts `app.post(trigger.path, …)` and fires the prompt with the request body as the payload:

```typescript theme={null}
agent.add_triggers([
  {
    type: 'webhook',
    path: '/hooks/stripe',
    prompt: 'Payment {{id}} of {{amount}} cents just settled; update the order.',
    payload_map: {
      id: 'data.object.id',
      amount: 'data.object.amount_total',
    },
  },
]);

await attach(app, { agent, mesh: { /* … */ } });
```

Hook Stripe (or anything else) at `https://your-app.com/hooks/stripe`. The webhook responds `200 { status: 'accepted' }` immediately — trigger handling runs in the background. Any thrown error surfaces as a `500` with the error message.

The webhook is an ordinary Express route, so any middleware you've installed (`express.json()`, auth, rate limiting) applies.

## Schedules

Cron expressions, standard 5-field: `minute hour day-of-month month day-of-week`.

```typescript theme={null}
{
  type: 'schedule',
  cron: '0 9 * * MON-FRI',
  prompt: 'Email the ops team the overnight order summary.',
}
```

Supported syntax: `*`, single values, comma lists (`0,15,30,45`), ranges (`9-17`), steps (`*/15`), weekday names (`MON`–`SUN`). Timezone is the process's local timezone — set `TZ` in your deploy environment to control it.

The scheduler checks every minute; cron expressions finer than one-minute granularity collapse to minute precision.

Schedules don't carry a payload — every run sees `payload = {}`. Use static prompts or interpolate values you inject via `on_trigger`.

## Emit events

Event-driven triggers listen on an in-process bus. Fire them with `handle.emit(event, payload)` or `handle.ask(event, payload)`.

```typescript theme={null}
agent.add_triggers([
  {
    type: 'emit',
    event: 'order_cancelled',
    prompt: 'Order {{order_id}} was cancelled; issue the refund and notify the customer.',
  },
]);

const handle = await attach(app, { agent, mesh: { /* … */ } });

// Somewhere else in the service:
app.post('/orders/:id/cancel', async (req, res) => {
  await db.orders.cancel(req.params.id);
  handle.emit('order_cancelled', { order_id: req.params.id });
  res.sendStatus(204);
});
```

Use `ask` when you want the trigger's result back (rejects after 30 s):

```typescript theme={null}
const summary = await handle.ask('order_cancelled', { order_id });
```

## Custom dispatch

By default, triggers create a Svantic session and send the prompt to the agent. Override with `agent.on_trigger` when you want different routing — for example, calling a specific capability directly:

```typescript theme={null}
import { RemoteAgent } from '@svantic/sdk';

agent.on_trigger = async (ctx) => {
  if (ctx.trigger.event === 'order_cancelled') {
    const self = await RemoteAgent.connect(agent.public_url);
    return self.invoke_capability('issue_refund', {
      order_id: ctx.payload.order_id,
    });
  }
};
```

The callback receives a context object with `prompt`, `trigger`, `payload`, and `mesh`. Return `{ skip: true }` to block delivery, `{ prompt: 'override' }` to change the prompt, or `{ metadata: {...} }` to attach metadata.

## Where triggers live

Register triggers on the agent with `agent.add_triggers()` — this is the canonical place. Forge scaffolds generate a `triggers.ts` file next to the agent that calls `add_triggers` for you. You can also hand-write them — they're plain TypeScript values.

## See also

* [`attach`](../reference/attach)
* [`RuntimeTrigger`](../reference/attach#runtimetrigger)
* [`interpolate_prompt` / `cron_matches_now`](../reference/attach#exported-helpers)
