Skip to content

List feedback records with filters

GET
/v1/feedback-records
curl --request GET \
--url 'http://localhost:8080/v1/feedback-records?tenant_id=org-123&submission_id=550e8400-e29b-41d4-a716-446655440000&source_type=survey&source_type=review&source_id=survey-123&field_id=q1&field_group_id=feature_priority&field_type=text&field_type=rating&value_id=opt_very_satisfied&user_id=user-abc-123&source_name=After%20match%20survey&language=en&language=ar&since=2024-01-01T00%3A00%3A00Z&until=2024-12-31T23%3A59%3A59Z&created_since=2026-01-01T00%3A00%3A00Z&created_until=2026-12-31T00%3A00%3A00Z&updated_since=2026-01-01T00%3A00%3A00Z&updated_until=2026-12-31T00%3A00%3A00Z&value_number_min=9&value_number_max=10&value_date_min=2026-01-01T00%3A00%3A00Z&value_date_max=2026-12-31T00%3A00%3A00Z&sentiment=negative&sentiment=very_negative&emotions=anger&emotions=fear&sentiment_score_min=-0.5&sentiment_score_max=0.5&has_sentiment=true&has_emotions=true&has_translation=false&sort=collected_at&order=asc&limit=100&cursor=eyJ0IjoiMjAyNC0wMS0xNVQxMDozMDowMFoiLCJpIjoiMDE4ZTEyMzQtNTY3OC05YWJjLWRlZjAtMTIzNDU2Nzg5YWJjIn0%3D' \
--header 'Authorization: Bearer <token>'

Lists feedback records for a required tenant_id with optional additional filters and pagination

tenant_id
required

Tenant ID (required for isolation). NULL bytes not allowed.

string
>= 1 characters <= 255 characters /^[^\x00]*$/

Example

org-123

Tenant ID (required for isolation). NULL bytes not allowed.

submission_id
Array<string>
<= 100 items

Example

[
"550e8400-e29b-41d4-a716-446655440000"
]

Filter by submission ID to group records belonging to one logical submission. Repeat the parameter to match any of several submissions; the values are OR-ed, and a single occurrence behaves exactly as it always has. Comma-separated values are NOT split. NULL bytes not allowed.

source_type
Array<string>
<= 100 items

Example

[
"survey",
"review"
]

Filter by source type. Repeat the parameter to match any of several source types; the values are OR-ed. NULL bytes not allowed.

source_id
Array<string>
<= 100 items

Example

[
"survey-123"
]

Filter by source ID. Repeat the parameter to match any of several sources; the values are OR-ed. NULL bytes not allowed.

field_id
Array<string>
<= 100 items

Example

[
"q1"
]

Filter by field ID — every answer to one question. Repeat the parameter to match any of several fields; the values are OR-ed. NULL bytes not allowed.

field_group_id
Array<string>
<= 100 items

Example

[
"feature_priority"
]

Filter by field group ID (for ranking/matrix questions). Repeat the parameter to match any of several groups; the values are OR-ed. NULL bytes not allowed.

field_type
Array<string>
<= 9 items
Allowed values: text categorical nps csat ces rating number boolean date

Example

[
"text",
"rating"
]

Filter by field type. Repeat the parameter to match any of several types; the values are OR-ed. An empty value is ignored rather than rejected, so ?field_type= is the same as omitting the filter.

value_id
Array<string>
<= 100 items

Example

[
"opt_very_satisfied"
]

Filter by the source system’s stable option id (e.g. all records for one survey choice). Repeat the parameter to match any of several options; the values are OR-ed. NULL bytes not allowed.

user_id
Array<string>
<= 100 items

Example

[
"user-abc-123"
]

Filter by end-user identifier — everything one person submitted. Repeat the parameter to match any of several users; the values are OR-ed. NULL bytes not allowed.

source_name
Array<string>
<= 100 items

Example

[
"After match survey"
]

Filter by source display name — the human-readable label stored alongside source_id. Prefer source_id where the records carry one: a name can be edited or translated, while the id is stable. Repeat the parameter to match any of several names; the values are OR-ed. NULL bytes not allowed.

language
Array<string>
<= 100 items

Example

[
"en",
"ar"
]

Filter by the language the feedback was given in. Repeat the parameter to match any of several languages; the values are OR-ed. NULL bytes not allowed.

since

Filter by collected_at >= since (ISO 8601 format). Must be between 1970-01-01 and 2080-12-31.

string format: date-time

Example

2024-01-01T00:00:00Z

Filter by collected_at >= since (ISO 8601 format). Must be between 1970-01-01 and 2080-12-31.

until

Filter by collected_at <= until (ISO 8601 format). Must be between 1970-01-01 and 2080-12-31.

string format: date-time

Example

2024-12-31T23:59:59Z

Filter by collected_at <= until (ISO 8601 format). Must be between 1970-01-01 and 2080-12-31.

created_since
string format: date-time

Example

2026-01-01T00:00:00Z

Filter by created_at >= created_since (ISO 8601, inclusive). created_at is when Hub stored the record; collected_at (see since) is when the feedback was given. They diverge on a historical re-import, so this is the parameter for “what did this import bring in”. Must be between 1970-01-01 and 2080-12-31.

created_until
string format: date-time

Example

2026-12-31T00:00:00Z

Filter by created_at <= created_until (ISO 8601, inclusive). See created_since for how created_at differs from collected_at. Must be between 1970-01-01 and 2080-12-31.

updated_since
string format: date-time

Example

2026-01-01T00:00:00Z

Filter by updated_at >= updated_since (ISO 8601, inclusive). updated_at is stamped on every write to a record — create, update, and every enrichment result (translation, sentiment, emotions) — so this selects records created OR changed since a point: the parameter for incremental extraction. Pair it with sort=updated_at&order=asc. For the next run, use the time the previous run started (its first response’s Date header) minus an overlap, not the largest updated_at received. The overlap must outlast the longest write to a record; Hub does not bound that, so a few minutes is a default rather than a guarantee. Permanently deleted records are never returned: periodically list every record, upsert what it returns (which also repairs any change a slow write slipped past the overlap), and remove only local records that were not returned and were created before that listing started (its first response’s Date header) minus the overlap. See “Incremental extraction” in the filtering and sorting guide.

updated_until
string format: date-time

Example

2026-12-31T00:00:00Z

Filter by updated_at <= updated_until (ISO 8601, inclusive). See updated_since for what updates updated_at.

value_number_min
number format: double

Example

9

Filter by value_number >= value_number_min (inclusive) — e.g. NPS promoters with value_number_min=9. Records whose value_number is NULL (every non-numeric answer) are excluded, so this is never a no-op filter.

value_number_max
number format: double

Example

10

Filter by value_number <= value_number_max (inclusive). Paired with value_number_min it selects a band: 9..10 is the NPS promoters, 0..6 the detractors. Records whose value_number is NULL are excluded. Supplying a max below the min is a 400, not an empty result.

value_date_min
string format: date-time

Example

2026-01-01T00:00:00Z

Filter by value_date >= value_date_min (ISO 8601, inclusive) — bounds the answer to a date question, not when the feedback was collected. Records whose value_date is NULL are excluded.

value_date_max
string format: date-time

Example

2026-12-31T00:00:00Z

Filter by value_date <= value_date_max (ISO 8601, inclusive). Records whose value_date is NULL are excluded.

sentiment
Array<string>
<= 6 items
Allowed values: very_negative negative neutral positive very_positive mixed

Example

[
"negative",
"very_negative"
]

Filter by sentiment label. Repeat the parameter to match any of several labels (?sentiment=negative&sentiment=very_negative); the values are OR-ed. An empty value is ignored rather than rejected, so ?sentiment= is the same as omitting the filter. Records that have not been enriched carry no sentiment and are therefore never matched — use has_sentiment=false to find them.

emotions
Array<string>
<= 6 items
Allowed values: joy anger sadness fear surprise disgust

Example

[
"anger",
"fear"
]

Filter by emotion label. Repeat the parameter to match ANY of the listed emotions: a record tagged {joy} and a record tagged {joy, anger} both match ?emotions=joy&emotions=anger. There is no “must carry all of them” form. An empty value is ignored rather than rejected, so ?emotions= is the same as omitting the filter.

sentiment_score_min
number format: double
>= -1 <= 1

Example

-0.5

Filter by sentiment_score >= sentiment_score_min (inclusive). The score is continuous where the label is bucketed, so this is the parameter for “the most negative feedback”. Records that have not been enriched are excluded.

sentiment_score_max
number format: double
>= -1 <= 1

Example

0.5

Filter by sentiment_score <= sentiment_score_max (inclusive). Records that have not been enriched are excluded.

has_sentiment
boolean

Example

true

Filter on whether sentiment enrichment has produced a label: true selects records where sentiment IS NOT NULL, false selects those where it IS NULL. Omit for no constraint.

has_emotions
boolean

Example

true

Filter on whether the record carries emotion labels: true selects records where emotions IS NOT NULL, false selects those where it IS NULL. Note that false covers both “not yet classified” and “classified, no emotion detected” — the two are indistinguishable here. Omit for no constraint.

has_translation
boolean

Example

false

Filter on whether the record has been translated: true selects records where translation_lang_key IS NOT NULL, false selects those where it IS NULL. Omit for no constraint.

sort
string
default: collected_at
Allowed values: collected_at created_at updated_at

Column to order by. Defaults to collected_at. collected_at and created_at never change. updated_at does, so it is accepted only with order=asc: ascending, a record that changes while you page normally moves ahead of the cursor and is returned again (upsert it by id); descending, every such record would land behind the cursor and be lost, so that combination is a 400. Ascending still misses a write that started before the cursor’s position and commits after it; the next run picks it up when its 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, both derived from the Date header of one request made at the run’s start (see the filtering and sorting guide): every page is one ordered index range.

order
string
default: desc
Allowed values: asc desc

Sort direction. Defaults to desc, so sort=updated_at needs order=asc given explicitly. Rows tied on the sort column are ordered by id ascending.

limit

Number of results to return (max 1000)

integer format: int64
default: 100 >= 1 <= 1000

Number of results to return (max 1000)

cursor
string

Example

eyJ0IjoiMjAyNC0wMS0xNVQxMDozMDowMFoiLCJpIjoiMDE4ZTEyMzQtNTY3OC05YWJjLWRlZjAtMTIzNDU2Nzg5YWJjIn0=

Omit for the first page. For the next page, use the exact value from the previous response’s next_cursor. Opaque (base64-encoded); keyset pagination.

A cursor is a position within one specific ordering. Presenting it with a different sort or order returns 400 — restart pagination without a cursor instead. A cursor issued before sort control existed records no ordering; it is treated as a position in the default collected_at/desc listing and is accepted only there.

OK

Media typeapplication/json
object
data
required

List of feedback records

Array<object>
object
collected_at
required

When the feedback was collected

string format: date-time
created_at
required

When this record was created

string format: date-time
field_id
required

Identifier for the question/field

string
field_label

The actual question text

string
field_group_id

Stable identifier grouping related fields (for ranking, matrix, grid questions)

string
field_group_label

Human-readable question text for the group

string
field_type
required

Type of field

string
Allowed values: text categorical nps csat ces rating number boolean date
id
required

UUIDv7 primary key

string format: uuid
language

ISO language code. NULL bytes not allowed.

string
/^[^\x00]*$/
metadata

Arbitrary context stored with this record, returned with equivalent values (key order is normalized, and so are number exponents — 1e2 reads back as 100). 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 request schemas accept. The wider union is for reading: the column stores whatever JSON was written, so a record created before that convention can return an array or a scalar, and an object-only response schema would make a generated client reject it.

Array | object | string | number | boolean | null
sentiment

Sentiment polarity inferred from value_text (sentiment enrichment). Read-only; absent until the record is enriched.

string
Allowed values: very_negative negative neutral positive very_positive mixed
sentiment_score

Signed sentiment polarity from -1.0 (very negative) to 1.0 (very positive) (sentiment enrichment). Read-only; absent until the record is enriched.

number format: double
>= -1 <= 1
taxonomy
Any of:
object
status
required
string
Allowed values: classified unclassified no_active_taxonomy
run_id
required
One of:
string format: uuid
path
required

Current visible non-root nodes in root-to-leaf order; empty unless classified.

Array<object>
object
id
required
string format: uuid
label
required
string
level
required
integer format: int64
node_type
required
string
Allowed values: branch leaf
emotions

Emotions inferred from value_text (emotion enrichment); multi-label from a fixed set. Read-only; absent until the record is enriched, and never an empty array.

Array<string>
>= 1 items
Allowed values: joy anger sadness fear surprise disgust
source_id

Reference to survey/form/ticket ID

string
source_name

Human-readable name

string
source_type
required

Type of feedback source

string
submission_id
required

Identifier for the logical submission this record belongs to (required).

string
/^[^\x00]*$/
tenant_id
required

Tenant/organization identifier. NULL bytes not allowed.

string
/^[^\x00]*$/
translation_lang_key

BCP-47 target locale that value_text_translated was produced in (language enrichment). Read-only; absent until the record is enriched.

string
<= 35 characters
updated_at
required

When this record was last updated

string format: date-time
user_id

User ID (e.g., anonymous ID or email hash)

string
value_boolean

Boolean response

boolean
value_date

Date response

string format: date-time
value_number

Numeric response

number format: double
value_text

Text response. NULL bytes not allowed.

string
/^[^\x00]*$/
value_id

Stable id of the selected option in the source system (e.g. a survey choice id), stored alongside value_text for durable option identity. Opaque to Hub. Absent for free-text/non-choice answers. NULL bytes not allowed.

string
/^[^\x00]*$/
value_text_translated

Value_text translated into the tenant’s configured target language (language enrichment). Read-only; absent until the record is enriched.

string
/^[^\x00]*$/
limit
required

Limit used in query

integer format: int64
next_cursor

Opaque cursor for the next page (keyset paging). Present only when there may be more results. Use as the cursor query param for the next page.

string

Examples

Examplebasic

Basic list response

{
"data": [
{
"id": "018e1234-5678-9abc-def0-123456789abc",
"collected_at": "2024-01-15T10:30:00Z",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z",
"source_type": "survey",
"field_id": "q1",
"field_type": "rating",
"field_label": "How satisfied are you?",
"value_number": 9,
"source_id": "survey-123",
"source_name": "Q1 NPS Survey",
"user_id": "user-abc-123",
"tenant_id": "org-123",
"submission_id": "550e8400-e29b-41d4-a716-446655440000"
}
],
"limit": 100
}

Bad Request. Returned for an unknown enum value (sentiment, emotions, field_type, sort, order), an inverted range whose _min/_since exceeds its _max/_until, a malformed cursor, or a cursor issued for a different sort/order.

Media typeapplication/problem+json

RFC 9457 problem details (application/problem+json). Branch on the stable code member rather than on title or type.

object
code
required

Stable, machine-readable error code; the primary value to branch on. service_unavailable indicates a feature or dependency is temporarily unavailable and the request may be retried later. tenant_write_conflict indicates the write conflicted with an in-progress tenant data purge (or vice versa) and may be retried later unchanged. All other codes are terminal until the request itself is changed.

string
Allowed values: validation bad_request unauthorized forbidden not_found conflict tenant_write_conflict method_not_allowed content_too_large service_unavailable internal_server_error
detail

A human-readable explanation specific to this occurrence of the problem.

string
details

Optional structured context for this problem, e.g. resource_type on a 404.

object
key
additional properties
any
instance

A URI reference (typically the request path) identifying the specific occurrence of the problem.

string
invalid_params

Field-level validation failures (RFC 9457 “invalid_params” extension). Present on validation errors.

Array<object>
<= 100 items

A single invalid request parameter (RFC 9457 “invalid_params” extension member).

object
name
required

Dotted path to the offending request field, e.g. ‘field_type’ or ‘items[0].field_type’.

string
reason
required

Human- and agent-readable explanation of why the value is invalid, naming allowed values or constraints where applicable so the request can be corrected without guessing.

string
request_id
required

Identifier for this request, also returned in the X-Request-ID response header; include it when reporting a problem.

string
status
required

HTTP status code

integer format: int64
title
required

A short, human-readable summary of the problem type. This value should not change between occurrences of the error.

string
type

A URI reference identifying the problem type.

string format: uri

Examples

A range filter supplied backwards

{
"type": "https://hub.formbricks.com/problems/validation",
"title": "Validation Failed",
"status": 400,
"code": "validation",
"detail": "One or more request parameters are invalid",
"request_id": "0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
"invalid_params": [
{
"name": "sentiment_score_min",
"reason": "must be less than or equal to sentiment_score_max"
}
]
}

Unauthorized (missing or invalid API key)

Media typeapplication/problem+json

RFC 9457 problem details (application/problem+json). Branch on the stable code member rather than on title or type.

object
code
required

Stable, machine-readable error code; the primary value to branch on. service_unavailable indicates a feature or dependency is temporarily unavailable and the request may be retried later. tenant_write_conflict indicates the write conflicted with an in-progress tenant data purge (or vice versa) and may be retried later unchanged. All other codes are terminal until the request itself is changed.

string
Allowed values: validation bad_request unauthorized forbidden not_found conflict tenant_write_conflict method_not_allowed content_too_large service_unavailable internal_server_error
detail

A human-readable explanation specific to this occurrence of the problem.

string
details

Optional structured context for this problem, e.g. resource_type on a 404.

object
key
additional properties
any
instance

A URI reference (typically the request path) identifying the specific occurrence of the problem.

string
invalid_params

Field-level validation failures (RFC 9457 “invalid_params” extension). Present on validation errors.

Array<object>
<= 100 items

A single invalid request parameter (RFC 9457 “invalid_params” extension member).

object
name
required

Dotted path to the offending request field, e.g. ‘field_type’ or ‘items[0].field_type’.

string
reason
required

Human- and agent-readable explanation of why the value is invalid, naming allowed values or constraints where applicable so the request can be corrected without guessing.

string
request_id
required

Identifier for this request, also returned in the X-Request-ID response header; include it when reporting a problem.

string
status
required

HTTP status code

integer format: int64
title
required

A short, human-readable summary of the problem type. This value should not change between occurrences of the error.

string
type

A URI reference identifying the problem type.

string format: uri

Example

{
"code": "validation",
"detail": "One or more request parameters are invalid",
"instance": "/v1/feedback-records",
"invalid_params": [
{
"name": "field_type",
"reason": "must be one of: text, categorical, nps, csat, ces, rating, number, boolean, date"
}
],
"request_id": "0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
"status": 400,
"title": "Bad Request",
"type": "https://hub.formbricks.com/problems/bad-request"
}

Error

Media typeapplication/problem+json

RFC 9457 problem details (application/problem+json). Branch on the stable code member rather than on title or type.

object
code
required

Stable, machine-readable error code; the primary value to branch on. service_unavailable indicates a feature or dependency is temporarily unavailable and the request may be retried later. tenant_write_conflict indicates the write conflicted with an in-progress tenant data purge (or vice versa) and may be retried later unchanged. All other codes are terminal until the request itself is changed.

string
Allowed values: validation bad_request unauthorized forbidden not_found conflict tenant_write_conflict method_not_allowed content_too_large service_unavailable internal_server_error
detail

A human-readable explanation specific to this occurrence of the problem.

string
details

Optional structured context for this problem, e.g. resource_type on a 404.

object
key
additional properties
any
instance

A URI reference (typically the request path) identifying the specific occurrence of the problem.

string
invalid_params

Field-level validation failures (RFC 9457 “invalid_params” extension). Present on validation errors.

Array<object>
<= 100 items

A single invalid request parameter (RFC 9457 “invalid_params” extension member).

object
name
required

Dotted path to the offending request field, e.g. ‘field_type’ or ‘items[0].field_type’.

string
reason
required

Human- and agent-readable explanation of why the value is invalid, naming allowed values or constraints where applicable so the request can be corrected without guessing.

string
request_id
required

Identifier for this request, also returned in the X-Request-ID response header; include it when reporting a problem.

string
status
required

HTTP status code

integer format: int64
title
required

A short, human-readable summary of the problem type. This value should not change between occurrences of the error.

string
type

A URI reference identifying the problem type.

string format: uri

Examples

Examplevalidation_error

Validation error

{
"type": "https://hub.formbricks.com/problems/validation",
"title": "Validation Error",
"status": 400,
"code": "validation",
"detail": "One or more request parameters are invalid",
"instance": "/v1/feedback-records",
"request_id": "0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b",
"invalid_params": [
{
"name": "tenant_id",
"reason": "is required"
}
]
}