# Flow data and Liquid reference

You can save visitor data with Set Data and insert it into text with Liquid. Look up action fields, variable expiry, scores, and the data paths available on your site.

Available on the Grow, Scale, and Pro plans.

**Set Data** changes visitor data when someone reaches the node. Liquid lets you insert that data into supported text fields. Use this reference to choose an action or find the Liquid path for a value you want to display.

A **Set Data** action changes a value or assignment. A Liquid expression displays a value without writing it to your email platform.

Flow edits save to your project draft. Normal site visitors use the [published flow](https://rightmessage.com/help/publishing-your-changes). Field and tag actions also need a connected integration that supports the operation. When a visitor reaches those actions in a published flow, RightMessage can send or queue the changes to your provider.

![Set Data editor with a score adjustment and a variable that expires after seven days](https://rightmessage.com/help/screens/flows-set-data.light.webp)

## Set Data action fields

You'll find **Set Data** under **Actions** in the flow builder. In the **Set Visitor Data** editor, choose an **Action Type**, then enter its values.

| If you'd like to…            | Editor choice               | Configuration type and availability                                                                                                                                        |
| ---------------------------- | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Add or subtract points       | **Update Score**            | `score`. Always shown. Select an existing score name or enter another name, then set the adjustment.                                                                       |
| Write to a custom field      | **Set Custom Field**        | `field`. Shown when an email-platform integration is connected. Select the field and enter its value.                                                                      |
| Store a value in the browser | **Add / Update Variable**   | `variable`. Always shown. Enter a name and value. A nonempty value exposes **Expires:**, with **Never** or **In...** and a duration.                                       |
| Assign a segment             | **Set Segment**             | `set_segment`. Always shown. Select the group, then one of its segments.                                                                                                   |
| Add or remove a tag          | **Add Tag**, **Remove Tag** | `add_tag`, `remove_tag`. Shown when your integration supports tag operations. Select an available tag. Your provider can use a different label for the tag or list picker. |

**Add another action** adds an action to the same node, with its own type and inputs. The trash control removes an action when the node has more than one. Changing an action's type clears its old settings. Switching between **Add Tag** and **Remove Tag** keeps the selected tag.

When a node has several variable or field actions, **Add Data** currently inserts into the first of those actions, even if you clicked another action's button. Check which value received the data before saving, or type the Liquid expression directly into the intended field.

In the saved configuration, a node has an ordered `actions` array. Each action has an `id` and a `type`. Action IDs prevent the same action from repeating as the flow runs. They're configuration identifiers, not provider field IDs.

| Type          | Fields                                                            | What happens and what's required                                                                                                                    |
| ------------- | ----------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `field`       | `field`: provider custom-field ID; `value`: Liquid-capable string | Renders the value and requests a field write. Without a field or integration, nothing happens. The editor excludes native fields from this picker.  |
| `set_segment` | `segmentGroupId`: group ID; `segmentId`: segment ID               | Replaces recorded answers for the group with the chosen segment. Refreshes segmentation and requests active synchronization of configured mappings. |
| `add_tag`     | `tagId`: provider tag ID                                          | Adds the tag, or queues it until the visitor is identified. Requires an integration that supports adding tags.                                      |
| `remove_tag`  | `tagId`: provider tag ID                                          | Removes the tag, or queues its removal until the visitor is identified. Requires an integration that supports removing tags.                        |
| `variable`    | `variableName`, `variableValue`, and expiry fields below          | Renders and stores a value in the browser. A blank or whitespace-only rendered value deletes it.                                                    |
| `score`       | `score`: score name; `adjust`: numeric amount                     | Adds the amount to the named runtime score. A new action starts with this type and an adjustment of `1`.                                            |

Provider operations run asynchronously. Their order in the array doesn't guarantee that one request finishes before the next action starts.

Use separate **Set Data** nodes for multiple score adjustments. RightMessage currently uses the node ID to prevent duplicate score adjustments, so it can record only the first score action in a node.

### Custom-field writes

The value field supports Liquid and **Add Data**. For example, `{{ score.fit }}` renders the current score as a string before requesting the field write.

If the visitor hasn't been identified, the integration can save the write until they're identified later. An identity lookup that's still running can also delay it. A queued write hasn't reached the provider yet. Check debugger warnings for provider failures rather than assuming the write succeeded because the visitor reached the node.

A field action updates an existing field. It doesn't create a field in your provider or identify a contact just because its value looks like an email address.

### Segment assignment

The editor has a **Question / Segment Group:** picker and a **Set To Segment:** picker. Changing the group clears the selected segment.

This action replaces recorded answers, including in multi-select groups. If a visitor chose several answers, it clears them and records only the selected segment. The action doesn't run if an ID or the group is missing.

It also requests an active sync, which can overwrite mapped provider data. What reaches your provider depends on the group's synchronization settings and whether the visitor is identified. The action doesn't change the group's automatic signal rules.

### Tags and identification

For anonymous visitors, RightMessage saves tag actions as pending additions or removals. It can send them after identifying the visitor. If you queue both an addition and a removal for the same tag, the later operation replaces the opposite one.

For identified visitors, RightMessage uses the known email or subscriber ID to request the change. It skips actions with missing tag IDs or unsupported integration methods. Provider errors produce warnings. They don't undo earlier local actions.

This queue belongs to **Set Data** actions. The [Code-node tag methods](https://rightmessage.com/help/code-node-javascript-api-reference) require an identified visitor and don't queue anonymous tag changes.

## Variable values and expiry

Variables live in the visitor's browser storage, not your email platform. Names are case-sensitive. Choose a stable name, such as `workforge_recommendation` for our example business, [WorkForge](https://workforge.rightmessage.com). Avoid using the same name for a variable and a score.

| Field             | Default and accepted values                                                                                   |
| ----------------- | ------------------------------------------------------------------------------------------------------------- |
| `variableName`    | The key used by `variable.<name>` and `RM.getVariable(name)`.                                                 |
| `variableValue`   | A string that can contain Liquid. The rendered text is stored as a string. Blank output deletes the variable. |
| `expiresType`     | `'never'` by default. `'duration'` enables timed expiry.                                                      |
| `expiresDuration` | Duration amount. The editor uses a minimum of `1` and initializes an omitted duration to `1`.                 |
| `expiresUnit`     | `'minutes'`, `'hours'`, `'days'`, or `'weeks'`. The editor initializes a duration unit to `'hours'`.          |

The duration starts when the action writes the value, not when you create the flow. A later write replaces both the value and its expiry. **Never** means there's no scheduled expiry. It doesn't preserve data across devices, browser resets, or cleared storage.

Expired values disappear from the available visitor data. RightMessage removes them when they're read or recalculated. Expiry doesn't trigger a flow or run a callback at that time.

Here's an illustrative action configuration. It hasn't been executed:

```json
{
	"id": "act_workforge_recommendation",
	"type": "variable",
	"variableName": "workforge_recommendation",
	"variableValue": "{% if score.fit >= 10 %}team{% else %}starter{% endif %}",
	"expiresType": "duration",
	"expiresDuration": 7,
	"expiresUnit": "days"
}
```

If `score.fit` is `12`, this stores the string `"team"` with an expiry seven days after the action runs. It doesn't write to your provider.

Unlike **Set Data**, [`RM.setVariable`](https://rightmessage.com/help/code-node-javascript-api-reference#variables-and-scores) stores an empty string instead of deleting the value. It also accepts JSON-compatible values, not just rendered text.

## How Liquid renders your text

On-site flow templates use LiquidJS with synchronous rendering. `{{ expression }}` displays a value. `{% if condition %}` and related tags control which text appears. Liquid isn't JavaScript, and its expressions aren't segment IDs for `data-rm-if`.

Text without Liquid markers passes through unchanged. If parsing or rendering fails, RightMessage returns the original template text rather than an error object. A visible `{{ ... }}` token can mean the Liquid is malformed. Missing visitor data can also mean the data hasn't loaded, rather than that the provider field is blank.

Liquid output is text. It isn't automatically a JavaScript literal, JSON document, escaped HTML value, or URL-safe parameter. Use the appropriate filter for its destination, such as `escape` for HTML text or `url_encode` for a URL value. In Code nodes, use typed `RM` getters rather than inserting visitor text into JavaScript source.

Here's an illustrative template. It hasn't been executed:

```liquid
{% if segments.seg_workforge_manager %}
	WorkForge for managers
{% else %}
	WorkForge for your team
{% endif %}
```

This chooses one text branch. It doesn't assign the visitor to a segment.

### Current dates in Set Data values

Put date logic in the action's value, not its field ID or variable name. The on-site renderer supports the LiquidJS [`date` filter](https://liquidjs.com/filters/date.html).

This illustrative **Set Data** value saves the date when the visitor reaches the action:

```liquid
{{ 'now' | date: '%Y-%m-%d' }}
```

The output is a string such as `2026-10-13`. `'now'` is a quoted special input to the filter, not a RightMessage visitor-data path. On-site rendering uses the visitor's browser clock and the runtime's local timezone by default. It doesn't schedule an action for that date.

A variable action keeps the rendered date in browser storage. A field action requests a write to an existing provider field. That field must accept the date's format. Timed variable expiry is separate from the date string.

## On-site visitor data paths

RightMessage builds the available visitor data from several sources. Data can be absent while it's still loading. Your connected provider appears under its runtime integration name, not a universal `contact` or `esp` object.

| Path                                                                                                          | What you can read                                                                                                                                                         |
| ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `score.<name>`                                                                                                | Numeric sum of current runtime score adjustments. Scores aren't persisted variables.                                                                                      |
| `variable.<name>`                                                                                             | Stored value that hasn't expired. Set Data values are strings. Code-node values can have other JSON-compatible types.                                                     |
| `input.<questionId>`                                                                                          | Direct stored input value keyed by question ID. Unlike the Code-node getter, it doesn't fall back to segment-option labels.                                               |
| `segments.<segmentId>`                                                                                        | Current segment decision, normally boolean. Missing data isn't a positive match.                                                                                          |
| `segments.names.<groupId>`                                                                                    | Selected segment name with HTML removed. Multiple winning names are joined with `, `. No winning segment means no name entry.                                             |
| `phrases.<phraseId>`                                                                                          | Phrase text selected for the visitor's current segments, including its configured fallback when applicable.                                                               |
| `query.current.<name>`                                                                                        | Current URL query value only.                                                                                                                                             |
| `query.<name>`                                                                                                | Current query value, otherwise its most recent stored pageview occurrence, with the stored first page as a final fallback.                                                |
| `currentpage.title`                                                                                           | Current document title.                                                                                                                                                   |
| `currentpage.url`                                                                                             | Current absolute URL.                                                                                                                                                     |
| `currentpage.pathname`, `currentpage.hostname`, `currentpage.protocol`, `currentpage.port`                    | Current URL components.                                                                                                                                                   |
| `currentpage.searchParams.<name>`                                                                             | Current page's query parameter from the URL.                                                                                                                              |
| `utm.current.source`, `utm.current.medium`, `utm.current.campaign`, `utm.current.term`, `utm.current.content` | Current page's corresponding `utm_*` query values.                                                                                                                        |
| `utm.source`, `utm.medium`, `utm.campaign`, `utm.term`, `utm.content`                                         | Current truthy UTM value, otherwise its most recent truthy stored occurrence. These aren't guaranteed first-touch values. Unlike `query`, this lookup skips empty values. |
| `referrer.url`, `referrer.domain`, `referrer.path`                                                            | Referrer from the stored first page. A missing or invalid referrer produces no values.                                                                                    |
| `location.country`, `location.city`                                                                           | Geolocation values when RightMessage has location data. They can be absent.                                                                                               |
| `jsdata.<name>`                                                                                               | A browser `window` value. This is visitor-controlled data, not a secret or a server-side variable.                                                                        |
| `<provider>.email`, `<provider>.urlEncodedEmail`                                                              | Loaded email and its URL-encoded form.                                                                                                                                    |
| `<provider>.isAnonymous`                                                                                      | Whether the integration currently has anonymous visitor context.                                                                                                          |
| `<provider>.subscriberId`                                                                                     | Persisted provider identifier. Depending on the integration, this can be an email rather than the provider's canonical numeric ID.                                        |
| `<provider>.customFields.<token>`                                                                             | Loaded custom-field value by templating token. JSON object or array strings beginning with `{` or `[` are parsed when valid.                                              |
| `<provider>.<nativeToken>`                                                                                    | Native field value at the provider object's root, not under a universal `nativeFields` object.                                                                            |
| `<provider>.tagIds`, `<provider>.listIds`                                                                     | Arrays of currently loaded IDs.                                                                                                                                           |
| `<provider>.tags`, `<provider>.lists`                                                                         | Loaded objects such as `{ id, name }`. Availability depends on the provider.                                                                                              |
| `<provider>.events`, `<provider>.purchases`, `<provider>.totalRevenue`                                        | Provider event, purchase, and revenue data when available.                                                                                                                |

`<provider>` is a placeholder in this table, not literal Liquid syntax. Get the integration name and field tokens from your project's data picker or inspected visitor data.

The name `current` is reserved under `query`. Empty strings and the string `'0'` are valid current query values and don't fall back to history. This matters when someone arrives with a campaign parameter and then visits a page without it.

For names that don't work with dot notation, Liquid bracket notation keeps the exact key. These illustrative expressions haven't been executed:

```liquid
{{ variable.workforge_recommendation }}
{{ input.raq_workforge_team_size }}
{{ segments.names.dim_workforge_role }}
{{ query.current['campaign-name'] | escape }}
{{ currentpage.pathname }}
```

Older score-source paths still work for compatibility. Use the paths above for new templates. RightMessage can convert numeric-looking variable strings to numbers for score comparisons, while `variable.<name>` keeps the stored value. A variable and score with the same name can have different types.

## Differences between flow, email, and HTML templates

| Where you're using data            | What to know                                                                                                                          |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| Set Data value                     | Liquid renders text before the action writes or stores it.                                                                            |
| Code node                          | Liquid renders before JavaScript runs. **Add Data** translates simple score, variable, and input tokens into local `RM` getter calls. |
| On-site widget or flow text        | Uses the site's current visitor data. Provider fields need an available profile.                                                      |
| Provider-native personalized email | Uses your email platform's template syntax and data at sending time, not necessarily LiquidJS or the browser paths above.             |
| `data-rm-if` HTML attribute        | Accepts a segment ID only, not a Liquid expression.                                                                                   |
| `__rm_test`                        | Overlays selected visitor data for testing. It doesn't change the normal data structure or publish a draft.                           |

Values from a visitor, URL, or browser storage don't prove that someone has permission to access protected data. Keep that data behind your application's own authentication and server-side access checks.

For provider-specific sending syntax, see [email personalization](https://rightmessage.com/help/topics/email-personalization). For browser testing, see [simulate visitor data with `__rm_test`](https://rightmessage.com/help/simulate-visitor-data-with-rm-test-parameter).
