# Extensibility

Extensibility customizes how the connector resolves contacts, maps fields, and formats events. This is mainly used by technical teams when default behavior does not match the data model.

## Setup

Extensibility is configured in The Wallet Crew admin console. Each entry point is a JavaScript script stored in the Bloomreach connector **Advanced settings**.

Staging is the recommended starting point. This reduces the risk of breaking production segmentation and scenario triggers.

{% stepper %}
{% step %}

#### Open the Bloomreach connector advanced settings

<p align="center"><a href="https://admin.thewalletcrew.io/tenant/~/integrations/bloomreach/advanced" class="button secondary" data-icon="chevrons-right">Open Advanced settings page</a></p>

Each entry point has its own script slot:

* Contact Finder
* Event Interceptor
* Customer Mapper
  {% endstep %}

{% step %}

#### Start with the smallest possible change

Contact Finder is commonly implemented first when email is not the primary identifier. Event Interceptor is commonly implemented first when event naming or governance is required.

Keeping changes small makes it easier to isolate attribution and ingestion issues.
{% endstep %}

{% step %}

#### Validate end-to-end behavior

Validation usually covers both connector directions:

* An activity still works (Update pass / Notify / Apply privilege).
* An event is still attributed to the expected Bloomreach contact.

When Event Interceptor is used, validation should confirm the final event name and properties visible in Bloomreach.
{% endstep %}

{% step %}

#### Roll out to production and monitor

Once staging behavior matches expectations, the same scripts can be applied to production. When events are used as scenario triggers, rolling out during a change window is recommended.
{% endstep %}
{% endstepper %}

## Extensibility entry points

Connector scripts are configured in the Bloomreach connector advanced settings in The Wallet Crew admin console.

{% hint style="warning" %}
Changes can impact event tracking and scenario execution. A staging environment is recommended for testing.
{% endhint %}

### Contact Finder

A pass is linked to external identifiers (email, phone, member ID, ...). When an event happens on a pass, the connector needs to attach it to a Bloomreach contact.

Contact Finder customizes how those identifiers are converted into Bloomreach contact identifiers.

```js
/**
 * Retrieve contact identifier from pass identifiers to determine the bloomreach contact of a pass.
 * Default implementation uses email as registered identifier
 *
 * This method is asynchronous, allowing calls to external webservices if needed.
 *
 * @param {Record<string, any>} identifiers - Key-value pairs of pass identifiers
 * @returns {Promise<Record<string, string>>} Object with 'registered' key containing the contact identifier on the bloomreach side
 */
async function getContactIdentifiers(identifiers) {
  // Example: use email as the contact identifier
  // this is the default implementation
  return { "registered": identifiers["email"] };
}

export default function (context) {
  context.register('extensions.bloomreach.contactFinder', {
    GetContactIdentifiers: getContactIdentifiers
  });
}
```

### Event Interceptor

Event Interceptor can transform `eventName` and `properties` before sending the event to Bloomreach. This is useful for naming conventions, filtering, and enrichment.

Returning `null` drops the event.

```js
/**
 * Intercept events before sending to Bloomreach.
 * Use this to transform event names or properties dynamically.
 * Example: Add calculated fields, rename properties, filter data
 *
 * @param {Object} eventData - Event data object
 * @param {string} eventData.Name - Name of the event being tracked
 * @param {Record<string, string>} eventData.Properties - Event attributes/metadata
 * @returns {Promise<Object>} Modified event data object - could be null to omit this event
 */
async function interceptEvent({ Name, Properties }) {
  // Example: return event as-is (no transformation)
  // Modify as needed for your use case
  return { Name, Properties };
}

export default function (context) {
  context.register('extensions.bloomreach.event.interceptor', {
    Intercept: interceptEvent
  });
}
```

### Customer Mapper

Customer Mapper customizes mapping between The Wallet Crew enrolment form fields and Bloomreach fields. This is used when data is sent to or retrieved from Bloomreach with components like PassDataProvider and CustomerFlowElement.

```js
/**
 * Maps wallet crew account data to Bloomreach properties.
 *
 * @param {Object} account - The wallet crew account object with fields like firstName, email, etc.
 * @param {Object} properties - Bloomreach properties object to populate (mutated in place)
 */
function mapToBloomreach(account, properties) {
  // Example mappings - customize based on your field names
  if (account?.firstName) {
    properties["first_name"] = account.firstName;
  }
  if (account?.lastName) {
    properties["last_name"] = account.lastName;
  }
  if (account?.email) {
    properties["email"] = account.email;
  }
}

/**
 * Maps Bloomreach customer data to the wallet crew account fields.
 *
 * @param {Object} customer - Bloomreach customer object with Properties and Identifiers
 * @param {Object} account - The wallet crew account object to populate (mutated in place)
 */
function mapFromBloomreach(customer, account) {
  // Example mappings - customize based on your field names
  if (customer && customer.Properties) {
    if (customer.Properties["first_name"]) {
      account["firstName"] = customer.Properties["first_name"];
    }
    if (customer.Properties["last_name"]) {
      account["lastName"] = customer.Properties["last_name"];
    }
    if (customer.Properties["email"]) {
      account["email"] = customer.Properties["email"];
    }
  }
}

export default function (context) {
  context.register('extensions.bloomreach.mapper', {
    MapToBloomreach: mapToBloomreach,
    MapFromBloomreach: mapFromBloomreach
  });
}
```

## FAQ

<details>

<summary><strong>Can Contact Finder call external services?</strong></summary>

Yes. Contact Finder is asynchronous and can call external services when needed.

This is typically used when the Bloomreach contact identifier is not directly stored on the pass. Common patterns include resolving a `memberId` into an email, or mapping multiple identifiers to a single “registered” Bloomreach identifier.

</details>

<details>

<summary><strong>Can an event be dropped before reaching Bloomreach?</strong></summary>

Yes. Event Interceptor can return `null` to omit an event.

This is useful for governance and noise reduction. Examples include filtering out scan events from test passes, removing sensitive properties, or ignoring high-volume events that are not actionable in Bloomreach.

</details>

<details>

<summary><strong>When is Customer Mapper used?</strong></summary>

Customer Mapper is used when Bloomreach data is read or written through connector components. Typical entry points are PassDataProvider (read) and CustomerFlowElement (write).

It is not used by every activity. For example, sending a Notify message does not require Customer Mapper unless Bloomreach properties are also used as a data source for pass rendering.

Customer Mapper is the right place to align naming conventions, data formats, and optional fields. It also helps keep pass templates stable when Bloomreach field names change.

</details>
