List feedback records
Changed onLists 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.
Request
- Base URLhttps://app.formbricks.com
- URLhttps://app.formbricks.com/api/v3/feedbackRecords
- AuthAPI key in header x-api-key
Query parameters
The feedback dataset to operate on (the dataset id, as shown by the MCP list_feedback_datasets tool). Required by the gateway on this operation: a missing or malformed value answers 400 before the request reaches the feedback store, and a dataset the API key's workspace is not assigned to answers 403. The two POST operations take the same value in the request body instead, and the single-record operations derive it from the record.
Records belonging to any of these logical submissions (e.g. response ids).
Records from any of these source types, e.g. survey, review.
Records from any of these sources (e.g. survey ids).
Records whose source display name is any of these. Prefer source_id where records carry one: a name can be edited or translated, the id is stable.
Every answer to any of these questions.
Records in any of these field groups (ranking / matrix questions).
Records of any of these field types. An empty value is ignored, so ?field_type= equals omitting the filter.
Records whose selected option id is any of these (e.g. every pick of one survey choice).
Everything any of these end users submitted.
Records given in any of these languages (ISO codes).
collected_at >= since (ISO 8601, inclusive). Must be between 1970-01-01 and 2080-12-31.
collected_at <= until (ISO 8601, inclusive).
created_at >= created_since (ISO 8601, inclusive). created_at is when the record was stored; collected_at (see since) is when the feedback was given. They diverge on a historical re-import, so this is the filter for "what did this import bring in".
created_at <= created_until (ISO 8601, inclusive).
updated_at >= updated_since (ISO 8601, inclusive). updated_at is stamped when a record is created, corrected, or enriched (translation, sentiment, emotions), so this selects records created or changed since a point — the filter for incremental extraction. Deleted records are never returned; see "Incremental extraction" above for choosing the next value and for detecting deletions.
updated_at <= updated_until (ISO 8601, inclusive).
value_number >= value_number_min (inclusive), e.g. NPS promoters with value_number_min=9. Records without a numeric answer are excluded. A max below the min answers 400.
value_number <= value_number_max (inclusive). Paired with the min it selects a band: 9..10 promoters, 0..6 detractors.
value_date >= value_date_min (inclusive) — bounds the answer to a date question, not when the feedback was collected.
value_date <= value_date_max (inclusive).
Records carrying any of these sentiment labels. An empty value is ignored.
Records tagged with any of these emotions: a record tagged {joy} and one tagged {joy, anger} both match ?emotions=joy&emotions=anger. There is no "all of them" form.
sentiment_score >= sentiment_score_min (inclusive). The score is continuous where the label is bucketed, so this is the filter for "the most negative feedback".
sentiment_score <= sentiment_score_max (inclusive).
true selects enriched records (a sentiment label is present), false the not-yet-enriched ones. Omit for no constraint.
true selects records carrying emotion labels, false those without. Note false covers both "not yet classified" and "classified, no emotion detected".
true selects translated records, false untranslated ones. Omit for no constraint.
Column to order by. collected_at and created_at never change. updated_at does, so it is accepted only with order=asc: a record that changes while you page then normally moves ahead of the cursor and is returned again (upsert it by id). Descending, or with no order (which defaults to desc), it answers 400. Even ascending, a write that started before the cursor's position and commits after it is missed by that run and picked up by the next, whose updated_since is the previous run's start minus an overlap. Use sort=updated_at&order=asc with updated_since and updated_until for incremental extraction.
Sort direction. Rows tied on the sort column are ordered by id ascending.
Page size. Out-of-range and non-numeric values are rejected — -5 and 1001 answer 400 — with one exception: limit=0 is indistinguishable from omitting the parameter, so it is accepted and the default of 100 applies. Send no limit rather than 0 if you are computing it. The search operations behave differently again: they clamp instead of rejecting.
Opaque keyset cursor returned as next_cursor by the previous page. Omit on the first request. A cursor is a position within one specific ordering: presenting it with a different sort or order answers 400 — restart without a cursor instead.
Response
Feedback records retrieved successfully
Changes
- info
added the new optional
queryrequest parameterupdated_since - info
added the new optional
queryrequest parameterupdated_until - info
added the new enum value
updated_atto thequeryrequest parametersort
- info
- info
added the optional property
//to the response with the status
- info