Skip to main content
LuaTrigger, created with defineTrigger, turns an inbound HTTP event into an agent turn, a workflow run, or one direct tool call, without an execute function. Four optional slots run on the platform before anything else: verify rejects the request, filter ignores it, transform shapes what runs, and tool binds the event to one tool. Every trigger has a paste-anywhere URL ending in /trigger/<agentId>/<token>, whose token is the secret; lua triggers list prints it, host included. For a handler that owns the HTTP response, use a webhook instead. Verified against lua-cli 3.33.0.

Quick example

src/triggers/order-created.trigger.ts
Register it on LuaAgent.triggers. lua push trigger --name order-created creates the trigger on first push and uploads a version, lua deploy trigger --name order-created --set-version latest --force makes that version live, and lua triggers list prints the URL.

Constructor

The two forms are equivalent. T types ctx.body inside the slots.
string
required
Server-side identifier. Lowercase with hyphens; lua compile warns Trigger name should be URL-safe (lowercase, hyphens only) otherwise. Also --name for lua push trigger and --trigger for lua triggers.
string
required
Note shown by lua triggers list. Not sent to the model.
'webhook'
default:"'webhook'"
Event source. 'webhook' is the only value in 3.33.0.
ZodType
Zod schema describing the body, carried into the version’s manifest. It doesn’t narrow ctx.body in TypeScript; pass T for that.
(ctx: TriggerContext<T>) => boolean | Promise<boolean>
Authentication gate. false answers 401 and records rejected_unverified. A signature scheme must hash ctx.rawBody, the exact bytes sent.
(ctx: TriggerContext<T>) => boolean | Promise<boolean>
Relevance gate, after verify. false answers 200 and records skipped_filtered, so the sender sees success and doesn’t retry.
(ctx: TriggerContext<T>) => string | AgentInvocationInput | TriggerStartWorkflow | Promise<…>
Shapes what runs, after filter. A string becomes the message; an invocation input object (prompt or messages, userId, threadId, systemPrompt, and the other fields Agents.invoke accepts) owns the whole turn; { startWorkflow } starts a workflow run instead of a turn. Returning null or undefined fails the delivery with 500; use filter to skip. Omit it to send the default message.
{ name: string; input?: (ctx: TriggerContext<T>) => Record<string, unknown> | Promise<Record<string, unknown>> }
Runs one tool directly after verify and filter pass: no model turn and no conversation. name is the tool’s own name and must be a string literal. input maps the context to the tool’s arguments; omit it to call with {}. When tool and transform are both declared, the tool runs and the transform is ignored. A tool that needs human approval under governance is refused and the refusal is recorded.
The constructor throws when:
  • name is empty or blank: LuaTrigger requires a non-empty `name` (used as the server-side identifier).
  • no slot is set: LuaTrigger requires at least one of verify, filter, transform, or tool.
  • tool.name is empty: LuaTrigger `tool` requires a non-empty `name` (the bare authored tool name).
lua compile fails with Trigger must define at least one of verify, filter, transform, or tool and Trigger `tool.name` must be a static string literal (the bare authored tool name). It warns Trigger declares both `tool` and `transform` — the tool executes directly and the transform is IGNORED; remove the transform.

Slots

The slots run in one sandboxed execution per delivery, in order, with a 15-second budget for all of them; the sender waits for the outcome. env() is available inside a slot. Any throw records failed and answers 500. Once the slots pass, the sender receives 200 with { status: 'accepted', executionId } and the turn or tool runs in the background; a startWorkflow adds runId. Without transform, the model receives the default message.
The instruction is set with lua triggers create --instruction; without one the JSON body follows the prefix directly. The body is capped at 50,000 characters, so return a transform to forward chosen fields of a larger payload. A string from transform is sent as [Trigger: <name>] <string> and the instruction is not applied. The turn runs as the userId a transform object supplies; otherwise as the trigger’s bound user, the developer whose push created it or the installer who consented when installing the agent template it came with; otherwise with the system identity.

Statuses

lua triggers logs --trigger <name> shows one row per delivery. An unknown agent, an unknown token, or a mismatched pair answers 404 with Trigger not found and records nothing. Rotate a leaked URL with lua triggers rotate-token --trigger <name>.

Methods

An instance exposes read-only name, description, source, inputSchema, verify, filter, transform, and tool, plus two getters.

Types

TriggerContext

The argument every slot receives. Exported.
T
Parsed request body.
string
The unparsed request bytes as UTF-8; optional on the type. Hash these for a signature check; JSON.stringify(ctx.body) doesn’t reproduce them.
Record<string, any>
Request headers with lowercase keys, for example ctx.headers['x-hub-signature-256'].
Record<string, any>
Parsed query string.
string
This trigger’s name.
string
'webhook'.

TriggerStartWorkflow

The object transform returns to start a workflow run. Not exported by name.
object
required
The run is created in the delivery request as the system principal and no turn fires; a prompt or messages beside startWorkflow is ignored, and a declared tool still wins over the whole transform. LuaTriggerConfig and TriggerContext are exported types. The invocation input object is documented on Agents.

See also