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

# Triggers

> Paste-anywhere URLs that wake the agent on an external event, the four slots that check and shape the event, and how triggers differ from webhooks

A trigger is a URL you paste into any system that can send an HTTP request; when the request arrives, the agent wakes up. It exists for the common case where an event should reach the model, run one tool, or start a workflow, and writing a webhook handler would be more code than the job deserves.

## How a trigger fires

`lua triggers create --name order-created` registers a trigger on the agent and prints its URL, which `lua triggers list` prints again; these pages show it as `<<TRIGGER_URL>>`. The URL ends in a token, and that token is the credential: anyone with the URL can fire the trigger, and `lua triggers rotate-token --trigger order-created` issues a new token and invalidates the old URL at once. A `POST` to the URL with no code on your side turns the request body into a message to the agent, prefixed by the `--instruction` you gave at creation, and the agent responds as it would to an end user. Bodies over 50,000 characters are truncated before they reach the model.

When the event needs checking or shaping, define the trigger in code with `defineTrigger` and register it on the agent's `triggers` array. It has no `execute` function; instead it has up to four slots that run in order on every delivery. `verify` returns `false` to reject the request with 401, the place for a vendor signature check over `ctx.rawBody`. `filter` returns `false` to accept the request with 200 and do nothing, for events you don't care about. `transform` turns the event into what the agent receives: a string message, a full invocation you control, or `{ startWorkflow: { name, input, idempotencyKey } }` to start a [workflow](/concepts/workflows) run instead of a model turn. `tool` names one skill tool to run directly with no model turn at all; when both `tool` and `transform` are declared, `tool` wins.

```ts src/triggers/stripe-payments.trigger.ts theme={null}
import { defineTrigger } from 'lua-cli';
import { z } from 'zod';

export default defineTrigger({
  name: 'stripe-payments',
  description: 'Wake the agent when a payment succeeds',
  inputSchema: z.object({ type: z.string(), data: z.any() }),
  filter: (ctx) => ctx.body.type === 'payment_intent.succeeded',
  transform: (ctx) =>
    `Payment received for order ${ctx.body.data.object.metadata.orderNumber}. ` +
    'Thank the customer and confirm the order.',
});
```

The slots share one 15-second budget and run before anything else happens, because senders such as Stripe and GitHub give up and retry after a few seconds; use them to check, drop, and shape the event rather than to do work. The code is pushed as a trigger version with `lua push trigger --name stripe-payments` and made live with `lua deploy trigger --name stripe-payments --set-version latest --force`; the trigger record and its URL are managed separately with `lua triggers`.

A trigger fires on behalf of the person who created it: the resulting agent turn runs as that user, with that user's tools and data, unless `transform` names another `userId`. A `{ startWorkflow }` result is the exception: it starts the run and fires no agent turn at all. Every delivery is recorded; `lua triggers logs --trigger order-created` lists executions with their status (`accepted`, `completed`, `failed`, `rejected_unverified`, `skipped_filtered`, `skipped_inactive`, `started_workflow`, `skipped_overlap`), duration, payload, the agent's reply, the tools used, and the workflow run ID when one was started. `failed` covers a slot that threw or ran past its budget, a tool or agent turn that errored, and a `{ startWorkflow }` the platform refused; `skipped_overlap` is a `{ startWorkflow }` refused because a run of that workflow was in flight under `concurrencyPolicy: 'forbid'`. Lua never redelivers an event: a slot failure answers the sender 500 so the sender's own retry policy applies, and a turn or tool that fails after the 200 is only recorded, so the sender has to send again. `lua triggers deactivate` pauses a trigger so deliveries are acknowledged but ignored; `lua triggers activate` resumes it.

## Triggers and webhooks

A [webhook](/concepts/webhooks) runs your `execute` function and returns whatever you return, so it is the right choice when you need to control the HTTP response, update data without involving the model, or chain your own logic. A trigger never runs handler code of yours; it decides whether the event counts and hands it to the agent, a tool, or a workflow. Events from a connected SaaS are a third case: `lua integrations webhooks create` subscribes to them without you registering a URL with the vendor, and delivers each one to the agent as a background turn on the `integration:<type>` channel, or, with `--hook-url`, to a webhook or trigger URL you choose.

## When to use a trigger

* An external system should "tell the agent" something and let the model decide what to do: a trigger, with `--instruction` or a `transform`.
* The event maps to exactly one tool with arguments computable from the payload: a trigger with `tool`.
* The event should start a durable multi-step process: a trigger with `{ startWorkflow }`, and an `idempotencyKey` from the vendor's delivery ID so a retry doesn't start a second run.
* You must answer the caller with specific data or status: a webhook.

## Limits

* A `defineTrigger` needs at least one of `verify`, `filter`, `transform`, `tool`; `tool.name` must be a string literal.
* A `transform` that returns nothing is an error; use `filter` to skip an event.
* Slots have 15 seconds in total. The tool a trigger runs afterwards has its own budget of 300 seconds.
* Default bodies are truncated at 50,000 characters; a `transform` can forward the fields you need instead.
* A paused trigger answers 200; an unknown token answers 404. Executions are kept for 90 days.
* A trigger with pushed versions is deactivated rather than deleted by `lua triggers delete`.
* Rotating a token needs the `api-keys:issue` scope on a scoped key.

## Next steps

<Columns cols={2}>
  <Card title="Create a trigger" href="/build/create-a-trigger">Register the URL, test a delivery, and read the log.</Card>
  <Card title="LuaTrigger reference" href="/reference/sdk/luatrigger">The context object, every slot, and `startWorkflow` fields.</Card>
  <Card title="lua triggers" href="/reference/cli/triggers">Create, logs, activate, rotate-token, delete.</Card>
  <Card title="Integration events" href="/integrations/events">Subscribe to SaaS events and route them to a trigger.</Card>
</Columns>
