---
updatedAt: 2026-02-09T21:11:20.000Z
agentTools:
  projectIndex: https://docs.searchspring.com/llms.txt
---

# Beacon 2.0 Technical Migration Guide

Your one stop shop on the key technical changes between Beacon 1.0 and Beacon 2.0 🚀

<br />

<Callout icon="🚧" theme="warn">
  **New to Searchspring Tracking?** If you aren't currently using our legacy tracking, you can skip this migration guide and head straight to our [Beacon 2.0 Documentation](https://docs.searchspring.com/v1.1.0/reference/beacon-tracking) for a fresh implementation.
</Callout>

## 🏁 Getting Started: Beacon Migration

### The Evolution: Batching to Referencing

Beacon 2.0 moves away from the "heavy lifting" of the legacy 1.0 environment. Previously, your implementation had to send massive batches of redundant product metadata (prices, images, names) for every interaction.

With **Beacon 2.0**, we’ve introduced a **Reference-Based model**. By utilizing a `responseId` from our API, our backend now handles the metadata mapping for you.

* **Smaller Payloads:** No more looping through product arrays.
* **Better Performance:** Lower browser overhead and faster execution.
* **Cleaner Code:** A simplified, intent-based event structure.

***

## 📖 Table of Contents

* [**Mapping Table**](https://docs.searchspring.com/reference/beacon-migration-guide#%EF%B8%8F-breaking-changes-and-mapping) - High-level architectural shifts.
* [**Developer Sandbox Mode**](https://docs.searchspring.com/reference/beacon-migration-guide#%EF%B8%8Fdeveloper-sandbox-mode) - new testing parameters that wont impact reports
* **[Event Flow](https://docs.searchspring.com/reference/beacon-migration-guide#event-flow)**- new event structure and updated endpoints
* [**Render Events**](https://docs.searchspring.com/reference/beacon-migration-guide#render-events) - Replacing `profile.render`.
* [**Impression Events**](https://docs.searchspring.com/reference/beacon-migration-guide#impression-events) - Viewport tracking best practices.
* [**Clickthrough Events**](https://docs.searchspring.com/reference/beacon-migration-guide#clickthrough-events) - Tracking shopper intent.

***

## 🛠️ Breaking Changes and Mapping

Use this table to map your current 1.0 logic to the 2.0 structure.

| Feature / Endpoint   | Status in 1.0      | Action in 2.0                      | Notes                                                                                |
| :------------------- | :----------------- | :--------------------------------- | :----------------------------------------------------------------------------------- |
| **Base URL**         | `/beacon`          | `/beacon/v2/{siteId}/`             | Each endpoint begins with `/{siteId}/`- Each store's siteId can be found in the SMC  |
| **Batching**         | Required           | Removed                            | Batching is no longer required for 2.0. outside of Personalization impression events |
| **Payload Envelope** | Mixed              | `context` + `data`                 | Strict requirements for both objects.                                                |
| **Payload Type**     | `application/json` | `text/plain` OR `application/json` | Every endpoint now accepts text/plain Post data.                                     |

***

## 🏖️Developer Sandbox Mode

**Beacon 2.0** has introduced a new developer-centric functionality, not available in 1.0, to streamline the API integration process. You can now test your event data pipelines without impacting your production analytics. API customers can now pass a dev field in the event payload's **context object** to facilitate seamless testing during development.

#### 💻Code Example:

```
{
  "context": {
    ...
    "dev": "true",
    ...
  }

```

* **Purpose:** Events sent with this flag are validated by the API to ensure your integration is working, but the data is **not** surfaced in your production reports.
* **Implementation Strategy:** We strongly recommend implementing a **global flag** or environment variable in your codebase to handle enabling/disabling the dev flag. This allows you to toggle developer mode on or off across your entire site with a single update.
* **Important Go-Live Step:** This flag **must be removed or set to false** for your production launch. If the dev flag is enabled in your code after go-live, your performance reports will remain empty.

<Callout icon="❗️" theme="error">
  **Troubleshooting** - Note that **400 errors** are an exception; these will still be captured and surfaced in the **Beacon Data Quality Report** regardless of the flag status. This ensures our internal teams can assist you in resolving implementation hurdles during the build phase.
</Callout>

***

## 🏗️ Resource-Specific Migration

### 🔄 Personalization: 1.0 vs 2.0 - The Logic Shift

In **Beacon 2.0**, the way we track recommendation events has been completely overhauled to reduce payload size and integration complexity.

### 🌊Event Flow

Typical event sequence for recommendations in Beacon 2.0:

1. **[Render](https://docs.searchspring.com/reference/beacon-migration-guide#render-events)** - When recommendations are requested from the API and rendered on the page. Uses the `/beacon/v2/{siteId}/recommendations/render` endpoint and **replaces** the `profile.render` and `profile.product.render` types from **Beacon 1.0**
2. **[Impression](https://docs.searchspring.com/reference/beacon-migration-guide#impression-events)** - When recommended items enter the viewport. Uses the `/beacon/v2/{siteId}/recommendations/impression` endpoint and **replaces** the `profile.impression` and `profile.product.impression` types from **Beacon 1.0**
3. **[Clickthrough](https://docs.searchspring.com/reference/beacon-migration-guide#clickthrough-events)**- When a shopper clicks on a rendered Searchspring personalized recommended result, and is taken to the product detail page (PDP). Uses the `/beacon/v2/{siteId}/recommendations/clickthrough` endpoint and **replaces** the `profile.click` and `profile.product.click` types from **Beacon 1.0**
4. **Add to Cart** - This event is new with Beacon 2.0. Covers when the Shopper adds a Searchspring personalized recommendation result to the cart via a Quick Add to Cart button in the rendered product card. **If frontend Quick Add to Cart is not implemented, omit usage of this endpoint.** Uses the `/beacon/v2/{siteId}/recommendations/addtocart` endpoint.  View more about this new endpoint under our [personalized recommendation tracking guide](https://docs.searchspring.com/reference/recommendations-tracking-guide).

***

## 💰Event Payloads

### Render Events

* In **Beacon 1.0**, you had to send a batch array containing a "Profile Render" event PLUS an individual "Product Render" event for every single product returned by the API.
* In **Beacon 2.0**, you send one event. By passing the **responseId** (returned by the Recommendations API), our backend automatically maps the products that were shown. No more looping through product arrays.

#### 💻 Code Example

**Beacon 1.0 (Old Batch Method)**
You had to build a complex array where the context was repeated for every single item.

```json
[
  {
    "category" : "searchspring.recommendations.user-interactions",
    "context" : {
      "pageLoadId" : "e560933a-b0fe-408d-8df5-807270e79fb8",
      "userId" : "e2cfa1b3-96bc-4857-b9ad-22f07af52403",
      "sessionId" : "5b5baff8-0f18-4b56-8f4b-9a6ce13435a2",
      "website" : {
        "trackingCode" : "YOUR_SITE_ID"
      }
    },
    "event": {
      "context" : {
        "type" : "product-recommendation",
        "tag" : "home-recs",
        "placement" : "home-page"
      },
      "profile" : {
        "tag" : "home-recs",
        "placement" : "home-page",
        "seed" : [""]
      }
    },
    "id" : "f77ce2b0-814f-44af-9caa-89fd75400991",
    "type" : "profile.render"
  },
  {
    "category" : "searchspring.recommendations.user-interactions",
    "context" : {
      "pageLoadId" : "e560933a-b0fe-408d-8df5-807270e79fb8",
      "userId" : "e2cfa1b3-96bc-4857-b9ad-22f07af52403",
      "sessionId" : "5b5baff8-0f18-4b56-8f4b-9a6ce13435a2",
      "website" : {
        "trackingCode" : "YOUR_SITE_ID"
      }
    },
    "event": {
      "context" : {
        "type" : "product-recommendation",
        "tag" : "home-recs",
        "placement" : "home-page"
      },
      "product" : {
        "id" : "123456789",
        "mappings" : {
          "core" : {
            "imageUrl" : "https://example.com/images/123456789/image",
            "msrp" : 24.99,
            "name" : "Short Sleeve Shirt",
            "price" : 14.99,
            "sku" : "S_SLV_SHRT_XS",
            "thumbnailImageUrl" : "https://example.com/images/123456789/thumbnailImage",
            "uid" : "123456789",
            "url" : "https://example.com/products/123456789"
          }
        }
      }
    }, 
    "id" : "a97d55c5-77d2-4b0f-939d-5d4b0de15f63",
    "pid" : "f77ce2b0-814f-44af-9caa-89fd75400991",
    "type" : "profile.product.render"
  },
  {
    "category" : "searchspring.recommendations.user-interactions",
    "context" : {
      "pageLoadId" : "e560933a-b0fe-408d-8df5-807270e79fb8",
      "userId" : "e2cfa1b3-96bc-4857-b9ad-22f07af52403",
      "sessionId" : "5b5baff8-0f18-4b56-8f4b-9a6ce13435a2",
      "website" : {
        "trackingCode" : "YOUR_SITE_ID"
      }
    },
    "event": {
      "context" : {
        "type" : "product-recommendation",
        "tag" : "home-recs",
        "placement" : "home-page"
      },
      "product" : {
        "id" : "987654321",
        "mappings" : {
          "core" : {
            "imageUrl" : "https://example.com/images/987654321/image",
            "msrp" : 29.99,
            "name" : "Long Sleeve shirt",
            "price" : 19.99,
            "sku" : "LNG_SLV_SHRT_XS",
            "thumbnailImageUrl" : "https://example.com/images/987654321/thumbnailImage",
            "uid" : "987654321",
            "url" : "https://example.com/products/987654321"
          }
        }
      }
    }, 
    "id" : "20902d3f-3aa0-436e-8920-af4f1fcd7ee1 ",
    "pid" : "f77ce2b0-814f-44af-9caa-89fd75400991",
    "type" : "profile.product.render"
  },
  {
    "category" : "searchspring.recommendations.user-interactions",
    "context" : {
      "pageLoadId" : "e560933a-b0fe-408d-8df5-807270e79fb8",
      "userId" : "e2cfa1b3-96bc-4857-b9ad-22f07af52403",
      "sessionId" : "5b5baff8-0f18-4b56-8f4b-9a6ce13435a2",
      "website" : {
        "trackingCode" : "YOUR_SITE_ID"
      }
    },
    "event": {
      "context" : {
        "type" : "product-recommendation",
        "tag" : "home-recs",
        "placement" : "home-page"
      },
      "product" : {
        "id" : "543216789",
        "mappings" : {
          "core" : {
            "imageUrl" : "https://example.com/images/543216789/image",
            "msrp" : 19.99,
            "name" : "Tank Top",
            "price" : 13.99,
            "sku" : "TNK_TOP_XS",
            "thumbnailImageUrl" : "https://example.com/images/543216789/thumbnailImage",
            "uid" : "543216789",
            "url" : "https://example.com/products/543216789"
          }
        }
      }
    }, 
    "id" : "20902d3f-3aa0-436e-8920-af4f1fcd7ee1 ",
    "pid" : "f77ce2b0-814f-44af-9caa-89fd75400991",
    "type" : "profile.product.render"
  }
]
```

**Beacon 2.0 (New Reference Method)**
The new endpoint `/recommendations/render` replaces `profile.render` and `profile.product.render` while handling everything via the `responseId` provided in the initial API response.

```json
{
  "context": {
    "userId": "e2cfa1b3-96bc-4857-b9ad-22f07af52403",
    "sessionId": "5b5baff8-0f18-4b56-8f4b-9a6ce13435a2",
    "pageLoadId": "e560933a-b0fe-408d-8df5-807270e79fb8",
    "pageUrl": "https://example.com/",
    "timestamp": "2026-01-26T15:04:05Z",
    "userAgent": "Mozilla/5.0...",
    "initiator": "searchspring/snap/preact/0.49.1"
  },
  "data": {
    "tag": "home-recs",
    "responseId": "f77ce2b0-814f-44af-9caa-89fd75400991",
  }
}
```

<Callout icon="💡" theme="default">
  **Implementation Tip:** When calling the Personalized Recommendations API, ensure you capture the `responseId` from the JSON response header or body to use in your **Beacon 2.0** calls.
</Callout>

<br />

#### Required Body Parameters: Render

| Beacon 1.0 (Required) | Type     | Beacon 2.0 (Required)  | Type     | Notes                                                             |
| :-------------------- | :------- | :--------------------- | :------- | :---------------------------------------------------------------- |
| **type**              | `string` | (Removed)              | N/A      | Handled by the endpoint URL                                       |
| **category**          | `string` | (Removed)              | N/A      | Handled by the endpoint URL                                       |
| **id / pid**          | `string` | **data.responseId**    | `string` | Connects the event to the API response                            |
| **event**             | `object` | **data**               | `object` | Top-level wrapper for event data                                  |
|                       |          | **data.tag**           | `string` | The recommendation profile tag                                    |
|                       |          | **context**            | `object` | Top-level wrapper for contextual data about the event             |
|                       |          | **context.userId**     | `string` | Found in `ss-user-id` cookie                                      |
|                       |          | **context.sessionId**  | `string` | Found in `ss-session-id` cookie. Should expire after store visit. |
|                       |          | **context.pageLoadId** | `string` | Unique identifier that represents a unique page load.             |
|                       |          | **context.pageUrl**    | `string` | The current page URL                                              |
|                       |          | **context.timestamp**  | `string` | RFC3339 formatted timestamp                                       |
|                       |          | **context.initiator**  | `string` | Application identifier that initiated the event                   |

<br />

#### Practice Sending Beacon Render Events with 2.0

Get started with Beacon Render events by using our Native fetch example below!

```javascript
/** Beacon 2.0 Render Event*/
/** Link this to your recommendation carousel mount/render logic.*/

/**Pass your responseId from the recommendation API request, and tag data as parameters*/
/**Adjust hard coded values to be dynamic as needed*/
const sendBeaconRenderEvent = async (responseId, tag) => {
  const url = 'https://analytics.searchspring.net/beacon/v2/{siteId}/recommendations/render';

  const payload = {
    context: {
      userId: "c1dbf50e-d9fd-427e-899e-ea2ce9e95620", // Found in ss-user-id cookie
      sessionId: "b2bc321a-a1fb-411d-911b-fa2ce9e95611", // Found in ss-session-id cookie
      pageLoadId: window.ssPageLoadId || "generate-unique-uuid-per-page", 
      pageUrl: window.location.href,
      timestamp: new Date().toISOString(),
      initiator: "custom-site-integration"
    },
    data: {
      tag: tag, 
      responseId: responseId 
    }
  };

  try {
    const response = await fetch(url, {
      method: 'POST',
      headers: { 
        'Content-Type': 'application/json',
        'Accept': 'application/json'
      },
      body: JSON.stringify(payload),
      keepalive: true 
    });
		if (response.ok) {
      console.log('Beacon sent successfully');
    }
  } catch (error) {
    console.error('Beacon Error: ', error);
  }
};

```

***

### Impression Events

In **Beacon 1.0**, tracking impressions was a heavy manual task. You had to send a batch array containing a "Profile Impression" event PLUS an individual "Product Impression" event for every single product that entered the viewport. Each of those objects required repeating the full context and product metadata (name, price, image URL).

In **Beacon 2.0**, we use a **Lightweight Reference** method. Instead of re-sending product metadata, you simply send an array of the `uid` and `parentId` for the products currently in view. Our backend uses the `responseId` to map the rest of the details automatically.

#### 💻 Code Example

**Beacon 1.0 (Old Batch Method)** Developers had to loop through visible items and construct an array of objects, each containing the full product mapping and redundant context.

```json
[
  {
    "category" : "searchspring.recommendations.user-interactions",
    "context" : {
      "pageLoadId" : "e560933a-b0fe-408d-8df5-807270e79fb8",
      "userId" : "e2cfa1b3-96bc-4857-b9ad-22f07af52403",
      "sessionId" : "5b5baff8-0f18-4b56-8f4b-9a6ce13435a2",
      "website" : {
        "trackingCode" : "YOUR_SITE_ID"
      }
    },
    "event": {
      "context" : {
        "type" : "product-recommendation",
        "tag" : "home-recs",
        "placement" : "home-page"
      },
      "profile" : {
        "tag" : "home-recs",
        "placement" : "home-page",
        "seed" : [""]
      }
    },
    "id" : "f77ce2b0-814f-44af-9caa-89fd75400991",
    "type" : "profile.impression"
  },
  {
    "category" : "searchspring.recommendations.user-interactions",
    "context" : {
      "pageLoadId" : "e560933a-b0fe-408d-8df5-807270e79fb8",
      "userId" : "e2cfa1b3-96bc-4857-b9ad-22f07af52403",
      "sessionId" : "5b5baff8-0f18-4b56-8f4b-9a6ce13435a2",
      "website" : {
        "trackingCode" : "YOUR_SITE_ID"
      }
    },
    "event": {
      "context" : {
        "type" : "product-recommendation",
        "tag" : "home-recs",
        "placement" : "home-page"
      },
      "product" : {
        "id" : "123456789",
        "mappings" : {
          "core" : {
            "imageUrl" : "https://example.com/images/123456789/image",
            "msrp" : 24.99,
            "name" : "Short Sleeve Shirt",
            "price" : 14.99,
            "sku" : "S_SLV_SHRT_XS",
            "thumbnailImageUrl" : "https://example.com/images/123456789/thumbnailImage",
            "uid" : "123456789",
            "url" : "https://example.com/products/123456789"
          }
        }
      }
    }, 
    "id" : "a97d55c5-77d2-4b0f-939d-5d4b0de15f63",
    "pid" : "f77ce2b0-814f-44af-9caa-89fd75400991",
    "type" : "profile.product.impression"
  },
  {
    "category" : "searchspring.recommendations.user-interactions",
    "context" : {
      "pageLoadId" : "e560933a-b0fe-408d-8df5-807270e79fb8",
      "userId" : "e2cfa1b3-96bc-4857-b9ad-22f07af52403",
      "sessionId" : "5b5baff8-0f18-4b56-8f4b-9a6ce13435a2",
      "website" : {
        "trackingCode" : "YOUR_SITE_ID"
      }
    },
    "event": {
      "context" : {
        "type" : "product-recommendation",
        "tag" : "home-recs",
        "placement" : "home-page"
      },
      "product" : {
        "id" : "987654321",
        "mappings" : {
          "core" : {
            "imageUrl" : "https://example.com/images/987654321/image",
            "msrp" : 29.99,
            "name" : "Long Sleeve shirt",
            "price" : 19.99,
            "sku" : "LNG_SLV_SHRT_XS",
            "thumbnailImageUrl" : "https://example.com/images/987654321/thumbnailImage",
            "uid" : "987654321",
            "url" : "https://example.com/products/987654321"
          }
        }
      }
    }, 
    "id" : "20902d3f-3aa0-436e-8920-af4f1fcd7ee1 ",
    "pid" : "f77ce2b0-814f-44af-9caa-89fd75400991",
    "type" : "profile.product.impression"
  }
]
```

**Beacon 2.0 (New Reference Method)** The new endpoint /recommendations/impression allows you to send one clean object. You only need to pass the IDs of the products the shopper actually saw. The new endpoint `/beacon/v2/{siteId}/recommendations/impression` replaces the old `profile.impression` and `profile.product.impression` types.

```json
{
  "context": {
    "IP": "7309:1fdb:e0d5:7632:7f2a:c269:7519:991b",
    "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
    "timestamp": "2024-06-11T17:47:04.075876Z",
    "pageUrl": "https://bocachica.searchspring.com/collections/mens/products/adventure-shorts-pitch-black",
    "userId": "41f87850-65f5-4546-851c-f166109e57b9",
    "sessionId": "1c311c14-c33b-4222-952a-f17db729ea31",
    "pageLoadId": "080d179f-0034-4278-8d3c-dfba71c9b897",
    "shopperId": "user123456",
    "initiator": "searchspring/snap/preact/0.49.1",
    "attribution": [
      {
        "type": "email",
        "id": "1234567890"
      }
    ],
    "currency": {
      "code": "USD"
    }
  },
  "data": {
    "responseId": "42c2dc64-d203-4d74-8122-515f26afb253",
    "tag": "similar",
    "results": [
      {
        "type": "product",
        "parentId": "8128331911111",
        "uid": "8128331907370"
      },
      {
        "type": "product",
        "parentId": "8128331922222",
        "uid": "8128021070122"
      },
      {
        "type": "product",
        "parentId": "8128331933333",
        "uid": "8127879741738"
      },
      {
        "type": "product",
        "parentId": "8128331944444",
        "uid": "8128173736234"
      },
      {
        "type": "product",
        "parentId": "8128331955555",
        "uid": "8128273711402"
      },
      {
        "type": "product",
        "parentId": "8128331966666",
        "uid": "8128028737834"
      }
    ],
    "banners": []
  }
}
```

<Callout icon="💡" theme="default">
  **Implementation Tip:**  Only send products that are **currently visible** in the shopper's viewport. If a shopper scrolls further and sees more products, trigger a new impression event for only the newly visible items. **We suggest a** `70%` **threshold for product card visibility to trigger an impression event**. To accurately track these impressions, we recommend using the **[Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API)**. This API allows you to trigger events only when a product enters the visible area of the carousel.
</Callout>

<br />

#### Required Body Parameters: Impressions

| Beacon 1.0 (Required) | Type     | Beacon 2.0 (Required)  | Type     | Note                                                  |
| :-------------------- | :------- | :--------------------- | :------- | :---------------------------------------------------- |
| **type**              | string   | (Removed)              | N/A      | Handled by endpoint URL path                          |
| **category**          | string   | (Removed)              | N/A      | Handled by endpoint URL path                          |
| **event**             | `object` | **data**               | `object` | Top-level wrapper for event data                      |
| `id` / `pid`          | string   | **data.responseId**    | string   | Connects event to the API response                    |
|                       |          | **context**            | `object` | Top-level wrapper for contextual data about the event |
|                       |          | **data.tag**           | string   | The recommendation profile tag                        |
|                       |          | **data.banners**       | array    | Use `[]` if no banners returned                       |
|                       |          | **context.userId**     | string   | Unique identifier for the customer                    |
|                       |          | **context.sessionId**  | string   | Unique identifier for current session                 |
|                       |          | **context.pageLoadId** | string   | Unique ID for the specific page load                  |
|                       |          | **context.pageUrl**    | string   | Current window\.location.href                         |
|                       |          | **context.timestamp**  | string   | RFC3339 formatted timestamp                           |
|                       |          | **context.initiator**  | string   | App identifier that initiated event                   |

**Inside the `data.results` Array (2.0):**
Each product object in the array must contain:

* `type`: (string) Typically "product"
* `uid`: (string) The unique product ID
* `parentId`: (string) The parent product ID (use same as `uid` if no parent exists)

<br />

<br />

#### Practice Sending Beacon Impression Events with 2.0

Get started with beacon impression events by using our native fetch example below!

```javascript
/**
 * Beacon 2.0 Impression Event
 * Trigger this as products scroll into view.
 * * @param {string} responseId - The unique ID from the Searchspring API response
 * @param {string} tag - The profile tag (e.g., "home-recs")
 * @param {Array} visibleProducts - Array of objects: { type, parentId, uid }
 */


const sendBeaconImpressionEvent = async (responseId, tag, visibleProducts) => {
  // Replace {siteId} with your 6-character Searchspring Site ID
  const url = 'https://analytics.searchspring.net/beacon/v2/{siteId}/recommendations/impression';

  const payload = {
    context: {
      userId: "c1dbf50e-d9fd-427e-899e-ea2ce9e95620", // Typically from ss-user-id cookie
      sessionId: "b2bc321a-a1fb-411d-911b-fa2ce9e95611", // Typically from ss-session-id cookie
      pageLoadId: window.ssPageLoadId || "generate-unique-uuid", 
      pageUrl: window.location.href,
      timestamp: new Date().toISOString(), // RFC3339/ISO 8601
      initiator: "custom-site-integration",
      userAgent: navigator.userAgent,
      // shopperId: "user@example.com" // Include if the user is logged in
    },
    data: {
      responseId: responseId, // The ID from the Recommendations API response
      tag: tag,               // e.g., "home-recs"
      results: visibleProducts, // Array of { type: "product", parentId: "123", uid: "123" }
      banners: []             // Must be an array, even if empty
    }
  };

  try {
    const response = await fetch(url, {
      method: 'POST',
      headers: { 
        'Content-Type': 'application/json' 
        // OR 'text/plain' to avoid CORS preflight (OPTIONS) requests
      },
      body: JSON.stringify(payload),
      keepalive: true 
    });

    if (response.ok) {
      console.log('Impression Beacon sent successfully');
    } else {
      const errorData = await response.json();
      console.error('Beacon 2.0 Error:', errorData);
    }
  } catch (error) {
    console.error('Network Error:', error);
  }
};

// --- Example Usage with Intersection Observer ---//
 /** Trigger new impression events as products scroll into view.*/
const observer = new IntersectionObserver((entries) => {
//The isIntersecting property is a boolean provided by the browser you can use in this case. It tells us when a specific element has actually entered the shopper's view.
const visible = entries
    .filter(entry => entry.isIntersecting) 
    .map(entry => ({
      type: "product",
      parentId: entry.target.dataset.parentId,
      uid: entry.target.dataset.uid
    }));

  if (visible.length > 0) {
    // Send the beacon for only the visible products
    sendBeaconImpressionEvent("f77ce2b0-814f-44af-9caa-89fd75400991", "home-recs", visible);
    // Unobserve after tracking if you only want to count the first view
    entries.forEach(entry => observer.unobserve(entry.target));
  }
}, { threshold: 0.7 }); // We suggest triggering when 70% of the product card is visible 

// Attach observer to your product cards, update selector as needed
document.querySelectorAll('.ss-recs-product').forEach(el => observer.observe(el));
```

***

### Clickthrough Events

In **Beacon 1.0**, tracking a click was a heavy, multi-step batch process. You had to send a `profile.click` event to register the interaction with the carousel, followed by a `profile.product.click` event containing the full metadata (name, price, SKU, etc.) of the specific product clicked.

In **Beacon 2.0**, we use a **Lightweight Reference** method. This consolidates the interaction into a single, streamlined request to the `/clickthrough` endpoint. By simply passing the `responseId` and the specific product `uid`, our backend automatically attributes the click to the correct recommendation profile and product details without needing redundant metadata.

#### 💻 Code Example

**Beacon 1.0 (Old Batch Method)** Developers had to send an array containing both the profile interaction and the full product mapping and redundant context.

```json
[
  {
    "category" : "searchspring.recommendations.user-interactions",
    "context" : {
      "pageLoadId" : "e560933a-b0fe-408d-8df5-807270e79fb8",
      "userId" : "e2cfa1b3-96bc-4857-b9ad-22f07af52403",
      "sessionId" : "5b5baff8-0f18-4b56-8f4b-9a6ce13435a2",
      "website" : {
        "trackingCode" : "YOUR_SITE_ID"
      }
    },
    "event": {
      "context" : {
        "type" : "product-recommendation",
        "tag" : "home-recs",
        "placement" : "home-page"
      },
      "profile" : {
        "tag" : "home-recs",
        "placement" : "home-page",
        "seed" : [""]
      }
    },
    "id" : "f77ce2b0-814f-44af-9caa-89fd75400991",
    "type" : "profile.click"
  },
  {
    "category" : "searchspring.recommendations.user-interactions",
    "context" : {
      "pageLoadId" : "e560933a-b0fe-408d-8df5-807270e79fb8",
      "userId" : "e2cfa1b3-96bc-4857-b9ad-22f07af52403",
      "sessionId" : "5b5baff8-0f18-4b56-8f4b-9a6ce13435a2",
      "website" : {
        "trackingCode" : "YOUR_SITE_ID"
      }
    },
    "event": {
      "context" : {
        "type" : "product-recommendation",
        "tag" : "home-recs",
        "placement" : "home-page"
      },
      "product" : {
        "id" : "543216789",
        "mappings" : {
          "core" : {
            "imageUrl" : "https://example.com/images/543216789/image",
            "msrp" : 19.99,
            "name" : "Tank Top",
            "price" : 13.99,
            "sku" : "TNK_TOP_XS",
            "thumbnailImageUrl" : "https://example.com/images/543216789/thumbnailImage",
            "uid" : "543216789",
            "url" : "https://example.com/products/543216789"
          }
        }
      }
    }, 
    "id" : "20902d3f-3aa0-436e-8920-af4f1fcd7ee1 ",
    "pid" : "f77ce2b0-814f-44af-9caa-89fd75400991",
    "type" : "profile.product.click"
  }
]
```

**Beacon 2.0 (New Reference Method)** The new endpoint `/recommendations/clickthrough` replaces the old `profile.click` and `profile.product.click` types with one clean object.

```json
{
  "context": {
    "IP": "89.76.6.25",
    "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
    "timestamp": "2006-01-02T15:04:05Z",
    "pageUrl": "https://bocachica.searchspring.com/collections/shop?q=shirt&view=products",
    "userId": "41f87850-65f5-4546-851c-f166109e57b9",
    "sessionId": "1c311c14-c33b-4222-952a-f17db729ea31",
    "pageLoadId": "080d179f-0034-4278-8d3c-dfba71c9b897",
    "shopperId": "user123456",
    "initiator": "searchspring/snap/preact/0.49.1",
    "currency": {
      "code": "USD"
    }
  },
  "data": {
    "responseId": "a907d0ad-720a-4666-b046-00c0c28fb78d",
    "tag": "similar",
    "results": [
      {
        "type": "product",
        "parentId": "8128331911111",
        "uid": "8128331907370"
      }
    ]
  }
}
```

<br />

#### Required Body Parameters: Clickthrough

| Beacon 1.0 (Required) | Type   | Beacon 2.0 (Required)  | Type   | Note                                               |
| :-------------------- | :----- | :--------------------- | :----- | :------------------------------------------------- |
| **type**              | string | (Removed)              | N/A    | Handled by endpoint URL path (`/clickthrough`)     |
| **category**          | string | (Removed)              | N/A    | Handled by endpoint URL path                       |
| **event**             | object | **data**               | object | Top-level wrapper for event-specific data          |
| **id / pid**          | string | **data.responseId**    | string | Connects the click to the original API response    |
|                       |        | **data.tag**           | string | The recommendation profile tag (e.g., "home-recs") |
|                       |        | **data.results**       | array  | Array containing the clicked item `{ type, uid }`  |
|                       |        | **context**            | object | Top-level wrapper for contextual shopper data      |
|                       |        | **context.userId**     | string | Unique identifier for the customer                 |
|                       |        | **context.sessionId**  | string | Unique identifier for current session              |
|                       |        | **context.pageLoadId** | string | Unique ID for the specific page load               |
|                       |        | **context.pageUrl**    | string | Current window\.location.href                      |
|                       |        | **context.timestamp**  | string | RFC3339 formatted timestamp                        |
|                       |        | **context.initiator**  | string | App identifier that initiated event                |

**Inside the `data.results` Array (2.0):**
Each product object in the array must contain:

* `type`: (string) Typically "product"
* `uid`: (string) The unique product ID
* `parentId`: (string) The parent product ID (use same as `uid` if no parent exists)

<br />

#### Practice Sending Beacon Clickthrough events with 2.0

Practice sending beacon clickthrough events with our native fetch example below

```javascript
/**
 * Tracks a recommendation clickthrough using Beacon 2.0
 * @param {string} responseId - From the original recommendations API response
 * @param {string} productUid - The UID of the product being clicked
 * @param {string} profileTag - The tag of the recommendation profile (e.g., 'similar')
 */
async function trackRecommendationClick(responseId, productUid, profileTag) {
    const url = 'https://analytics.searchspring.net/beacon/v2/{siteId}/recommendations/clickthrough';

    const payload = {
        context: {
            userId: "41f87850-65f5-4546-851c-f166109e57b9", // Typically from a cookie
            sessionId: "1c311c14-c33b-4222-952a-f17db729ea31",
            pageLoadId: "080d179f-0034-4278-8d3c-dfba71c9b897",
            pageUrl: window.location.href,
            timestamp: new Date().toISOString(), // RFC3339 format
            userAgent: navigator.userAgent,
            initiator: "your-app-name/1.0.0"
        },
        data: {
            responseId: responseId,
            tag: profileTag,
            results: [
                {
                    type: "product",
                    uid: productUid,
                    parentId: productUid // Use UID if no parent exists
                }
            ]
        }
    };

    try {
        const response = await fetch(url, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(payload)
        });

        if (!response.ok) {
            throw new Error(`Beacon error: ${response.statusText}`);
        }
        
        console.log('Clickthrough tracked successfully');
    } catch (error) {
        console.error('Failed to send beacon:', error);
    }
}

// --- Example Usage ---
// This would be triggered by an onClick event on your carousel item
// trackRecommendationClick('a907d0ad-720a-4666-b046-00c0c28fb78d', '8128331907370', 'home-recs');
```

<Callout icon="💡" theme="default">
  **Implementation Tip:** When a user clicks a product link, the browser usually navigates away immediately, which might cancel your fetch request. To prevent this, you can use the **keepalive: true** flag in your fetch options or use [**navigator.sendBeacon()**](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) if you want to ensure the data reaches the server without delaying the page transition.
</Callout>

***

### 🚦Ready to Begin? No, Ready to Beacon.

This concludes the 1 to 1 mappings between **Beacon 1.0** and **Beacon 2.0**. You’ve successfully mapped your legacy 1.0 logic to the 2.0 structure! Now that you have updated your existing beaconing, the next step is [**diving headfirst into our expanded beaconing options!**](https://docs.searchspring.com/reference/beacon-tracking)