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

# List conversation messages (delta read by version cursor)

> Returns the messages in this conversation with `sequence_no > since`, plus the
conversation's `current_version` at the moment the response was generated. The
`messages` list is ordered by `sequence_no` ascending and is gap-free — if the
response contains messages with `sequence_no` 3, 4, 5, you have all three.

## Polling pattern

```text
since = 0
loop forever:
  r = GET /agents/conversations/{id}/messages?since={since}
  process r.messages       # may be empty
  since = r.current_version # use the head, not r.messages.last.sequence_no
  wait briefly
```

`current_version` is the conversation's head at the moment the response was generated
— using it as the next request's `since` is the only way to ensure gap-free reads
even when new messages land between the time the response was generated and the time
your client processed it.

## When messages get added

Messages are persisted in a single transaction by the workflow's finalize step (when
a run reaches a terminal state). You won't see partial reads — a run that produces 4
messages either lands all 4 or none.

A run's `id` appears in `messages[].run_id`, so you can correlate messages to the
runs that produced them.

## What's in `content_blocks`

Three discriminated variants:

- `text` — plain text. The dominant variant.
- `tool_use` — the model requesting a tool call. Carries `tool_use_id`, the aliased
  `name`, and the model's `arguments`.
- `tool_result` — the result of a tool call, nested as `content_blocks` inside the
  `tool_result` block. Carries the original `tool_use_id` plus an `is_error` flag.

The same `tool_use_id` appears on the assistant's `tool_use` block and on the
subsequent `tool` (or `user`, for resume payloads) turn's `tool_result` block —
that's how a UI matches them up.

## Permission

`agent_conversations` resource with `read` verb.

## Example: paging from scratch

```bash
curl "$API/agents/conversations/{id}/messages?since=0" \
  -H "Authorization: Bearer $TOKEN"
# → { "current_version": 4, "messages": [ <4 entries> ] }
```

Then later:

```bash
curl "$API/agents/conversations/{id}/messages?since=4" \
  -H "Authorization: Bearer $TOKEN"
# → { "current_version": 4, "messages": [] }   # nothing new
```



## OpenAPI

````yaml https://docs-cdn.narrative.io/api-reference/main/openapi.json get /agents/conversations/{id}/messages
openapi: 3.1.1
info:
  contact:
    email: support@narrative.io
    name: Narrative Support
    url: https://www.narrative.io
  termsOfService: https://www.narrative.io/legal/terms-of-service
  x-logo:
    url: >-
      https://cdn.narrative.io/images/company-logos/prod/narrative-logo-text-white.svg
    backgroundColor: rgb(9, 34, 166)
    altText: Narrative Logo
  description: >-
    The [Narrative Data Collaboration Platform](https://app.narrative.io) API is
    organized around REST. Our API has predictable resource-oriented URLs,
    accepts form-encoded request bodies, returns JSON-encoded responses, and
    uses standard HTTP response codes, authentication, and verbs.



    The current version is a pre-release beta.  It may result in unexpected
    behavior and there may be breaking changes in future releases up to the 1.0
    release.
  title: Narrative Data Collaboration Platform API
  version: 2.4.x
servers:
  - url: https://api-dev.narrative.io
  - url: https://api.narrative.io
security: []
tags:
  - name: Access Rules
    description: >-
      Access rules let data providers control who can purchase their data and
      the terms of purchase.


      The `access-rules` API allows you to manage access rules for your
      datasets.


      Related guides:
        - [What is an access rule?](https://kb.narrative.io/access-rules)
  - name: Agent Conversations
    description: >-
      Build LLM-driven workflows that can call MCP tools, ask the caller for
      input, and return structured answers.


      A conversation pins a model, a system prompt, and a tool catalog. Each run
      sends the model a user message (or

      a batch of tool outputs from a previously-paused run); the model decides
      whether to answer directly, call a

      server-side tool (resolved by the platform via Model Context Protocol), or
      call a client-side tool (which pauses

      the run with `requires_action` and waits for the caller to reply).


      Related guides:
        - [Agent Conversations Reference](https://docs.narrative.io/reference/architecture/agent-conversations)
        - [Error catalog](https://docs.narrative.io/reference/architecture/agent-conversations/errors/conversation-not-found)
  - name: MCP Connections
    description: >-
      Connect external (non-Narrative) MCP servers so agent runs can call their
      tools with the

      calling user's own OAuth authorization.


      A connection is created interactively: `POST /mcp-connections` runs OAuth
      discovery and

      Dynamic Client Registration against the server and returns a consent URL;
      the user authorizes

      in a browser; the authorization server redirects to `GET
      /mcp-connections/callback`, which

      finishes the token exchange and marks the connection `connected`. Tokens
      are stored encrypted

      and used server-side — they are never returned by the API. Once connected,
      reference the

      connection by id from an agent run's `mcp_servers[].connection_id`.
  - name: App Invites
    description: >-
      App invites allow applications to create one-time, shareable links on
      behalf of their users. These links

      enable users to invite third parties who do not have a Narrative account
      to perform actions within the

      application.


      For example, a Narrative user can send an invite link to a third party who
      then completes a Pinterest

      OAuth flow and creates a connector profile in the inviter's account,
      allowing the inviter to deliver

      audience data to the third party's Pinterest account without the third
      party needing a Narrative account.


      This API is intended to be used by applications (via app client
      credentials) to create and manage invites

      on behalf of their users, which can then be shared with invitees.
  - name: Apps
    description: >-
      Apps are applications bundled with a UI that can perform various actions
      on behalf of a user utilizing the Narrative API.

      Related guides:
        - [Building a Narrative Native App](https://www.narrative.io/knowledge-base/how-to-guides/building-a-narrative-native-app)
  - name: Access Tokens
    description: API Access Token management
  - name: Attributes
    description: >-
      An attribute models a standardized data point available for sale on the
      Narrative marketplace.


      Narrative automatically turns data points from provider datasets into
      attributes so that buyers can purchase well-formed, standardized data from
      any supplier on the marketplace.
  - name: Auth
    description: >-
      API token is a crucial step for developers to securely authenticate
      requests to the Narrative API

      Related guides:
        - [How to Create an API Token](https://www.narrative.io/knowledge-base/how-to-guides/understanding-narratives-apis/create-an-api-token)
  - name: Authentication
    description: User login and registration
  - name: Billings
    description: Used by Narrative internally to bill customers
  - name: Companies
    description: A collection of employees
  - name: Company Marketing Information
    description: Useful information related to companies
  - name: Compute Pools
    description: >-
      Compute pools represent compute resources (e.g. Snowflake warehouses)
      provisioned within a data plane.

      Companies can manage and share compute pools, assign them to jobs, and set
      a default compute pool on data planes.
  - name: Connections
    description: Associations between connectors and datasets
  - name: Data Shops
    description: |-
      Self-hosted website to sell your data
      Related guides:
        - [Setting up your datashop](https://www.narrative.io/knowledge-base/how-to-guides/shop-builder/settting-up-your-data-shop)
  - name: Data Streams
    description: >-
      The `data-stream` API endpoints allows one to create and update
      data-streams. Additionally the endpoints allow

      finding data-streams using free text search. A few of the endpoints are
      behind authorization.


      Update endpoint allows a client to post an edited data-stream document as
      is, without having to change its shape.

      The API ensures that only certain fields are allowed to be modified.
      Attempts to modify fields not up for client

      modifications are ignored.


      Related guides:
        - [What is a data stream?](https://kb.narrative.io/what-is-a-data-stream)
  - name: Contracts
    description: Contracts related APIs
  - name: Datasets
    description: >-
      Any kind of data, in any schema, can be pushed into the Narrative Data
      Collaboration Platform as a dataset exactly as it is stored in your own
      system.


      The `datasets` API allows you to manage your datasets.
  - name: Destinations
    description: >-
      Destinations associate a subscription to a profile. Optionally, ad-hoc
      quick settings can be configured to a destination.

      Those quick settings have to match the format defined on the app manifest.
  - name: Installations
    description: Installations of Applications for a profile
  - name: Jobs
    description: >-
      Jobs represent an operation done on a given data plane. All jobs today are
      tied to a query that represents a forecast or a materialized view.


      The jobs API provides an interface for interacting with the jobs table,
      which stores various operations involving reading or writing data. This
      API allows users to retrieve detailed information about specific jobs,
      including cost forecasts, data forecasts, NQL forecasts and materialized
      views.
  - name: Mappings
    description: >-
      A mapping is a transformation from a dataset to an attribute. Defining a
      mapping between a dataset and an attribute makes the dataset eligible to
      participate in subscriptions where a buyer is purchasing the target
      attribute.
  - name: Model Inference
    description: Model Inference
  - name: Models
    description: >-
      Machine learning models for training and inference.


      Models can be stored in HuggingFace or Narrative repositories and have
      configurable

      collaborator permissions for training and inference access.


      The `models` API allows you to list, retrieve, and update models
      accessible to your company.
  - name: NQL
    description: >-
      Narrative Query Language (NQL) is a specialized, SQL-inspired language
      designed to query and manipulate data within the Narrative platform. While
      it looks and feels much like standard SQL, it offers extended
      functionality and syntax that let you leverage platform-specific
      features—such as referencing datasets by their IDs, creating materialized
      views, or generating forecasts—without having to manage the complexities
      of different query engines behind the scenes. NQL queries can ultimately
      compile down to multiple underlying engines (e.g., Snowflake, Spark) to
      execute your requests efficiently in the Narrative ecosystem.
  - name: Payment Methods
    description: Payment methods used to purchase data
  - name: Products
    description: Internal routes used to offer datastream as products
  - name: Profiles
    description: >-
      App profiles are associated with an installation. They represent a
      reference to a configuration that the app can use to save confidential
      information outside of Narrative's control.

      Profiles are currently used to configure settings for connector apps.
  - name: Resources
    description: >-
      Narrative gives you access to managed resources, like your own AWS S3
      bucket, so that you can effortlessly buy and sell data on the platform.


      The `resources` API allows you to manage your resources.
  - name: Schema Inference
    description: >-
      The `schema-inference` API analyzes submitted files to automatically infer
      and return their structure as a dataset schema.
  - name: Schema Presets
    description: >-
      The `schema-presets` API allows you to list the available schema presets,
      get detailed information about a specific one and manage its life cycle.


      You can create a schema preset from scratch or create one based on an
      existing one, administrators can create platform wide available (public)
      schema preset.
  - name: Subscriptions
    description: >-
      In the Narrative Data Collaboration Platform a subscription represents a
      set of rules dictating the commercial terms related to the licensing of
      data.


      The `subscriptions` API allows you to set and get information about
      `subscription` objects owned by the authenticated account.
  - name: Uploads
    description: >-
      The `uploads` API allows you to send files to Narrative and use them to
      perform tasks like creating a list or adding data to a dataset.
  - name: Usage
    description: >-
      The `usage` API enables the recording of usage events associated with a
      product.
  - name: Workflows
    description: >-
      The `workflows` API allows you to create, schedule, trigger, and archive
      workflows.

      Workflows are defined using a serverlessworkflow YAML specification.
paths:
  /agents/conversations/{id}/messages:
    parameters:
      - in: path
        name: id
        required: true
        schema:
          $ref: '#/components/schemas/ConversationId'
        description: The conversation's UUID.
      - in: query
        name: since
        required: false
        schema:
          type: integer
          minimum: 0
          default: 0
        description: >-
          Return messages with `sequence_no > since`. Use the previous
          response's

          `current_version` as the next request's `since` for gap-free
          incremental reads.


          Default `0` returns the entire conversation from the beginning.
    get:
      tags:
        - Agent Conversations
      summary: List conversation messages (delta read by version cursor)
      description: >-
        Returns the messages in this conversation with `sequence_no > since`,
        plus the

        conversation's `current_version` at the moment the response was
        generated. The

        `messages` list is ordered by `sequence_no` ascending and is gap-free —
        if the

        response contains messages with `sequence_no` 3, 4, 5, you have all
        three.


        ## Polling pattern


        ```text

        since = 0

        loop forever:
          r = GET /agents/conversations/{id}/messages?since={since}
          process r.messages       # may be empty
          since = r.current_version # use the head, not r.messages.last.sequence_no
          wait briefly
        ```


        `current_version` is the conversation's head at the moment the response
        was generated

        — using it as the next request's `since` is the only way to ensure
        gap-free reads

        even when new messages land between the time the response was generated
        and the time

        your client processed it.


        ## When messages get added


        Messages are persisted in a single transaction by the workflow's
        finalize step (when

        a run reaches a terminal state). You won't see partial reads — a run
        that produces 4

        messages either lands all 4 or none.


        A run's `id` appears in `messages[].run_id`, so you can correlate
        messages to the

        runs that produced them.


        ## What's in `content_blocks`


        Three discriminated variants:


        - `text` — plain text. The dominant variant.

        - `tool_use` — the model requesting a tool call. Carries `tool_use_id`,
        the aliased
          `name`, and the model's `arguments`.
        - `tool_result` — the result of a tool call, nested as `content_blocks`
        inside the
          `tool_result` block. Carries the original `tool_use_id` plus an `is_error` flag.

        The same `tool_use_id` appears on the assistant's `tool_use` block and
        on the

        subsequent `tool` (or `user`, for resume payloads) turn's `tool_result`
        block —

        that's how a UI matches them up.


        ## Permission


        `agent_conversations` resource with `read` verb.


        ## Example: paging from scratch


        ```bash

        curl "$API/agents/conversations/{id}/messages?since=0" \
          -H "Authorization: Bearer $TOKEN"
        # → { "current_version": 4, "messages": [ <4 entries> ] }

        ```


        Then later:


        ```bash

        curl "$API/agents/conversations/{id}/messages?since=4" \
          -H "Authorization: Bearer $TOKEN"
        # → { "current_version": 4, "messages": [] }   # nothing new

        ```
      operationId: listAgentConversationMessages
      responses:
        '200':
          description: >-
            A page of messages with `sequence_no > since`, plus the current head
            version.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListMessagesResponse'
        '401':
          description: Missing or invalid bearer token.
        '403':
          description: Token does not have `agent_conversations:read` permission.
        '404':
          description: >-
            Conversation does not exist, belongs to a different company, or was
            created by a

            different user in the same company. Conversations are scoped to the

            `(company_id, user_id)` pair on the bearer token.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RFCError'
      security:
        - BearerAuth: []
components:
  schemas:
    ConversationId:
      description: >-
        UUID identifying a conversation. Returned from `POST
        /agents/conversations` and used

        in every other agent endpoint that operates on this conversation.
      type: string
      format: uuid
      example: bc2505b7-068d-44ed-8055-a6f6ffe54ab1
    ListMessagesResponse:
      description: >-
        Returned by `GET /agents/conversations/{id}/messages?since=N`. Carries
        the

        conversation's current head version (`current_version`) plus the
        requested page of

        messages.


        **Polling pattern:**

        ```

        since = 0

        loop:
          response = GET /agents/conversations/{id}/messages?since={since}
          process response.messages
          since = response.current_version
          sleep or wait for an event
        ```


        The `current_version` is the conversation's head at the moment the
        response was

        generated — using it as the next request's `since` gives gap-free
        incremental

        reads.
      type: object
      required:
        - current_version
        - messages
      properties:
        current_version:
          $ref: '#/components/schemas/ConversationVersion'
        messages:
          type: array
          description: >-
            Messages with `sequence_no > since`. Empty array means no new
            messages — the

            conversation is at version `current_version` but you've already seen
            everything.
          items:
            $ref: '#/components/schemas/MessageDto'
    RFCError:
      description: >-
        RFC 7807 Problem Details. Returned with `Content-Type: application/json`
        for every

        4xx/5xx response on `/agents/*`.


        **Field semantics:**


        - `type` — stable URL to the docs page describing this error class.
        Treat it as the
          machine-readable identifier; the same URL is referenced by `RunError.docs_url` on
          failed runs. Optional on 500 errors that don't map to a documented class.
        - `title` — short human-readable summary.

        - `status` — HTTP status code echoed in the body (so clients that read
        only the body
          can still dispatch on status).
        - `detail` — request-specific detail. Includes which alias was
        malformed, which
          `tool_use_id` was missing, etc.
        - `instance` — the API surface group (`/agents` for all agent
        endpoints). Stable —
          it's the prefix you call, not the full path.
        - `log_id` — UUID correlated with server logs. Always include this when
        reporting an
          issue to support.
        - `debug` — usually `null`. Reserved for richer payloads in specific
        failure modes.


        Common error pages indexed by `type`:


        | HTTP | Common cause | Docs page |

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

        | 400 | Bad request body, validation failure | various — see `type` |

        | 404 | Resource not found, or owned by a different company |
        `/conversation-not-found`, `/agent-run-not-found` |

        | 409 | Conversation version moved while you were preparing the run |
        `/version-conflict` |

        | 500 | Internal error (schema drift, workflow start failure) |
        `/invalid-stored-field`, `/workflow-start-failed` |
      type: object
      required:
        - title
        - status
        - log_id
      properties:
        type:
          type:
            - string
            - 'null'
          format: uri
          example: >-
            https://docs.narrative.io/reference/architecture/agent-conversations/errors/invalid-tool-alias
        title:
          type: string
          example: Invalid Tool Alias
        status:
          type: integer
          example: 400
        detail:
          type: string
          example: >-
            invalid tool aliases (must match ^[a-zA-Z][a-zA-Z0-9]{0,7}$):
            with_underscore
        instance:
          type: string
          example: /agents
        log_id:
          type: string
          format: uuid
          example: 1f6e5d4c-3b2a-1098-7654-3210fedcba98
        debug:
          type:
            - object
            - 'null'
      example:
        type: >-
          https://docs.narrative.io/reference/architecture/agent-conversations/errors/invalid-tool-alias
        title: Invalid Tool Alias
        status: 400
        detail: >-
          invalid tool aliases (must match ^[a-zA-Z][a-zA-Z0-9]{0,7}$):
          with_underscore
        instance: /agents
        log_id: 1f6e5d4c-3b2a-1098-7654-3210fedcba98
        debug: null
    ConversationVersion:
      description: >-
        Monotonic per-conversation counter, bumped by 1 (or more) every time a
        successful run

        appends messages. Two roles:


        1. **Delta cursor** for `GET .../messages?since=N` — fetch only messages
        with
           `sequence_no > N`.
        2. **Compare-and-swap token** for `POST .../runs` — set
        `expected_version` to the
           conversation's current head; if it doesn't match at run-creation time, you get a
           [Version Conflict](https://docs.narrative.io/reference/architecture/agent-conversations/errors/version-conflict)
           (HTTP 409).

        Always start a new run cycle by reading the current `version` from

        `GET /agents/conversations/{id}` rather than caching a value from
        earlier.
      type: integer
      minimum: 0
      example: 4
    MessageDto:
      description: >-
        A single persisted message. Returned by `GET
        /agents/conversations/{id}/messages`.


        Messages are ordered by `sequence_no` ascending. Use `sequence_no` as a
        stable cursor

        for paging — every successful run inserts one or more messages, and
        `sequence_no`

        increases by the number of messages inserted.
      type: object
      required:
        - id
        - sequence_no
        - role
        - content_blocks
        - run_id
        - created_at
      properties:
        id:
          type: string
          format: uuid
          description: Unique identifier for this message row.
          example: 7c2863a2-6a85-4c3f-b400-c55370e9b04a
        sequence_no:
          type: integer
          minimum: 1
          description: >-
            Monotonic position of this message within its conversation. Also
            used as the

            delta cursor for `GET .../messages?since=N` — pass the highest
            `sequence_no` you

            already have to fetch only newer messages.
          example: 2
        role:
          $ref: '#/components/schemas/MessageRole'
        content_blocks:
          type: array
          description: >-
            Ordered list of content blocks making up this message. Most messages
            have one

            block, but assistant tool-call turns may carry multiple `tool_use`
            blocks (the

            model issuing several tool calls in parallel) and `tool` turns may
            carry

            multiple `tool_result` blocks.
          items:
            $ref: '#/components/schemas/ContentBlock'
        run_id:
          type: string
          format: uuid
          description: >-
            The run that produced this message. Use it to cross-reference with
            `GET

            /agents/runs/{id}` to see which run the message belongs to (useful
            when multiple

            runs span the same conversation).
          example: 82eb9cb7-619e-46bb-ac1c-8a32b0112c1c
        created_at:
          type: string
          format: date-time
    MessageRole:
      description: >-
        Who produced this message.


        - `user` — the caller's input. Either a `user_message` payload, or a
        synthesized
          user-role turn carrying `tool_result` blocks when continuing from a
          `requires_action` run (Bedrock's chat-completion model requires tool results to
          live on a user-role turn).
        - `assistant` — the model's output. Either a plain answer (`text`
        blocks) or a
          tool-call request (`tool_use` blocks).
        - `tool` — synthesized turn that carries `tool_result` blocks from
        server-side tool
          calls. Conceptually distinct from `user` even though the underlying protocol may
          collapse them.
      type: string
      enum:
        - user
        - assistant
        - tool
    ContentBlock:
      description: >-
        A single block inside a message. Messages carry an ordered list of
        blocks. Three

        variants discriminated by `type`:


        - `text` — plain text content. The dominant variant for user messages
        and final
          assistant answers.
        - `tool_use` — the model requesting a tool call. Carries the call's
        `tool_use_id`
          (used to match it with the subsequent result), the fully-aliased `name`, and the
          `arguments` the model produced.
        - `tool_result` — the response to a `tool_use`. References the same
        `tool_use_id`
          and carries nested content blocks with the actual result text. `is_error` flags
          tool failures so the model can decide how to react.

        The discriminator field is `type`. Use it to dispatch in your client.
      oneOf:
        - title: Text
          type: object
          required:
            - type
            - text
          properties:
            type:
              type: string
              enum:
                - text
            text:
              type: string
          example:
            type: text
            text: What is 2 + 2?
        - title: ToolUse
          type: object
          required:
            - type
            - tool_use_id
            - name
            - arguments
          properties:
            type:
              type: string
              enum:
                - tool_use
            tool_use_id:
              type: string
              description: >-
                Server-assigned identifier the model produced when emitting the
                tool call.

                Use this to match a `tool_result` with its `tool_use`, and to
                fill

                `outputs[].tool_use_id` when resuming a `requires_action` run.
              example: tooluse_DWXPKZ50JDGib5GmShyUgJ
            name:
              type: string
              description: >-
                Fully-aliased tool name (`{alias}-{tool_name}`) — the same
                string the model

                saw in its tool catalog. The alias prefix is how the platform
                routes the call

                back to its definition.
              example: docs-search_narrative_i_o_knowledge_base
            arguments:
              type: object
              description: >-
                Arguments object produced by the model. Must conform to the
                tool's

                `input_schema` — if the model produced something that doesn't
                validate, the

                run terminates with the

                [MCP Tool Argument Validation
                Failed](https://docs.narrative.io/reference/architecture/agent-conversations/errors/mcp-tool-validation)

                error for server-side tools, or surfaces in `pending_tool_calls`
                as-is for

                client-side tools.
              example:
                query: rate limiting
        - title: ToolResult
          type: object
          required:
            - type
            - tool_use_id
            - content
            - is_error
          properties:
            type:
              type: string
              enum:
                - tool_result
            tool_use_id:
              type: string
              description: >-
                The `tool_use_id` from the matching `tool_use` block on the
                prior assistant turn.
              example: tooluse_DWXPKZ50JDGib5GmShyUgJ
            content:
              type: array
              description: >-
                Nested content blocks carrying the actual result. Almost always
                a single

                `text` block; the platform splits long results into multiple
                text blocks when

                the underlying MCP server returns multiple chunks.
              items:
                $ref: '#/components/schemas/ContentBlock'
            is_error:
              type: boolean
              description: >-
                Whether the tool call failed. The model decides how to react to
                errors —

                retry, ask the user, fall through to a partial answer, etc. Note
                that an

                MCP server returning a non-2xx response that the platform can
                still parse

                counts as `is_error: false` (the tool *succeeded* in returning a
                result, even

                if that result reports an underlying failure).
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer

````