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

# Get apiv2logs

> `GET /api/v2/logs` — this caller's runtime executions, newest first.

§63's cursor envelope (`?limit=&after=`). Tool names for the whole page
come from one grouped query rather than one per row, which is §89's rule
applied to the endpoint that would otherwise break it most obviously.

**Required scope:** `logs:read`. A key without it is a 403 naming the scope; the endpoint is never silently skipped.



## OpenAPI

````yaml /openapi/v2.yaml get /api/v2/logs
openapi: 3.0.3
info:
  description: >
    The Impel Labs public API, version 2.


    Every operation here is reachable with a machine credential and demands a

    scope, named on the operation as `x-required-scope`. Scopes are not implied
    by

    one another: a key that may read conversations cannot run an agent, and a
    key

    that may edit an assistant cannot preview its drafts.


    ## Streaming


    Set `"stream": true` on any execution endpoint and the body is

    `text/event-stream` instead of JSON. One contract for all three

    endpoints — `/responses`, `/conversations/{id}/messages` and

    `/agents/{id}/runs` — so an event means the same thing wherever it arrives.


    Event types (contract `2026-09-07`):


    - `response.started`

    - `response.output_text.delta`

    - `response.output_json.delta`

    - `response.tool.started`

    - `response.tool.completed`

    - `response.tool.failed`

    - `response.completed`

    - `response.failed`

    - `response.cancelled`


    Exactly one terminal event is emitted per stream, ever:
    `response.cancelled`, `response.completed`, `response.failed`. A stream

    that ends without one is a bug on this side, not a state to handle.


    A line beginning `:` is a comment, sent about every

    15 seconds so that proxies do not close an idle

    connection. Comments are not events and must be skipped, not parsed.


    **Failures before the first event are not streamed.** No credential, no

    credits, an unpublished agent, a schema this runtime cannot enforce: each is

    the ordinary JSON error with its own status code, because nothing has been

    written to the wire yet and §36 says not to answer 200 for a failed
    execution.

    Once `response.started` is on the wire the response is already a 200, and a

    later failure arrives as `response.failed` carrying the same

    `{"error": {code, message, request_id}}` payload.


    A client that disconnects is not billed differently and does not leave a run

    running forever: the run is cancelled and settles once, like any other.


    ## Structured output


    Ask for structured output (§31). `json_schema` validates the model's output
    against `schema` and retries a mismatch once before failing with **422**
    `structured_output_failed`, which names the paths that did not match. The
    bound is read before the run starts, so the number of model calls a
    structured request can cost is knowable in advance.


    The schema is checked when the request is parsed — before authorization,
    before the idempotency claim, and before any spend — so a schema this
    runtime cannot enforce is a 400 rather than a charge. `strict` is accepted
    only as `true`: there is no mode in which a schema is requested and not
    enforced.


    `POST /api/v2/reports` accepts `format: "json"` with a `schema`.


    ## Preview and drafts


    Run the agent's unpublished configuration (§94). **Asking is free; being
    allowed is not.** A machine caller needs the `agent:preview` scope, which is
    issued on its own and is not implied by any other — a key that may edit an
    assistant does not thereby preview its drafts.


    Without permission the answer is **404, not 403**: confirming that a draft
    exists but may not be run is the confirmation the not-found rule exists to
    withhold.


    Statuses a preview run may execute: `active`, `draft`, `paused`, `preview`.
    Without preview, only `active`.


    ## Errors


    Every failure is `{"error": {"code", "message", "request_id"}}`. Switch on

    `code`; `message` is prose. A failure that ends a run that had already
    started

    also carries `id`, `status` and `trace_id` (see `RunError`).


    | code | HTTP | run status |

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

    | `agent_not_available` | 409 | `failed` |

    | `agent_not_found` | 404 | `failed` |

    | `authentication_error` | 401 | `failed` |

    | `conversation_not_found` | 404 | `failed` |

    | `idempotency_conflict` | 409 | `failed` |

    | `internal_error` | 500 | `failed` |

    | `invalid_context` | 403 | `failed` |

    | `invalid_request` | 400 | `failed` |

    | `knowledge_unavailable` | 503 | `failed` |

    | `not_implemented` | 501 | `failed` |

    | `permission_denied` | 403 | `failed` |

    | `provider_unavailable` | 502 | `failed` |

    | `rate_limited` | 429 | `blocked` |

    | `run_not_found` | 404 | `failed` |

    | `structured_output_failed` | 422 | `failed` |

    | `tool_execution_failed` | 502 | `failed` |

    | `usage_blocked` | 402 | `blocked` |


    Two of those are decisions rather than faults, and both end a run as
    `blocked`:

    `usage_blocked` (**402** — the workspace cannot spend) and `rate_limited`

    (**429**). Filing either as `failed` is what makes an error-rate dashboard

    useless and a support answer wrong.


    A resource in another workspace and a resource that does not exist answer

    **404** identically, byte for byte. There is no 403 that confirms existence.


    ## Paging


    Cursor-based, `?limit=&after=` where the endpoint says so. Cursors are
    opaque

    base64url — do not build one. A raw ISO timestamp is not a cursor: `+00:00`

    decodes to a space in a query string, which silently breaks every second
    page.


    The v2 surface does **not** yet use one cursor name everywhere. `/logs`
    returns

    `next`; `/documents`, `/extractions`, `/reports` and `/knowledge/ingestions`

    return `next_cursor` and read it back as `cursor`;
    `/conversations/{id}/messages`

    returns `next_cursor` and reads it back as `after`, and its cursor is a
    message

    id rather than an encoded pair; `/jobs` pages by `offset` and returns no
    cursor

    at all. Each operation documents what it actually takes.


    ## Idempotency


    Send `Idempotency-Key` on any operation that lists it. Same key and same
    body

    replays the stored response including its status — so some resources replay

    **200** where they first answered **201** or **202**. Same key and a
    different

    body is **409**, never a replay.
  title: Impel Labs API v2
  version: 2.0.0
servers: []
security: []
paths:
  /api/v2/logs:
    get:
      tags:
        - logs
      description: >-
        `GET /api/v2/logs` — this caller's runtime executions, newest first.


        §63's cursor envelope (`?limit=&after=`). Tool names for the whole page

        come from one grouped query rather than one per row, which is §89's rule

        applied to the endpoint that would otherwise break it most obviously.


        **Required scope:** `logs:read`. A key without it is a 403 naming the
        scope; the endpoint is never silently skipped.
      operationId: get_logs
      parameters:
        - description: >-
            An opaque cursor. Pass back the value the previous page returned; do
            not construct or parse one. Cursors are base64url and may contain no
            padding — they are safe in a query string, which a raw ISO timestamp
            is not (`+00:00` decodes to a space and silently breaks every second
            page).
          in: query
          name: after
          required: false
          schema:
            type: string
        - description: >-
            Page size. Clamped to the endpoint's maximum rather than refused,
            except on `/conversations/{id}/messages`, which answers 400 for a
            limit above 100.
          in: query
          name: limit
          required: false
          schema:
            type: integer
      responses:
        '200':
          content:
            application/json:
              schema:
                description: >-
                  Cursor paging. Follow the cursor field this endpoint returns —
                  the v2 surface does not yet use one name for it
                  (`next_cursor`, `next`, or none at all on `/jobs`, which pages
                  by `offset`).
                properties:
                  data:
                    items:
                      type: object
                    type: array
                  has_more:
                    type: boolean
                  next:
                    description: >-
                      An opaque cursor. Pass back the value the previous page
                      returned; do not construct or parse one. Cursors are
                      base64url and may contain no padding — they are safe in a
                      query string, which a raw ISO timestamp is not (`+00:00`
                      decodes to a space and silently breaks every second page).
                    nullable: true
                    type: string
                  object:
                    enum:
                      - list
                    type: string
                type: object
          description: A page of results.
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: |-
            No credential, or one that does not resolve.

            Codes with this status: `authentication_error`
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: >-
            The credential is valid and does not carry the scope this operation
            demands, or the request context named something the caller does not
            hold. **Never** returned for a resource in another tenant — that is
            404, so that guessing ids cannot become an inventory.


            Codes with this status: `invalid_context`, `permission_denied`
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: >-
            Something failed on this side. The response carries a `request_id`;
            nothing else about the failure is disclosed.


            Codes with this status: `internal_error`
      security:
        - CompanyApiKey: []
        - ApplicationCredential: []
        - DashboardSession: []
components:
  schemas:
    Error:
      additionalProperties: false
      properties:
        error:
          description: >-
            Some endpoints add one extra key beside these three, carrying the
            resource the failure was about. Treat this object as open.
          properties:
            code:
              description: >-
                A stable machine-readable code. Switch on this, never on
                `message`, which is prose and may be reworded without a version
                bump.
              enum:
                - agent_not_available
                - agent_not_found
                - authentication_error
                - conversation_not_found
                - idempotency_conflict
                - internal_error
                - invalid_context
                - invalid_request
                - knowledge_unavailable
                - not_implemented
                - permission_denied
                - provider_unavailable
                - rate_limited
                - run_not_found
                - structured_output_failed
                - tool_execution_failed
                - usage_blocked
              type: string
            message:
              description: >-
                A sentence safe to show a person. It never contains a stack
                trace, a provider name, a database id or any prompt text — the
                diagnostic detail is logged against `request_id` and is not
                serialised.
              type: string
            request_id:
              description: >-
                Quote this in a support ticket. It is also on the `X-Request-Id`
                response header, including on responses that carry no body.
              type: string
          required:
            - code
            - message
          type: object
      required:
        - error
      type: object
  securitySchemes:
    CompanyApiKey:
      description: >-
        A managed API key, sent as `Authorization: Bearer <key>`. A workspace
        key is prefixed `tgcc_`; a reseller key is prefixed `tgpk_`. Keys carry
        scopes; the scope each operation demands is on the operation as
        `x-required-scope`.
      scheme: bearer
      type: http
    ApplicationCredential:
      description: >-
        An embedded application credential, sent as `Authorization: Bearer
        impa_<environment>_<id>.<secret>`. The environment is part of the
        credential, so a development credential can never read production data.
      scheme: bearer
      type: http
    DashboardSession:
      description: >-
        The dashboard's own session. Listed because the same endpoints serve the
        console; an integration uses a key.
      in: cookie
      name: tg_access
      type: apiKey

````