Triggers and Schedules
A loop’s triggers say what starts a run. A conversation is the default: a person starts the
run through a delivery. The other three triggers start a headless run, a run with no user.
| Trigger | What starts a run |
|---|---|
conversation | A person, through the embed, the SDK or your own interface. |
schedule | A time, written as a five-field cron, in UTC. |
event with source: webhook | A signed POST from your own system. |
event with source: collection | A record written to one of the loop’s collections. |
api | Your server, through the run API. |
Rules for a run with no user
- It has a subject and no user. The subject is what the run works on, read from the payload or fixed by the schedule. The per-user cap counts per subject.
- It cannot ask anyone a question. A run with no user is never offered
ask_user. A step can still wait for your decision. - It must send its result somewhere. Publish refuses a headless loop with no
outputs. - Its input is data, never instructions. A webhook body, a record or a schedule’s subject reaches the run as untrusted content.
- A flood is stopped before a model runs.
concurrencycaps how many runs one trigger may have in flight. It is required oneventandapitriggers, and optional onschedule. - The entry agent must be able to read the payload. Publish refuses the trigger otherwise.
On a schedule
triggers:
- kind: schedule
cron: "0 9 * * 1" # 09:00 UTC every Monday
subject: weekly_digest
Publishing the version installs the schedule, and the loop’s detail on Loops shows it. A new version moves the schedule to that version. Archiving the loop removes it.
Schedules lists every schedule in the account. For each one you can Run now, pause, resume or delete it, and read its recent runs with what each cost. A run that woke an agent says so and links to the run it woke.
Each firing starts one run, with the subject the trigger names.
From a signed webhook
triggers:
- kind: event
source: webhook
subject: $.lead_id # where the payload names what the run works on
concurrency: 5
secret_ref: ssm:/… # where the signing secret lives
A loop can declare one webhook trigger. If secret_ref is wrong, publish refuses it and names the
path it expects.
Once the version is published, the loop’s page shows the webhook’s address with Copy the address, and a form to store its secret. Every request is refused until the secret is stored. The value is never shown again, and storing a new one replaces it.
Sign each request with the Pio-Signature header: the same scheme as the platform’s outbound
webhooks. Webhooks shows the header, the limits and every refusal.
From a new record
triggers:
- kind: event
source: collection
collection: leads
subject: $.lead_id
concurrency: 2
Each record written to leads starts one run. The collection must be one the loop declares.
Records that such a run writes start nothing, so collection triggers never chain. To do a second
step, do it in the same run, or in a schedule that reads the collection.
When the trigger is at its concurrency cap, the write still lands, and its run ends at once as
cancelled, in words that name the fix.
From your server
An api trigger lets your server start headless runs with POST /api/runs/batch, up to 25 at a
time, under an API key. See Runs.