Overview
A trigger starts agent sessions without anyone at the keyboard: on a cron schedule, or when GitHub, GitLab or any service that signs its webhooks posts to the trigger’s URL. Each firing renders the trigger’s prompt, runs it in a session on the environment’s latest version, and records the outcome as an execution.How a firing runs
- The firing is recorded as a
pendingexecution. A webhook delivery gets202 {"execution_id": ...}right away; the session is never started inside the request. - A session starts on the environment’s latest version (
freshmode) or the bound session wakes up (reusemode). - Once the session is
ready, the rendered prompt is sent and the execution isrunning. - When the run completes, the execution is
succeeded(orfailedwhen the run errored or was interrupted). Afreshsession is then ended, so it stops billing and frees its slot under your workspace’s concurrent session limit; areusesession is paused again. - The result goes to the trigger’s output and to your webhook endpoints subscribed to
trigger.execution.succeededortrigger.execution.failed.
Every execution keeps its
session_id, so you can open the session to read the full transcript.
Session modes
A
reuse session keeps its own budget. A paused session still counts toward the concurrent session limit, so a reuse trigger holds one slot permanently; prefer fresh when your limit is small.
Cron triggers
cron is a five-field POSIX expression: minute, hour, day of month, month, day of week (0-6 start on Sunday; 7 is Sunday too; MON-SUN and JAN-DEC work). @hourly, @daily, @weekly, @monthly and @yearly are accepted. timezone is an IANA name (default UTC); the expression is read on that zone’s wall clock, so a daylight-saving jump skips a time that does not exist that day and fires a repeated time once.
- Minute granularity, and at most one firing every 5 minutes: an expression whose closest two occurrences are less than 300 seconds apart is refused (
cron_too_frequent). - Firings start up to 30 seconds after the scheduled minute (jitter spreads load).
- Missed firings are not replayed: after a pause, or while a firing is still in flight, the schedule simply continues from now.
- Cron firings have no payload:
{{payload}}and{{.field}}render empty.
Webhook triggers
A webhook trigger has a URL,https://api.veri.studio/v1/hooks/{trigger_id}, and a signing secret (whsec_...) that is returned once, by create and by rotate-secret. Store it in your sender’s settings. Deliveries without a valid signature are refused with 401. Bodies are capped at 1 MiB and each trigger accepts 60 deliveries a minute.
Signing a
custom delivery in Python:
Rotating the secret
veri triggers rotate-secret <id> [--grace SECONDS] (or POST /v1/triggers/{id}/rotate-secret {"grace_s": 300}) returns a new secret. The previous one keeps verifying for grace_s seconds (default 300, at most 3600) so deliveries already in flight still land; 0 revokes it at once if it leaked.
Prompt templates
A missing field renders as an empty string. Any other
{{...}} is left as written. veri triggers run <id> --payload '{...}' renders a test payload the same way.
Outputs
Every finished execution firestrigger.execution.succeeded or trigger.execution.failed to the workspace’s webhook endpoints that subscribe to it, with id, trigger_id, environment_id, kind, session_id, status, failure_reason and result_excerpt. The trigger’s output adds one more destination:
Guardrails
- No approvals. A triggered session runs with nobody to answer an
askpermission rule, so a trigger cannot be created on an environment whose latest version usesask(400 ask_not_allowed), and while an environment has triggers a new version that asks is refused the same way. Areusesession runs the version it was started on, so that version must not ask either. Useallowwith targeteddenyrules, ordefault = "deny"with targetedallowrules. - Budget per session. Each
freshsession gets the trigger’sbudget_usd(default $2.00) and pauses itself when it reaches it, which fails the execution. - No overlap. One firing per trigger at a time.
- Concurrent session limit. A
freshfiring that finds the workspace at its session limit retries for up to 3 minutes, then fails withconcurrent_session_limit. Areusefiring that finds its session busy waits up to 3 minutes, then fails withsession_busy. - Balance. A firing in a workspace whose credit balance is exhausted fails at once with
insufficient_balanceinstead of starting a sandbox. - Timeout. A run still going after 1 hour fails with
timeoutand itsfreshsession is ended. - Auto-pause. Five failed executions in a row pause the trigger (
paused_reasonsays so); a success resets the count, andresumeresets it too. - Limits. 10 active triggers per workspace. Executions are kept for 30 days (also the window in which a provider redelivery is recognized as a duplicate).
- Deleting. Deleting an environment pauses its triggers. Deleting a trigger deletes its executions and ends a
freshsession still running for it.
Failures
API
Create body:
"kind": "cron", "cron": "0 9 * * 1", "timezone": "America/New_York" instead of provider; a reuse trigger adds "session_mode": "reuse", "reuse_session_id": "...".
