---
title: "List comments"
method: GET
path: "/api/apps/{app_id}/comments"
---

# List comments

`GET /api/apps/{app_id}/comments`

## Request

- Base URL: `https://app.base44.com`
- URL: `https://app.base44.com/api/apps/{app_id}/comments`
- Auth: HTTP bearer

<Info>This API is in beta. Endpoints, fields, and behavior may still change, so avoid depending on it in production.</Info>

Returns the app's open comment threads with all their comments, newest first. Set `include_resolved` to `true` to include resolved threads too.

Each [branch](/developers/references/app-management/get-started/concepts#branches) has its own comment threads. Set `branch_id` to work with a branch's threads. Leave it out for main's. Merging or deleting a branch deletes its threads, so treat a merged branch's threads as gone.

Threads are ordered by when they were created, newest first. A page holds up to `limit` threads, 500 by default. While `has_more` is `true`, request the next page with `cursor` set to `next_cursor` and the same `branch_id`, and nothing else beside them. A cursor works for 24 hours, only on this app and branch. A thread created after you start paging shows up on a fresh first page, not in later pages.

Screenshot links in the response work for one hour. Call this again for fresh ones, or use [Create comment screenshot link](/api-reference/create-comment-screenshot-link) for a link that lasts a year.

This is limited to 600 requests per minute per app, shared by [List comments](/api-reference/list-comments) and [List mentionable users](/api-reference/list-mentionable-users). A signed-in session has its own limit, and every personal access token for the workspace shares one. Some workspaces have a different limit. Every request that counts against the limit gets `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` (Unix time in seconds) headers, and a `429` also gets `Retry-After` in seconds. If the limiter itself is unavailable, requests go through without these headers.

<Note>Call this as an editor of the app, with a personal access token sent as a Bearer token or from a signed-in session. A read-only token works here. Workspace API keys aren't accepted.</Note>

<Warning>The response includes fields beyond the ones documented here. Don't rely on undocumented response fields, as they can change at any time.</Warning>

## Path parameters

- `app_id` string, required: ID of the app.

## Query parameters

- `include_resolved` boolean: Set to `true` to include resolved threads. Defaults to `false`.
- `limit` integer: Threads per page, 1 to 500. Defaults to 500.
- `cursor` string: `next_cursor` from the previous page. Send it on its own: it carries `include_resolved` and `limit`, so sending either beside it returns a `400`.
- `branch_id` string: ID of the branch to work on, as returned by [List branches](/api-reference/list-branches). Omit it to work on the app's main branch.

## Response `200`

One page of the app's comment threads, newest first.

- CommentThreadList: One page of an app's comment threads.
  - `items` CommentThreadItem[], required: Threads on this page, newest first.
    - `thread` CommentThread, required: A comment thread's state.
      - `id` string, required: ID of the thread.
      - `page_path` string, required: Path of the app page the thread is on.
      - `anchor` CommentAnchor, required: Where a comment thread is pinned in the app's preview. When the builder's AI edits the anchored file, Base44 moves an open thread's `source_location` to the element's new line.
        - `source_location` string, nullable: Position of the element's tag in the app's code, as `file:line:column`, exactly as the element's `data-source-location` attribute in the preview. The pin shows only on an element with this value. `null` for a thread with no pin.
        - `element_tag` string, nullable: HTML tag of the element, such as `button`.
        - `instance_index` integer, nullable: Which of the elements sharing `source_location` the pin is on, counting from `0` in page order, for an element repeated in a list. `null` means the first.
        - `point` CommentAnchorPoint: Fractions of the anchored element's rect, not of the document.
          - `x` number, required: Horizontal position, as a fraction of the element's width.
          - `y` number, required: Vertical position, as a fraction of the element's height.
        - `crop` CommentAnchorCrop: The screenshot's area in page pixels: the visible area plus the page's scroll when it was taken.
          - `x` number, required: Left edge, in page pixels.
          - `y` number, required: Top edge, in page pixels, scroll included.
          - `width` number, required: Width, in pixels.
          - `height` number, required: Height, in pixels.
        - `region` CommentAnchorRegion: The screenshot's area as fractions of the visible preview when it was taken, so it depends on the scroll.
          - `x` number, required: Left edge, as a fraction of the preview's width.
          - `y` number, required: Top edge, as a fraction of the preview's height.
          - `width` number, required: Width, as a fraction of the preview's width.
          - `height` number, required: Height, as a fraction of the preview's height.
        - `viewport_size` CommentAnchorViewportSize: Size of the preview when the screenshot was taken.
          - `width` integer, required: Width of the preview, in pixels.
          - `height` integer, required: Height of the preview, in pixels.
      - `screenshot_url` string, nullable, required: Signed link to the screenshot attached to the thread, valid for one hour. `null` when the thread has no screenshot or it can't be read.
      - `resolved_at` string, date-time, nullable, required: When the thread was resolved, in UTC, or `null` while it's open.
      - `message_count` integer, required: Number of replies, not counting the first comment.
      - `last_activity_at` string, date-time, nullable, required: When the thread was created or last replied to, in UTC.
      - `created_date` string, date-time, required: When the thread was created, in UTC.
      - `agent_working_since` string, date-time, nullable, required: When the AI started working on the thread, in UTC, or `null` when it isn't working on it. A value older than an hour is left over from a turn that stopped.
      - `unread` boolean, required: Whether the thread has a comment from someone else posted after you last read it. Always `false` in the responses of Create comment and Update comment anchor.
    - `comment` CommentMessage, required: One comment: the first comment of a thread or a reply.
      - `id` string, required: ID of the comment.
      - `content` string, required: Text of the comment.
      - `sender_id` string, required: ID of the Base44 user who wrote it, or `base44` for a reply from the AI.
      - `sender_name` string, required: Name of the author when they wrote it, or their email when they had no name. `Base44` for the AI.
      - `sender_avatar_url` string, nullable, required: URL of the author's profile image, or `null` when they have none.
      - `created_date` string, date-time, required: When the comment was posted, in UTC.
      - `edited_at` string, date-time, nullable, required: When the comment was last edited, in UTC, or `null` when it never was.
      - `reactions` object, required: Each emoji reacted with, mapped to the IDs of the users who reacted with it.
    - `replies` CommentMessage[], required: Replies, oldest first.
      - `id` string, required: ID of the comment.
      - `content` string, required: Text of the comment.
      - `sender_id` string, required: ID of the Base44 user who wrote it, or `base44` for a reply from the AI.
      - `sender_name` string, required: Name of the author when they wrote it, or their email when they had no name. `Base44` for the AI.
      - `sender_avatar_url` string, nullable, required: URL of the author's profile image, or `null` when they have none.
      - `created_date` string, date-time, required: When the comment was posted, in UTC.
      - `edited_at` string, date-time, nullable, required: When the comment was last edited, in UTC, or `null` when it never was.
      - `reactions` object, required: Each emoji reacted with, mapped to the IDs of the users who reacted with it.
    - `reactor_names` object, required: User ID mapped to name, for the people who reacted anywhere in the thread.
  - `has_more` boolean, required: Whether more threads follow this page.
  - `next_cursor` string, nullable, required: Pass as `cursor` to get the next page. `null` on the last page.

## Other responses

- `400`: `cursor` is invalid, expired, or from another app or branch, or `include_resolved` or `limit` was sent beside it.
- `401`: Missing or invalid credentials.
- `403`: You don't have editor access to this app, the app is blocked, or you used a workspace API key.
- `404`: App not found, or no branch you can see has this `branch_id`.
- `409`: Your workspace requires an unlocked SSO session.
- `422`: Validation Error
- `429`: Rate limit exceeded. Wait the number of seconds in `Retry-After` before retrying.

## Changes

- **2026-10-21** `c23775445de2`: 5 breaking, 2 warning, 2 info
  - `query` request parameter `cursor` list-of-types was narrowed by removing types `null`
  - `query` request parameter `include_resolved` list-of-types was narrowed by removing types `null`
  - `query` request parameter `limit` list-of-types was narrowed by removing types `null`
  - for the `query` request parameter `include_resolved`, the type changed from no type to `boolean`
  - …5 more
- **2026-10-11** `80287813ab84`: 1 info
  - the endpoint scheme security `PersonalAccessTokenAuth` was added to the API
- **2026-10-07** `dddf17e0f9f0`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/base44/apis/base44-app-management-api/changes/api/apps/:app_id/comments/get.md)

---

[API](https://skmtc.dev/base44/apis/base44-app-management-api.md) - [All operations](https://skmtc.dev/base44/apis/base44-app-management-api/llms.txt) - [OpenAPI document](https://skmtc.dev/base44/apis/base44-app-management-api/revisions/c534f09cf314?raw)
