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

# List a conversation's messages

> Lists the messages of a group or named channel the bot is a member of, newest first. The bot sees what a person who joined when it did would see: nothing sent before it joined, and no deleted messages. Direct messages cannot be read. `files` names each attached file; `GET /v1/conversations/{conversation_id}/files/{id}` returns its details and a download link. Needs `conversations:history`. Limited to 20 requests a minute per installation.



## OpenAPI

````yaml /openapi/prod.json get /v1/conversations/{conversation_id}/messages
openapi: 3.0.0
info:
  contact:
    email: support@workchats.com
    name: Workchats API Support
  description: >
    Post messages into Workchats as an installed App's bot user, and read

    the people and conversations it can reach.


    Authenticate with the bot token from the OAuth install, in the

    Authorization header as `Bearer wc_bot_…`. Every error response has

    the shape `{"ok": false, "error": {"code", "message"}, "request_id"}`.


    Lists are cursor-paginated: pass `limit` (default 200, at most 1000)

    and the previous page's `next_cursor` as `cursor`. `next_cursor` is

    null on the last page.


    ## Callbacks


    Workchats POSTs events to the App's registered callback URL, over

    HTTPS only. Answer with any 2xx within 3 seconds. Anything else,

    including a redirect, which is never followed, is retried after 1, 5

    and 30 minutes with the same event `id`, so ignore an `id` you have

    already handled. `X-Workchats-Attempt` counts the attempts from 1.


    Every event has the shape `{"id", "type", "workspace_id",

    "created_at", "data"}`:


    - `app.uninstalled`: `data` is `{"removed_by": {"id": "usr_…"} | null,
      "reason": "admin" | "app_disabled"}`. A company admin uninstalled
      the App, or Workchats disabled it (`removed_by` is null). The token
      has already stopped working. Revoking the token yourself through
      `POST /oauth/revoke` sends nothing.
    - `directory.changed`: `data` is `{"kind", "id"}`. Re-read the user
      with `GET /v1/users/{id}` or the conversation with
      `GET /v1/conversations/{id}`. Kinds: `user.created`,
      `user.updated` (name, email or title), `user.deactivated`
      (deactivated or removed), `conversation.created` (the bot was
      added), `conversation.renamed`, `conversation.archived` (groups),
      `conversation.deleted`, `app.removed_from_conversation`. User kinds
      need `users:read`; conversation kinds name only conversations the
      bot is, or was just, in. Ignore kinds you don't know.

    **Verifying a callback.** `X-Workchats-Signature` is `v1=` and the

    lower-case hex HMAC-SHA256 of `<X-Workchats-Timestamp>.<raw body>`,

    keyed with the App's signing secret. Compare in constant time, and

    refuse a timestamp more than 5 minutes old. For 24 hours after the

    secret is rotated the header carries two signatures,

    `v1=<new>,v1=<old>`: accept the request if any one matches.


    Worked example:


    - Signing secret: `whsec_docs_example`

    - X-Workchats-Timestamp: 1790593600

    - Body:
    `{"id":"evt_5f0c7a52-6d1e-4c1b-9a8e-2f4b7c3d1e90","type":"directory.changed","workspace_id":"cmp_0b7e4c1a-2d3f-4a5b-8c6d-7e8f9a0b1c2d","created_at":"2026-09-28T11:06:40Z","data":{"kind":"user.deactivated","id":"usr_9a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d"}}`

    - X-Workchats-Signature:
    v1=42ec825e4ad44370ea02385d30a001c349640cff974ed895361b3c7a080e5339
  title: Workchats Public API
  version: '1.0'
servers:
  - url: https://public-api.workchats.com
    variables: {}
security:
  - bearerAuth: []
tags:
  - description: Token checks
    name: Auth
  - description: Groups and channels the bot is a member of, and their messages
    name: Conversations
  - description: Files attached to messages the bot can read
    name: Files
  - description: Send, edit and delete the bot's messages
    name: Messages
  - description: Code exchange and token revocation
    name: OAuth
  - description: The workspace's people
    name: Users
paths:
  /v1/conversations/{conversation_id}/messages:
    get:
      tags:
        - Conversations
      summary: List a conversation's messages
      description: >-
        Lists the messages of a group or named channel the bot is a member of,
        newest first. The bot sees what a person who joined when it did would
        see: nothing sent before it joined, and no deleted messages. Direct
        messages cannot be read. `files` names each attached file; `GET
        /v1/conversations/{conversation_id}/files/{id}` returns its details and
        a download link. Needs `conversations:history`. Limited to 20 requests a
        minute per installation.
      operationId: WorkchatsApiServerWeb.PublicApi.ConversationMessageController.index
      parameters:
        - description: A group (`grp_`) or named channel (`chn_`) id
          in: path
          name: conversation_id
          required: true
          schema:
            type: string
        - description: Page size, 200 by default. Values above 1000 are clamped.
          in: query
          name: limit
          required: false
          schema:
            default: 200
            minimum: 1
            type: integer
        - description: >-
            The previous page's `next_cursor`, for older messages. Omit it for
            the newest page.
          in: query
          name: cursor
          required: false
          schema:
            type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiMessagesResponse'
          description: A page of messages
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
          description: >-
            `invalid_cursor`, `invalid_request` (a bad `limit`, or a body that
            could not be parsed) or `token_in_query`
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
          description: '`not_authed`, `invalid_auth` or `token_revoked`'
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
          description: >-
            `missing_scope` (the token lacks `conversations:history`) or
            `not_in_conversation` (the App was removed from the conversation)
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
          description: >-
            `conversation_not_found`: no such group or channel, or the App was
            never a member of it
        '406':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
          description: '`not_acceptable`: the Accept header does not allow application/json'
        '429':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
          description: '`rate_limited`: retry after the Retry-After header''s seconds'
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicApiError'
          description: '`internal_error`: something went wrong on our side'
      callbacks: {}
      security:
        - bearerAuth: []
components:
  schemas:
    PublicApiMessagesResponse:
      description: A page of messages, newest first.
      example:
        messages:
          - conversation_id: grp_6b0c1d2e-3f40-4a5b-8c6d-7e8f9a0b1c2d
            created_at: '2026-09-28T11:05:12Z'
            files:
              - id: fil_1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d
            id: gmsg_9b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d
            text: The signed contract
            thread_id: null
            type: file
            user_id: usr_9a1b2c3d-4e5f-4a6b-8c7d-8e9f0a1b2c3d
        next_cursor: null
        ok: true
      properties:
        messages:
          items:
            $ref: '#/components/schemas/PublicApiHistoryMessage'
          type: array
        next_cursor:
          description: Pass as `cursor` for older messages; null on the last page
          nullable: true
          type: string
        ok:
          enum:
            - true
          type: boolean
      required:
        - ok
        - messages
        - next_cursor
      title: PublicApiMessagesResponse
      type: object
    PublicApiError:
      description: Every non-2xx response from the public API.
      example:
        error:
          code: token_revoked
          message: The token has been revoked.
        ok: false
        request_id: F7p0mJcXk3tq2x0AAAAB
      properties:
        error:
          properties:
            code:
              description: >-
                Stable, machine-readable error code. Each code always comes with
                the same HTTP status, except `client_error` and `server_error`,
                which carry the response's own 4xx or 5xx status. New codes may
                be added; treat an unknown one as a generic failure.
              type: string
              x-extensible-enum:
                - cannot_dm_user
                - client_error
                - conversation_archived
                - conversation_not_found
                - file_not_found
                - idempotency_key_reused
                - internal_error
                - invalid_auth
                - invalid_blocks
                - invalid_client
                - invalid_cursor
                - invalid_grant
                - invalid_request
                - message_not_found
                - missing_idempotency_key
                - missing_scope
                - not_acceptable
                - not_allowed_in_conversation
                - not_authed
                - not_found
                - not_in_conversation
                - payload_too_large
                - rate_limited
                - server_error
                - token_in_query
                - token_revoked
                - unsupported_grant_type
                - unsupported_media_type
                - user_deactivated
                - user_not_found
            message:
              description: Human-readable explanation
              type: string
          required:
            - code
            - message
          type: object
        ok:
          enum:
            - false
          type: boolean
        request_id:
          description: Also sent as the X-Request-Id header. Quote it to support.
          type: string
      required:
        - ok
        - error
        - request_id
      title: PublicApiError
      type: object
    PublicApiHistoryMessage:
      description: >-
        A message in a group or channel: the message `POST /v1/messages`
        returns, with its sender, type, text and files.
      properties:
        conversation_id:
          description: '`grp_` or `chn_` prefixed'
          type: string
        created_at:
          format: date-time
          type: string
        files:
          description: Attached files; details from the files endpoint with `files:read`
          items:
            properties:
              id:
                description: '`fil_` prefixed'
                type: string
            required:
              - id
            type: object
          type: array
        id:
          description: '`gmsg_` prefixed'
          type: string
        text:
          description: >-
            The message text. Null for a system message: notices name people,
            including guests the App may not see, so an App gets only their type
            and time
          nullable: true
          type: string
        thread_id:
          description: >-
            The message this one replies to, `gmsg_` prefixed. Null unless the
            App can read that message in this same conversation: a reply to a
            message in another channel or group, or to one that is deleted,
            hidden or cleared for the App or older than its join, has null
          nullable: true
          type: string
        type:
          description: '`system` for notices such as someone joining'
          enum:
            - text
            - image
            - file
            - system
            - audio
            - video
            - location
            - contact
            - call
            - poll
            - survey
          type: string
        user_id:
          description: >-
            The sender, `usr_` prefixed. Null for a system message, and for a
            guest the App may not see: one hidden from the company, or one shown
            only in shared chats who is no longer in the group. The message
            itself is still listed
          nullable: true
          type: string
      required:
        - id
        - conversation_id
        - thread_id
        - created_at
        - user_id
        - type
        - text
        - files
      title: PublicApiHistoryMessage
      type: object
  securitySchemes:
    bearerAuth:
      description: Bot token (`wc_bot_live_…` or `wc_bot_test_…`) from the OAuth install
      scheme: bearer
      type: http

````