Heartbeat (cron) triggers
A heartbeat trigger is declared withkind: heartbeat instead of event::
Fields
"heartbeat"
required
Marks the trigger as a schedule.
event, events, connection, optional, endpoint, and auth are all rejected on a heartbeat trigger, and checks: is rejected too (checks are legal only on github.pull_request.* events).string
required
The cron expression, 1–512 characters. auto passes it verbatim to a Temporal Schedule, so use the classic 5-field form
minute hour day-of-month month day-of-week — for example 0 9 * * 1-5 for 9:00 on weekdays.string
default:"UTC"
IANA timezone name (e.g.
America/Los_Angeles) the cron expression is evaluated in.message, where, fallback, and routing work exactly as on event triggers.
How ticks run
Applying the agent creates one durable schedule per heartbeat trigger, reconciled on every apply — editing the cron updates the schedule, deleting the trigger (or the agent) deletes it. Each trigger fires under a synthetic event key of the formheartbeat.<agentResourceId>.<ordinal>, which only that trigger can match — one agent’s heartbeat never wakes another agent.
Operational behavior worth knowing:
- No overlap: if a tick’s dispatch workflow is still running when the next tick is due, the new tick is skipped rather than stacked.
- Catch-up window: ticks missed during a short outage are replayed within a 1-minute window; older missed ticks are dropped. Each tick is deduplicated on
<scheduleId>:<scheduledAt>, so a replay never fires twice. - Self-cleaning: a tick whose agent no longer exists deletes its own schedule instead of erroring forever.
Payload and placeholders
Every tick delivers this payload:{{heartbeat.scheduledAt}} is the placeholder that matters: it is the tick’s scheduled time (not the delivery time), so use it as the anchor for reporting windows — “the 24 hours ending at {{heartbeat.scheduledAt}}” stays exact even if delivery lags.
Routing patterns
routing: { kind: spawn }— one fresh session per tick. The right default for reports and digests.routing: { kind: deliver, onUnmatched: spawn }— for a standing agent withconcurrency: 1: the tick is delivered into the agent’s one live session, and spawns it if none is running. A plaindeliverwith norouteByis only legal on aconcurrency: 1agent (or whenonUnmatched: spawncan claim the slot).
Example: a daily digest
Adapted from the daily digest example:.auto/agents/ship-digest.yaml
Custom webhook endpoints
A trigger that listens on awebhook.* event declares its own HTTP endpoint inline: endpoint: names it, auth: says how callers authenticate.
Provisioning
Applying the agent provisions the endpoint and returns a receipt per webhook trigger —{ event, endpoint, ingestUrl, status: "ready" } — with the public ingest URL:
Authentication
"hmac_sha256" | "bearer_token" | "none"
How inbound requests authenticate.
hmac_sha256 and bearer_token require secretRef. auth is required on the apply that first creates the endpoint; a later trigger binding an existing endpoint may omit it and inherit the endpoint’s auth, and declaring different auth for an existing endpoint fails apply.string
The name of a project secret holding the shared credential. The plaintext is resolved server-side at delivery time; rotating the secret rotates the endpoint’s credential without re-applying.
Both credentialed kinds compare in constant time. CORS is wide open on the ingest route (auth is header-based, never cookie-based), so a browser page can POST to a bearer endpoint directly.
Request contract
The body must be a JSON object. Two optional top-level fields have meaning to auto; everything else is your payload:string
Names the event key: a body with
"event": "incident.opened" routes as webhook.incident.opened (a value already starting with webhook. is kept verbatim). A body without event routes as the fixed fallback key webhook.received.string
Idempotency key. A repeated
dedupKey returns the original event record and does not re-route. Without it, every delivery is a fresh event.202 with { eventRecordId, created: true, routerStatus } on first delivery; 200 with created: false on a dedup hit; 401 on bad credentials; 400 on a non-object or unparseable body.
Payload, placeholders, and filters
The trigger payload is the raw request body — there is no normalization. Template placeholders andwhere paths address whatever the caller posts:
Catching unshaped providers
Most third-party webhook senders do not put anevent field at the top level. Point them at the endpoint anyway and author a fallback: true trigger on webhook.received; it fires only when no normal trigger on the endpoint matched, and where filters on the provider’s own body discriminate from there:
Diagnostics
Sessions inspect endpoints with theauto.webhooks.list and auto.webhooks.get tools, which report each endpoint’s ingest URL, auth mode, secret presence, attached triggers, and problems: a credentialed endpoint with no secretRef, or a secretRef naming a nonexistent secret, is an error; an endpoint with no active triggers or a not-yet-promoted reservation is a warning.
Example: incident response
Adapted from the incident response example:.auto/agents/incident-response.yaml
See also
- Triggers reference — routing kinds, filter grammar, fallback semantics
- Secrets — creating the
secretRefvalues webhook auth resolves - Lifecycle events — the internal
auto.*events that need no endpoint at all