curl --request GET \
--url https://app.formbricks.com/api/v3/feedbackRecords/{id} \
--header 'x-api-key: <api-key>'import requests
url = "https://app.formbricks.com/api/v3/feedbackRecords/{id}"
headers = {"x-api-key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'x-api-key': '<api-key>'}};
fetch('https://app.formbricks.com/api/v3/feedbackRecords/{id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.formbricks.com/api/v3/feedbackRecords/{id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.formbricks.com/api/v3/feedbackRecords/{id}"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("x-api-key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://app.formbricks.com/api/v3/feedbackRecords/{id}")
.header("x-api-key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.formbricks.com/api/v3/feedbackRecords/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["x-api-key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"id": "018e1234-5678-7abc-8ef0-123456789abd",
"tenant_id": "ph7zv3w2u1x5k9d8m4q6c0bn",
"collected_at": "2026-08-15T10:30:00Z",
"created_at": "2026-08-15T10:30:02Z",
"updated_at": "2026-08-15T10:31:40Z",
"source_type": "survey",
"source_id": "cm2k7q9x00003v8h1a2b3c4d5",
"source_name": "Post-match survey",
"submission_id": "cm2k7qa1z0007v8h1e6f7g8h9",
"field_id": "q2",
"field_label": "What could we improve?",
"field_type": "text",
"value_text": "Queues at the north gate were far too long.",
"user_id": "fan-8813",
"language": "en",
"sentiment": "negative",
"sentiment_score": -0.62,
"emotions": [
"anger"
],
"value_text_translated": "Die Warteschlangen am Nordtor waren viel zu lang.",
"translation_lang_key": "de"
}Get a feedback record
Returns one feedback record by id. The gateway looks the record up to learn its dataset and
authorizes against that, so no tenant_id is sent. Requires an API key with read
permission (or higher) on a workspace the record’s dataset is assigned to.
An unknown id, or one in a dataset you cannot read, answers 403 rather than 404, so ids cannot be probed. The exception is the minute after a delete: the gateway still has the record’s dataset cached, authorizes the call, and the store answers 404.
curl --request GET \
--url https://app.formbricks.com/api/v3/feedbackRecords/{id} \
--header 'x-api-key: <api-key>'import requests
url = "https://app.formbricks.com/api/v3/feedbackRecords/{id}"
headers = {"x-api-key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'x-api-key': '<api-key>'}};
fetch('https://app.formbricks.com/api/v3/feedbackRecords/{id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.formbricks.com/api/v3/feedbackRecords/{id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.formbricks.com/api/v3/feedbackRecords/{id}"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("x-api-key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://app.formbricks.com/api/v3/feedbackRecords/{id}")
.header("x-api-key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.formbricks.com/api/v3/feedbackRecords/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["x-api-key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"id": "018e1234-5678-7abc-8ef0-123456789abd",
"tenant_id": "ph7zv3w2u1x5k9d8m4q6c0bn",
"collected_at": "2026-08-15T10:30:00Z",
"created_at": "2026-08-15T10:30:02Z",
"updated_at": "2026-08-15T10:31:40Z",
"source_type": "survey",
"source_id": "cm2k7q9x00003v8h1a2b3c4d5",
"source_name": "Post-match survey",
"submission_id": "cm2k7qa1z0007v8h1e6f7g8h9",
"field_id": "q2",
"field_label": "What could we improve?",
"field_type": "text",
"value_text": "Queues at the north gate were far too long.",
"user_id": "fan-8813",
"language": "en",
"sentiment": "negative",
"sentiment_score": -0.62,
"emotions": [
"anger"
],
"value_text_translated": "Die Warteschlangen am Nordtor waren viel zu lang.",
"translation_lang_key": "de"
}Authorizations
Management API key; must include workspaceId as an allowed workspace with read, write, or manage permission.
Path Parameters
Feedback record id (UUID). The dataset is resolved from the record itself, so the caller cannot choose which dataset it is authorized against. An id that is not a UUID answers 400, and an id whose dataset the API key cannot reach answers 403 rather than 404, so ids cannot be probed. The gateway caches the record→dataset lookup for 60 seconds, so for up to a minute after a delete the id is still authorized and the store answers 404; after that the same id answers 403.
Response
Feedback record retrieved
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?