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

# Stream the operation event log via Server-Sent Events

> Always returns `text/event-stream` (SSE) on success.  The body
is a sequence of three kinds of lines (per RFC W3C SSE), each
terminated by `\n\n`:

### Frame shapes

**Normal event frame** — one `OperationEvent` JSON object in
`data:`, dispatchable via `event:`:
```
id: <integer event.id>
event: <OperationEventType, e.g. stdout|stderr|spawn|exit>
data: {"id": N, "ts": "...", "spid": ..., "type": "...", "data": {...}}
```
`event:` always matches `data.type`.  See `OperationEvent` and
`OperationEventType` for the per-type payload shape.  Native
`EventSource` dispatches each frame to the matching
`addEventListener("<type>", ...)` handler.

**Server-side error frame** — surfaces handler exceptions to
the connected client without tearing the TCP connection down
mid-stream.  Sent right before the server closes the response:
```
: stream ended with error, retry since last event id

event: sse_error
data: <one-line exception message>
```
The leading SSE comment line lets pure-EventSource clients see
the intent ("retry from Last-Event-Id"); the `sse_error` frame
carries the error text for logging.  Clients should treat this
as a non-clean close and reconnect with the standard SSE
`Last-Event-Id` semantics (see Resuming).

**Keepalive comment** — periodic `: keepalive\n\n` lines
emitted while the stream is idle.  They keep upstream
`proxy_read_timeout` / TCP idle timers fresh and have no
application meaning.  Spec-compliant; EventSource and any SSE
parser must ignore comment lines (those beginning with `:`).

### `follow` semantics

The `follow` parameter controls only whether the stream stays
open after the last currently-known event:

  * `follow=0` (default) — the server emits everything
    currently available and closes the connection.
  * `follow=1` — same start; if the operation is still
    running the server keeps the connection open and forwards
    new events as they appear.  For terminal operations
    `follow=1` is a no-op: there's nothing more to subscribe
    to, so the connection closes after the last event just
    like `follow=0`.

Optional filters:

  * `?spid=N` — only events whose `spid` (Spawned Process ID,
    the routing identity in `OperationEvent`) matches.
  * `?since=N` — only events with `id > N`.

`follow=1` requires `SPAWN` or `SPAWN_DISPOSABLE` on top of
the baseline `LIST` — a long-lived subscription reserves
per-operation server state.  `follow=0` is read-only and
stays under `LIST`.

### Resuming after a drop

Every normal frame carries `id:` — the canonical event id.
On reconnect send the last successfully received id in the
standard `Last-Event-Id` request header (or `?since=N` on
manual GETs); the server resumes from that point.

### Error handling

| Status | Meaning                                          | Client action                                |
|--------|--------------------------------------------------|----------------------------------------------|
| 200    | Stream open                                      | Read frames; reconnect with Last-Event-Id if the stream closes before reaching a terminal event |
| 400    | Malformed query/header (non-integer param)       | Fix request                                  |
| 401    | Missing/invalid auth                             | Refresh credentials                          |
| 403    | Auth lacks LIST (or SPAWN for `follow=1`)        | Use a token with the right scope             |
| 404    | Operation not in this namespace                  | Wrong UUID                                   |
| 410    | Stream is over but the terminal artifact isn't ready yet | Retry after `Retry-After`                    |
| 425    | Not yet ready (operation is still being prepared)| Retry after `Retry-After`                    |
| 502    | Upstream returned an error opening the stream    | Retry; if persistent, escalate               |
| 504    | Upstream took too long to respond                | Retry                                        |

A `200` followed by an `event: sse_error` frame and immediate
close is the in-band equivalent of "the stream broke after we
already committed to a 200" — handle it the same way as 410:
wait briefly, reconnect with `Last-Event-Id`.

See `OperationEvent` for the per-event shape.




## OpenAPI

````yaml https://eu-north.nebius.computer/static/api.yaml get /operations/{operationId}/events
openapi: 3.0.0
info:
  title: Contree API
  description: >
    Contree is a container management system combining virtual machine isolation
    with container operational efficiency. 

    Designed for security-sensitive workloads requiring hardware-enforced
    boundaries while maintaining

    container workflow compatibility.
  version: 1.0.0
servers:
  - url: '{baseUrl}/v1'
    description: |
      Nebius IAM-fronted endpoint (use `IAMBearerAuth` + `IAMProjectHeader`).
      Override `baseUrl` to point at a different deployment.
    variables:
      baseUrl:
        default: https://api.tokenfactory.nebius.com/sandboxes
        description: |
          Base URL of the contree service. Defaults to the public Nebius
          IAM-fronted endpoint; set to your self-hosted contree origin
          (e.g. `https://contree.example.com`) when running elsewhere.
security:
  - IAMBearerAuth: []
    IAMProjectHeader: []
tags:
  - name: images
    description: Operations related to container images
  - name: files
    description: Operations related to file uploads
  - name: instances
    description: Operations related to container instances
  - name: inspect
    description: Operations related to inspecting container images
  - name: operations
    description: Operations related to long-running operations
  - name: auth
    description: Authentication and token introspection
paths:
  /operations/{operationId}/events:
    get:
      tags:
        - operations
      summary: Stream the operation event log via Server-Sent Events
      description: >
        Always returns `text/event-stream` (SSE) on success.  The body

        is a sequence of three kinds of lines (per RFC W3C SSE), each

        terminated by `\n\n`:


        ### Frame shapes


        **Normal event frame** — one `OperationEvent` JSON object in

        `data:`, dispatchable via `event:`:

        ```

        id: <integer event.id>

        event: <OperationEventType, e.g. stdout|stderr|spawn|exit>

        data: {"id": N, "ts": "...", "spid": ..., "type": "...", "data": {...}}

        ```

        `event:` always matches `data.type`.  See `OperationEvent` and

        `OperationEventType` for the per-type payload shape.  Native

        `EventSource` dispatches each frame to the matching

        `addEventListener("<type>", ...)` handler.


        **Server-side error frame** — surfaces handler exceptions to

        the connected client without tearing the TCP connection down

        mid-stream.  Sent right before the server closes the response:

        ```

        : stream ended with error, retry since last event id


        event: sse_error

        data: <one-line exception message>

        ```

        The leading SSE comment line lets pure-EventSource clients see

        the intent ("retry from Last-Event-Id"); the `sse_error` frame

        carries the error text for logging.  Clients should treat this

        as a non-clean close and reconnect with the standard SSE

        `Last-Event-Id` semantics (see Resuming).


        **Keepalive comment** — periodic `: keepalive\n\n` lines

        emitted while the stream is idle.  They keep upstream

        `proxy_read_timeout` / TCP idle timers fresh and have no

        application meaning.  Spec-compliant; EventSource and any SSE

        parser must ignore comment lines (those beginning with `:`).


        ### `follow` semantics


        The `follow` parameter controls only whether the stream stays

        open after the last currently-known event:

          * `follow=0` (default) — the server emits everything
            currently available and closes the connection.
          * `follow=1` — same start; if the operation is still
            running the server keeps the connection open and forwards
            new events as they appear.  For terminal operations
            `follow=1` is a no-op: there's nothing more to subscribe
            to, so the connection closes after the last event just
            like `follow=0`.

        Optional filters:

          * `?spid=N` — only events whose `spid` (Spawned Process ID,
            the routing identity in `OperationEvent`) matches.
          * `?since=N` — only events with `id > N`.

        `follow=1` requires `SPAWN` or `SPAWN_DISPOSABLE` on top of

        the baseline `LIST` — a long-lived subscription reserves

        per-operation server state.  `follow=0` is read-only and

        stays under `LIST`.


        ### Resuming after a drop


        Every normal frame carries `id:` — the canonical event id.

        On reconnect send the last successfully received id in the

        standard `Last-Event-Id` request header (or `?since=N` on

        manual GETs); the server resumes from that point.


        ### Error handling


        | Status | Meaning                                          | Client
        action                                |

        |--------|--------------------------------------------------|----------------------------------------------|

        | 200    | Stream open                                      | Read
        frames; reconnect with Last-Event-Id if the stream closes before
        reaching a terminal event |

        | 400    | Malformed query/header (non-integer param)       | Fix
        request                                  |

        | 401    | Missing/invalid auth                             | Refresh
        credentials                          |

        | 403    | Auth lacks LIST (or SPAWN for `follow=1`)        | Use a
        token with the right scope             |

        | 404    | Operation not in this namespace                  | Wrong
        UUID                                   |

        | 410    | Stream is over but the terminal artifact isn't ready yet |
        Retry after `Retry-After`                    |

        | 425    | Not yet ready (operation is still being prepared)| Retry
        after `Retry-After`                    |

        | 502    | Upstream returned an error opening the stream    | Retry; if
        persistent, escalate               |

        | 504    | Upstream took too long to respond                |
        Retry                                        |


        A `200` followed by an `event: sse_error` frame and immediate

        close is the in-band equivalent of "the stream broke after we

        already committed to a 200" — handle it the same way as 410:

        wait briefly, reconnect with `Last-Event-Id`.


        See `OperationEvent` for the per-event shape.
      operationId: getOperationEvents
      parameters:
        - name: operationId
          in: path
          required: true
          description: The ID of the operation
          schema:
            $ref: '#/components/schemas/UUIDSchema'
        - name: follow
          in: query
          required: false
          allowEmptyValue: true
          description: |
            HTTP-flag boolean.  Present without a value (`?follow`)
            and `?follow=1` both enable; `?follow=0` and absence
            disable.  When enabled, the SSE stream stays open after
            the currently-known events have been sent — for a
            running operation new events are forwarded as they
            appear; for a terminal operation it has no effect.
          schema:
            type: string
            enum:
              - ''
              - '0'
              - '1'
            default: '0'
        - name: spid
          in: query
          required: false
          description: |
            Only emit events whose `spid` field matches.  Filter is
            applied server-side regardless of whether the operation is
            still running or already terminal.
          schema:
            type: integer
            minimum: 0
        - name: since
          in: query
          required: false
          description: |
            Only emit events with `id > since` (event IDs are
            monotonically increasing).  Overridden by the
            `Last-Event-Id` header when present.
          schema:
            type: integer
            minimum: 0
        - name: Last-Event-Id
          in: header
          required: false
          description: |
            Standard SSE resume header sent automatically by
            `EventSource` on reconnect.  Treated as `?since=N`;
            takes precedence over the query param.
          schema:
            type: integer
            minimum: 0
      responses:
        '200':
          description: |
            OK — `text/event-stream` body.  Normal events,
            `: keepalive` comments, and a terminal `event: sse_error`
            frame may all appear; see endpoint description.  With
            `follow=0` (or any terminal operation) the response ends
            after the last available event; with `follow=1` on a
            still-running operation the connection stays open until the
            client disconnects or the operation reaches a terminal
            state.
          headers:
            Content-Type:
              schema:
                type: string
                enum:
                  - text/event-stream
            Cache-Control:
              schema:
                type: string
                enum:
                  - no-cache
          content:
            text/event-stream:
              schema:
                $ref: '#/components/schemas/OperationEventStream'
              examples:
                ordered_lifecycle:
                  summary: SSE frames for a tiny successful run
                  value: >
                    : keepalive


                    id: 0

                    event: init

                    data:
                    {"id":0,"ts":"2026-06-08T20:00:00Z","spid":0,"type":"init","data":{"init_pid":1}}


                    id: 1

                    event: spawn

                    data:
                    {"id":1,"ts":"2026-06-08T20:00:00.10Z","spid":1,"type":"spawn","data":{"pid":4242}}


                    id: 2

                    event: exit

                    data:
                    {"id":2,"ts":"2026-06-08T20:00:00.12Z","spid":1,"type":"exit","data":{"pid":4242,"code":0}}
                broken_mid_stream:
                  summary: |
                    Stream that committed to 200 but then hit a
                    server-side error.  Reconnect with `Last-Event-Id: 1`.
                  value: >
                    id: 0

                    event: init

                    data:
                    {"id":0,"ts":"2026-06-08T20:00:00Z","spid":0,"type":"init","data":{"init_pid":1}}


                    id: 1

                    event: spawn

                    data:
                    {"id":1,"ts":"2026-06-08T20:00:00.10Z","spid":1,"type":"spawn","data":{"pid":4242}}


                    : stream ended with error, retry since last event id


                    event: sse_error

                    data: upstream event source closed unexpectedly
        '400':
          description: |
            Bad request — a query parameter failed to parse as an
            integer.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '410':
          description: |
            Gone — the live stream is finished but the terminal
            artifact isn't durable yet.  Reconnect after the
            `Retry-After` delay; the subsequent request will deliver
            the full event log.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                finalizing:
                  summary: Live stream over, terminal artifact still being committed
                  value:
                    status: 410
                    error: operation finished; events not yet durable
        '425':
          description: |
            Too Early (RFC 8470) — the operation isn't streamable yet.
            It may still be queued, or just starting up.  Retry after
            `Retry-After`.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                pre_attach:
                  summary: Operation accepted but events not yet flowing
                  value:
                    status: 425
                    error: events not yet available for operation ...
                pending_budget_exhausted:
                  summary: Operation still queued past the server's wait budget
                  value:
                    status: 425
                    error: Events not yet available; retry shortly.
        '502':
          description: |
            Bad Gateway — an upstream component returned an error
            opening the event source.  Retry after a short delay.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '504':
          description: |
            Gateway Timeout — an upstream component didn't respond
            within the server-side deadline.  Idempotent on this
            endpoint — retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  schemas:
    UUIDSchema:
      type: string
      format: uuid
      description: A UUID string
      example: 12345678-9abc-baba-deda-0123456789ab
    OperationEventStream:
      type: string
      format: binary
      description: >
        SSE response body for `GET /operations/{operationId}/events`.

        Wire format: a sequence of `\n\n`-separated frames; each frame

        is an
        [`OperationEventSSEFrame`](#/components/schemas/OperationEventSSEFrame).


        OpenAPI doesn't have first-class SSE support, so this content

        is typed as binary at the protocol level — but the per-frame

        contract is the linked schema.  Clients should parse frame by

        frame and either dispatch on the `event:` line (e.g. via

        native `EventSource`) or JSON-decode the `data:` payload.
    Error:
      type: object
      required:
        - error
      properties:
        error:
          oneOf:
            - type: string
            - type: object
            - type: array
              items:
                type: object
          description: Error message or structured validation errors
          example: Some error occurred, this is an example of error message
        status:
          type: integer
          description: HTTP status code
          example: 500
        traceback:
          type: array
          items:
            type: string
  responses:
    Unauthorized:
      description: |
        Unauthorized - Invalid or missing authentication credentials.
        Either the `Authorization` bearer token is missing or invalid, or
        the `Project` header is missing.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: Forbidden - Token does not have sufficient permissions
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: Not Found - The requested resource does not exist
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    IAMBearerAuth:
      type: http
      scheme: bearer
      bearerFormat: IAM
      description: |
        IAM bearer token issued by the Nebius IAM service. Sent as
        `Authorization: Bearer <iam-token>`. Must be combined with the
        `Project` header (see `IAMProjectHeader`).

        This is the recommended authentication scheme for new clients.
    IAMProjectHeader:
      type: apiKey
      in: header
      name: Project
      description: |
        Nebius project ID associated with the IAM token. Required together
        with `IAMBearerAuth`. Identifies the project context the request is
        scoped to.

````