> For the complete documentation index, see [llms.txt](https://docs.neonblue.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.neonblue.ai/email-integration/braze-integration/braze-custom-attributes-and-variant-routing.md).

# Braze Custom Attributes & Variant Routing

## Braze Custom Attributes & Variant Routing

### Overview

By default, Neon Blue assigns users to any Active variant in an experiment, allowing the system to freely optimize toward the best-performing variant for each user. **Variant Routing** restricts which variants a user is eligible for based on their Braze custom attributes. Neon Blue can only optimize within a user's eligible variant pool.

This is useful when variants are thematically tied to a user attribute—for example, different messaging strategies for different churn reasons or self-identified personas—where showing a mismatched variant would be nonsensical.

***

### How It Works

1. A **custom attribute** in Braze (e.g., `SELF_IDENTIFIED_PERSONA`) is mapped to an event context field in Neon Blue.
2. Each variant in the experiment is scoped to one or more attribute values.
3. At render time, the attribute value is passed to Neon Blue, which filters the eligible variant pool before making an assignment.

***

### Setup

#### 1. Configure the Event Context Mapper

In the Neon Blue portal, navigate to your experiment's **Setup** tab.

Add a mapping entry:

| Field Name                | Braze Custom Attribute Path                        |
| ------------------------- | -------------------------------------------------- |
| `SELF_IDENTIFIED_PERSONA` | `$.event.custom_attribute.SELF_IDENTIFIED_PERSONA` |

The left side is the context key you'll reference when scoping variants. The right side is the JSONPath to the attribute passed from Braze.

> **Note:** To modify the Event Context Mapper on a live experiment, you must first roll the experiment back to **Draft** state.

#### 2. Scope Variants to Attribute Values

For each variant in the experiment, set the **Event Context Filter** values that determine eligibility.

A variant can match on:

* **One or more specific values** — the variant is eligible when the user's attribute matches any listed value.
* **`<UNMATCHED>`** — a special catch-all value. The variant is eligible for any user whose attribute doesn't match any other variant's filter (including users with no value set).

**Example Configuration**

| Variant    | Event Context Filter (`SELF_IDENTIFIED_PERSONA`) |
| ---------- | ------------------------------------------------ |
| Variant #1 | `I don't create music, lyrics, or poetry`        |
| Variant #2 | `I make music as a hobby`, `<UNMATCHED>`         |
| Variant #3 | `I write poetry/lyrics`                          |
| Variant #4 | `I'm a content creator`, `<UNMATCHED>`           |
| Variant #5 | `I'm a creative professional (e.g. advertising)` |
| Variant #6 | `I'm a professional musician, producer, or DJ`   |
| Variant #7 | `I make music as a hobby`, `<UNMATCHED>`         |

#### 3. Pass the Attribute in Braze

In your Braze Message node, add an `{% assign %}` tag **before** the Neon Blue content block so the attribute is available at render time:

```liquid
{% assign SELF_IDENTIFIED_PERSONA = {{custom_attribute.${SELF_IDENTIFIED_PERSONA}}} %}
```

This makes the value accessible to Neon Blue's Connected Content call.

***

### Matching Logic

When a render request arrives, Neon Blue evaluates the user's attribute value against the variant filters:

1. **Exact match** — If the value matches a variant's filter, that variant is eligible.
2. **Multiple matches** — If the value appears on multiple variants, Neon Blue optimizes within that subset (e.g., bandit selects the best performer among eligible variants).
3. **No match / no value** — If the attribute is unset or doesn't appear in any variant's filter, only variants tagged with `<UNMATCHED>` are eligible.

**Matching Examples**

Given the configuration above:

| User's `SELF_IDENTIFIED_PERSONA`        | Eligible Variants | Behavior                                            |
| --------------------------------------- | ----------------- | --------------------------------------------------- |
| `I write poetry/lyrics`                 | #3 only           | Always served Variant #3                            |
| `I make music as a hobby`               | #2, #7            | Bandit optimizes between #2 and #7                  |
| `Just here for fun` (not in any filter) | #2, #4, #7        | Bandit optimizes between all `<UNMATCHED>` variants |
| *(not set)*                             | #2, #4, #7        | Bandit optimizes between all `<UNMATCHED>` variants |

***

### Finding Attribute Values

To identify valid attribute values in Braze, check the **Custom Attribute Usage Report** in your Braze dashboard:

`Dashboard > App Settings > Custom Attributes > [Your Attribute]`

This shows all values currently in use and their user counts, which helps you map your variants correctly.

***

### Best Practices

* **Use variant routing only when variants are semantically tied to user attributes.** If all variants make sense for all users, let Neon Blue optimize freely across the full pool.
* **Include `<UNMATCHED>` on at least one variant** to ensure users with missing or unexpected attribute values still receive content.
* **Keep attribute values stable.** If Braze attribute values change over time (e.g., new onboarding options), update your variant filters to match.
* **Test with different attribute values** in Braze's preview/test mode before going live to confirm routing works as expected.
