Leveraging events in Affinity
Affinity emits events to broadcast crucial information about customer behavior, and listens for directives that let your custom code trigger Affinity behaviors. Use events to track customer journeys, synchronize custom content, and build custom flows.
NoteThis article applies to both the original Affinity experience (Affinity 1.0) and the new Affinity experience (Affinity 2.0). Version availability is noted for each event. If you're migrating from 1.0 to 2.0, see the migration section at the end of this article.
Before you start
You must be on the Recharge Plus or Custom pricing plans to make advanced customizations to the Affinity customer portal.
Event types
| Event type | When it occurs | Use it for | Available on |
|---|---|---|---|
| Click | When a customer begins an action | Measuring intent or replacing the default Affinity flow | Affinity 1.0, Affinity 2.0 |
| Action | After an action completes successfully | Measuring completed outcomes | Affinity 1.0, Affinity 2.0 |
| Navigation | When the customer moves to another Affinity page | Journey analytics or page-specific extension behavior | Affinity 2.0 |
| Slot lifecycle | When slot content is added to or removed from the DOM | Managing the lifecycle of custom code linked to slot content | Affinity 1.0 |
| Directive | Sent by your code when you want Affinity to perform a supported behavior | Opening common actions or refreshing Affinity after external data changes | Affinity 2.0 |
Click and action events
Click and action events describe different points in the same journey. For example, Recharge::click::skip is emitted when the customer clicks Skip, while Recharge::action::skip is emitted only after the order has been skipped. Comparing the two can show where customers abandon a flow.
| Action | Trigger | Click event | Completion event | Available on |
|---|---|---|---|---|
| Skip | The customer clicks Skip from an order or the subscription management page. | Recharge::click::skip | Recharge::action::skip | 1.0, 2.0 |
| Unskip | The customer clicks Unskip from an order or subscription page. | Recharge::click::unskip | Recharge::action::unskip | 1.0, 2.0 |
| Manage subscription | The customer clicks Manage subscription. | Recharge::click::manageSubscription | — | 1.0, 2.0 |
| Order now | The customer clicks Send now on the next order page. | Recharge::click::orderNow | Recharge::action::orderNow | 1.0, 2.0 |
| Reschedule | The customer clicks Reschedule from an order or the subscription management page. | Recharge::click::reschedule | Recharge::action::reschedule | 1.0, 2.0 |
| Cancel | The customer clicks Cancel this subscription on the Overview page (Affinity 2.0) or the Subscription details page. | Recharge::click::cancel | — | 1.0, 2.0 |
| Reactivate | The customer clicks Reactivate on the subscription management page, or the Affinity overview page if the customer doesn't have active subscriptions. | Recharge::click::reactivate | Recharge::action::reactivate | 1.0, 2.0 |
| Changes to an upcoming order | Any change affecting an upcoming order, such as a new product being added, a subscription being updated, or an order being rescheduled. | — | Recharge::action::orderChanged | 1.0, 2.0 |
Warning
Recharge::click::cancelis gradually replacingRecharge::extension::cancellation_flow. If your existing extension listens toRecharge::extension::cancellation_flow, it continues to work, but useRecharge::click::cancelfor new implementations.
Track customer behavior
Listen for events with document.addEventListener. The following example records when a customer starts and successfully completes the Skip flow:
document.addEventListener('Recharge::click::skip', event => {
analytics.track('Affinity skip started', event.detail?.payload);
});
document.addEventListener('Recharge::action::skip', event => {
analytics.track('Affinity skip completed', event.detail?.payload);
});Click events occur before Affinity continues with its default behavior. To intercept a click event and replace the standard flow with your own experience, see Personalize user journeys with custom flow extensions.
Navigation events
Listen for Recharge::location::change to detect when a customer moves to another page in Affinity. The event detail includes the new path, which you can use for journey analytics:
document.addEventListener('Recharge::location::change', event => {
analytics.track('Affinity page viewed', {
path: event.detail?.pathname,
});
});Slot lifecycle events
Slot lifecycle events are triggered when slot content is added to or removed from the Document Object Model (DOM). Use them to manage the lifecycle of custom code linked to slot content.
| Event | Event name |
|---|---|
| Mounted | Recharge::slot::mounted |
| Unmounted | Recharge::slot::unmounted |
Slot content in Affinity is dynamically loaded, meaning it is not present in the DOM during the initial page render but is added shortly afterward. When developing advanced content extensions, host initial placeholder elements in Slots, then wait for those elements to be present in the DOM before initializing your custom code:
<script type="text/html" data-recharge-slot="overview.header">
<div id="placeholder-for-my-app"></div>
</script>document.addEventListener("Recharge::slot::mounted", (event) => {
if (event.detail.name === "header" && event.detail.pathname === "/overview") {
// Initialize my app
}
});Directives
Directives work in the opposite direction from events: your code dispatches a supported event for Affinity to receive and act on. Use directives to open common actions or refresh portal data after external changes.
| What you want Affinity to do | Directive | Optional details |
|---|---|---|
| Open Reschedule | Recharge::order::openRescheduleModal | Omit detail to reschedule the complete upcoming charge, or provide subscriptionId to reschedule one active subscription. |
| Open Skip | Recharge::order::openSkipModal | Omit detail to skip the complete upcoming charge, or provide subscriptionId to skip one active subscription. |
| Open Order now | Recharge::order::openOrderNowModal | None |
| Unskip | Recharge::order::unskip | None |
| Open Add product | Recharge::order::openAddProductModal | productId opens a product directly; collectionIds opens a filtered product list. When both are provided, productId takes priority. |
| Open Discount | Recharge::order::openDiscountModal | None |
| Refresh Affinity data | Affinity:refresh | None |
Dispatch a directive as a CustomEvent on document. Unlike events emitted by Affinity, directive details are placed directly on event.detail rather than inside event.detail.payload.
To open Reschedule for the complete upcoming charge:
document.dispatchEvent(new CustomEvent('Recharge::order::openRescheduleModal'));To open Reschedule for one specific subscription:
document.dispatchEvent(
new CustomEvent('Recharge::order::openRescheduleModal', {
detail: { subscriptionId: 12345 },
})
);If custom code changes data outside the standard Affinity flow, dispatch the refresh directive so the page requests the latest data:
document.dispatchEvent(new CustomEvent('Affinity:refresh'));
Note:Directives only work while the part of Affinity that handles them is available. Order directives are intended for the Next order experience, and the Add product directive requires the Add product carousel to be available. A targeted Reschedule or Skip directive is ignored when
subscriptionIddoesn't identify an eligible active subscription.
Migrating event code to Affinity 2.0
If you built event-driven customizations on Affinity 1.0, review the following before migrating:
Recharge::extension::cancellation_flowis deprecated. It continues to work, but update your listeners toRecharge::click::cancel.- Slot lifecycle events are not available in 2.0. If your extension initializes custom code on
Recharge::slot::mounted, it needs a new initialization strategy in 2.0. [TODO: Confirm, and document the 2.0 replacement pattern for content extensions if one exists.] - Event detail structure. Events emitted by Affinity place data in
event.detail.payload; directive details are placed directly onevent.detail. [TODO: Confirm whether this differs between 1.0 and 2.0.] - Click event payloads include subscription IDs. All click events include the relevant subscription IDs under
event.detail.payload.subscriptionIds.
All click and action events use the same names in both versions, so intent/completion analytics carry over without changes.
Updated about 14 hours ago
