Triggers
A trigger is what starts a run. This page covers the eight trigger types, the data each one hands your workflow, which version of your workflow a trigger fires, and the limits and safeguards around them. Read it when you wire an entry point or when a trigger does not fire.
The eight trigger types
A workflow can carry several triggers. Each one starts its own graph and keeps its own history of fires. Every trigger output is available downstream as {{trigger:<label>.output.<field>}}, where <label> is the normalized trigger label. A trigger node has no inputs: it is the entry point of the graph.
| Trigger | Fires when | Key outputs |
|---|---|---|
| Webhook | an HTTP request reaches the webhook URL. | one field per body key, _webhookMethod, triggered_at |
| Scheduler | a cron time is reached, in the timezone you pick. | execution_count, next_execution |
| Manual | someone runs the workflow by hand. | triggered_at, triggered_by |
| Chat | a message arrives, optionally only when it matches a filter. | message, extracted_message, matched |
| Form | someone submits the form. | form_data, submission_id, one field per input |
| Tables | a row is created, updated, or deleted in a table. | row, previous_row, event_type |
| Workflows | another workflow finishes a cycle without a failed step. | parentStatus, result |
| Error | a cycle of another workflow has a failed step. | status, errorMessage, failedSteps |
Reference any of them by the trigger's label:
{{trigger:webhook.output.order_id}} → one field of the JSON body a webhook received
{{trigger:contact_form.output.email}} → one field of a form submission
{{trigger:new_orders.output.row.status}} → a column from the changed table row
{{trigger:on_failure.output.errorMessage}} → why the upstream workflow failedWhen an agent adds a trigger through the builder, the accepted type values are manual, chat, webhook, schedule, table, datasource, workflow, form, and error. table is an alias of datasource. Trigger labels must be unique within a workflow, and a trigger label cannot collide with another node's label after normalization.
Epochs: one run, many fires
Every trigger type is reusable. A fire does not start a new run: it opens a new epoch on the workflow's live run, which then rests in WAITING_TRIGGER until the next fire. Each epoch keeps its own results, so you can browse every fire of a schedule or every call of a webhook on the same run. Epochs are numbered per trigger. See Runs & execution for how to read them.
Which version a trigger fires
Each save that changes what the workflow does records a new plan version (moving its steps around does not, and the changes Orbi makes in one conversation share one version). Pinning a version, labelled Set as production in the version history, tells LiveContext which version the outside world talks to. There is no automatic pin.
| Fire | Version it runs | Without a pinned version |
|---|---|---|
| Run from the editor (Manual, or testing a chat or form in the builder) | The version open in the editor | Works. The fire reuses the live run of that version when there is one, and opens a new epoch on it. |
| Webhook, Scheduler, Tables, Workflows | The pinned version | Refused. A webhook answers 409 (not_active), a schedule is not armed, a table event is skipped. |
| The public chat or form URL | The pinned version, read when each message or submission arrives | Refused. The message or submission does not start anything. |
| Error | The handler workflow’s newest live run (of the pinned version, when one is pinned) | Works. The pin is optional for an error handler, it only needs a live run. |
Pin a version
- Open the version historyOpen the workflow in the builder and open its version history.
- Choose Set as productionPick the version and choose Set as production. If you have unsaved changes, Save & set as production saves them as a new version first.
- Check the triggersThe Webhook, Scheduler, Tables, Chat, and Form triggers are re-synced to the pinned plan straight away; a Workflows trigger reads the pinned version each time it fires. Open Public access or the Agenda to see them armed.
You can pin a version that has never run. If no run exists at that version, pinning creates the production run itself. Nothing executes and no credits are used; the run simply waits for its first fire. A plan with no trigger is pinned without a run.
Remove from production clears the pin and suspends the workflow's production triggers rather than deleting them. Pinning again arms them again. Moving production to another version switches every future execution to it; a chat conversation in progress runs the new version from its very next message.
When the production run ends
If the production run ends as failed, cancelled, or timed out, LiveContext points production at the newest other live run of the pinned version (or, failing that, a completed one). It never creates a new run for you. When no such run exists, production triggers skip until you pin again (which provisions a run) or reactivate the run from the run panel.
Webhook
A webhook trigger listens on a URL of the form {base}/webhook/{token}. Accepted methods are GET, POST, PUT, PATCH, and DELETE; a webhook accepts exactly one, and the default is POST.
| Auth type | How it works |
|---|---|
None (Public) (none) | No verification: anyone with the URL can call it. |
Basic Auth (basic) | HTTP Basic authentication with a username and password. |
Header Auth (header) | A header name and value you choose (API-key style). |
JWT Auth (jwt) | A bearer JWT verified with an HMAC secret: HS256 by default, or HS384 or HS512. |
Authentication fails closed: a request that does not pass the configured check, or a webhook configured with an auth type the platform does not recognize, gets 401.
| Field | Notes |
|---|---|
| Each body field | Every top-level key of the JSON body sits directly under output: a body {"order_id": 42} is read as {{trigger:<label>.output.order_id}}. Query parameters are added for any key the body does not already have. For a GET request, the query parameters are the fields. Keys the engine uses for itself (such as status, error, trigger_id, item_id) are overwritten or left out of the stored output: give your fields distinct names. |
| _webhookMethod | The HTTP method used. |
| _webhookTimestamp | ISO timestamp of when the request arrived. |
| triggered_at | ISO timestamp of the fire. |
| triggered_by | Display name of the workflow owner. |
There is no payload, headers, query, or method field: read body fields directly under output. Request headers never reach the workflow, so to check a shared secret, use the webhook's Header Auth. The sync query parameter is never passed on.
| Status | Meaning |
|---|---|
| 202 | Accepted: the fire was queued. |
| 200 | Completed: a synchronous call finished and returns the response. |
| 400 | The fire could not start (the response body says why), or the request carries a LiveContext sign-in response, which a webhook always refuses. |
| 401 | Authentication failed. |
| 402 | The workspace is out of credits, or the workflow has spent its cost budget, for the current period or in total for a cap that never resets (status budget_reached). |
| 404 | No webhook exists for this token (for example after the token was regenerated). |
| 405 | The request used a different HTTP method than the webhook accepts. |
| 409 | Not active: the workflow has no pinned version, its production run has ended (for example it was cancelled), or the webhook is inactive. |
| 429 | Rate limited. Retry after the delay in the Retry-After header. |
When an agent builds a webhook trigger, the webhook endpoint is created at once, so the URL works before the workflow is even saved. Its token is kept across re-pins, so the URL does not change as you iterate. It only changes when you regenerate the token from Public access, where you can also read the call history. The full HTTP surface, including the Respond to Webhook node, is in REST API & webhooks.
Scheduler
| Parameter | Default | Notes |
|---|---|---|
| schedule | 0 * * * * (hourly) | Standard 5-field cron: minute, hour, day of month, month, day of week. |
| timezone | UTC | Any IANA zone, for example America/New_York. A schedule you add on the canvas starts in your display time zone instead; UTC applies when no zone is set. |
| enabled | true | Set false to keep the trigger defined but idle. |
| maxExecutions | unlimited | Optional cap on the number of fires (alias max_executions). |
In the inspector you set the schedule with Frequency (presets, or Custom cron expression… to type one in Cron expression) and, under Advanced options, Timezone and Max executions. In runs mode, Schedule status shows the last and next run, and Execute now fires the schedule once right away.
Outputs: triggered_at, execution_count (starts at 1, alias executionCount), next_execution (aliases nextExecution and nextScheduled), and triggered_by (the workflow owner's display name). A schedule is armed only while the workflow has a pinned version. To see upcoming fires, move one, or run one early, use the Agenda.
Manual
No parameters. Running the workflow from the editor fires it. The fire reuses the live run of the same version when one exists and opens a new epoch on it; a new run is created only for a new version or when no live run exists. Outputs: triggered_at and triggered_by (alias user), the display name of whoever ran it (an empty string if unknown). Extra data_inputs passed when an agent executes the workflow are added as top-level fields.
Chat
A chat trigger fires on incoming messages. With no filter it fires on every message.
| Field | Notes |
|---|---|
| message | The raw message text. |
| extracted_message | The message with the matched prefix or suffix trimmed (alias extractedMessage). |
| conversation_id | Alias conversationId. |
| attachments | An array of file references. |
| matched | Boolean: whether the optional chatMatch filter matched. |
| match_type, match_value | Which rule matched and against what value (aliases matchType, matchValue). |
| triggered_at, triggered_by | ISO timestamp and the display name of the sender. |
Filter which messages fire the run
An optional chatMatch block decides which messages fire the trigger.
| Match type | Fires when | Needs a value? |
|---|---|---|
| ANY | every message (the default). | No |
| STARTS_WITH | the message starts with the value. | Yes |
| ENDS_WITH | the message ends with the value. | Yes |
| CONTAINS | the message contains the value anywhere. | Yes |
| EQUALS | the message equals the value exactly. | Yes |
| REGEX | the value, as a regular expression, matches anywhere in the message. | Yes |
Options: caseSensitive (default false), trimPrefix (default true, for STARTS_WITH) and trimSuffix (default true, for ENDS_WITH). With trimming on, extracted_message drops the matched command token. REGEX matches a substring, not the whole string.
A chat trigger built by an agent creates its public chat endpoint at once. You manage the endpoint and its share links in Public access.
Form
The form builder offers 17 field types:
| Group | Types |
|---|---|
| Text | text, email, password, textarea, url, tel, hidden |
| Numbers and dates | number, date, datetime, time |
| Choices | select, multiselect, checkbox, checkboxGroup, radio |
| Files | file |
| Accepted aliases | string and str become text, int and integer become number, bool and boolean become checkbox, phone becomes tel |
select, multiselect, radio, and checkboxGroup need an options list, either plain strings or {label, value} pairs.
Outputs: submission_id, submitted_at (alias submittedAt), form_data (every field in one object, alias formData), triggered_at, triggered_by, plus one output per field, named after the field's name. The hosted form page and its share links are managed in Public access.
Tables (row changes)
The Tables trigger fires on changes to one of your tables. One row-level event fires once.
Configuration: table_id (or datasource_id, required), event_types to choose which changes fire (row_created, row_updated, row_deleted; omit it for all three), and an optional filter ({column, operator, value}) so only matching rows fire.
Filter operators: = (or ==, eq), != (or neq), > (gt), >= (gte), < (lt), <= (lte), in, not_in, contains, starts_with, ends_with, is_null, and is_not_null. The last two take no value; every other operator needs one.
| Field | Notes |
|---|---|
| row | The row after the change, or its last known state for row_deleted. |
| previous_row | The row before the change. Filled only for row_updated, null otherwise. |
| event_type | row_created, row_updated, or row_deleted. |
| row_id | The primary key of the affected row. |
| datasource_id | Which table. |
| triggered_at | ISO timestamp, right after the change was saved. |
| triggered_by | Alias triggeredBy, empty by default. |
When an agent executes the workflow without a real row event (a batch scan), the trigger emits data (an array of {id, data} rows) and count instead. Put a Split over output.data to process every row. See Workflows.
Workflows (chaining)
Starts this workflow when a parent workflow finishes. The only setting is workflow_id, the parent's id (required).
- For a parent with a reusable trigger (the usual case), the chain fires once after every cycle of the parent that had no failed step, when that cycle ends: also a cycle that waited on an approval, an interface or a timer. A restart that ends the same cycle again does not fire it a second time, unless that cycle had failed or was stopped and the restart is what made it succeed.
- For a single-shot parent run, it fires when that run ends
COMPLETED. Itsresultis keyed by step id (mcp:fetch_rows) rather than by step name.
It never fires on a failure. To react to one, use the Error trigger below.
| Field | Notes |
|---|---|
| triggered_at, triggered_by | Timestamp and identity. |
| parentWorkflowId | Alias parent_workflow_id. |
| parentRunId | Alias parent_run_id. |
| parentStatus | Alias parent_status. |
| result | The outputs of the parent’s cycle, one entry per step, named after the step (a step labelled Fetch Rows is result.fetch_rows), the parent’s trigger left out. They are also copied to the top level. Empty when, in a workspace, the owner of this workflow is not allowed to see the parent workflow. |
| parentStatistics | Alias parent_statistics. |
Read the parent's outputs with {{trigger:on_done.output.result.fetch_rows}} or directly with {{trigger:on_done.output.fetch_rows}}. There is no parent_outputs field.
Error
Starts an error-handler workflow when a parent workflow has a step fail. The only setting is parent_workflow_id.
- For a parent with a reusable trigger, it fires on any cycle with a failed step, whatever the run's own status.
- For a single-shot parent run, it fires when that run ends
FAILED(or with the olderPARTIAL_SUCCESSstatus).
| Field | Notes |
|---|---|
| parentWorkflowId, parentRunId | Which parent workflow and run failed. |
| status | The parent run’s status when the failure was reported. |
| errorMessage | What went wrong. |
| triggered_at | ISO timestamp (alias triggeredAt). |
| failedSteps, completedSteps, totalSteps, skippedSteps | Step counts, present when the parent recorded them. |
| triggered_by | Identity field. |
Limits and safeguards
Endpoint limits per plan
On the cloud, the number of webhooks, schedules, chat endpoints, and form endpoints you can hold depends on your plan. Each kind has its own quota, shown as a gauge on its tab in Public access. Creating one past the limit is refused.
| Plan | Per kind |
|---|---|
| Free | 5 |
| Starter (no longer sold) | 25 |
| Pro | 50 |
| Team, Enterprise | 1,000 |
| No active plan | 10 |
| Self-hosted Community Edition | Unlimited |
Buying pay-as-you-go credits does not change your plan, so it does not change these limits.
Chains and error handlers
- A Workflows or Error trigger is skipped when the target workflow already has 5 runs executing at once.
- Chains and error handlers never fire across workspaces.
Credits and spending caps
When the workspace is out of credits, a fire is not silently dropped: the trigger node fails with the error code CREDIT_EXHAUSTED and every downstream node is skipped. The run stays reusable, so the next fire after a top-up works with no action from you. A webhook caller may receive 402.
A workflow or application can also carry a Cost budget (set in the Advanced section when you create or edit it), which Resets every month, every week, or never. It counts what agents spend, sub-agents and browser agents included, on every run except a test fire from the builder: runs opened from the Applications page count. Once the spend reaches it (this period's, or the total for a cap that never resets), no new epoch opens and no new agent starts until the allowance starts again or, for a cap that never resets, until you raise it. The spend of an agent is recorded when it finishes, so agents already running then (the other items of a split, for example) finish too, and the total can end above the cap by what they spend. A refused fire leaves the run exactly as it was, including an epoch still in progress. A webhook caller receives 402 with the status budget_reached, a form submitter, an application's user (through its share link or the public application API) or a chat visitor is told it is not available for now (without your figures), and a refused schedule tick does not count toward the schedule's maximum number of executions. See Plans & billing.
Troubleshooting
| Symptom | What to check |
|---|---|
| A webhook answers 409 | Set a version as production. If one is pinned, the production run has ended: reactivate it from the run panel, or pin again. |
| A webhook answers 404 | The token was regenerated or the webhook deleted. Copy the current URL from Public access. |
| A webhook answers 401 | The caller does not send the configured Basic, header, or JWT credentials. |
| A schedule never fires | The workflow is not pinned, the schedule is suspended (for example after the run was cancelled), or it reached maxExecutions. Check it in the Agenda. |
| The public chat or form does nothing | The workflow has no pinned version. |
| Every run fails at the trigger | Look for CREDIT_EXHAUSTED on the trigger node, or a spending cap reached on the run. |
| An error handler never runs | The handler has no live run. Pin it or execute it once. |
| A chat trigger fires on every message | Check the chatMatch type spelling: an unknown type falls back to ANY. |