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

# Server Events

Server events are JSON messages emitted on the Streaming World control connection. They report Session state, the lifecycle of submitted World Events, media progress, captions, usage, and errors. Every message uses the common envelope below.

<Note>
  **Forward compatibility.** Vivix may add event types, fields, and enum values over time. Ignore values you do not recognize, retain the fields you need, and use the Session capabilities to decide which optional operations are available.
</Note>

<h2 id="streaming-world-server-event-envelope">
  Common envelope
</h2>

<ResponseField name={"event_id"} type={"string"}>
  Server-generated unique identifier for this message.
</ResponseField>

<ResponseField name={"type"} type={"string"}>
  Event discriminator, such as `session.ready` or `event.applied`.
</ResponseField>

<ResponseField name={"sequence"} type={"integer"}>
  Monotonically increasing position in this Session's server-event stream.
</ResponseField>

<ResponseField name={"created_at"} type={"integer"}>
  Unix timestamp in milliseconds when the server created the event.
</ResponseField>

<ResponseField name={"request_event_id"} type={"optional string"}>
  The client event\_id that caused this message, when the event is a direct result of a client command.
</ResponseField>

<Note>
  **Two different identifiers.** `event_id` identifies this server message. `request_event_id` correlates it with a client command. `world_event_id`, used by the domain Event lifecycle below, identifies the durable steering intent created by `event.create`.
</Note>

### Ordering and reconnects

Process messages in ascending `sequence` order. A gap means the client no longer has a complete live view, but `sequence` is not a replay cursor. After a control connection reconnects, the service sends `session.snapshot` instead of replaying missed messages. Treat the snapshot sequence as the new baseline.

A snapshot is a compact control-state view, not a transcript or a semantic description of everything that has happened in the world. Applications that need a durable audit trail should persist events while the connection is live.

## Which event should I listen to?

| Need | Listen to |
| - | - |
| Know the control connection is ready. | `session.ready` |
| Restore control state after reconnecting. | `session.snapshot` |
| Know a World Event passed admission. | `event.accepted` |
| Know a World Event first affected outgoing media. | `event.applied` |
| Know newer steering replaced the active frontier. | `event.superseded` |
| Render live or final captions. | `caption.delta` and `caption.completed` |
| Monitor running, held, stalled, or failed media. | `media.state.updated` |
| Monitor cumulative billable generation. | `usage.updated` |

* **Session**: [`session.ready`](#event-world-session-ready), [`session.snapshot`](#event-world-session-snapshot), [`session.closing`](#event-world-session-closing), [`session.closed`](#event-world-session-closed)
* **World Event**: [`event.accepted`](#event-world-event-accepted), [`event.applied`](#event-world-event-applied), [`event.superseded`](#event-world-event-superseded), [`event.cancelled`](#event-world-event-cancelled), [`event.failed`](#event-world-event-failed)
* **Media**: [`media.state.updated`](#event-world-media-state-updated)
* **Caption**: [`caption.delta`](#event-world-caption-delta), [`caption.completed`](#event-world-caption-completed)
* **Usage**: [`usage.updated`](#event-world-usage-updated)
* **Error**: [`error`](#event-world-error)

<h3 id="world-server-session-events">
  Session Events
</h3>

Connection readiness, reconnect state, and terminal Session transitions.

<h3 id="event-world-session-ready">
  session.ready
</h3>

Emitted once after a new control connection is established and the Session can accept client events. A reconnect receives `session.snapshot` instead.

<ResponseField name={"type"} type={"\"session.ready\""}>
  Event type.
</ResponseField>

<ResponseField name={"session"} type={"object"}>
  Current Session identity and state.

  <Expandable title="properties">
    <ResponseField name={"session_id"} type={"string"}>
      Session identifier.
    </ResponseField>

    <ResponseField name={"status"} type={"\"active\""}>
      Current REST Session lifecycle status.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name={"media"} type={"object"}>
  Current outgoing media state.

  <Expandable title="properties">
    <ResponseField name={"state"} type={"\"connecting\" | \"running\" | \"held\" | \"stalled\" | \"disconnected\" | \"failed\""}>
      Current media state.
    </ResponseField>

    <ResponseField name={"media_time_ms"} type={"integer"}>
      Current Session media time in milliseconds.
    </ResponseField>
  </Expandable>
</ResponseField>

```json session.ready theme={null}
{
  "event_id": "evt_server_001",
  "type": "session.ready",
  "sequence": 1,
  "created_at": 1784899200123,
  "session": {
    "session_id": "sws_01K4JH8X9M",
    "status": "active"
  },
  "media": {
    "state": "connecting",
    "media_time_ms": 0
  }
}
```

<h3 id="event-world-session-snapshot">
  session.snapshot
</h3>

Emitted after a control connection reconnects. It supplies the current control baseline; events missed while disconnected are not replayed.

<ResponseField name={"type"} type={"\"session.snapshot\""}>
  Event type.
</ResponseField>

<ResponseField name={"session"} type={"object"}>
  Current Session identity and state.

  <Expandable title="properties">
    <ResponseField name={"session_id"} type={"string"}>
      Session identifier.
    </ResponseField>

    <ResponseField name={"status"} type={"\"active\" | \"closing\""}>
      Current non-terminal Session status.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name={"media"} type={"object"}>
  Current outgoing media state.

  <Expandable title="properties">
    <ResponseField name={"state"} type={"\"connecting\" | \"running\" | \"held\" | \"stalled\" | \"disconnected\" | \"failed\""}>
      Current media state.
    </ResponseField>

    <ResponseField name={"media_time_ms"} type={"integer"}>
      Current Session media time in milliseconds.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name={"steering"} type={"object"}>
  Current World Event pointers.

  <Expandable title="properties">
    <ResponseField name={"applied_world_event_id"} type={"nullable string"}>
      Most recently applied World Event, if any.
    </ResponseField>

    <ResponseField name={"pending_world_event_id"} type={"nullable string"}>
      Accepted World Event awaiting application, if any.
    </ResponseField>
  </Expandable>
</ResponseField>

<Note>
  **Snapshot only.** Use this state to resume control. Do not infer the missing lifecycle or caption messages from the pointer values.
</Note>

```json session.snapshot theme={null}
{
  "event_id": "evt_server_214",
  "type": "session.snapshot",
  "sequence": 214,
  "created_at": 1784899256123,
  "session": {
    "session_id": "sws_01K4JH8X9M",
    "status": "active"
  },
  "media": {
    "state": "running",
    "media_time_ms": 48620
  },
  "steering": {
    "applied_world_event_id": "world_event_002",
    "pending_world_event_id": null
  }
}
```

<h3 id="event-world-session-closing">
  session.closing
</h3>

Emitted when shutdown has begun. Stop sending new client events and continue reading until `session.closed` or the connection ends.

<ResponseField name={"type"} type={"\"session.closing\""}>
  Event type.
</ResponseField>

<ResponseField name={"session_id"} type={"string"}>
  Session being closed.
</ResponseField>

<ResponseField name={"reason"} type={"string"}>
  Shutdown reason, such as `client_request`, `idle_limit`, or `service_shutdown`.
</ResponseField>

```json session.closing theme={null}
{
  "event_id": "evt_server_301",
  "type": "session.closing",
  "sequence": 301,
  "created_at": 1784899310450,
  "session_id": "sws_01K4JH8X9M",
  "reason": "client_request"
}
```

<h3 id="event-world-session-closed">
  session.closed
</h3>

Emitted when the Session reaches its terminal state. No further client event can affect this Session. The final cumulative usage is included for reconciliation.

<ResponseField name={"type"} type={"\"session.closed\""}>
  Event type.
</ResponseField>

<ResponseField name={"session_id"} type={"string"}>
  Closed Session identifier.
</ResponseField>

<ResponseField name={"reason"} type={"string"}>
  Terminal shutdown reason.
</ResponseField>

<ResponseField name={"usage"} type={"object"}>
  Final cumulative generated media usage.

  <Expandable title="properties">
    <ResponseField name={"generated_video_ms"} type={"integer"}>
      Total generated video duration in milliseconds.
    </ResponseField>

    <ResponseField name={"generated_audio_ms"} type={"integer"}>
      Total generated audio duration in milliseconds.
    </ResponseField>
  </Expandable>
</ResponseField>

```json session.closed theme={null}
{
  "event_id": "evt_server_302",
  "type": "session.closed",
  "sequence": 302,
  "created_at": 1784899310820,
  "session_id": "sws_01K4JH8X9M",
  "reason": "client_request",
  "usage": {
    "generated_video_ms": 81240,
    "generated_audio_ms": 79820
  }
}
```

<h3 id="world-server-domain-event-events">
  World Event Lifecycle
</h3>

These messages describe the lifecycle of the domain Event submitted with `event.create`. They use `world_event_id` so the domain identifier cannot be confused with the common-envelope `event_id`.

<Note>
  **There is no event.completed.** A World Event is an open-ended steering intent. Its influence may continue until newer steering takes over, so semantic completion would be misleading.
</Note>

<h3 id="event-world-event-accepted">
  event.accepted
</h3>

Emitted after `event.create` passes validation and admission. The service has taken responsibility for processing the World Event, but outgoing media has not necessarily changed.

<ResponseField name={"type"} type={"\"event.accepted\""}>
  Event type.
</ResponseField>

<ResponseField name={"request_event_id"} type={"string"}>
  The event\_id of the accepted event.create client message.
</ResponseField>

<ResponseField name={"world_event_id"} type={"string"}>
  Server-assigned identifier for the World Event.
</ResponseField>

```json event.accepted theme={null}
{
  "event_id": "evt_server_120",
  "type": "event.accepted",
  "sequence": 120,
  "created_at": 1784899230105,
  "request_event_id": "evt_world_020",
  "world_event_id": "world_event_020"
}
```

<h3 id="event-world-event-applied">
  event.applied
</h3>

Emitted when the first media causally influenced by the World Event enters the server-side outgoing media path. This is the earliest stable application boundary exposed by the API.

<Note>
  **What applied does not mean.** It does not confirm that a viewer rendered the media, that every requested development occurred, or that the Event stopped influencing the world.
</Note>

<ResponseField name={"type"} type={"\"event.applied\""}>
  Event type.
</ResponseField>

<ResponseField name={"request_event_id"} type={"string"}>
  The event\_id of the event.create client message.
</ResponseField>

<ResponseField name={"world_event_id"} type={"string"}>
  World Event that first affected outgoing media.
</ResponseField>

<ResponseField name={"effective_at"} type={"object"}>
  Server-observed application boundary.

  <Expandable title="properties">
    <ResponseField name={"media_time_ms"} type={"integer"}>
      Session media time at which the first influenced media entered the outgoing path.
    </ResponseField>
  </Expandable>
</ResponseField>

```json event.applied theme={null}
{
  "event_id": "evt_server_127",
  "type": "event.applied",
  "sequence": 127,
  "created_at": 1784899231440,
  "request_event_id": "evt_world_020",
  "world_event_id": "world_event_020",
  "effective_at": {
    "media_time_ms": 18420
  }
}
```

<h3 id="event-world-event-superseded">
  event.superseded
</h3>

Emitted when a newer World Event takes over the active steering frontier. If the older Event was already applied, its published consequences remain part of Session history.

<Note>
  **No rollback.** Superseding changes the unpublished future. It does not withdraw published media, erase established continuity, or make the world forget prior consequences.
</Note>

<ResponseField name={"type"} type={"\"event.superseded\""}>
  Event type.
</ResponseField>

<ResponseField name={"request_event_id"} type={"string"}>
  The event\_id of the newer client command that caused the transition.
</ResponseField>

<ResponseField name={"world_event_id"} type={"string"}>
  World Event that lost the active frontier.
</ResponseField>

<ResponseField name={"by_world_event_id"} type={"string"}>
  Newer World Event that took over steering.
</ResponseField>

<ResponseField name={"was_applied"} type={"boolean"}>
  Whether the superseded World Event had already crossed its application boundary.
</ResponseField>

<ResponseField name={"effective_at"} type={"object"}>
  Boundary where newer steering took over.

  <Expandable title="properties">
    <ResponseField name={"media_time_ms"} type={"integer"}>
      Session media time for the transition.
    </ResponseField>
  </Expandable>
</ResponseField>

```json event.superseded theme={null}
{
  "event_id": "evt_server_148",
  "type": "event.superseded",
  "sequence": 148,
  "created_at": 1784899236740,
  "request_event_id": "evt_world_021",
  "world_event_id": "world_event_020",
  "by_world_event_id": "world_event_021",
  "was_applied": true,
  "effective_at": {
    "media_time_ms": 23580
  }
}
```

<h3 id="event-world-event-cancelled">
  event.cancelled
</h3>

Emitted when `event.cancel` successfully removes a pending, unapplied World Event. Cancellation is available only when the Session advertises the corresponding capability.

<Note>
  **Applied Events cannot be cancelled.** Once influence has entered outgoing media, send a newer World Event to redirect what happens next.
</Note>

<ResponseField name={"type"} type={"\"event.cancelled\""}>
  Event type.
</ResponseField>

<ResponseField name={"request_event_id"} type={"string"}>
  The event\_id of the successful event.cancel client message.
</ResponseField>

<ResponseField name={"world_event_id"} type={"string"}>
  World Event removed before application.
</ResponseField>

```json event.cancelled theme={null}
{
  "event_id": "evt_server_165",
  "type": "event.cancelled",
  "sequence": 165,
  "created_at": 1784899240120,
  "request_event_id": "evt_cancel_022",
  "world_event_id": "world_event_022"
}
```

<h3 id="event-world-event-failed">
  event.failed
</h3>

Emitted when an accepted World Event cannot reach its application boundary. Validation and admission failures that occur before a World Event exists use the generic `error` event instead.

After `event.applied`, later Session or media problems are reported through `media.state.updated` or `error`; they do not retroactively fail the World Event.

<ResponseField name={"type"} type={"\"event.failed\""}>
  Event type.
</ResponseField>

<ResponseField name={"request_event_id"} type={"string"}>
  The event\_id of the original event.create client message.
</ResponseField>

<ResponseField name={"world_event_id"} type={"string"}>
  Accepted World Event that could not be applied.
</ResponseField>

<ResponseField name={"error"} type={"object"}>
  Structured error details.

  <Expandable title="properties">
    <ResponseField name={"type"} type={"string"}>
      Broad error category, such as `invalid_request_error` or `server_error`.
    </ResponseField>

    <ResponseField name={"code"} type={"string"}>
      Stable, machine-readable error code.
    </ResponseField>

    <ResponseField name={"message"} type={"string"}>
      Human-readable diagnostic message.
    </ResponseField>

    <ResponseField name={"param"} type={"optional string"}>
      Related request field, when known.
    </ResponseField>

    <ResponseField name={"retryable"} type={"boolean"}>
      Whether retrying after correcting transient conditions can succeed.
    </ResponseField>
  </Expandable>
</ResponseField>

```json event.failed theme={null}
{
  "event_id": "evt_server_171",
  "type": "event.failed",
  "sequence": 171,
  "created_at": 1784899242350,
  "request_event_id": "evt_world_023",
  "world_event_id": "world_event_023",
  "error": {
    "type": "server_error",
    "code": "event_application_failed",
    "message": "The accepted World Event could not be applied.",
    "retryable": true
  }
}
```

<h3 id="world-server-media-events">
  Media Events
</h3>

State transitions for the Session's outgoing media.

<h3 id="event-world-media-state-updated">
  media.state.updated
</h3>

Emitted when outgoing media changes state. A transition caused by `session.hold` or `session.resume` includes the triggering `request_event_id`; autonomous transitions may omit it.

<ResponseField name={"type"} type={"\"media.state.updated\""}>
  Event type.
</ResponseField>

<ResponseField name={"media"} type={"object"}>
  Updated media state.

  <Expandable title="properties">
    <ResponseField name={"state"} type={"\"connecting\" | \"running\" | \"held\" | \"stalled\" | \"disconnected\" | \"failed\""}>
      New media state.
    </ResponseField>

    <ResponseField name={"previous_state"} type={"optional string"}>
      State immediately before this transition.
    </ResponseField>

    <ResponseField name={"media_time_ms"} type={"integer"}>
      Current Session media time in milliseconds.
    </ResponseField>

    <ResponseField name={"reason"} type={"optional string"}>
      Machine-readable reason for the transition.
    </ResponseField>
  </Expandable>
</ResponseField>

```json media.state.updated theme={null}
{
  "event_id": "evt_server_190",
  "type": "media.state.updated",
  "sequence": 190,
  "created_at": 1784899249870,
  "request_event_id": "evt_hold_001",
  "media": {
    "state": "held",
    "previous_state": "running",
    "media_time_ms": 33120,
    "reason": "client_hold"
  }
}
```

<h3 id="world-server-caption-events">
  Caption Events
</h3>

Incremental and final caption text aligned to Session media time.

<h3 id="event-world-caption-delta">
  caption.delta
</h3>

Emitted as caption text becomes available. Append deltas with the same `caption_id` in sequence order to render low-latency text.

<ResponseField name={"type"} type={"\"caption.delta\""}>
  Event type.
</ResponseField>

<ResponseField name={"caption_id"} type={"string"}>
  Identifier shared by all messages for this caption.
</ResponseField>

<ResponseField name={"delta"} type={"string"}>
  Text fragment to append.
</ResponseField>

<ResponseField name={"start_media_time_ms"} type={"integer"}>
  Approximate Session media time at which the caption begins.
</ResponseField>

```json caption.delta theme={null}
{
  "event_id": "evt_server_201",
  "type": "caption.delta",
  "sequence": 201,
  "created_at": 1784899252010,
  "caption_id": "caption_014",
  "delta": "I knew there was",
  "start_media_time_ms": 35640
}
```

<h3 id="event-world-caption-completed">
  caption.completed
</h3>

Emitted with the authoritative final text for a caption. Replace any locally assembled deltas for the same `caption_id` with `text`.

<ResponseField name={"type"} type={"\"caption.completed\""}>
  Event type.
</ResponseField>

<ResponseField name={"caption_id"} type={"string"}>
  Completed caption identifier.
</ResponseField>

<ResponseField name={"text"} type={"string"}>
  Authoritative final caption text.
</ResponseField>

<ResponseField name={"start_media_time_ms"} type={"integer"}>
  Approximate Session media start time for the caption.
</ResponseField>

<ResponseField name={"end_media_time_ms"} type={"integer"}>
  Approximate Session media end time for the caption.
</ResponseField>

```json caption.completed theme={null}
{
  "event_id": "evt_server_204",
  "type": "caption.completed",
  "sequence": 204,
  "created_at": 1784899253150,
  "caption_id": "caption_014",
  "text": "I knew there was more to your story.",
  "start_media_time_ms": 35640,
  "end_media_time_ms": 38220
}
```

<h3 id="world-server-usage-events">
  Usage Events
</h3>

Cumulative generated-media usage for metering and live cost displays.

<h3 id="event-world-usage-updated">
  usage.updated
</h3>

Emitted periodically as generated-media usage changes. Values are cumulative for the Session and may grow independently. Replace the previous totals; do not add updates together.

<ResponseField name={"type"} type={"\"usage.updated\""}>
  Event type.
</ResponseField>

<ResponseField name={"usage"} type={"object"}>
  Current cumulative generated-media usage.

  <Expandable title="properties">
    <ResponseField name={"generated_video_ms"} type={"integer"}>
      Cumulative generated video duration in milliseconds.
    </ResponseField>

    <ResponseField name={"generated_audio_ms"} type={"integer"}>
      Cumulative generated audio duration in milliseconds.
    </ResponseField>
  </Expandable>
</ResponseField>

```json usage.updated theme={null}
{
  "event_id": "evt_server_220",
  "type": "usage.updated",
  "sequence": 220,
  "created_at": 1784899258420,
  "usage": {
    "generated_video_ms": 51240,
    "generated_audio_ms": 49820
  }
}
```

<h3 id="world-server-error-events">
  Error Events
</h3>

Request-level and Session-level errors that do not belong to an accepted World Event.

<h3 id="event-world-error">
  error
</h3>

Emitted when client validation, Session control, or service processing fails. Most errors do not close the Session. If an accepted World Event specifically fails before application, the service emits `event.failed` instead.

<ResponseField name={"type"} type={"\"error\""}>
  Event type.
</ResponseField>

<ResponseField name={"request_event_id"} type={"optional string"}>
  The client event\_id that caused the error, when applicable.
</ResponseField>

<ResponseField name={"error"} type={"object"}>
  Structured error details.

  <Expandable title="properties">
    <ResponseField name={"type"} type={"string"}>
      Broad error category, such as `invalid_request_error` or `server_error`.
    </ResponseField>

    <ResponseField name={"code"} type={"string"}>
      Stable, machine-readable error code.
    </ResponseField>

    <ResponseField name={"message"} type={"string"}>
      Human-readable diagnostic message.
    </ResponseField>

    <ResponseField name={"param"} type={"optional string"}>
      Related request field, when known.
    </ResponseField>

    <ResponseField name={"retryable"} type={"boolean"}>
      Whether retrying after correcting transient conditions can succeed.
    </ResponseField>
  </Expandable>
</ResponseField>

```json error theme={null}
{
  "event_id": "evt_server_231",
  "type": "error",
  "sequence": 231,
  "created_at": 1784899261220,
  "request_event_id": "evt_cancel_030",
  "error": {
    "type": "invalid_request_error",
    "code": "event_not_cancelable",
    "message": "The World Event has already been applied.",
    "param": "world_event_id",
    "retryable": false
  }
}
```
