Skip to main content
POST

Authorizations

x-api-key
string
header
required

Management API key; must include workspaceId as an allowed workspace with read, write, or manage permission.

Body

application/json

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.

tenant_id
string<cuid2>
required

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.

source_type
string
required

Type of feedback source, e.g. survey, review, feedback_form.

Required string length: 1 - 255
submission_id
string
required

Identifier 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.

Required string length: 1 - 255
field_id
string
required

Identifier of the question / field.

Required string length: 1 - 255
field_type
enum<string>
required

The type of a feedback field, which determines which value_* member carries the answer: textvalue_text (the only enrichable type), categoricalvalue_text + value_id, nps / csat / ces / rating / numbervalue_number, booleanvalue_boolean, datevalue_date.

Available options:
text,
categorical,
nps,
csat,
ces,
rating,
number,
boolean,
date
source_id
string | null

Reference to the survey / form / ticket the record came from.

Maximum string length: 255
source_name
string | null

Human-readable name of the source.

Maximum string length: 255
field_label
string | null

The question text.

Maximum string length: 2048
field_group_id
string

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

Maximum string length: 255
field_group_label
string | null

Human-readable question text for the group.

Maximum string length: 2048
language
string

ISO language code the feedback was given in.

Maximum string length: 10
user_id
string

End-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.

Maximum string length: 255
collected_at
string<date-time>

When the feedback was given. Defaults to now. Must be between 1970-01-01 and 2080-12-31.

value_text
string | null

Text answer. The only member that feeds sentiment, emotion, translation and embedding enrichment.

Maximum string length: 30000
value_id
string | null

Stable 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.

Maximum string length: 255
value_number
number<double>

Numeric answer (ratings, NPS scores, numbers). Must be between -1e15 and +1e15.

Required range: -1000000000000000 <= x <= 1000000000000000
value_boolean
boolean

Yes / no answer.

value_date
string<date-time>

Date answer. Must be between 1970-01-01 and 2080-12-31.

metadata
object | null

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.

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.

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.

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_type
enum<string>
required

The type of a feedback field, which determines which value_* member carries the answer: textvalue_text (the only enrichable type), categoricalvalue_text + value_id, nps / csat / ces / rating / numbervalue_number, booleanvalue_boolean, datevalue_date.

Available options:
text,
categorical,
nps,
csat,
ces,
rating,
number,
boolean,
date
source_id
string

Reference to the survey / form / ticket the record came from.

source_name
string

Human-readable name of the source.

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.

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

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
enum<string>

Sentiment polarity label inferred from value_text by the sentiment enrichment: five ordinal levels plus a distinct mixed. Server-generated and read-only.

Available options:
very_negative,
negative,
neutral,
positive,
very_positive,
mixed
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.

Required range: -1 <= x <= 1
emotions
enum<string>[]

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.

Minimum array length: 1

A 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.

Available options:
joy,
anger,
sadness,
fear,
surprise,
disgust
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.

Maximum string length: 35