curl --request POST \
--url https://app.formbricks.com/api/v3/feedbackRecords \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"tenant_id": "ph7zv3w2u1x5k9d8m4q6c0bn",
"source_type": "survey",
"source_id": "cm2k7q9x00003v8h1a2b3c4d5",
"source_name": "Post-match survey",
"submission_id": "cm2k7qa1z0007v8h1e6f7g8h9",
"field_id": "q1",
"field_label": "How satisfied were you with the stadium experience?",
"field_type": "rating",
"value_number": 4,
"user_id": "fan-8813",
"language": "en"
}
'{
"id": "018e1234-5678-7abc-8ef0-123456789abc",
"tenant_id": "ph7zv3w2u1x5k9d8m4q6c0bn",
"collected_at": "2026-08-15T10:30:00Z",
"created_at": "2026-08-15T10:30:02Z",
"updated_at": "2026-08-15T10:30:02Z",
"source_type": "survey",
"source_id": "cm2k7q9x00003v8h1a2b3c4d5",
"source_name": "Post-match survey",
"submission_id": "cm2k7qa1z0007v8h1e6f7g8h9",
"field_id": "q1",
"field_label": "How satisfied were you with the stadium experience?",
"field_type": "rating",
"value_number": 4,
"user_id": "fan-8813",
"language": "en"
}Create a feedback record
Stores one feedback record in the dataset named by tenant_id in the body. The gateway reads
tenant_id from the JSON body to authorize the call — a body that is not JSON, or has no
tenant_id, answers 400 before anything is stored.
Requires an API key with write permission (or higher) on a workspace the dataset is assigned to. Creating is allowed on shared datasets (it is ordinary workspace work, like a CSV import); only changing and deleting existing records is restricted to unshared ones.
Ingestion is idempotent per (tenant_id, submission_id, field_id): a second record with the
same triple answers 409 rather than creating a duplicate. Send exactly one value_* member,
matching field_type. A text record is enriched asynchronously — sentiment, emotions and
the translation members appear on the record once enrichment completes, and it becomes
searchable once embedded.
curl --request POST \
--url https://app.formbricks.com/api/v3/feedbackRecords \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"tenant_id": "ph7zv3w2u1x5k9d8m4q6c0bn",
"source_type": "survey",
"source_id": "cm2k7q9x00003v8h1a2b3c4d5",
"source_name": "Post-match survey",
"submission_id": "cm2k7qa1z0007v8h1e6f7g8h9",
"field_id": "q1",
"field_label": "How satisfied were you with the stadium experience?",
"field_type": "rating",
"value_number": 4,
"user_id": "fan-8813",
"language": "en"
}
'{
"id": "018e1234-5678-7abc-8ef0-123456789abc",
"tenant_id": "ph7zv3w2u1x5k9d8m4q6c0bn",
"collected_at": "2026-08-15T10:30:00Z",
"created_at": "2026-08-15T10:30:02Z",
"updated_at": "2026-08-15T10:30:02Z",
"source_type": "survey",
"source_id": "cm2k7q9x00003v8h1a2b3c4d5",
"source_name": "Post-match survey",
"submission_id": "cm2k7qa1z0007v8h1e6f7g8h9",
"field_id": "q1",
"field_label": "How satisfied were you with the stadium experience?",
"field_type": "rating",
"value_number": 4,
"user_id": "fan-8813",
"language": "en"
}Authorizations
Management API key; must include workspaceId as an allowed workspace with read, write, or manage permission.
Body
A new feedback record. Exactly one value_* member should carry the answer, matching field_type. Strings must be valid UTF-8 without NULL bytes; violations answer 400.
The feedback dataset to write into (the dataset id). The gateway authorizes the request against this dataset, so it must be one the API key's workspace is assigned to.
Type of feedback source, e.g. survey, review, feedback_form.
1 - 255Identifier of the logical submission this record belongs to. Groups multi-field submissions and makes ingestion idempotent: records are unique per (tenant_id, submission_id, field_id) and a duplicate answers 409. If a record has no logical submission, reuse field_id.
1 - 255Identifier of the question / field.
1 - 255The 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.
text, categorical, nps, csat, ces, rating, number, boolean, date Reference to the survey / form / ticket the record came from.
255Human-readable name of the source.
255The question text.
2048Stable identifier grouping related fields (ranking, matrix and grid questions).
255Human-readable question text for the group.
2048ISO language code the feedback was given in.
10End-user identifier (e.g. an anonymous id or an email hash). Set it on anything you may later have to erase: DELETE /api/v3/feedbackRecords matches on user_id.
255When the feedback was given. Defaults to now. Must be between 1970-01-01 and 2080-12-31.
Text answer. The only member that feeds sentiment, emotion, translation and embedding enrichment.
30000Stable id of the selected option in the source system (e.g. a survey choice id or matrix column id), stored alongside value_text. Opaque, not validated. Omit for free-text, "other" and non-choice answers.
255Numeric answer (ratings, NPS scores, numbers). Must be between -1e15 and +1e15.
-1000000000000000 <= x <= 1000000000000000Yes / no answer.
Date answer. Must be between 1970-01-01 and 2080-12-31.
Arbitrary context for this record, stored as JSON: the dimensions you want to slice a dashboard by, such as channel, device, country, campaign, plan or tags. Values may be strings, numbers, booleans, null, or nested objects and arrays. Use stable snake_case keys — a key is what a dashboard groups by. Numbers keep full precision in storage. Metadata is not sent to the enrichment providers, but it is included in the feedback_record.created / feedback_record.updated webhook payloads.
Show child attributes
Show child attributes
Response
Feedback record created
One piece of feedback in the normalized Feedback Record shape. Field names are snake_case: the payload is the Hub's, passed through the API gateway unchanged. Optional members are absent (not null) when unset.
UUIDv7 primary key.
The feedback dataset this record belongs to (the dataset id, as shown by the MCP list_feedback_datasets tool).
When the feedback was given.
When the record was stored. Diverges from collected_at on a historical re-import.
When the record was last changed.
Type of feedback source, e.g. survey, review, feedback_form.
Identifier of the logical submission this record belongs to (e.g. a response id). Records are unique per (tenant_id, submission_id, field_id).
Identifier of the question / field.
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.
text, categorical, nps, csat, ces, rating, number, boolean, date Reference to the survey / form / ticket the record came from.
Human-readable name of the source.
The question text.
Stable identifier grouping related fields (ranking, matrix and grid questions).
Human-readable question text for the group.
ISO language code the feedback was given in.
End-user identifier (e.g. an anonymous id or an email hash).
Text answer.
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.
Numeric answer (ratings, NPS scores, numbers).
Yes / no answer.
Date answer.
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.
Sentiment polarity label inferred from value_text by the sentiment enrichment: five ordinal levels plus a distinct mixed. Server-generated and read-only.
very_negative, negative, neutral, positive, very_positive, mixed Signed sentiment polarity from -1.0 (very negative) to 1.0 (very positive). Read-only; absent until the record is enriched.
-1 <= x <= 1Emotions 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.
1A single emotion label inferred from value_text by the emotion enrichment (the six Ekman basic emotions). Emotions are multi-label, so a record carries zero or more of these; "mixed" is not a label, it is two or more present at once. Server-generated and read-only.
joy, anger, sadness, fear, surprise, disgust value_text translated into the dataset's configured target language. Read-only; absent until the record is enriched.
BCP-47 locale that value_text_translated was produced in. Read-only; absent until the record is enriched.
35Was this page helpful?