Events
Every observable fact about a session arrives as an event on one ordered log.
This page is the catalog: all 19 types, the exact data payload of each, the
moment each is appended, and what is deliberately absent from the stream. For
the polling loop that reads the log, see
Follow a run live.
The envelope
Section titled “The envelope”Every event, from every endpoint and from every webhook delivery, has the same seven-field envelope:
{ "id": "6f1c1d80-6c2a-4f0e-9d5f-0c2a2f0f4b11", "type": "wamp.run.completed", "createdAt": "2026-08-11T09:04:18.220Z", "sequence": 42, "visibility": "summary", "subject": { "sessionId": "3f7d4f4c-2b6a-4a2e-9c1a-1f2b3c4d5e6f", "runId": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d" }, "data": { "status": "completed", "stopReason": "completed", "costUsd": 0.0123, "turns": 3 }}| Field | Type | Notes |
|---|---|---|
id |
UUID | Stable across replays. Use it as your deduplication key. |
type |
string | One of the 19 values below. Unknown future values must be ignored, not rejected. |
createdAt |
ISO 8601 | When the event was appended, not necessarily when the fact occurred — timeline events carry their own occurredAt. |
sequence |
integer ≥ 1 | The total order within the session, and the only cursor. |
visibility |
"summary" |
Always this literal on the public API. |
subject.sessionId |
UUID | Always present. |
subject.runId |
UUID | Present when the event belongs to a run; absent on publication and merge events. |
data |
object | Type-specific, documented per type below. May carry properties beyond the ones listed. |
The same envelope, byte for byte, is the body of a webhook delivery. There is one producer for both, so you can write one handler.
Four properties of the log are worth designing against, and each is enforced
rather than best-effort: sequence is total-ordered and gap-free within a
session, so you may assert sequence === previous + 1; events are immutable
and append-only; the log is exactly-once at rest, because the runtimes
that produce events retry at-least-once and the store deduplicates before
insert; and after is exclusive, so replaying a cursor never repeats an item.
Follow a run live covers what those mean for a loop.
Which subject each event carries
Section titled “Which subject each event carries”| Events | subject.runId |
|---|---|
| Run, interaction, and timeline events | always present |
wamp.artifact.created for a run result or a presented file |
present |
wamp.artifact.created for a pull request record |
absent — data.publicationId identifies it instead |
wamp.publication.* |
absent — publications are session-scoped commands |
Match on subject.runId when you follow one specific run. A session accumulates
runs, and a terminal event from an earlier run will otherwise end your loop
early.
Run events
Section titled “Run events”Five types, one per run outcome plus the start. Exactly one of the four terminal types closes any run.
wamp.run.started
Section titled “wamp.run.started”Appended when the run begins executing on a sandbox — after the turn was admitted and a workspace was provisioned.
{ "status": "running" }wamp.run.completed
Section titled “wamp.run.completed”The agent finished the turn on its own terms.
{ "status": "completed", "stopReason": "completed", "costUsd": 0.0123, "turns": 3 }| Field | Type | Always present |
|---|---|---|
status |
"completed" |
yes |
stopReason |
see below | no |
costUsd |
number | no |
turns |
integer | no |
stopReason is one of completed, aborted, max_turns, max_response,
error, cost_budget, turn_budget, time_budget, loop_detected. It tells
you why the agent stopped, which is not the same as whether it succeeded: a
completed run with stopReason: "max_turns" ran out of budget mid-task.
wamp.run.failed
Section titled “wamp.run.failed”The run ended in an error the platform recognized as terminal. Same payload
shape as wamp.run.completed, with status: "failed".
wamp.run.cancelled
Section titled “wamp.run.cancelled”The run stopped because cancellation was requested through
POST …/runs/{runId}/cancel. Same payload shape, with status: "cancelled".
Cancellation is a request, not an immediate state: the accepted request appears
on the run resource as cancellationRequested: true, and this event arrives
when the run actually stops.
wamp.run.crashed
Section titled “wamp.run.crashed”The run ended without a clean result — the runtime or the sandbox was lost. Same
payload shape, with status: "crashed". The platform may retry the run on its
own; the run resource exposes attempts so you can see that it did.
Interaction events
Section titled “Interaction events”Three types, covering the whole life of one blocking question.
wamp.interaction.opened
Section titled “wamp.interaction.opened”The agent needs a decision and the run has stopped to wait. The run’s status
moves to awaiting.
{ "interactionId": "ask_user:01H8…", "kind": "ask_user", "request": { "questions": [ { "question": "Which package should the test live in?", "options": ["core", "cli"], "header": "Placement", "recommendedIndex": 0, "multiSelect": false, "allowCustom": true } ] }}| Field | Type | Always present |
|---|---|---|
interactionId |
string, ≤ 255 chars | yes |
kind |
ask_user | exit_plan_mode |
yes |
request.questions[] |
array | only for ask_user |
Within a question, question, options, multiSelect, and allowCustom are
always present; header and recommendedIndex are optional. exit_plan_mode
carries no request: the agent is asking for approval to leave planning and
start editing, and the answer is a normal turn.
You answer by submitting a turn with replyTo: { interactionId }. Answering the
wrong or an already-answered interaction is 409 cloud_interaction_conflict;
submitting a plain turn while one is open is 409 cloud_turn_unresolved.
wamp.interaction.resolved
Section titled “wamp.interaction.resolved”Your reply was accepted and the run continues.
{ "interactionId": "ask_user:01H8…", "resolvedByTurnId": "…" }resolvedByTurnId is the turn you submitted with replyTo. One turn resolves
at most one interaction.
wamp.interaction.expired
Section titled “wamp.interaction.expired”The question can no longer be answered.
{ "interactionId": "ask_user:01H8…", "reason": "workspace_lost" }reason is one of workspace_lost (the sandbox went away),
session_archived, or run_cancelled. After this event, a replyTo naming
that interaction is a conflict. If your process restarted and you do not know
what is outstanding, GET /v1/sessions/{sessionId}/interactions returns only
the still-open ones.
Artifact events
Section titled “Artifact events”Two types: one per artifact recorded, one honest report of what was not kept.
wamp.artifact.created
Section titled “wamp.artifact.created”A durable output is now readable. data has three shapes, discriminated by
kind.
kind |
When | Fields |
|---|---|---|
result.summary |
at the end of a run — the agent’s written summary of what it did | artifactId, role: "output", contentType: "text/markdown; charset=utf-8", size, sha256, state: "available", truncated |
presented.file |
a file the agent chose to hand back | artifactId, role: "output", contentType, size, sha256, state: "available", truncated: false |
github.pull_request |
a publication opened or updated a pull request | artifactId, publicationId, role: "output", contentType: "application/json", size, sha256, state: "available", truncated: false |
{ "artifactId": "b1c2d3e4-…", "kind": "result.summary", "role": "output", "contentType": "text/markdown; charset=utf-8", "size": 1284, "sha256": "9f2b…", "state": "available", "truncated": false}truncated: true on a result.summary means the summary hit its size cap and
was cut. The event carries the manifest only; fetch the bytes from
GET …/artifacts/{artifactId}/content. sha256 is also the ETag that
endpoint returns, so you can skip a download you already have.
wamp.artifact.omitted
Section titled “wamp.artifact.omitted”The run produced files that were not retained, and this is the platform saying so rather than under-reporting.
{ "omittedArtifacts": 3, "reason": "not_retained" }reason is always not_retained today. The usual cause is a size cap — a
single file over 32 MiB, or a run whose presented files exceed 64 MiB in total
(see Limits). subject.runId is always present on this
event, and the count is always at least 1.
Publication events
Section titled “Publication events”Four types covering opening a pull request and merging it. None of them carries
a subject.runId.
wamp.publication.created
Section titled “wamp.publication.created”The pull request exists. This event is only emitted on success, so its status
is the literal succeeded.
{ "publicationId": "…", "status": "succeeded", "kind": "github.pull_request", "branch": "wamp/session-3f7d4f4c", "commitSha": "6f1c1d8047b2c3d4e5f60718293a4b5c6d7e8f90", "pullRequest": { "number": 412, "url": "https://github.com/…/pull/412", "draft": true }}branch is the head branch with refs/heads/ stripped, commitSha is a 40-hex
git object id, and draft reflects what you asked for (publications default to
draft). A github.pull_request artifact event is appended alongside this one.
wamp.publication.failed
Section titled “wamp.publication.failed”The publish attempt ended terminally.
{ "publicationId": "…", "status": "failed", "errorCode": "branch_conflict" }errorCode is a repository-level machine code, not one of the HTTP error codes
in Errors. Treat it as a string to log and surface, not as
a closed set to branch on exhaustively.
wamp.publication.merge_succeeded
Section titled “wamp.publication.merge_succeeded”The exact reviewed head was merged.
{ "publicationId": "…", "mergeId": "…", "status": "succeeded", "strategy": "squash", "pullRequestNumber": 412, "expectedHeadSha": "6f1c1d80…", "mergedCommitSha": "0a1b2c3d…"}strategy is merge, squash, or rebase. expectedHeadSha is the head the
merge was authorized against and mergedCommitSha is the 40-hex commit that
resulted — a merge only ever integrates the head that was reviewed.
wamp.publication.merge_failed
Section titled “wamp.publication.merge_failed”{ "publicationId": "…", "mergeId": "…", "status": "failed", "errorCode": "head_moved" }As with wamp.publication.failed, errorCode is a repository-level code. A
merge that is merely not ready yet (waiting on checks or branch protection) does
not fail — it stays in waiting, and polling it returns
409 cloud_publication_merge_not_ready.
Timeline events
Section titled “Timeline events”Five types. Together they are the transcript: what the agent said, what it ran, and what it dropped from its own context. They are the events a chat UI is built from.
Timeline events are captured at the run’s terminal boundary, not streamed as the
agent works. They are appended in one batch, in occurrence order, immediately
before the terminal wamp.run.* event of that run. So a client that pages
during a long run sees run and interaction events promptly and receives the
transcript when the run ends.
wamp.message.created
Section titled “wamp.message.created”Assistant text.
{ "messageId": "3b8f…", "role": "assistant", "text": "I added a regression test for the date parser and ran the suite.", "truncated": false, "occurredAt": "2026-08-11T09:03:02.104Z"}role is always the literal assistant — there is no user-message event,
because your messages are turns and you already have them. text is bounded and
truncated: true says it was cut.
wamp.activity.completed
Section titled “wamp.activity.completed”One tool call or runtime step finished.
{ "activityId": "7c1a…", "category": "tool", "name": "bash", "status": "success", "occurredAt": "2026-08-11T09:02:41.900Z"}category is tool or runtime. name is the tool name (or the runtime id
for runtime), bounded to 128 characters. status is success, error, or
unknown. You learn that a tool ran and whether it worked — never its
arguments or its output.
wamp.context.compacted
Section titled “wamp.context.compacted”The agent compacted its own conversation to stay inside its context window.
{ "factId": "5d2e…", "droppedMessages": 42, "summaryRetained": true, "occurredAt": "…" }summaryRetained: true means a summary of the dropped messages was kept.
This is why a later message can reference work you no longer see in full.
wamp.conversation.cleared
Section titled “wamp.conversation.cleared”The conversation was reset; earlier context is gone from the agent’s view. Your event log still holds everything.
{ "factId": "5d2e…", "occurredAt": "…" }wamp.timeline.truncated
Section titled “wamp.timeline.truncated”The run produced more timeline facts than were retained.
{ "omittedFacts": 118 }subject.runId is always present, and omittedFacts is always at least 1. Like
wamp.artifact.omitted, this exists so that a gap in the transcript is
explicit rather than silent.
What the stream deliberately does not contain
Section titled “What the stream deliberately does not contain”The timeline is a security-reviewed projection, not a debug log. It carries
bounded assistant output and activity metadata; raw tool inputs, raw tool
outputs, and model reasoning are intentionally not part of the public contract.
No visibility value on /v1 exposes them — summary is the only one served.
Two more absences worth knowing: there is no event for a turn you submitted (the
turn resource is your record of that), and there is no event for sandbox
lifecycle. Read session.workspace for the sandbox and
Sandboxes for what its states mean.
Webhook coverage: 14 of 19
Section titled “Webhook coverage: 14 of 19”Webhook subscriptions are per family, not per type, and there are four families. Fourteen of the nineteen types map to one:
| Family | Types delivered |
|---|---|
run |
wamp.run.started, wamp.run.completed, wamp.run.failed, wamp.run.cancelled, wamp.run.crashed |
interaction |
wamp.interaction.opened, wamp.interaction.resolved, wamp.interaction.expired |
artifact |
wamp.artifact.created, wamp.artifact.omitted |
publication |
wamp.publication.created, wamp.publication.failed, wamp.publication.merge_succeeded, wamp.publication.merge_failed |
The five timeline types are never delivered by webhook. They are stream-only:
wamp.message.created, wamp.activity.completed, wamp.context.compacted,
wamp.conversation.cleared, and wamp.timeline.truncated. That is a deliberate
consent boundary — pushing transcript content to a subscriber would widen an
installation’s data flow, so it waits for an explicit transcript family rather
than being folded into run or artifact.
The pattern this implies: subscribe to run, and when a terminal run webhook
arrives, page GET /v1/sessions/{sessionId}/events to collect the transcript.
Since timeline events are appended just before the terminal run event, they are
already durable by the time that webhook reaches you. Registration, signature
verification, and retries are in Receive webhooks.
Forward compatibility
Section titled “Forward compatibility”The catalog is closed today and grows additively. Write your handler so that a new type costs you nothing:
switch (event.type) { case 'wamp.message.created': /* … */ break; case 'wamp.run.completed': /* … */ break; default: break; // unknown types are ignored, by contract}Do not treat an unrecognized type as an error, do not reject an unrecognized
property inside data, and do not assume data fields marked optional above
will be present. Deduplicate on event.id, which is stable across replays and
across webhook redeliveries.
Related
Section titled “Related”- Follow a run live — the polling loop, cursors, and resuming after a disconnect.
- Receive webhooks — delivery, signing, and registration.
- Events operations — the generated endpoint reference for reading the log.
- the API reference — the
SessionEventandEventPageobject definitions. - Errors — the HTTP failures you meet while reading it.