---
title: "List feedback records"
method: GET
path: "/api/v3/feedbackRecords"
tags: ["V3 Feedback Records"]
---

# List feedback records

`GET /api/v3/feedbackRecords`

## Request

- Base URL: `https://app.formbricks.com`
- URL: `https://app.formbricks.com/api/v3/feedbackRecords`
- Auth: API key in header `x-api-key`

Lists the feedback records of one dataset, newest first, with optional filters and keyset
pagination. Served by the API gateway, which authorizes the call against `tenant_id` and
forwards it to the feedback store — see the introduction for how the two layers answer.

Requires an API key with **read** permission (or higher) on a workspace the dataset is assigned
to. The organization must hold the Unify Feedback entitlement.

Multi-value filters are OR-ed within a parameter and AND-ed across parameters. Repeat the
parameter to pass several values (`?field_type=text&field_type=rating`); comma-separated values
are **not** split. Filters on enrichment members (`sentiment`, `emotions`, `sentiment_score_*`)
never match records that have not been enriched — use `has_sentiment=false` / `has_emotions=false`
to find those.

**Incremental extraction.** To keep a copy of a dataset in sync, filter on `updated_since` and
page with `sort=updated_at&order=asc`, bounded by `updated_until`: every page is then one index
range, and a record that changes while you page normally comes back later in the run. Start each
run with a `limit=1` request and read its `Date` header: that value is this run's `updated_until`
and, minus a few minutes, the next run's `updated_since` — both bounds from the same value, or
records between them are never read. Do not use the newest `updated_at` received instead: it
skips a write that was still committing when you read past its timestamp.
Upsert by `id` and let the most recent fetch win. Deleted records leave nothing for
`updated_since` to match, so reconcile deletions separately: periodically list every record id
with no `updated_since`, and remove the local records that were not returned and were created
before that listing's start (its first response's `Date` header) minus the overlap. Run one
sync at a time.

## Query parameters

- `tenant_id` string, cuid2, required
- `submission_id` string[]
- `source_type` string[]
- `source_id` string[]
- `source_name` string[]
- `field_id` string[]
- `field_group_id` string[]
- `field_type` FeedbackRecordFieldType[]
- `value_id` string[]
- `user_id` string[]
- `language` string[]
- `since` string, date-time
- `until` string, date-time
- `created_since` string, date-time
- `created_until` string, date-time
- `updated_since` string, date-time
- `updated_until` string, date-time
- `value_number_min` number, double
- `value_number_max` number, double
- `value_date_min` string, date-time
- `value_date_max` string, date-time
- `sentiment` FeedbackRecordSentiment[]
- `emotions` FeedbackRecordEmotion[]
- `sentiment_score_min` number, double
- `sentiment_score_max` number, double
- `has_sentiment` boolean
- `has_emotions` boolean
- `has_translation` boolean
- `sort` 'collected_at' | 'created_at' | 'updated_at'
- `order` 'asc' | 'desc'
- `limit` integer
- `cursor` string

## Response `200`

Feedback records retrieved successfully

- FeedbackRecordList
  - `data` FeedbackRecord[], required
    - `taxonomy` FeedbackRecordTaxonomy: Read-time view of the record's membership in the active directory taxonomy.
      - `status` 'classified' | 'unclassified' | 'no_active_taxonomy', required
      - `run_id` string, uuid, nullable, required
      - `path` object[], required: Currently visible non-root nodes in root-to-leaf order; empty unless classified.
        - `id` string, uuid, required
        - `label` string, required
        - `level` integer, required
        - `node_type` 'branch' | 'leaf', required
    - `id` string, uuid, required: UUIDv7 primary key.
    - `tenant_id` string, cuid2, required: The feedback dataset this record belongs to (the dataset id, as shown by the MCP `list_feedback_datasets` tool).
    - `collected_at` string, date-time, required: When the feedback was given.
    - `created_at` string, date-time, required: When the record was stored. Diverges from `collected_at` on a historical re-import.
    - `updated_at` string, date-time, required: When the record was last changed.
    - `source_type` string, required: Type of feedback source, e.g. `survey`, `review`, `feedback_form`.
    - `source_id` string: Reference to the survey / form / ticket the record came from.
    - `source_name` string: Human-readable name of the source.
    - `submission_id` string, required: Identifier of the logical submission this record belongs to (e.g. a response id). Records are unique per (`tenant_id`, `submission_id`, `field_id`).
    - `field_id` string, required: Identifier of the question / field.
    - `field_label` string: The question text.
    - `field_group_id` string: Stable identifier grouping related fields (ranking, matrix and grid questions).
    - `field_group_label` string: Human-readable question text for the group.
    - `field_type` 'text' | 'categorical' | 'nps' | 'csat' | 'ces' | 'rating' | 'number' | 'boolean' | 'date', required: The type of a feedback field, which determines which `value_*` member carries the answer: `text` → `value_text` (the only enrichable type), `categorical` → `value_text` + `value_id`, `nps` / `csat` / `ces` / `rating` / `number` → `value_number`, `boolean` → `value_boolean`, `date` → `value_date`.
    - `language` string: ISO language code the feedback was given in.
    - `user_id` string: End-user identifier (e.g. an anonymous id or an email hash).
    - `value_text` string: Text answer.
    - `value_id` string: Stable id of the selected option in the source system (e.g. a survey choice id), stored alongside `value_text` so choice answers keep their identity across label edits and languages. Opaque. Absent for free-text and non-choice answers.
    - `value_number` number, double: Numeric answer (ratings, NPS scores, numbers).
    - `value_boolean` boolean: Yes / no answer.
    - `value_date` string, date-time: Date answer.
    - `metadata` union: Arbitrary context stored with the record and returned with equivalent values (key order and number formatting are normalized). Omitted when the record has no metadata; a record explicitly stored with a JSON `null` returns `null`. An object is the supported shape and the only one the write operations accept; the wider union exists so records written before that convention still deserialize.
      - object
      - unknown[]
        - unknown
      - string
      - number
      - boolean
    - `sentiment` 'very_negative' | 'negative' | 'neutral' | 'positive' | 'very_positive' | 'mixed': Sentiment polarity label inferred from `value_text` by the sentiment enrichment: five ordinal levels plus a distinct `mixed`. Server-generated and read-only.
    - `sentiment_score` number, double: Signed sentiment polarity from `-1.0` (very negative) to `1.0` (very positive). Read-only; absent until the record is enriched.
    - `emotions` FeedbackRecordEmotion[]: Emotions inferred from `value_text`; multi-label from a fixed set. Read-only, and **never an empty array** — the column is either absent or has at least one label. Absent covers two different states: not yet classified, and classified with no emotion detected. Do not read absence as "not enriched yet"; `has_emotions=false` selects both, and the enrichment-status endpoint is what distinguishes them.
    - `value_text_translated` string: `value_text` translated into the dataset's configured target language. Read-only; absent until the record is enriched.
    - `translation_lang_key` string: BCP-47 locale that `value_text_translated` was produced in. Read-only; absent until the record is enriched.
  - `limit` integer, required: The page size that was applied.
  - `next_cursor` string: Opaque keyset cursor for the next page. **Present only when there may be more results**; absent on the last page. Pass it back unchanged as `cursor`, with the same `sort` and `order`.

## Other responses

- `400`: Bad Request. From the **gateway** (`text/plain`): a missing or malformed `tenant_id`; a request body that is not a single JSON object; a body whose top-level keys can be read more than one way (a repeated key, a key with non-ASCII characters, or a key that differs from `tenant_id` only in letter case); a `tenant_id` query parameter given more than once; or a path under `/api/v3/feedbackRecords` that is not one of the documented operations. A record id that is not a UUID and a path such as `/count` both fall in that last case and share the one `Unsupported FeedbackRecords route` body, so the two cannot be told apart from the response. From the **feedback store** (`application/problem+json`): schema validation failures — an unknown enum value, an inverted range, a malformed or re-ordered cursor, or a string with NULL bytes or invalid UTF-8.
- `401`: Unauthorized — no credentials, or an API key that does not exist or is revoked. Answered by the gateway as `text/plain`, before the request reaches the feedback store.
- `403`: Forbidden. Answered by the gateway as `text/plain` whenever the request cannot be authorized: the dataset does not exist, is archived, or is not assigned to a workspace the API key has the required permission on; the organization lacks the Unify Feedback entitlement; a mutation was attempted with an API key on a dataset shared by more than one workspace; or the record id does not exist. Existence is not leaked: an unknown dataset or record is refused here rather than with a 404. The single-record operations are the one exception, and only for a minute after a delete — see their `404`.
- `500`: Internal Server Error from the feedback store (`application/problem+json`, `code: internal_server_error`).
- `503`: Service Unavailable. From the **gateway** (`text/plain`): the record's dataset could not be resolved because the feedback store did not answer, the authorization service itself was unreachable, or the record's stored `tenant_id` is not a dataset id this API can address. From the **feedback store** (`application/problem+json`, `code: service_unavailable`): the operation needs a capability that is not configured on this instance — semantic search and similar records require an embedding model.

## Changes

- **2026-10-14** `801584c50960`: 3 info
  - added the new optional `query` request parameter `updated_since`
  - added the new optional `query` request parameter `updated_until`
  - added the new enum value `updated_at` to the `query` request parameter `sort`
- **2026-10-01** `f917fab96782`: 1 info
  - added the optional property `data/items/taxonomy` to the response with the `200` status
- **2026-09-09** `b127aa22f81c`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/formbricks/apis/formbricks-api-v3/changes/api/v3/feedbackRecords/get.md)

---

[API](https://skmtc.dev/formbricks/apis/formbricks-api-v3.md) - [All operations](https://skmtc.dev/formbricks/apis/formbricks-api-v3/llms.txt) - [OpenAPI document](https://skmtc.dev/formbricks/apis/formbricks-api-v3/revisions/25335c959a13?raw)
