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

# Automations and webhooks

> How Vendo drives UTC cron schedules and duration timers, ingests host events, and verifies external webhook triggers for the automations block.

Vendo fires automations on a schedule, in response to a host event, or from
a verified external webhook.

Zero config: the block mounts unless you pass `automations: false`, and host
events work immediately. `vendo.emit(...)` runs the matching automations in
your own process, with no key and no cron. Schedules are the one trigger that
needs a firing authority, and you pick one: `automations.start()` in a
long-lived process (it polls every 60s by default), or an external cron
against the tick route below.

## Schedules

Long-lived hosts can call `automations.start()`. Serverless hosts schedule:

```http theme={null}
POST /api/vendo/tick
Authorization: Bearer <secret>
```

Schedules support exactly one of a five-field `cron`, a duration such as
`15m`, or a one-shot UTC timestamp. Cron evaluates in UTC. If the host misses
a window, the next tick fires once and never back-fills.

The bearer is the host-configured `VENDO_TICK_SECRET`. Point any external
cron at it (Vercel cron, a GitHub Actions schedule, or crontab) as often as
you like: firing is idempotent within a cron window, so a double-hit never
double-runs anything.

## Machine app schedules

The same tick drives graduated apps' machines. An app whose box ships a
`vendo.json` manifest declaring schedules:

```json theme={null}
{ "schedules": [{ "cron": "0 8 * * *", "fn": "chaseInvoices" }] }
```

gets each due target fired as `POST /fn/<name>` on its machine: the tick
wakes the machine, posts the fn as the app owner's away execution, records
last-fired state, and the machine goes back to sleep on the normal idle
policy. Due-ness is computed from store-cached state, so a tick never wakes a
machine with nothing due. The response's additive `schedules` field reports
what fired.

The host learns a box's schedules by reading `vendo.json` over the machine
door whenever the machine is awake at tick time (and once, on the first tick
after graduation). A manifest edited while the machine sleeps is picked up the
next time it is awake. `vendo doctor` reports machine-bearing apps, whether a
schedule caller is configured, and last-fired times.

## Host events

Emit from the host code path that owns the event:

```ts theme={null}
await vendo.emit("invoice.paid", invoice, principal);
```

This runs enabled automations for that principal whose host-event name matches.

## External deliveries

Mount `POST /webhooks/:source`. Connector sources use their own signature
scheme. Self-minted sources sign HMAC-SHA256 over
`id.timestamp.rawBody`, place signature, timestamp, and delivery id in headers,
and accept only a five-minute clock window.

Vendo rejects unverified deliveries before principal resolution or dispatch.
Delivery ids are deduplicated, so at-least-once retries do not double-run an
automation. Secrets never travel in the URL.

## Stopping a run

Cancel an in-flight automation run when a schedule fires against stale state,
an agent is looping on a bad plan, or a user asks to abort. Post to the run
under its owning principal:

```http theme={null}
POST /api/vendo/runs/:id/stop
Content-Type: application/json
```

The engine signals cancellation cooperatively: the current step finishes,
outstanding tool calls are drained, and the run transitions to a cancelled
terminal state. Parked approvals attached to the run are dropped rather than
resumed. Stopping a run that has already reached a terminal state is a no-op
and returns 200.

Cancellation is idempotent and audit-logged. The engine uses the store's
atomic compare-and-set primitive on the run record, so concurrent stop
requests and scheduler ticks cannot race a run into a resurrected state.
