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

# Braze

> Build better campaigns on Braze with up-to-date customer data from your data warehouse

## Overview

Braze is a customer engagement platform that supports real-time and batch messaging across email, SMS, mobile, and web. Use Hightouch to sync user data, events, subscription status, and catalog information so Braze can run data-driven messaging and lifecycle campaigns.

## Supported syncing

| Sync Type | Description | Supported Sync Modes | API Reference |
| - | - | - | - |
| [**Event**](#event) | Send track events and track purchase type events to Braze | Insert | [Track events ↗](https://www.braze.com/docs/api/objects_filters/event_object/) |
| [**Object**](#object) | Sync records to Braze objects like `User` or `Organization`. | Upsert, Update | [Update user data ↗](https://www.braze.com/docs/api/endpoints/user_data/) |
| [**Subscription Status**](#subscription-status) | Sync SMS or email subscription groups. | Upsert | [Manage subscription groups ↗](https://www.braze.com/docs/api/endpoints/subscription_groups/) |
| [**User Identify**](#merge-anonymous-and-known-users) | Merge alias-only users with an identified profile and remove the alias-only profile | Insert | [Identify users ↗](https://www.braze.com/docs/api/endpoints/user_data) |
| [**Catalog**](#catalog) | Sync catalog items such as products or offers. | Upsert, Update | [Manage catalog items ↗](https://www.braze.com/docs/api/endpoints/catalogs) |
| [**Campaign Trigger**](#trigger-campaigns) | Trigger campaigns with context from your records. | Insert, Update, Upsert | [Send triggered campaigns ↗](https://www.braze.com/docs/api/endpoints/messaging/send_messages/post_send_triggered_campaigns/) |
| [**Canvas Trigger**](#trigger-canvases) | Trigger Canvases with context from your records. | Insert, Update, Upsert | [Send triggered Canvases ↗](https://www.braze.com/docs/api/endpoints/messaging/send_messages/post_send_triggered_canvases/) |

## Connect to Braze

Connecting Braze to Hightouch requires an **API key** and **region**.

### API key

Generate or locate a REST API key in your Braze dashboard under **Settings > APIs and Identifiers > API Keys**. Create a dedicated REST API key for Hightouch rather than reusing a Partner Integration key — Partner Integration keys don't include the permissions that Hightouch requires.

![Finding an API key in the Braze dashboard](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/destinations/destination-braze-api-key.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=75bb5156b6f817c77f2d1ca8c34619c5)

#### Required permissions

The API key must include permissions for each sync type you plan to use.

| Sync Type | Required permissions |
| - | - |
| **Event** | `users.track`, `custom_attributes.get`, `users.export.ids` (only required if using the **Retry** [user existence behavior](#event)) |
| **Object** | `users.track`, `users.export.ids`, `users.delete` (optional if you want to rely on Hightouch for user deletions), `users.alias.update` (if you are updating existing users), `custom_attributes.get` |
| **Subscription Status** | `subscription.status.set` |
| **User Identify** | `users.identify` |
| **Catalog** | `catalogs.add_items`, `catalogs.update_items`, `catalogs.get_item`, `catalogs.create`, `catalogs.get`, `catalogs.delete_items`, `catalogs.replace_items` |
| **Campaign Trigger** | `campaigns.trigger.send`, `campaigns.list`, `campaigns.details` |
| **Canvas Trigger** | `canvas.trigger.send`, `canvas.list`, `canvas.details` |
| **Segments** | `segments.list`, `segments.details` (only required if you use Braze with Hightouch Agents) |

### Region

To find your Braze region, check the domain in your Braze dashboard URL and match it to the table below.

![Find your Braze region from your dashboard URL](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/destinations/destination-braze-region.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=0dd87f2d302c9780947eb5f80d88e256)

| URL | Region |
| - | - |
| `https://dashboard-01.braze.com` | `iad-01.braze.com` |
| `https://dashboard-02.braze.com` | `iad-02.braze.com` |
| `https://dashboard-03.braze.com` | `iad-03.braze.com` |
| `https://dashboard-04.braze.com` | `iad-04.braze.com` |
| `https://dashboard-05.braze.com` | `iad-05.braze.com` |
| `https://dashboard-06.braze.com` | `iad-06.braze.com` |
| `https://dashboard-07.braze.com` | `iad-07.braze.com` |
| `https://dashboard-08.braze.com` | `iad-08.braze.com` |
| `https://dashboard-01.braze.eu` | `fra-01.braze.eu` |
| `https://dashboard-02.braze.eu` | `fra-02.braze.eu` |

> [!TIP]
>
> Refer to the [Braze endpoint
> documentation](https://www.braze.com/docs/api/basics/?redirected=true#api-definitions)
> to view additional regions. To derive the region, remove the prefix
> "`https://rest`" from the respective value in the **REST ENDPOINT** column.

## Sync configuration

The configuration options below vary by sync type.

> [!NOTE]
>
> Destination fields appear greyed out until you select a sync type and
> configure record matching. This is expected behavior during initial setup, not
> an error.

### Event

Send events to Braze and assign them to a user. Choose between two event types:

* **Track Event:** Sends events of a particular name when a record is added
* **Track Purchase:** Track when a customer makes a purchase

> [!WARNING]
>
> To ensure syncs send each event, your event model must use a truly unique [primary key](https://docs.hightouch.com/getting-started/concepts.md#unique-primary-key-requirement). See the [events syncs](https://docs.hightouch.com/syncs/types-and-modes.md#event-syncs) documentation for more information.

When syncing events, you can configure what happens when the associated user doesn't exist in Braze yet. This is controlled by the **user existence behavior** setting:

* **Create user**: Braze automatically creates a new user profile when the event is received.
* **Skip event**: Braze won't create a new profile or track the event.
* **Retry until user exists**: Hightouch polls Braze to check whether the user exists before sending the event. If the user hasn't been created after 2 minutes, the event is rejected as retriable and retried later.

![Event configuration in the Hightouch UI](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/destinations/destination-braze-create-users-events.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=725b89a0c28fa7dcf49ccef7d4a77d21)

#### Record matching

Hightouch can match events to users on different Braze fields. If user existence behavior is set to **Create user**, you can only match on **Braze External ID** or **User Alias Object**. If set to **Skip event** or **Retry until user exists**, you can also match on **Braze ID**.

For `track event` type events you can also send the event name and event timestamp. `Track purchases` don't require an event name.

> [!TIP]
>
> Hightouch accepts a standard date time format for the timestamp and
> automatically converts it to the format Braze requires.

![Record matching for events in the Hightouch UI](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/destinations/destination-braze-track-config.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=29631c1748c869f91d43c5822877efe5)

#### Field mapping

Map model columns to the event properties of a Braze event.

![Field mapping for events in the Hightouch UI](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/destinations/destination-braze-event-properties.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=2000062e7418c237e8ef9193557723c3)

#### Event property configuration

Send your custom properties as a single object column or as multiple columns from your model.

![Event property configuration in the Hightouch UI](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/destinations/destination-braze-event-property-object.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=73ae8ae70facf4bd5210026209d9d8e0)

> [!NOTE]
>
> When using a single column, make sure that the model column's [data
> type](https://docs.hightouch.com/models/data-types-casting.md#data-types) is **Object / Array**.

### Object

Sync rows from your model to Braze users. Use **Upsert** when Hightouch should create missing users and keep all attributes up to date. Use **Update** when only users that already exist in Braze should change.

Hightouch automatically normalizes some user data fields to meet Braze's specifications.

| Field name | Hightouch normalization |
| - | - |
| Date of birth | Convert from timestamps to year-month-date |
| Email | Convert to all lowercase letters |
| Phone numbers | Remove leading plus signs (`+`) and white spaces |

#### Record matching

In Upsert mode, you can match records from your source to your Braze workspace using the Braze External User ID. In Update mode, you can also match records based on Braze ID or a user alias object.

> [!WARNING]
>
> If you match records with a column **other** than the primary key of your
> model, changes in the model column used for record matching will not be
> reflected in Braze. This is to ensure duplicate records are not created in
> Braze.

> [!TIP]
>
> When syncing a large amount of rows, make sure to match the records using
> Braze External User ID instead of Braze ID. Using the Braze ID requires more
> requests which often results in slower sync speeds. See the [slow initial sync
> section](#slow-initial-sync) for more details.

![Selecting a model column to use as the Braze external ID in the Hightouch UI](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/destinations/destination-braze-record-matching.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=3e8cc26c61bccec2f86085da380fb952)

#### Field mapping

You can sync columns from your source to Braze's default and custom fields. If you send data for a custom field that doesn't exist, Hightouch adds the field and automatically detects its type.

![Mapping model columns to fields in Braze](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/destinations/destination-braze-field-mapping.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=4ef7a5567ccdd94b2dd884cff82f7461)

You can manage your custom fields in **Manage App Group > Custom Attributes** in Braze.

You can also map your model columns to **user aliases** in Braze.
This configuration only appears if you select **Braze External ID** for [record matching](https://docs.hightouch.com/destinations/braze.md#record-matching).

![Field mapping to Braze user aliases](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/destinations/destination-braze-field-mapping-user-alias.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=5fb7f2a917f8425b0c08e9e9924a6c30)

#### Nested objects and object arrays

Braze supports user attributes containing JSON objects or [object arrays](https://www.braze.com/docs/user_guide/data_and_analytics/custom_data/custom_attributes/array_of_objects/). These objects and object arrays are commonly used to support "entities" related to each user—for example, devices, medications, pets, etc.—especially when marketing communication needs to include metadata about these related entities.

To sync to these attributes, build the full JSON object into a single column within your Hightouch data model.

Modeling nested attributes into JSON can be done directly in your warehouse with something like [dbt](https://www.getdbt.com/), or you can use SQL functions appropriate for your data warehouse and build these objects within the [SQL Editor](https://docs.hightouch.com/models/sql-editor.md) in Hightouch.

#### Nested object array example

Let's say you want an array of a customer's pets to be an attribute named `pets` in Braze. You would need a data model with a column named `pets` and would then build the full JSON pets object array to populate that column. This array might look something like this:

```json theme={null}
[
  {
    "id": 0,
    "type": "dog",
    "breed": "Mutt",
    "name": "Rae"
  },
  {
    "id": 1,
    "type": "cat",
    "breed": "Tabby",
    "name": "Garfield"
  }
]
```

This full object array would need to be the value of a column in your Hightouch data model:

![Value of an example "Pets" field shown in the Hightouch UI](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/destinations/destination-braze-pets-array.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=460920476ba9f13b7f2d978e315dd636)

Then you would map this column to the `pets` custom attribute in Braze in your Hightouch sync configuration:

![Mapping the "Pets" field in the Hightouch UI](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/destinations/destination-braze-pets-mapping.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=6addce0504dbc2a0eb49c0955e486f91)

After scheduling your sync, you'd have relevant pet data on each user in Braze.

![Pet data appearing in the Braze UI](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/destinations/destination-braze-pets.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=9936b671e39426db75261683149b302a)

### Subscription Status

Sync users to Email and SMS subscription groups.

![Subscription status configuration in the Hightouch UI](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/destinations/destination-braze-subscription-types.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=6022c4f41355a46ddc7b3a1ba621c082)

Braze has a designated set of fields to manage subscription group opt-ins independent of user attributes.

Sync to existing subscription groups within Braze. You can enter a static subscription group ID or select a column from your model containing the subscription group ID.

![Subscription group configuration in the Hightouch UI](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/destinations/destination-braze-subscription-id.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=7b9e4e7279776c93875edf01cc9f80bb)

> [!TIP]
>
> Read [Braze's
> docs](https://www.braze.com/docs/user_guide/message_building_by_channel/email/managing_user_subscriptions/)
> for information on creating a subscription group.

#### Record matching

You can match records from your source to your Braze subscription groups by Braze External User ID, Phone Number, or Email.

#### Delete behavior

> [!WARNING]
>
> When a user is removed from a Subscription status sync, Hightouch makes an API
> request to set the user's
> [`subscription_state`](https://www.braze.com/docs/api/endpoints/subscription_groups/post_update_user_subscription_group_status_v2/#request-body)
> to `unsubscribed`.

### Merge anonymous and known users

Merge alias-only "anonymous" users with "known" Braze users that have an external ID.

It's common to have “soft-signup” workflows where all you collect from a user is an email or phone without forcing the user to create an account. This workflow results in users where all you may have is PII. When they create an account, you need to use this endpoint to “identify” these users with a new login ID.

For example, your workflow might look like this:

1. A user visits your website or app. The user doesn't have a Braze ID to start with—they only enter an email address, phone number, or some other PII. You create a user in you customer database, and an alias-only user profile in Braze via the Braze SDK. Hightouch isn't involved at this point.
2. You can use Hightouch to send user attributes or user-related events to Braze via Hightouch by [matching on a user alias object](#record-matching), since there isn't an external ID yet. You must use Update mode.
3. Once you have a unique user ID in your customer database, you can use Hightouch to call Braze's [Identify endpoint](https://www.braze.com/docs/api/endpoints/user_data/post_user_identify/) to "identify" the alias-only user using your unique user ID as the external ID in Braze. This merges the alias-only user in Braze with a known Braze user. If an external ID doesn't exist when using the Identify endpoint, Braze creates the external ID and the alias-only user is considered “identified.”
4. All subsequent updates to the user should be done in Hightouch using the external user ID, not the user alias.

#### Record matching

You can match and merge records using Braze user external IDs.

![Selecting a model column to use as the Braze external ID in the Hightouch UI](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/destinations/destination-braze-identify-record-matching.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=4493654e51c74fcc000b8d9eb0ce9b29)

#### Field mapping

Map your Braze aliases to known users using the alias label and value.

![Field mapping for users in the Hightouch UI](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/destinations/destination-braze-identify-field-mapping.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=c38b8e82a89255227c9ab21aadf9abdc)

If you're adding a JSON object directly to your model, each [`user_alias`](https://www.braze.com/docs/api/objects_filters/user_alias_object) object should consist of `alias_label` and `alias_name` as top-level attributes, rather than nested in another object.

```text theme={null}
# CORRECT FORMAT
"user_aliases": [
    {
      "alias_label": "label",
      "alias_name": "name_1"
    },
    ...
  ]

# INCORRECT FORMAT
"user_aliases": [
    {
      "user_alias" : {
        "alias_name" : "label",
        "alias_label" : "name_1"
      }
    },
    ,
    ...
  ]

```

### Catalog

Sync rows from your models to your existing catalog through the [Braze Catalogs API](https://www.braze.com/docs/api/endpoints/catalogs).

> [!NOTE]
>
> Braze's Catalog endpoint is in early access. Contact your Braze account
> manager if you are interested in participating.

You can choose to create a new [Braze catalog](https://www.braze.com/docs/user_guide/personalization_and_dynamic_content/catalogs/catalog/) the first time the
sync runs or use an existing catalog. If creating a new catalog, you can name the catalog from Hightouch. If you leave this field blank, the name defaults to the
model name.

![Hightouch UI showing option to create new catalogs](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/destinations/destination-braze-auto-create-catalog.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=c68b6cbd45f2a03f0df6d9d4ae45b7ed)

> [!NOTE]
>
> With the **Create a new catalog** option enabled, Hightouch only creates a
> catalog the first time the sync runs. Hightouch doesn't modify the catalog
> name after the first run. If you want to change the name or fields, you have
> to create a new catalog using a new sync.

Use **Upsert** to insert new catalog items into Braze. Use **Update** to update fields on catalog items that already exist in Braze.

#### Record matching

In Upsert and Update modes, you can match records from your source to your Braze workspace using the catalog item ID.

#### Field mapping

You can map data from your source to any existing field in your catalog.

> [!NOTE]
>
> If automatically creating a catalog, you can't add new fields or change data
> types after the initial sync.

#### Test a catalog sync

If you are using an existing catalog, you can test your sync by syncing a single row. You can verify that a row was successfully synced by going to your Catalog and finding the synced row by its catalog item ID. If you are automatically creating a catalog, it's not possible to test by row.

### Trigger campaigns

Use records from your models to trigger campaigns through the [Braze Campaign API](https://www.braze.com/docs/api/endpoints/messaging/send_messages/post_send_triggered_campaigns/).

Once configured, your Hightouch sync triggers your campaign once rows appear in your model. Hightouch triggers your campaign for every row in your model the first time you run the sync, whenever you do a full resync, and for every new row added to your query results in subsequent syncs.

#### Sync modes

The sync mode controls which rows re-trigger the campaign on subsequent runs ([CDC behavior](https://docs.hightouch.com/syncs/cdc.md#cdc-by-sync-mode)). It doesn't change the underlying Braze API call. To control whether messages are only sent to users that already exist in Braze, use [Send to existing only](#send-to-existing-only).

* **Insert** (default): triggers the campaign for every row the first time you run the sync and on every full resync, and for every new row added to your query results in subsequent syncs.
* **Update**: triggers the campaign only for rows that change in your model.
* **Upsert**: triggers the campaign for new rows and for rows that change in your model.

> [!WARNING]
>
> In Update and Upsert modes, a row that changes re-triggers the campaign, so
> the same user can receive duplicate messages. Set [delivery
> controls](https://www.braze.com/docs/user_guide/engagement_tools/campaigns/building_campaigns/delivery_types/reeligibility/#canvas)
> in Braze to prevent duplicates.

#### Before you begin

Before you can set up a Hightouch sync to trigger a campaign, you need set up an [API-triggered](https://www.braze.com/docs/user_guide/engagement_tools/campaigns/building_campaigns/delivery_types/api_triggered_delivery/) campaign in Braze. See [how to create an email campaign in Braze](https://www.braze.com/docs/user_guide/message_building_by_channel/email/creating_an_email_campaign/) for more information.

> [!NOTE]
>
> Hightouch can only trigger campaigns that are
> [API-triggered](https://www.braze.com/docs/user_guide/engagement_tools/campaigns/building_campaigns/delivery_types/api_triggered_delivery/).
> These are different from [API
> campaigns](https://www.braze.com/docs/api/api_campaigns/).

#### Record matching

Trigger a campaign for your Braze users by matching on one of these identifiers:

* **External user ID**
* **Braze ID**: Matches the ID Braze assigns to every profile, including anonymous profiles that don't have an external user ID. [Send to existing only](#send-to-existing-only) isn't available, because a Braze ID can only match a profile that already exists in Braze.
* **Email address**: When several Braze profiles share an email address, Braze uses a prioritization to choose one. Set the order in the prioritization field. The default is **Identified**, then **Most recently updated**. Braze only sends when the prioritization resolves to a single profile, and you can't select both **Identified** and **Unidentified**.
* **User alias object**: Map an object column with `alias_name` and `alias_label` fields. [Send to existing only](#send-to-existing-only) isn't available, because Braze doesn't support it for user aliases.

Hightouch rejects rows with an empty email address, a Braze ID that isn't a 24-character hexadecimal string, or an invalid user alias object.

> [!WARNING]
>
> This destination doesn't check for duplicates. Hightouch recommends that you
> set [delivery
> controls](https://www.braze.com/docs/user_guide/engagement_tools/campaigns/building_campaigns/delivery_types/reeligibility/#canvas)
> in Braze to prevent duplicated campaign messages in case your source has
> duplicates.

#### Field mapping

Customize your messages via [API trigger properties](https://www.braze.com/docs/api/objects_filters/trigger_properties_object). Hightouch automatically lists trigger properties you can map values with. See [using the templated content included with an API request](https://www.braze.com/docs/user_guide/engagement_tools/campaigns/building_campaigns/delivery_types/api_triggered_delivery/#setting-up-an-api-triggered-campaign) for more information.

#### Send to existing only

Keep **Send to existing only** enabled when the campaign should target only users that already exist in Braze. To create or update users when you trigger the campaign, uncheck the setting and map the user properties.

![Send to existing only](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/destinations/destination-braze-send-to-existing-only.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=c65eec2caa780eff6195b5cc7ebd5726)

> [!NOTE]
>
> Note: Mapping user properties when using this setting is *required*

#### Test campaign triggers safely

Hightouch recommends that you test with your email before running an actual sync for your users.

You can test by following these steps:

1. Create a Google Sheet with a valid `external_id` and your email. You will use this to sync yourself as user in Braze. See [Braze's user ID convention](https://www.braze.com/docs/developer_guide/platform_integration_guides/web/analytics/setting_user_ids/#suggested-user-id-naming-convention) if you are unfamiliar with `external_id`.

   ![Screenshot of Google Sheet with test information](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/destinations/destination-braze-campaign-trigger-test-sheet.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=2854e430bded3dac32f33b80859300b9)
2. Use the Google Sheet as a source to [add yourself to Braze as a user](https://docs.hightouch.com/destinations/braze.md#object).

   ![Syncing information from Google Sheet to Braze using Hightouch](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/destinations/destination-braze-campaign-trigger-sync-user.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=343a36288d638927a10f55701bf82c6f)
   > [!TIP]
   >
   > You can use the same sync to remove yourself from Braze once your testing
   > is complete. Set the sync configuration's delete mode to "Delete
   > destination record associated with source record," remove yourself from the
   > source sheet and rerun the sync.
3. Build your campaign in Braze. See Braze's guide on [how to build a campaign](https://www.braze.com/docs/user_guide/engagement_tools/campaigns/building_campaigns) for more information. When setting up your campaign schedule, make sure to select [API-triggered Delivery](https://www.braze.com/docs/user_guide/engagement_tools/campaigns/building_campaigns/delivery_types/api_triggered_delivery/).
4. Set up your campaign trigger sync and run it on Hightouch.
5. Verify that you are able to receive your campaign message. If you need to make corrections and thus want to re-run the sync, click **Resync full query** next to the **Run** button. Also note that your [re-eligibility settings](https://www.braze.com/docs/user_guide/engagement_tools/campaigns/building_campaigns/delivery_types/reeligibility/#canvas) in Braze campaign must be loosely set for you to re-trigger the sync and actually receive the email.

### Trigger Canvases

Use records from your models to trigger Canvases through the [Braze Canvas API](https://www.braze.com/docs/api/endpoints/messaging/send_messages/post_send_triggered_canvases/).

Once configured, your Hightouch sync triggers your Canvases once rows appear in your model. Hightouch triggers your Canvases for every row in your model the first time you run the sync, whenever you do a full resync, and for every new row added to your query results in subsequent syncs.

#### Sync modes

The sync mode controls which rows re-trigger the Canvas on subsequent runs ([CDC behavior](https://docs.hightouch.com/syncs/cdc.md#cdc-by-sync-mode)). It doesn't change the underlying Braze API call. To control whether messages are only sent to users that already exist in Braze, use [Send to existing only](#send-to-existing-only).

* **Insert** (default): triggers the Canvas for every row the first time you run the sync and on every full resync, and for every new row added to your query results in subsequent syncs.
* **Update**: triggers the Canvas only for rows that change in your model.
* **Upsert**: triggers the Canvas for new rows and for rows that change in your model.

> [!WARNING]
>
> In Update and Upsert modes, a row that changes re-triggers the Canvas, so the
> same user can receive duplicate messages. Set [delivery
> controls](https://www.braze.com/docs/user_guide/engagement_tools/campaigns/building_campaigns/delivery_types/reeligibility/#canvas)
> in Braze to prevent duplicates.

#### Before you begin

Before you can set up a Hightouch sync to trigger a Canvas, you need set up an [API-triggered](https://www.braze.com/docs/user_guide/engagement_tools/canvas/create_a_canvas/create_a_canvas/#step-2b-set-your-canvas-entry-schedule) Canvas in Braze. See [Braze Ideas & Strategies](https://www.braze.com/docs/user_guide/engagement_tools/canvas/ideas_and_strategies) for more information and best practices.

> [!NOTE]
>
> Hightouch can only trigger Canvases that are
> [API-triggered](https://www.braze.com/docs/user_guide/engagement_tools/canvas/create_a_canvas/create_a_canvas/#step-2b-set-your-canvas-entry-schedule).
> Only non-archived, active canvases are available in sync configuration.

#### Record matching

Trigger a Canvas for your Braze users by matching on one of these identifiers:

* **External user ID**
* **Braze ID**: Matches the ID Braze assigns to every profile, including anonymous profiles that don't have an external user ID. [Send to existing only](#send-to-existing-only) isn't available, because a Braze ID can only match a profile that already exists in Braze.
* **Email address**: When several Braze profiles share an email address, Braze uses a prioritization to choose one. Set the order in the prioritization field. The default is **Identified**, then **Most recently updated**. Braze only sends when the prioritization resolves to a single profile, and you can't select both **Identified** and **Unidentified**.
* **User alias object**: Map an object column with `alias_name` and `alias_label` fields. [Send to existing only](#send-to-existing-only) isn't available, because Braze doesn't support it for user aliases.

Hightouch rejects rows with an empty email address, a Braze ID that isn't a 24-character hexadecimal string, or an invalid user alias object.

#### Field mapping

Customize your messages via [Canvas entry properties](https://www.braze.com/docs/api/objects_filters/canvas_entry_properties_object). Hightouch automatically lists Canvas entry properties you can map values to.

See [Braze's docs](https://www.braze.com/docs/user_guide/engagement_tools/canvas/create_a_canvas/canvas_entry_properties_event_properties) on using the templated content included with an API request for more information.

#### Send to existing only

Keep **Send to existing only** enabled when the Canvas should target only users that already exist in Braze. To create or update users when you trigger the Canvas, uncheck the setting and map the user properties.

![Send to existing only](https://mintcdn.com/hightouch-3c037897/L_vQfA0JQEpeGUUo/images/destinations/destination-braze-send-to-existing-only.png?fit=max\&auto=format\&n=L_vQfA0JQEpeGUUo\&q=85\&s=c65eec2caa780eff6195b5cc7ebd5726)

> [!NOTE]
>
> Note: Mapping user properties when using this setting is *required*

## Tips and troubleshooting

To see the exact API requests and responses from each sync run, use the [live debugger](https://docs.hightouch.com/syncs/debugger.md). To get notified by Slack, PagerDuty, SMS, or email when a sync fails, set up [sync alerts](https://docs.hightouch.com/syncs/alerting.md). If you run into an issue this page doesn't cover, [contact support](#pylon-chat).

> [!NOTE]
>
> For Hightouch platform error codes related to Braze, see [Error codes:
> Braze](https://docs.hightouch.com/developer-tools/error-codes.md#braze).

### Behavior and limitations

#### NULL values clear profile attributes

When syncing user attributes to Braze, any mapped field containing a NULL value [clears the corresponding attribute](https://www.braze.com/docs/api/objects_filters/user_attributes_object#braze-user-profile-fields) on the Braze user profile. If you don't want to overwrite existing attribute values with empty data, exclude NULL rows from your model using a SQL `WHERE` clause, or select the **Don't sync NULL values** option in your sync's [field mapping configuration](https://docs.hightouch.com/syncs/mapping-data.md).

#### Syncs consume Braze data points

Hightouch [only syncs data that needs updating](https://docs.hightouch.com/getting-started/concepts.md#change-data-capture-cdc) to minimize the number of API requests made to your destinations.

Braze offers different pricing models, and how Hightouch syncs impact your usage depends on which model you're using.

To optimize for speed instead of data point updates, you can disable data point optimization in your Hightouch sync configuration.

##### Data points based pricing

In this model, pricing is based on data points, which count the number of updates made by you (and Hightouch). Each update contributes to your total data point usage, so efficient syncing can help reduce consumption.

##### Monthly Active Users (MAU) based pricing (CY24–25 SKU)

This pricing model emphasizes data flexibility for customers. There is no concept of "data points" in this SKU. However, overall data consumption is still constrained by product limits such as API rate limits and other usage thresholds, as detailed in Braze's documentation.

##### How data points are consumed

These are some key points to understand how Hightouch syncs affect your Braze data point consumption.

When syncing arrays without nested objects, you consume one Braze [data point](https://www.braze.com/docs/user_guide/data_and_analytics/data_points/) per API call:

* Creating an array consumes one data point.
* Updating arrays consumes one data point per value updated.

When syncing [arrays of objects](https://www.braze.com/docs/user_guide/data_and_analytics/custom_data/custom_attributes/array_of_objects/), you may consume multiple data points with one API call:

* Creating an array of objects consumes one data point *per attribute added* within each object. For example, creating two `pet` objects with four attributes each (`id`, `type`, `color`, and `size`) consumes eight data points.
* Updating an array of objects consumes one data point *per attribute changed*.
* Removals consume one data point *per removal criteria*. For example, removing objects where `id = 4` removes one object and costs one data point. Removing objects where `type = dog` may remove many objects but still only consumes one data point.

When syncing records that don't exist in Braze, Hightouch updates **all** mapped attributes with the values from your query results. This means you consume one data point per attribute per created record.

When syncing new records in the model that already exist in Braze, Hightouch first gets the user attributes from Braze, calculates which columns actually changed, and then only sends the changes for those columns. Each updated attribute equals one data point consumed.

When syncing records that have been previously synced to Braze, Hightouch compares the new record's columns to the previously sent record's columns, calculates which columns actually changed, and then only sends the changes for those columns. Each updated attribute equals one data point consumed.

> [!NOTE]
>
> Hightouch sync strategy aims for the lowest Braze data point consumption
> possible. Because Hightouch makes a `GET` request to first see what user
> attributes haven't changed since the last sync, sync speed is slower than
> other methods which overwrite all data in Braze and consume more data points.

#### Syncing nested or array attributes

To sync nested or array attributes to Braze, structure your model data as JSON objects. In the sync configuration, map the JSON column to the corresponding Braze custom attribute. For an array of objects, the custom attribute's data type in Braze must be **array of objects**, not **array**. While array allows the addition and removal of values, it does not support updates to existing values. Array of objects supports those updates, along with adds and removes. If the attribute is typed as array and updates to existing values are attempted, sync runs will still show as successful because Braze accepts the request, but the nested updates are not applied. For arrays of objects, see the Braze documentation on [nested custom attributes](https://www.braze.com/docs/user_guide/data_and_analytics/custom_data/custom_attributes/nested_custom_attribute_support/).

#### Multiple requests per user

When updating an attribute's value in a nested object to `null`, Braze's API doesn't let you combine this change with other non`-null` updates. Therefore, when updating values to `null`, Hightouch sends the full nested object with the `_merge_objects` parameter set to `false`.

Setting `_merge_objects` to `true` deep merges your update with the existing object data. See the Braze [API documentation](https://www.braze.com/docs/user_guide/data_and_analytics/custom_data/custom_attributes/nested_custom_attribute_support/#api-request-body) for more details.

This means the same user may require two update actions:

* Updates to nested objects with non-null values with `_merge_objects:true`
* Updates setting one or more attributes in nested objects to `null` with `_merge_objects:false`

### Validate your setup

After the sync runs, confirm the data landed in Braze:

* For user syncs, search for the test user in the Braze dashboard under **Audience > Search Users** and confirm the mapped attributes appear with expected values.
* For event syncs, check the user's profile activity feed to confirm events were received.
* For catalog syncs, go to **Data Settings > Catalogs** and verify items were created or updated.

### Required permissions

If your syncs are failing, check that the REST API key you use to [connect Braze to Hightouch](#api-key) has the [correct permissions](#required-permissions) for your sync type or types.

### Slow initial sync

If you're using a Braze ID to [match user records](#record-matching), your initial sync may be slow. When Hightouch updates Braze with new records or fields, Hightouch queries users in Braze for [change data capture](https://docs.hightouch.com/syncs/cdc.md).

Braze allows batch searches on external IDs, but only allows querying for one user at a time if using Braze IDs. That means Hightouch needs to make a separate request for each user if using Braze IDs.

It's best to match records on external ID rather than Braze ID. This speeds up the initial sync and limits the number of requests to make to Braze.

### Audience count drops to 0 when adding Braze as a destination

If your audience count drops to 0 after you add Braze as a destination, the audience's parent model is likely missing the identifier field that Braze requires (for example, `external_id` or `braze_id`). The destination filter needs matching identifiers to calculate the sendable count. Verify that your parent model includes the correct Braze identifier column and that values are populated for the users you expect to sync.

## Common errors

| Error message | Description |
| :- | :- |
| Invalid phone number | Braze rejects a phone number as invalid. [More detail.](#invalid-phone-number) |
| `alias objects must be a hash` | The User Alias Object isn't formatted as a JSON object. [More detail.](#alias-objects-must-be-a-hash) |
| Batch too large or payload exceeds Braze limits | A payload exceeds Braze's size limits. [More detail.](#batch-too-large-or-payload-exceeds-braze-limits) |
| Invalid or missing user identifiers | Identifiers are missing or invalid, so rows are dropped. [More detail.](#invalid-or-missing-user-identifiers) |
| Event attribute type errors | Event or purchase properties don't match Braze's required types. [More detail.](#event-attribute-type-errors) |
| Catalog field type or schema mismatches | Catalog field types or names don't match the existing catalog schema. [More detail.](#catalog-field-type-or-schema-mismatches) |
| Subscription group ID or permission errors | The subscription group ID is invalid or the API key lacks scopes. [More detail.](#subscription-group-id-or-permission-errors) |
| Campaign or Canvas trigger failures | A trigger doesn't fire because identifiers or trigger properties are missing. [More detail.](#campaign-or-canvas-trigger-failures) |
| `rowsPerBatch must be less than or equal to 50` | The advanced mapper's batch size exceeds 50 rows. [More detail.](#rows-per-batch-exceeds-the-limit) |

### Invalid phone number

```text theme={null}
400 – The value provided for the phone number field is not a valid phone number
```

**Cause:** Braze marks some phone numbers as invalid if they are deactivated, rejected by providers, on an [opt-out list](https://www.braze.com/docs/user_guide/message_building_by_channel/sms/keywords/optin_optout), or already associated with another user. See Braze's guidance on [importing phone numbers](https://www.braze.com/docs/user_guide/message_building_by_channel/sms/phone_numbers/user_phone_numbers#importing-phone-numbers).

**Solution:**

* Validate phone number formatting in your source.
* Remove numbers that are known to be deactivated or opted out.
* Check for conflicting assignments of the same number to multiple users.

### Alias objects must be a hash

```text theme={null}
alias objects must be a hash
```

**Cause:** The alias object is not in the expected format with both `alias_label` and `alias_name`. A common cause is mapping a string column (for example, a JSON string) to the User Alias Object field instead of a JSON object. The expected format looks like this:

```json theme={null}
{
  "alias_label": "label",
  "alias_name": "name_1"
}
```

See Braze's [User Alias Object](https://www.braze.com/docs/api/objects_filters/user_alias_object) documentation for the required structure.

**Solution:**

* Ensure the model column mapped to the User Alias Object is of type **Object**, not string.
* Build the alias object in the correct shape before syncing.

### Batch too large or payload exceeds Braze limits

```text theme={null}
Max input length exceeded
Request body too large
```

**Cause:** Braze returns errors when event payloads, user attributes, or catalog items exceed size limits.

**Solution:**

* Reduce the number of mapped fields.
* Remove large nested objects or unnecessary attributes.
* Split very large catalogs or audiences across multiple syncs.

### Invalid or missing user identifiers

```text theme={null}
Missing matching row
```

**Cause:** Braze cannot update or target users unless identifiers are valid and non-null. You may also see dropped rows or partial updates.

**Solution:**

* Prefer **external\_id** as the primary identifier when possible.
* Ensure identifier columns are populated and stable in your model.
* For alias-based syncs, include both `alias_name` and `alias_label`.

### Event attribute type errors

```text theme={null}
TypeError
Invalid event attribute
```

**Cause:** Braze enforces strict types for event and purchase properties.

**Solution:**

* Convert timestamps to ISO 8601 or Unix milliseconds.
* Ensure numeric fields (such as price or quantity) are not strings.
* Avoid nested objects or arrays in event attributes unless Braze explicitly supports them.

### Catalog field type or schema mismatches

```text theme={null}
Field type mismatch
```

**Cause:** Catalog syncs fail when field types change or when new attributes do not match the existing catalog schema. You may also see partial item failures.

**Solution:**

* Keep catalog field names and types consistent across syncs.
* When schema changes are required, consider creating a new catalog (Braze catalog deletions are permanent).

### Subscription group ID or permission errors

```text theme={null}
Invalid subscription group ID
```

**Cause:** Subscription syncs fail when the subscription group ID is invalid or its API key lacks required scopes. You may also see the subscription state not updating.

**Solution:**

* Use the subscription group's **GUID** (not the display name).
* Ensure the API key includes `subscription.status.set` or an equivalent permission.

### Campaign or Canvas trigger failures

A campaign or Canvas trigger fails silently or returns a `400` error.

**Cause:** Triggers do not fire when user identifiers or required trigger properties are missing.

**Solution:**

* Confirm record matching uses a valid Braze identifier (for example, `external_id`).
* Verify that all required trigger properties exist in the model and are mapped in the sync configuration.

### Rows per batch exceeds the limit

```text theme={null}
rowsPerBatch must be less than or equal to 50
```

**Cause:** The advanced mapper's batch size exceeds the Braze API limit of 50 rows per batch.

**Solution:** Reduce the `rowsPerBatch` value in your sync's advanced mapper configuration to 50 or fewer.
