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

# List conversations for the calling user against this agent.

> Returns long-lived dialog channels explicitly created via
`POST /agents/{agentId}/conversations`. **Synchronous one-shot
`POST /agents/{agentId}/invoke` calls do NOT appear here** —
they create short-lived `protocol=openapi` task channels that
surface under `GET /agents/{agentId}/tasks` and
`GET /tasks` instead. Mixing the two would conflate "long
dialog" with "single RPC call" and is intentionally avoided so
list semantics stay stable per channel kind.

If `GET /conversations` returns an empty list immediately after
an `invoke`, that is expected: call `GET /tasks` for the task
channel record, or pre-create a conversation with `POST
/conversations` and post messages into it explicitly.




## OpenAPI

````yaml /openapi/beeos-platform-v1.yaml get /api/v1/agents/{agentId}/conversations
openapi: 3.1.0
info:
  title: BeeOS OpenAPI (user contract)
  version: 1.1.0
  license:
    name: Proprietary
    identifier: LicenseRef-Proprietary
  description: |
    The **BeeOS OpenAPI** user contract. The sole implementer is
    **[`services/openapi-gateway`](../services/openapi-gateway)** (`openapi-gw`,
    local `:8095`). SDKs (Go / TypeScript) published under
    [`sdks/beeos-ai-sdk-go`](../../sdks/beeos-ai-sdk-go) and
    [`sdks/beeos-ai-sdk`](../../sdks/beeos-ai-sdk) are generated directly from
    this document.

    The main user Gateway (`services/gateway`, `:9080`) serves web / mobile /
    desktop clients and is **not** described by this document. It maintains its
    own route set independent of the SDK contract.

    **Auth:** every operation requires either a user **JWT** or a **`oag_`**
    User API Key, both on `Authorization: Bearer`. Agent-level protocol
    endpoints (A2A JSON-RPC with `bak_`, MCP) live in
    [beeos-agent-integration-v1.yaml](beeos-agent-integration-v1.yaml) and are
    hosted by A2A Gateway / MCP Gateway — not by `openapi-gateway`.

    ## Changelog

    ### 1.1.0 (ADR-0022 — dual-layer message storage)

    BREAKING for clients that depended on full streaming chunk replay
    via the persistent message log:

    * `GET /api/v1/agents/{agentId}/conversations/{convId}/messages`
      and `GET /api/v1/agents/{agentId}/tasks/{taskId}/messages` now
      **default-filter out** ephemeral streaming chunks
      (`agent_reply_delta`, `agent_thought_chunk`,
      `agent_message_chunk`). Add `?include_deltas=true` to opt back in.
    * `latest_offset` continues to reflect the TRUE server-side max
      offset across both filtered and unfiltered rows, so `since=` /
      cursor-style pagination is unaffected.
    * SSE `/events` is **NOT** affected — live consumers continue to
      receive every envelope as it happens.
    * Backwards-compatible recovery path: SSE `/events` may now emit
      a `backfill_truncated` event frame when the client reconnects
      with `Last-Event-ID` before the oldest retained chunk. SDKs
      that don't recognise the frame should treat it as a hint to
      resume from `oldest_redis_offset` instead (the field carrying
      the smallest offset still readable on the per-channel stream).
    * **Clarified contract**: SSE event `offset` is strictly monotonic
      per channel but **NOT guaranteed contiguous** (ADR-0022 §1.2).
      Producer-side write failures may leave small holes; clients MUST
      use `offset > since`, never `offset == since + 1`.

    BREAKING for clients that depended on the `oag_` API key scope
    vocabulary:

    * The per-route scope gate (`agents:read`, `agents:write`,
      `tasks:read`, `tasks:write`, `files:read`, `files:write`,
      `instances:read`, `instances:write`, plus the `admin:*` wildcard)
      has been **removed entirely**. The 37 routes that previously
      required scopes are now governed exclusively by owner-ACL — an
      `oag_` User API Key inherits full access to its owner's resources.
    * The `403 insufficient_scope` error code is no longer emitted.
      Existing callers that branched on it should fold the case into
      their generic 403 / `forbidden` handler.
    * SDK clients that explicitly passed `scopes` to `createAPIKey`
      should drop that argument. The Web / Desktop UIs no longer render
      scope badges on existing keys.
    * Existing `oag_` keys continue to work unchanged — the owner_id
      binding is preserved, and no key needs to be re-issued.

    BREAKING for clients of `GET /api/v1/providers` that read the
    `capabilities` object on the wire:

    * 7 of the 8 `capabilities` field names are now **camelCase**
      (`longRunning`, `browserUse`, `codeExec`, `fileSystem`,
      `customImage`, `maxDurationSec`, `costModel`) — matching this
      OpenAPI contract. Previous wire emitted **snake_case**
      (`long_running`, `browser_use`, …) because the server DTO bound
      `capabilities` to the raw protobuf message
      (`*pb.ProviderCapabilities`) whose auto-generated `json` tags
      use snake_case. SDK consumers (`@beeos-ai/sdk`,
      `github.com/beeos-ai/sdk-go`) generated from this spec were
      **already unable** to read any `capabilities` field — the
      camelCase property names produced by the generator never
      matched the snake_case the server actually sent. Raw HTTP
      callers that hand-coded against the old snake_case names must
      migrate; SDK callers gain access to the field set for the first
      time without code changes.
    * Note: the `device` boolean was always a single word — its wire
      name (`device`) is unchanged. Only the seven multi-word
      capability fields are affected by the rename.
    * `meta`, `id`, `name`, `description`, `version` are unchanged.
    * A new drift-guard (`internal/dto/contract_test.go`) now locks
      the server DTO to the spec property names, so the two cannot
      silently diverge again.

    ### 1.0.0

    Initial OpenAPI contract (ADR-001).
servers:
  - url: https://openapi.beeos.ai
    description: Production
  - url: https://openapi-staging.beeos.ai
    description: Staging
  - url: http://localhost:8095
    description: Local dev (goreman)
security:
  - bearerAuth: []
tags:
  - name: deploy
    description: Provider / region / model catalog.
  - name: instances
    description: Agent instance lifecycle (deploy, start, stop, restart, destroy).
  - name: computer
    description: |
      Synchronous desktop control (linux / windows / mac instances):
      screenshot, click, move, scroll, type, key. Coordinates are
      absolute device pixels (origin top-left); query
      `GET /instances/{id}/computer` for the live screen geometry and
      supported action set. Android instances are controlled via the
      `mobile` tag instead (calling `computer` on an android instance
      returns 404). Aligns with Anthropic / OpenAI "computer use".
  - name: mobile
    description: |
      Synchronous mobile (android) control: screenshot, tap, swipe,
      scroll, type, key, press_button. Coordinates are absolute device
      pixels. Query `GET /instances/{id}/mobile` for live geometry and
      supported actions. Desktop instances are controlled via the
      `computer` tag (calling `mobile` on a desktop instance returns 404).
  - name: agents
    description: Agent listing, details, and synchronous invocation.
  - name: tasks
    description: |
      Async agent tasks (the canonical task core — see ADR-0017
      `0017-unified-task-core`). `tasks` differs from `agents/invoke` in three
      ways: it returns 202 immediately with a `task_id` (no blocking on the
      reply), exposes a polling endpoint plus an SSE event stream, and supports
      cancel/continue lifecycle controls.
  - name: conversations
    description: >
      Long-lived multi-turn dialogs. Unlike tasks (single-shot, finite, terminal

      status), conversations are open until the caller closes them — messages

      are individually persisted and addressable by offset. Use the conversation

      API when you need to interleave caller and agent turns over an extended

      session; use `tasks` when a single prompt has a bounded reply with
      optional

      pause-and-resume.
  - name: files
    description: |
      Presigned URLs for caller uploads (`POST /files/presign-upload`) and
      result downloads (`GET /files/presign-download`) — the canonical
      attachment mechanism shared by `agents/{id}/invoke` and
      `agents/{id}/tasks`. Callers obtain a short-lived signed URL, upload the
      bytes directly to object storage (S3 / MinIO), and pass the resulting
      `file_id` in subsequent invoke / task requests via `attachments`.
paths:
  /api/v1/agents/{agentId}/conversations:
    parameters:
      - name: agentId
        in: path
        required: true
        schema:
          type: string
          maxLength: 128
    get:
      tags:
        - conversations
      summary: List conversations for the calling user against this agent.
      description: |
        Returns long-lived dialog channels explicitly created via
        `POST /agents/{agentId}/conversations`. **Synchronous one-shot
        `POST /agents/{agentId}/invoke` calls do NOT appear here** —
        they create short-lived `protocol=openapi` task channels that
        surface under `GET /agents/{agentId}/tasks` and
        `GET /tasks` instead. Mixing the two would conflate "long
        dialog" with "single RPC call" and is intentionally avoided so
        list semantics stay stable per channel kind.

        If `GET /conversations` returns an empty list immediately after
        an `invoke`, that is expected: call `GET /tasks` for the task
        channel record, or pre-create a conversation with `POST
        /conversations` and post messages into it explicitly.
      operationId: listConversations
      parameters:
        - name: state
          in: query
          required: false
          description: |
            Filter by conversation state. `active` is a backward-compat
            wire alias for v2's `open` — both return non-closed
            conversations. `all` returns both open and closed.
            Default: `active`.
          schema:
            type: string
            enum:
              - active
              - open
              - closed
              - all
            default: active
        - name: since
          in: query
          required: false
          description: Pagination cursor returned by the previous call (`next_since`).
          schema:
            type: string
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            format: int32
            minimum: 1
            maximum: 100
      responses:
        '200':
          description: Paged list of conversations.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListConversationsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        5XX:
          $ref: '#/components/responses/ServerError'
components:
  schemas:
    ListConversationsResponse:
      allOf:
        - $ref: '#/components/schemas/SuccessEnvelope'
        - type: object
          required:
            - data
          properties:
            data:
              type: object
              required:
                - conversations
              properties:
                conversations:
                  type: array
                  items:
                    type: object
                    required:
                      - conversation_id
                      - agent_id
                      - state
                    properties:
                      conversation_id:
                        type: string
                      agent_id:
                        type: string
                      title:
                        type: string
                      state:
                        type: string
                        enum:
                          - active
                          - closed
                      metadata:
                        type: object
                        additionalProperties:
                          type: string
                      created_at:
                        type: string
                        format: date-time
                      last_activity_at:
                        type: string
                        format: date-time
                      closed_at:
                        type: string
                        format: date-time
                      modelOverrideId:
                        type: string
                        description: >
                          Session model override projected by Message Service
                          after

                          `PUT …/conversations/{id}/model` (session/set_model).
                          Absent

                          when the conversation uses the agent/instance default.
                      effectiveModelId:
                        type: string
                        description: >
                          Resolved effective model when Core/MS projects one
                          alongside

                          the override. Clients may fall back to modelOverrideId
                          when

                          this field is absent.
                next_since:
                  type: string
                  description: |
                    Pagination cursor. Empty when the caller has reached the
                    end of results.
    SuccessEnvelope:
      type: object
      required:
        - success
      properties:
        success:
          type: boolean
          const: true
    ErrorResponse:
      type: object
      required:
        - success
        - error
      properties:
        success:
          type: boolean
          const: false
        error:
          $ref: '#/components/schemas/Error'
    Error:
      type: object
      required:
        - code
        - message
      properties:
        type:
          type: string
          description: |
            RFC 9457 problem-type slug. Most SDK consumers key on
            `code` rather than `type`; this field is kept for
            structural parity with the wider apierror catalogue.
          enum:
            - api_error
            - invalid_request_error
            - authentication_error
            - permission_error
            - rate_limit_error
            - not_found_error
            - conflict_error
            - validation_error
        code:
          type: string
          description: >
            Stable machine-readable error code. See

            [docs/reference/errors.md](https://github.com/beeos-ai/beeos/blob/main/docs/reference/errors.md)

            for the complete catalogue.
          examples:
            - agent_not_found
            - agent_service_unavailable
            - service_timeout
            - conflict
            - forbidden
            - invalid_param
            - rate_limited
            - internal_error
        message:
          type: string
          description: Human-readable explanation; presentation-only, never key on this.
        request_id:
          type: string
          description: |
            Per-request correlation id propagated end-to-end (also returned
            in the `X-Request-Id` response header). Include this value when
            reporting errors so the server-side trace can be located.
        param:
          type: string
          description: |
            Field path of the offending parameter for validation-style
            errors (`code` in `missing_param` / `invalid_param`). Empty for
            errors that don't reference a specific field.
        metadata:
          type: object
          additionalProperties:
            type: string
          description: |
            Optional structured key-value hints for the client (retry
            strategy, related resource ids, etc.). Always a string-to-string
            map.
  responses:
    BadRequest:
      description: >-
        Malformed request body, query parameter, or path parameter (`code` is
        one of `invalid_json` / `invalid_body` / `invalid_param` /
        `missing_param`).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Unauthorized:
      description: >-
        Missing or invalid `Authorization` header (`code` is one of
        `unauthorized` / `invalid_token` / `missing_token`).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Forbidden:
      description: Caller doesn't own the resource. `code` is `forbidden`.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    NotFound:
      description: >-
        The agent / task / instance / conversation / webhook referenced doesn't
        exist or isn't visible to the caller. `code` is typically
        `agent_not_found`.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    RateLimited:
      description: >-
        Per-caller × endpoint quota exceeded. `code` is `rate_limited`. Honour
        `Retry-After` if present.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    ServerError:
      description: >
        Server-side fault. `code` is one of:


        - `internal_error` (500) — unexpected

        - `agent_offline` / `agent_service_unavailable` (503) — agent or
        messaging
          subsystem unreachable
        - `auth_unavailable` / `auth_transient` (503) — Auth gRPC transient

        - `service_timeout` (504) — gateway gave up waiting for the agent


        See
        [docs/reference/errors.md](https://github.com/beeos-ai/beeos/blob/main/docs/reference/errors.md).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: |
        Pass a user **JWT** or a **`oag_`** User API Key on the
        `Authorization: Bearer <token>` header. Both are validated by
        `openapi-gateway` against the Auth service.

        Both credential types are **user-scoped**: every key (and every
        JWT) is bound to exactly one owner, and every route grants the
        caller full access to that owner's own resources. Cross-tenant
        access is denied by owner-ACL inside the handlers — there is no
        per-route scope vocabulary on this API.

        > **Removed in v1.1.0:** the legacy `agents:*` / `tasks:*` /
        > `files:*` / `instances:*` scope set has been dropped together
        > with the `403 insufficient_scope` error. Existing `oag_` keys
        > automatically gain full owner-level access and do **not** need
        > to be re-issued. SDK calls that previously passed `scopes` to
        > `createAPIKey` should drop the argument. See the changelog at
        > the bottom of this spec for the full migration note.

````