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

# List Job Run Container Logs

> Read a Job Run’s saved container output (`stdout`) and error output (`stderr`) as lines. Get the run’s UUID from `GET /v1/job-runs`.

### Read logs
1. Call with no parameters to read from the first line, oldest first. Each page holds at most `max_bytes` of text (default 64 KiB).
2. While `has_more` is true, call again with `cursor` set to the returned `next_cursor`.
3. To follow a running container, keep calling with the latest `next_cursor`. `next_cursor` is never null; when you are caught up, the page is empty until new output arrives.
4. Stop when `complete` is true.

Use `tail=N` for the last N lines, and `since_ms` / `until_ms` to limit lines by time. The cursor remembers these filters, so send them only on the first request.

### Load earlier
Start with tail=N, then pass prev_cursor unchanged as cursor to load an adjacent earlier page. Optional tail controls backward page size (default 100). next_cursor always reads forward, including on a backward response.
prev_cursor_status is available, start, or unsupported_boundary. A null prev_cursor at unsupported_boundary means forced partial-line boundaries cannot safely be reconstructed; it does not mean history started there.
On backward requests, has_more refers to backward scan work. A bounded filtered scan may return an empty page with a new prev_cursor; continue with it. All pages remain in forward emission order. Backward pages keep complete=false; follow next_cursor to determine forward completion.

### Response
Each line has `timestamp_ms`, `stream` (`stdout` or `stderr`) and `message`. Lines are returned exactly once, in the order they were saved. A line still being written (no newline yet) is returned once it ends, once it exceeds 16 KiB, or when the run ends. `run_status` is the run’s status, so you can tell when the container has started or finished. `complete` turns true once the run has ended and you have read to the end of its output. It turns true no sooner than 15 seconds after the run ends, and 60 seconds when the server cannot confirm that nothing is still in transit. Treat it as best effort: output delayed in transit past these checks can still arrive afterwards.

### Errors
A 400 response carries `error.code`: `cursor_invalid` (the cursor is malformed or belongs to another run) or `parameters_conflict` (time filters sent with a cursor, or tail sent with a forward cursor). `cursor_expired` is reserved. `cursor_reset_required` means a bounded tail scan cannot reconstruct a partial line safely; restart without `tail` or `cursor`. A 503 with `log_history_not_ready` means the run’s saved output is still being indexed; retry after `Retry-After` seconds.

### Access
Sign requests with your API key and HMAC headers. You need `job_runs.read.own` and must be the job’s creator or the assigned node’s operator. Accounts with `job_runs.read.all` can read any run.

### Polling
This route uses the POLLING rate limit. Poll no more than once per second per run. A 429 response carries `Retry-After` with the seconds to wait.



## OpenAPI

````yaml https://api.dispersed.com/openapi.json get /v1/job-runs/{uuid}/container-logs
openapi: 3.1.0
info:
  title: Dispersed API
  version: 1.0.0
servers:
  - url: https://api.dispersed.com
security: []
paths:
  /v1/job-runs/{uuid}/container-logs:
    get:
      tags:
        - Job Runs
      summary: List Job Run Container Logs
      description: >-
        Read a Job Run’s saved container output (`stdout`) and error output
        (`stderr`) as lines. Get the run’s UUID from `GET /v1/job-runs`.


        ### Read logs

        1. Call with no parameters to read from the first line, oldest first.
        Each page holds at most `max_bytes` of text (default 64 KiB).

        2. While `has_more` is true, call again with `cursor` set to the
        returned `next_cursor`.

        3. To follow a running container, keep calling with the latest
        `next_cursor`. `next_cursor` is never null; when you are caught up, the
        page is empty until new output arrives.

        4. Stop when `complete` is true.


        Use `tail=N` for the last N lines, and `since_ms` / `until_ms` to limit
        lines by time. The cursor remembers these filters, so send them only on
        the first request.


        ### Load earlier

        Start with tail=N, then pass prev_cursor unchanged as cursor to load an
        adjacent earlier page. Optional tail controls backward page size
        (default 100). next_cursor always reads forward, including on a backward
        response.

        prev_cursor_status is available, start, or unsupported_boundary. A null
        prev_cursor at unsupported_boundary means forced partial-line boundaries
        cannot safely be reconstructed; it does not mean history started there.

        On backward requests, has_more refers to backward scan work. A bounded
        filtered scan may return an empty page with a new prev_cursor; continue
        with it. All pages remain in forward emission order. Backward pages keep
        complete=false; follow next_cursor to determine forward completion.


        ### Response

        Each line has `timestamp_ms`, `stream` (`stdout` or `stderr`) and
        `message`. Lines are returned exactly once, in the order they were
        saved. A line still being written (no newline yet) is returned once it
        ends, once it exceeds 16 KiB, or when the run ends. `run_status` is the
        run’s status, so you can tell when the container has started or
        finished. `complete` turns true once the run has ended and you have read
        to the end of its output. It turns true no sooner than 15 seconds after
        the run ends, and 60 seconds when the server cannot confirm that nothing
        is still in transit. Treat it as best effort: output delayed in transit
        past these checks can still arrive afterwards.


        ### Errors

        A 400 response carries `error.code`: `cursor_invalid` (the cursor is
        malformed or belongs to another run) or `parameters_conflict` (time
        filters sent with a cursor, or tail sent with a forward cursor).
        `cursor_expired` is reserved. `cursor_reset_required` means a bounded
        tail scan cannot reconstruct a partial line safely; restart without
        `tail` or `cursor`. A 503 with `log_history_not_ready` means the run’s
        saved output is still being indexed; retry after `Retry-After` seconds.


        ### Access

        Sign requests with your API key and HMAC headers. You need
        `job_runs.read.own` and must be the job’s creator or the assigned node’s
        operator. Accounts with `job_runs.read.all` can read any run.


        ### Polling

        This route uses the POLLING rate limit. Poll no more than once per
        second per run. A 429 response carries `Retry-After` with the seconds to
        wait.
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: uuid
          in: path
        - schema:
            type: string
            minLength: 1
            description: >-
              Either next_cursor or prev_cursor, copied unchanged. It carries
              position and time filters. Only backward cursors accept tail;
              neither accepts since_ms or until_ms.
          required: false
          description: >-
            Either next_cursor or prev_cursor, copied unchanged. It carries
            position and time filters. Only backward cursors accept tail;
            neither accepts since_ms or until_ms.
          name: cursor
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 1048576
            default: 65536
            description: >-
              Maximum UTF-8 bytes of `message` text per page (1–1,048,576;
              default 65,536). A page ends at a whole line; a single line longer
              than the cap is returned on its own.
          required: false
          description: >-
            Maximum UTF-8 bytes of `message` text per page (1–1,048,576; default
            65,536). A page ends at a whole line; a single line longer than the
            cap is returned on its own.
          name: max_bytes
          in: query
        - schema:
            type:
              - integer
              - 'null'
            minimum: 0
            description: >-
              Return only lines with `timestamp_ms` at or after this Unix time
              in milliseconds.
          required: false
          description: >-
            Return only lines with `timestamp_ms` at or after this Unix time in
            milliseconds.
          name: since_ms
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 10000
            description: >-
              Return the last N lines (1–10,000), oldest first, within
              max_bytes. With a backward cursor, select up to N earlier lines
              (default 100). next_cursor continues forward.
          required: false
          description: >-
            Return the last N lines (1–10,000), oldest first, within max_bytes.
            With a backward cursor, select up to N earlier lines (default 100).
            next_cursor continues forward.
          name: tail
          in: query
        - schema:
            type:
              - integer
              - 'null'
            minimum: 0
            description: >-
              Return only lines with `timestamp_ms` before this Unix time in
              milliseconds.
          required: false
          description: >-
            Return only lines with `timestamp_ms` before this Unix time in
            milliseconds.
          name: until_ms
          in: query
      responses:
        '200':
          description: A page of saved container output lines, with a cursor to continue.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JobRunContainerLogLinesResponse'
        '400':
          description: >-
            Invalid request: cursor_invalid, parameters_conflict, or
            cursor_reset_required (restart without tail or cursor).
            cursor_expired is reserved.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: >-
            The run’s saved output is still being indexed
            (`log_history_not_ready`). Retry after `Retry-After` seconds.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  schemas:
    JobRunContainerLogLinesResponse:
      type: object
      properties:
        complete:
          type: boolean
          description: >-
            True when the run has ended and this response reaches the end of its
            output. `tail`, `since_ms` and `until_ms` do not change this: it
            describes the whole output, not the filtered lines. Once true, no
            later request returns more lines; stop polling. It turns true no
            sooner than 15 seconds after the run ends, and 60 seconds after when
            the server cannot confirm that no output is still in transit. These
            checks are best effort: output delayed in transit past them (for
            example, a large burst at the end of the run, or a queue backlog
            longer than 10 minutes) can still arrive after `complete` is true.
        has_more:
          type: boolean
          description: >-
            More scan work remains in the requested direction. Follow
            next_cursor for forward reads or prev_cursor for backward reads,
            including on empty filtered pages.
        lines:
          type: array
          items:
            $ref: '#/components/schemas/JobRunContainerLogLine'
          description: Lines oldest first. Empty when there is nothing new.
        next_cursor:
          type: string
          minLength: 1
          description: >-
            Pass this value unchanged as `cursor` to continue after the last
            line returned. Never null: when you are caught up, calling again
            with it returns new lines or an empty page.
        prev_cursor:
          type:
            - string
            - 'null'
          minLength: 1
          description: >-
            Pass unchanged as cursor to load the adjacent earlier page. Null at
            history start or an unsupported partial boundary.
        prev_cursor_status:
          type: string
          enum:
            - available
            - start
            - unsupported_boundary
          description: >-
            Explains whether earlier history is readable. unsupported_boundary
            is not the start of history.
        run_status:
          type: string
          enum:
            - ASSIGNED
            - PREPARING
            - RUNNING
            - COMPLETED
            - CANCELLING
            - CANCELLED
            - FAILING
            - FAILED
          description: >-
            The run’s status when this page was read, so you can tell a running
            container from a finished one without another request.
      required:
        - complete
        - has_more
        - lines
        - next_cursor
        - prev_cursor
        - prev_cursor_status
        - run_status
      example:
        complete: false
        has_more: false
        lines:
          - message: Container started
            stream: stdout
            timestamp_ms: 1793750400000
          - message: 'warn: no GPU cache'
            stream: stderr
            timestamp_ms: 1793750400120
        next_cursor: eyJ2IjozfQ
        prev_cursor: null
        prev_cursor_status: start
        run_status: RUNNING
    ErrorResponse:
      type: object
      properties:
        error:
          $ref: '#/components/schemas/Error'
      required:
        - error
    JobRunContainerLogLine:
      type: object
      properties:
        message:
          type: string
          description: >-
            The line’s text, without the trailing newline. Terminal formatting
            and unsupported control characters are removed.
        stream:
          type: string
          enum:
            - stdout
            - stderr
          description: '`stdout` for standard output, `stderr` for error output.'
        timestamp_ms:
          type: integer
          exclusiveMinimum: 0
          description: >-
            When the node recorded the output this line began in, as a Unix
            timestamp in milliseconds. Lines recorded together share a
            timestamp.
      required:
        - message
        - stream
        - timestamp_ms
    Error:
      type: object
      properties:
        code:
          type: string
        details:
          type: array
          items:
            type: object
            properties:
              code:
                type: string
              expected:
                type: string
              input:
                type: string
              keys:
                type: array
                items:
                  type: string
              message:
                type: string
              path:
                type: array
                items:
                  anyOf:
                    - type: string
                    - type: number
            required:
              - message
              - path
          default: []
        message:
          type: string
        status:
          type: number
        timestamp:
          type: string
      required:
        - code
        - message
        - status
        - timestamp

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.