Skip to main content
Beyond provider webhooks, auto emits its own auto.* events when platform state changes: a resource apply starts or finishes, a connection becomes usable in a project, a session binding transitions, an onboarding run advances. Agents use these to continue work on facts instead of polling or asking a human to say “done”. This page catalogs each event, what emits it, and the trigger shapes that consume it. auto.* triggers need no connection: — the events are internal and routed per project through the same pipeline as every other event. Payload placeholders render against the bare payload ({{apply.operationId}}, {{provider}}), and where filters address it with $. paths, exactly as on provider events.

Apply lifecycle

auto.project_resource_apply.{started,completed,failed}

Emitted by the apply workflow for every non-dry-run apply of project resources — whether it came from GitHub Sync after a merge or from an apply the platform ran outside Sync. Dry runs emit nothing. Each event is deduplicated on the apply’s operation id plus status, so one apply produces at most one started and one terminal completed or failed. The payload’s stable core is the apply object:
Field notes:
  • apply.auditAction is github_sync.apply for Sync-driven applies and project_resources.apply for applies that ran outside Sync — the filter that separates “a merge landed” from every other apply path.
  • apply.plan is present only on completed; apply.error { name, message } is present only on failed (with a structured diagnostic attached for validation failures). started carries the request summary but no plan.
  • apply.plan.changedResources is a bounded preview (up to 50 changed resources, with changedResourcesOmitted counting the rest). apply.plan.createdAgentNames is the complete sorted list of agents the apply created — use it, not the preview, for exact first-install routing like $.apply.plan.createdAgentNames: { contains: "coder" }.
  • The top-level artifact is present when the apply carries one. GitHub Sync resolves the merged pull request for production-branch pushes (with a merge-commit fallback for the async commit→PR association), so a Sync apply event usually carries the merged PR’s artifact — which means bind routing can deliver it straight back to the session that owns that PR.
Placeholders: {{apply.operationId}}, {{apply.plan.counts.create}}, {{apply.plan.counts.update}}, {{apply.error.message}}. The canonical consumer is a session that shipped a .auto/ change by PR and wants to resume the moment Sync applies it:
For applies without an artifact (applies outside Sync, or the rare Sync apply whose PR could not be resolved), use a project-scoped route instead — deliver with routeBy: { kind: allLiveSessions } and a narrow where, or spawn. For failed events, surface {{apply.error.message}} first, then use apply.request.resources and apply.request.delete to identify which file or delete operation needs repair.

Connection lifecycle

auto.connection.established / auto.connection.removed

A connection event fires exactly when the set of connections usable by a project changes. established means “this connection just became usable here”; removed means it no longer is. Nothing a project can observe changed — a token refresh, a repeat allow, a same-account re-auth — means no event. established fires once per grant × project when:
  • a provider OAuth flow completes with the project allowed (Slack, Linear, built-in MCP providers, …)
  • a GitHub App installation is connected to the project
  • a Telegram manager bot or model-provider API token is connected with the project allowed
  • an existing org-level connection is allowed into the project
  • an agent-tool MCP OAuth connection completes
removed fires once per grant × project when availability ends: reason: "revoked" when the grant is removed, reason: "replaced" when a replace flow retires the old grant (the replacement then fires established with reestablished: true). Internal platform grants never emit either event, and agent-tool MCP connections currently have no removal path — they only emit established. Two payload shapes, discriminated by kind. Provider grants:
Agent-tool MCP connections:
removed payloads drop scopes, resourceSelection, reestablished, and requester and carry reason instead. On established, requester is present when the flow had an authenticated actor: { kind: "user", userId } for an auto user, { kind: "provider", provider, externalId, displayName, accountRef } for a provider identity.
Select connections with where: filters on the payload — never with the trigger-level connection: binding. That binding resolves a connection name to a grant at apply time, which cannot work for a connection that does not exist yet, the exact situation this event exists for.
A working consumer from this repo’s own production config — an observer that announces each new connection in Slack (trimmed):
.auto/agents/connection-observer.yaml
To watch one provider, add where: { $.provider: slack }. This composes with optional: true connection-bound triggers: an agent whose Slack triggers are marked optional (so apply skips them while Slack is absent) can carry an auto.connection.established trigger that wakes it to bootstrap the moment Slack arrives — and in a Sync-enabled project, an established event also schedules a regating Sync run automatically, so those optional triggers activate without a new commit. Semantics worth knowing:
  • Forward-looking only. Triggers fire on changes after they exist. An agent applied after the connection was made gets no synthetic catch-up event; check auto.connections.list at first run for “on connect or already connected” behavior.
  • Delivery is per project. An org-level grant allowed into three projects fires three events, each visible only to that project’s agents.
  • Dedup is durable. Events dedup on the grant × project access-row lifetime, so retried flows and repeat allows cannot double-fire; removing and re-allowing mints a fresh row and fires again.
  • Emission is fail-safe. A connection flow never fails because event dispatch did; a dispatch failure after the record is written is retried by the pending-event sweeper.

auto.connection.authorization_attempt.finished (not authorable)

When a session starts an OAuth flow through the runtime auto tools, the platform binds that session to the authorization attempt and delivers the terminal outcome — completed, failed, or expired — straight back to it. This continuation is automatic: agents do not declare a trigger for this event, and it answers “what happened to the authorization my session started?” rather than “what connections changed in this project?”. A session that also declares an auto.connection.established trigger receives both messages, clearly distinct.

Session-binding lifecycle

auto.session.binding.{bound,updated,unbound}

Emitted transactionally on every binding transition: a session claims a target (a PR, an issue, a thread, an agent slot), a binding’s context updates, or a binding releases. The payload describes the holder session and the transition:
  • session — the holder: { id, agentResourceId, agent, projectId, bindingRevision }
  • binding{ id, revision, target { type, externalId }, source, releasePolicy, continuity, metadata, context, boundAt }, plus releasedBy and unboundAt on unbound
  • transition{ kind, cause, context }, with previousContext, previousHolderSessionId, or nextHolderSessionId when applicable
transition.cause records what drove the transition: manual_bind, manual_unbind, manual_update, attributed_event, mention, observed_binding_event, session_spawn, trigger_spawn, chat_send, trigger_release, archive, takeover, rollover, or system. Useful filters: $.binding.target.type: github.pull_request, $.transition.kind: bound, $.transition.cause: trigger_spawn. These events route to the holder session as an auto.session bind target, which makes their consumer shape unusual: the listener is an observer agent whose sessions are bound to the sessions they watch (for example, a coordinator observing the workers it spawned — spawned-session observation is on by default via the agent’s session.observeSpawnedSessions). The observer declares a bind route over target: auto.session, and can mirror the observed session’s claims with observedTarget:
observedTarget: { action: bind } claims the observed event’s own binding.target for the observer session, so later events about that PR also reach the coordinator; action: unbind releases the observer’s claim. observedTarget is legal only on bind routes over target: auto.session listening on these three event keys.

Onboarding

auto.onboarding.phase_changed

Emitted transactionally whenever a project onboarding run’s state changes. Payload: project { organizationId, projectId }, run { id, teamId, commission, phase, evidence, revision, completedAt }, transition { previousPhase, phase, changed { phase, commission, evidence, completed } }, and session { id, agent } identifying the writer. Deduplicated per run revision. Useful filters: $.transition.phase, $.run.teamId. Routing: project-scoped deliver or spawn — the event carries no bind target. Most teams never author triggers on this event; it exists for onboarding orchestration agents.

Quick reference

For provider events see GitHub events, Slack events, and Linear events; for schedules and custom webhooks see Cron and webhooks.