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

# Embedded Data

> Everything on a response that is not an answer to a question: values you pass in, values the survey calculates, and the context Formbricks captures on its own.

Embedded Data is **data about this response**. A page type, a plan, a campaign, a quiz score, the device the survey was answered on. It rides along with the answers, shows up in the response table and in exports, and can be used in recall, logic and redirect URLs.

That makes it the counterpart to **contact attributes**, which are **data about this person** and persist across every response they ever give.

| | Embedded Data | Contact attributes |
| - | - | - |
| Describes | one response / one session | one person, across responses |
| Stored on | the response | the contact record, snapshotted onto the response |
| Set with | URL params, `setEmbeddedData()`, survey logic | `setUserId()`, `setAttributes()`, `setEmail()` |
| Lives past the survey | no | yes |

> [!NOTE]
>
> **The API payloads keep their shape** and no survey needs editing; only `hiddenFields.enabled` is now
> derived (`true` whenever a passed-in field exists) and ignored on write. What did change is the editor: the
> Hidden Fields and Variables cards are now one **Embedded Data** card, where a field's source is **Passed in**
> or **Calculated**. See [Hidden Fields and Variables](https://formbricks.com/docs/surveys/embedded-data/migration.md) if you already use
> them.

## The Three Sources

Every Embedded Data field has exactly one source, and the source decides who is allowed to write it.

| Source | Written by | Called this in the editor | Types today |
| - | - | - | - |
| **Ingested** | the outside world: URL params or the SDK | **Passed in** | `string`, `number`, `boolean`, `date` |
| **Computed** | the survey itself, through logic actions | **Calculated** | `string`, `number` |
| **Reserved** | Formbricks, automatically, on every response | not authored at all | `string`, `number`, `boolean`, `date` |

Ingested fields are the only ones anything outside the survey can write. Computed fields are set by logic while the respondent answers, and reserved fields are read-only auto-capture, so a URL param or an SDK call naming one of them is dropped and logged rather than stored.

## Where Values Come From

Which capture channel is available depends on the survey type, and this is the part worth reading twice, because a URL param does nothing for an app survey and `setEmbeddedData()` does nothing for a link survey.

| Capture channel | Link surveys | Website & app surveys (JS) | App surveys (mobile SDKs) |
| - | - | - | - |
| Ingested via **URL parameter** (`?plan=pro`) | Yes | No | No |
| Ingested via **`setEmbeddedData()`** | No | Yes | Yes |
| Computed by **survey logic** | Yes | Yes | Yes |
| Reserved — **page and campaign** (`pagePath`, `pageReferrer`, `utm*`, `url`, `source`) | Yes | Yes | No |
| Reserved — **device** (`screenWidth`, `viewportWidth`, `timezone`, `locale`, …) | Yes | Yes | Yes |
| Reserved — **server-derived** (`country`, `browser`, `os`, `deviceType`, `ipAddress`, timings) | Yes | Yes | Yes |

> [!NOTE]
>
> Mobile SDKs render the survey inside a WebView that has no host page, so `location` and `document.referrer`
> describe nothing real there. The page and campaign fields are left **absent** on mobile responses rather
> than filled with a meaningless value. The device fields are honest measurements of the device and are
> captured. If you want the equivalent of a page name on mobile, pass it yourself:
> `setEmbeddedData({ screen: "checkout" })`.

A field does not care which channel filled it. `plan` is declared the same way, filled by `?plan=pro` on a link survey and by `setEmbeddedData({ plan: "pro" })` on an app survey, and reads back identically in recall, logic, filters and exports.

## Declaring a Field

An ingested or computed field has to exist before anything can fill it, and there are two places to declare one:

* **In the survey editor**, for a field only that survey uses: the **Embedded Data** card, where one dialog creates either source.
* **In Settings → Workspace → Embedded Data**, for a field the whole workspace shares. A field declared there can be linked from any survey, and editing it once changes it for every survey using it. The same page lists which surveys use each field, and refuses to delete one that is still in use. See [Field library](https://formbricks.com/docs/surveys/embedded-data/library.md).

Reserved fields are never declared, they are always available.

A passed-in field carries two things you type, and they do different jobs. Its **name** is what you call it — what the response table, the summary and your exports show, renameable at any time. Its **key** is the address the value lives under: the URL parameter, the SDK key, and what recall and logic resolve. The key fills itself in from the name, and cannot change once the field exists, because responses are already stored under it. A calculated field added on a survey has a name only; one in the [library](https://formbricks.com/docs/surveys/embedded-data/library.md) also takes a key, because that is how other surveys find it. Either way its storage address is generated.

Keys follow one rule everywhere, and reserved names are refused for new fields in any casing. The full rule and the refused list live on the [Hidden Fields](https://formbricks.com/docs/surveys/general-features/hidden-fields.md#naming) page.

A field also carries a **type** that decides how its value is read back and compared. In this release:

* **Passed-in fields** can be `string`, `number`, `boolean` or `date`, wherever they are declared, and incoming values are coerced to the declared type.
* **Calculated fields** are text or number — the types survey logic can produce — and the initial value you set is the default the field falls back to.
* **Reserved fields** are typed by Formbricks, and several of them are genuinely numeric: `viewportWidth < 768` is a numeric comparison in logic, not a comparison of digit strings.

> [!NOTE]
>
> A field's type and default are set in its own dialog, on a survey field and a
> [library field](https://formbricks.com/docs/surveys/embedded-data/library.md) alike. A **passed-in** field also has a **lock** there: a
> locked field ignores whatever a URL param or the SDK sends and uses its default instead, which is how you
> fix a value nobody outside can change. A calculated field has no lock: only the survey's own logic writes it.

## What Happens to an Incoming Value

The same contract runs in the browser when the survey is displayed, and again on the server when the response is saved, so what the client accepts is never taken on trust.

**1. Allow-list**

Only names the survey declares as **ingested** fields are stored. An unknown key, a computed field, a
reserved field or a locked field is dropped and logged, never stored, and never an error.

**2. Case-insensitive matching**

A field declared `customerRef` is filled by `?customerref=x` as well as `?customerRef=x`. The value is
always stored under the declared spelling, so exports and recall see one key.

**3. Coercion to the declared type**

Values are coerced rather than rejected. `"42"` into a `number` field becomes `42`; `true`, `1`, `yes`
and `on` (and their negatives) become the strings `"true"` and `"false"` — that canonical pair is what a
response stores, what an export column shows, and what a filter compares against; a `date` field accepts `2026-08-06` and full ISO 8601
datetimes, stores ISO 8601, and reads a datetime with no zone as UTC rather than as the respondent's
local time. A value that cannot be coerced is stored verbatim and flagged, so a bad parameter never
blocks a response. A field left as `string` — every field declared in the survey editor — passes through
this step unchanged, since every scalar has a lossless text form; it does visible work on a shared field
that declares a narrower type.

**4. Size limit**

One value is capped at **16 KB** of UTF-8. Anything longer is truncated on a character boundary and
flagged, not rejected.

**5. Collision with a question**

A question's answer always wins its own key. A field whose name collides with a question id can never
hold a value, and the collision is reported.

> [!NOTE]
>
> A field nothing arrived for keeps its key **absent** rather than empty, and reads back as its default value
> if it has one. That is what makes `#recall:plan/fallback:unknown#` show the fallback instead of a blank.

### Precedence

When two sources offer the same field on the same response, the more explicit one wins:

1. a question's answer, always, for its own key;
2. an explicit `formbricks.track("action", { hiddenFields: { plan: "pro" } })` value;
3. the ambient bag set with `setEmbeddedData()`, or the URL parameter on a link survey;
4. the field's default value;
5. otherwise the field is unset.

Reserved fields sit outside this list because they are never written from outside. The one interaction is **shadowing**: if a survey declares its own field named `url` or `country`, that name resolves to the declared field everywhere, and the reserved field of the same name is not offered, listed or exported for that survey.

## Reading the Values Back

Once a value is on a response it behaves like any other response data:

* **Recall** it into question copy, ending cards and redirect URLs with `#recall:<name>/fallback:<text>#`.
* Use it as a **logic** operand to branch the survey.
* Filter and group **responses** by it.
* **Export** it as a stable, typed column in CSV and Excel.

Mid-survey surfaces (recall inside a running survey, logic conditions) can only use values that exist while the respondent is still answering. Server-derived reserved fields such as `country` or `durationSeconds` are only known once the response reaches Formbricks, so they are available on the response but not offered to a running survey. The [Reserved Fields](https://formbricks.com/docs/surveys/embedded-data/reserved-fields.md) page marks which is which.

## Next Steps

> **[Reserved Fields](https://formbricks.com/docs/surveys/embedded-data/reserved-fields.md)**
>
> The full catalog of auto-captured fields, their types, and what the Anonymize toggle suppresses.

> **[setEmbeddedData](https://formbricks.com/docs/surveys/embedded-data/set-embedded-data.md)**
>
> The JavaScript SDK reference: merge semantics, SPA route changes, GTM and the debug trace.

> **[Mobile SDKs](https://formbricks.com/docs/surveys/embedded-data/mobile-sdks.md)**
>
> `setEmbeddedData` on React Native, iOS, Android and Flutter, with each language's value types.

> **[Hidden Fields & Variables](https://formbricks.com/docs/surveys/embedded-data/migration.md)**
>
> What changes for surveys you already run, and what does not.


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