> ## Documentation Index
> Fetch the complete documentation index at: https://formbricks.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Feedback Records

> The normalized unit of feedback inside a Feedback Dataset.

A **Feedback Record** is one piece of feedback expressed in a normalized schema. Whether it came from a survey response, a CSV row, or an API push, it lands in the same shape so you can query everything together.

## The Feedback Record schema

Every record has the following fields. Required fields must be mapped by every source.

| Field | Type | Required | Description |
| - | - | - | - |
| `submission_id` | string | Yes | Stable ID for the submission (e.g. `response_id`, `ticket_id`, `order_id`). Used for idempotent re-imports. |
| `collected_at` | timestamp | Yes | When the feedback was originally collected. |
| `source_type` | string | Yes | The kind of source (e.g. `survey`, `csv`, `review`). |
| `field_id` | string | Yes | Stable identifier for the question/field. |
| `field_type` | enum | Yes | One of `text`, `categorical`, `nps`, `csat`, `ces`, `rating`, `number`, `boolean`, `date`. |
| `tenant_id` | string | No | Feedback Dataset ID. Set automatically when ingesting. |
| `source_id` | string | No | Reference to the survey/form/ticket/review ID. |
| `source_name` | string | No | Human-readable source name for display. |
| `field_label` | string | No | The question text or field label. |
| `field_group_id` | string | No | Groups related fields (matrix, ranking, grid questions). |
| `field_group_label` | string | No | Human-readable group label. |
| `value_text` | string | No | Text responses. |
| `value_number` | float64 | No | Numeric responses (ratings, NPS, CSAT). |
| `value_boolean` | boolean | No | Yes/no responses. |
| `value_date` | timestamp | No | Date responses. |
| `metadata` | jsonb | No | Free-form context (device, campaign, custom fields). See below for what survey records carry. |
| `language` | string | No | ISO 639-1 language code (`en`, `de`, `fr`, ...). |
| `user_id` | string | No | Anonymous user ID. Never store PII here. |

The right `value_*` field is set based on `field_type`. For example a `nps` field uses `value_number`, an open-text comment uses `value_text`.

### Metadata on survey records

Records created from a survey response carry the context of that response on every record of the submission, so you can group and segment by it later:

* Flat keys for the response itself: `source`, `url`, `browser`, `os`, `device`, `country`, `action`, `finished`, `duration_seconds`, `ending_id` and `survey_type`.
* A nested `embedded_data` object holding the response's [Embedded Data](https://formbricks.com/docs/surveys/embedded-data/overview.md), keyed by field name.

```json theme={null}
{
  "source": "link",
  "country": "PT",
  "finished": true,
  "embedded_data": { "brand": "AEG", "plan": "pro", "cart_value": 129.9 }
}
```

Embedded Data values keep their declared type — text stays text, numbers stay numbers, dates are ISO 8601 strings. A locked field publishes its default value rather than anything supplied with the response, and text values longer than 256 characters are truncated.

Four things can leave a declared field out of `embedded_data`, so do not assume every field appears on every record:

* The value is missing on that response, or does not match the field's type, **and the field has no default value**. A field with a default falls back to it rather than going missing.
* Two fields share a name. Only one is published: an **ingested** field wins over a **computed** one, and between two of the same source, the one declared first. The winner keeps the name even when it has no value on that response, so the other field does not take its place.
* `embedded_data` is capped at 8 KiB, measured as its serialized JSON. Once a response's fields reach it, any further field that does not fit is skipped while smaller ones still publish — so a field may be present on some responses and absent on others.
* The field is named `__proto__`. Readers of the record, the record drawer among them, cannot keep a key with that name, so it is not published.

> [!NOTE]
>
> `metadata` is returned with a record but is not filterable or searchable through the API, so narrowing by a metadata value means fetching and filtering on your side.

## Viewing and managing records

Inside a workspace, navigate to **Analyze → Feedback Data → Feedback Records**. You'll see the latest records across every dataset the workspace has access to, sorted by `collected_at`.

From the table you can:

* **Filter** by dataset, source, field type, or date range.
* **Open** a record drawer to see the full field set and metadata.
* **Edit** values inline for cleanup (e.g. relabel a categorical answer).
* **Delete** a record.
* **Add** a record manually via the "+ Add" button.

## Idempotent imports

Sources that re-ingest data (CSV uploads, API ingestions) use `submission_id` as the dedup key. Re-importing the same `submission_id` updates the existing record instead of creating a duplicate.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.