# Get comments

Source: https://docs.recoupable.dev/api-reference/comments/get

Read existing stored comments on posts linked to a creator the caller can access. Authenticate with exactly one x-api-key or Authorization: Bearer credential.

## GET /api/comments

Full OpenAPI specification: https://docs.recoupable.dev/spec/social.json

## Authentication

This operation requires one of the security alternatives in the specification below. Security scheme definitions are included where present in the published specification.

[Authentication guide](https://docs.recoupable.dev/authentication)

## Operation and referenced schemas

```json
{
  "openapi": "3.1.0",
  "info": {
    "title": "Recoup API - Social",
    "description": "API documentation for the Recoup platform - an AI agent platform for the music industry",
    "license": {
      "name": "MIT"
    },
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "https://api.recoupable.dev"
    }
  ],
  "paths": {
    "/api/comments": {
      "get": {
        "operationId": "getComments",
        "description": "Read existing stored comments on posts linked to a creator the caller can access. Authenticate with exactly one x-api-key or Authorization: Bearer credential. Caller identity comes from authentication; artist_account_id is the required target creator UUID (the retained wire name), not a caller override. account_id and other unknown or repeated query parameters return 400.\n\nAccess uses direct roster, shared-organization and Recoup-administrator checks, plus endpoint-local self-creator and owning-organization-key reads. These read rules do not grant artist mutation permission. Missing and denied targets both return 403. Access is checked on each request; this is not an atomic guarantee against revocation during an executing request. Ownership follows the creator’s linked socials through the post, not the commenter’s social_id. Optional post_id is a stored posts.id UUID, never a URL, and only narrows the authorized linked-post set. An unrelated post or no linked socials returns an empty page; neither permits an unscoped read.\n\npage defaults to 1 and limit to 10; omitted or empty values use these defaults. Both otherwise require positive integers. Larger positive limits are accepted and clamped to 100; pagination.limit reports the effective value. Requests must satisfy page × effective_limit <= 2147483647 or return 400. Comments are ordered by commented_at descending, then id descending. Counts use the same ownership/post filter. Offset pages are deterministic for a fixed dataset, not a frozen snapshot during concurrent changes. A page beyond the results can be empty while retaining a nonzero exact total; no results means total_pages=0. A missing exact count is a 500, not an invented zero.\n\nEach result has only id, post_id, social_id, comment and commented_at. Saved nulls are preserved in the nullable fields; a missing comment author/text is not invented. The stored post join determines inclusion; nullable post_id describes the retained row projection, not permission to read unlinked rows. No joined profile, account or grant metadata is returned. GET success and errors use Cache-Control: private, no-store. Authentication failures are 401; lookup failures are a masked 500 with no unscoped/provider fallback.\n\nThis reads saved evidence and does not start scraping, enrichment, billing or comment-writing work. The existing link lookup reads at most 10,000 social links; stored reads do not establish complete ingestion/history, a live report, or the original customer weekly-report journey. The implementation is merged in API #1062; hosted/customer verification remains separate in Mono #327/app #2023.",
        "parameters": [
          {
            "name": "artist_account_id",
            "in": "query",
            "description": "Required target creator account UUID, using the retained artist wire name. It never overrides the authenticated caller.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "11111111-1111-4111-8111-111111111111"
          },
          {
            "name": "post_id",
            "in": "query",
            "description": "Optional stored posts.id UUID. Narrows the authorized linked-post set; an unrelated stored post returns an empty page. This is not a post URL.",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "22222222-2222-4222-8222-222222222222"
          },
          {
            "name": "page",
            "in": "query",
            "description": "Positive integer page, default 1 for omitted or empty values. page × effective_limit must not exceed 2147483647.",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            },
            "example": 1
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Positive requested page size, default 10 for omitted or empty values. Values above 100 are accepted and clamped to 100; the response reports the effective size.",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 10
            },
            "example": 10
          }
        ],
        "responses": {
          "200": {
            "description": "Stored comments and exact scoped pagination",
            "headers": {
              "Cache-Control": {
                "description": "All GET success and error responses disable shared caching.",
                "schema": {
                  "type": "string",
                  "const": "private, no-store"
                },
                "example": "private, no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommentsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid, repeated, unknown or unsupported-offset query parameters",
            "headers": {
              "Cache-Control": {
                "description": "All GET success and error responses disable shared caching.",
                "schema": {
                  "type": "string",
                  "const": "private, no-store"
                },
                "example": "private, no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommentsValidationErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing/conflicting credentials or rejected authentication; existing error or message envelope",
            "headers": {
              "Cache-Control": {
                "description": "All GET success and error responses disable shared caching.",
                "schema": {
                  "type": "string",
                  "const": "private, no-store"
                },
                "example": "private, no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommentsAuthenticationErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Target is missing or not accessible; both use the same denial",
            "headers": {
              "Cache-Control": {
                "description": "All GET success and error responses disable shared caching.",
                "schema": {
                  "type": "string",
                  "const": "private, no-store"
                },
                "example": "private, no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommentsErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Masked lookup/count failure; no unscoped or provider fallback",
            "headers": {
              "Cache-Control": {
                "description": "All GET success and error responses disable shared caching.",
                "schema": {
                  "type": "string",
                  "const": "private, no-store"
                },
                "example": "private, no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommentsErrorResponse"
                }
              }
            }
          }
        },
        "summary": "Read saved comments on an authorized creator’s linked posts",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ]
      }
    }
  },
  "components": {
    "schemas": {
      "CommentsResponse": {
        "type": "object",
        "required": [
          "status",
          "comments",
          "pagination"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ],
            "description": "Status of the request"
          },
          "comments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Comment"
            },
            "description": "Saved comments within the authorized creator’s linked-post set and optional post UUID filter."
          },
          "pagination": {
            "$ref": "#/components/schemas/CommentsPagination",
            "description": "Pagination metadata for the response"
          }
        },
        "additionalProperties": false,
        "example": {
          "status": "success",
          "comments": [
            {
              "id": "33333333-3333-4333-8333-333333333333",
              "post_id": "22222222-2222-4222-8222-222222222222",
              "social_id": "44444444-4444-4444-8444-444444444444",
              "comment": "Saved fixture comment",
              "commented_at": "2026-10-10T12:00:00Z"
            },
            {
              "id": "55555555-5555-4555-8555-555555555555",
              "post_id": "22222222-2222-4222-8222-222222222222",
              "social_id": null,
              "comment": null,
              "commented_at": "2026-10-09T12:00:00Z"
            }
          ],
          "pagination": {
            "total_count": 2,
            "page": 1,
            "limit": 10,
            "total_pages": 1
          }
        }
      },
      "CommentsValidationErrorResponse": {
        "type": "object",
        "required": [
          "status",
          "error",
          "missing_fields"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ]
          },
          "error": {
            "type": "string"
          },
          "missing_fields": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Validation path, possibly empty for unknown query keys."
          }
        }
      },
      "CommentsAuthenticationErrorResponse": {
        "anyOf": [
          {
            "type": "object",
            "properties": {
              "status": {
                "description": "Always `error`.",
                "type": "string",
                "enum": [
                  "error"
                ]
              },
              "error": {
                "description": "Human-readable error message or documented error code.",
                "type": "string"
              }
            },
            "required": [
              "status",
              "error"
            ],
            "additionalProperties": {}
          },
          {
            "type": "object",
            "properties": {
              "status": {
                "description": "Always `error`.",
                "type": "string",
                "enum": [
                  "error"
                ]
              },
              "message": {
                "description": "Human-readable authentication error message.",
                "type": "string"
              }
            },
            "required": [
              "status",
              "message"
            ],
            "additionalProperties": {}
          }
        ]
      },
      "CommentsErrorResponse": {
        "type": "object",
        "properties": {
          "status": {
            "description": "Always `error`.",
            "type": "string",
            "enum": [
              "error"
            ]
          },
          "error": {
            "description": "Human-readable error message or documented error code.",
            "type": "string"
          }
        },
        "required": [
          "status",
          "error"
        ],
        "additionalProperties": {}
      },
      "Comment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID of the comment"
          },
          "post_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Stored post UUID when present. Saved nullable row shape is retained; ownership is still enforced through the linked post join."
          },
          "social_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Stored commenter social UUID, or null when absent. This is not the creator ownership filter."
          },
          "comment": {
            "type": [
              "string",
              "null"
            ],
            "description": "Saved comment text, or null when unavailable; no text is synthesized."
          },
          "commented_at": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp with timezone of when the comment was made"
          }
        },
        "required": [
          "id",
          "post_id",
          "social_id",
          "comment",
          "commented_at"
        ],
        "additionalProperties": false
      },
      "CommentsPagination": {
        "type": "object",
        "properties": {
          "total_count": {
            "type": "integer",
            "description": "Exact total within the same authorized linked-post and optional post filter.",
            "minimum": 0
          },
          "page": {
            "type": "integer",
            "description": "Current page number",
            "minimum": 1
          },
          "limit": {
            "type": "integer",
            "description": "Effective page size after clamping to 100.",
            "minimum": 1,
            "maximum": 100
          },
          "total_pages": {
            "type": "integer",
            "description": "Ceiling of total_count/effective limit; zero when total_count is zero.",
            "minimum": 0
          }
        },
        "required": [
          "total_count",
          "page",
          "limit",
          "total_pages"
        ],
        "additionalProperties": false
      }
    },
    "securitySchemes": {
      "apiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Your Recoup API key. [Learn more](/quickstart#api-keys)."
      },
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer"
      }
    }
  }
}
```
