# Retail Media Network

This guide consolidates how to onboard a **Retail Media Network (RMN)** as a publisher or network in Collective Audience™: inventory setup, passing page context for better campaign matching, and using dynamic carousels with your product catalog.


---

## What is a Retail Media Network (RMN) in Collective Audience™?

In our model, an **RMN** is:

* **Inventory owner**: you operate one or more sites or apps (e.g. retailer site, marketplace) where ads or sponsored content can be displayed.
* **Context provider**: you have first-party signals (search query, category, product, basket) that improve **campaign matching**—i.e. which creatives or product recommendations we show.
* **Revenue**: you monetize via sponsored content and/or product carousels. We match campaigns to context and return **assets** (creatives) or **items** (catalog products) to render.

**Terminology:**

| Term | Meaning |
|------|---------|
| **Inventory** | Ad space (placements) on your pages. |
| **Placement / slot** | A specific location (e.g. below search, sidebar) where a widget appears. |
| **Keywords** | Strings sent with the page request to improve targeting (e.g. category, brand). |
| **Context** | Page-type and metadata (SRP, PLP, PDP, search query, category, product_id, etc.). |
| **Campaign matching** | Our logic that selects which campaign/creative to serve based on context and keywords. |
| **Assets** | Creatives (images, videos, links) returned by the content server. |
| **Items** | Catalog products returned for dynamic carousels (e.g. product cards). |
| **Catalogue** | Product feed you provide; we host it and filter/sort for carousels. |

Monetization works at a high level: when a user visits a page, we receive context and keywords, run campaign matching, and return the most relevant ad or product set. You display it in your placement; revenue is attributed to your account (or network partner).


---

## Integration paths (choose one)

Pick **Path A** if the RMN is a single site/app (one account). Pick **Path B** if the RMN aggregates multiple sites or apps and needs reporting per property.

| Criteria | Path A: Standard SDK | Path B: Network SDK |
|----------|----------------------|---------------------|
| **Model** | One site = one publisher account | One network account, multiple sites/placements |
| **Use when** | Single retailer site or app | Multiple storefronts, white-labels, or distinct placements |
| **Init parameter** | `account: "YOUR_ACCOUNT_ID"` | `network: "YOUR_NETWORK_ID"`, optional `networkPartnerID`, optional `networkPublisherID` |
| **Reporting** | Per account          | Per account; per-site possible via `networkPartnerID` |

* **Path A** → JavaScript SDK *(→ sdk)* (publisher).
* **Path B** → Network SDK *(→ network-sdk)* (network + optional partner ID per site).


---

## Step-by-step: Install and initialize

### Step 1: Install the script

Use the same script for both paths; only the init config changes.

**Path A (publisher):**

```markup
<script>
  window.beOpAsyncInit = function () {
    BeOpSDK.init({
      account: "YOUR_ACCOUNT_ID",
    });
    BeOpSDK.watch();
  };
</script>
<script async src="https://widget.collectiveaudience.co/sdk.js"></script>
```

**Path B (network):**

```markup
<script>
  window.beOpAsyncInit = function () {
    BeOpSDK.init({
      network: "YOUR_NETWORK_ID",
      networkPartnerID: "OPTIONAL_SITE_OR_PLACEMENT_ID",
      // networkPublisherID: "OPTIONAL_PUBLISHER_ID",  // per-slot override if needed
    });
    BeOpSDK.watch();
  };
</script>
<script async src="https://widget.collectiveaudience.co/sdk.js"></script>
```

Replace `YOUR_ACCOUNT_ID` / `YOUR_NETWORK_ID` with the IDs provided by Collective Audience™. Use `networkPartnerID` in Path B for per-site or per-placement reporting; `networkPublisherID` is optional for per-slot publisher override (see Network SDK *(→ network-sdk)*).

### Step 2: Create placements (slots)

Declare where widgets appear using a named slot so we can track and fill consistently:

```html
<div class="BeOpWidget" data-name="below-search"></div>
<div class="BeOpWidget" data-name="sidebar-plp"></div>
<div class="BeOpWidget" data-name="pdp-recommendations"></div>
```

Slots can accept both editorial and sponsored content by default. To allow only your own editorial content in a slot, use `data-my-content="1"`. See SDK – Declaring slots *(→ sdk (#declaring-slots))*.

### Step 3: Verify integration


1. **Diagnostics:** Use the Collective Audience™ Diagnostics *(→ running-diagnostics)* bookmarklet on your page. Green checkmarks mean the SDK and slots are detected.
2. **Console:** Ensure no script errors; the SDK loads from `https://widget.collectiveaudience.co/sdk.js`.
3. **Network:** In DevTools, check requests to the Collective Audience™ content server when the page loads or when context is updated (e.g. after navigation in an SPA).


:::info
If you use Path B and Prebid, you can also use the BeOp Prebid adapter *(→ network-sdk (#use-collective-audience-prebidjs-adapter-as-a-network))* with `networkId` and `networkPartnerId` for header bidding.

:::


---

## Passing context to maximize performance

The more accurate the **page context** and **keywords** you send, the better our **campaign matching** and the more relevant the ads or product carousels.

### How we use context and keywords

* **Keywords:** Short strings (e.g. category, brand, product type) used for targeting. We use them in campaign rules and for filtering dynamic carousels (e.g. show only “hair” or “beauty”).
* **Page context:** Structured metadata (page type, search query, category, product_id, etc.) that we use together with keywords for matching and filtering.

Our SDK reads **keywords** from a global variable set **before** the SDK initializes. You can also override or extend this with a structured context object if your integration supports it (see placeholder below and Identify pages – Page keywords *(→ identify-pages (#page-keywords))*).

### Setting keywords (all RMN pages)

Set an array of strings **before** the SDK runs. The SDK reads `window.beOpKeywords` when it builds the serve request (and falls back to `<meta name="keywords">` if unset). See Identify pages – Page keywords *(→ identify-pages (#page-keywords))*.

```javascript
// Before SDK init (e.g. in <head> or first script)
window.beOpKeywords = ["beauty", "skincare", "brand-x"];
```

Keywords are sent in every content request as `kwds` and used for campaign matching and broaden diffusion.

### Context examples by page type (keywords only)

The SDK currently accepts **keywords only** (array of strings). Use the examples below to set `window.beOpKeywords` for each page type. Richer structured context (e.g. pageType, productId) may be supported in a future API; for now, encode context as keyword tokens.

**1) Search results page (SRP)**

```javascript
// User searched "shampoo for dry hair", category filter: Hair
window.beOpKeywords = ["srp", "hair", "shampoo", "dry-hair"];
```

**2) Product listing page (PLP)**

```javascript
// Category: Beauty > Skincare; brand facets visible
window.beOpKeywords = ["plp", "beauty", "skincare", "moisturizer", "brand-a"];
```

**3) Product detail page (PDP)**

```javascript
// Single product: brand, category, product type (no PII; use buckets for price)
window.beOpKeywords = ["pdp", "beauty", "skincare", "brand-a", "serum"];
```

> **caution:** Do **not** send PII (emails, names, exact prices, etc.). Use category/brand/product-type tokens only. See [Data sharing & privacy](#data-sharing--privacy).

### Updating context on navigation (SPA)

On single-page apps, update keywords whenever the page context changes, then call `BeOpSDK.watch()` so the SDK refetches content for the new context.

```javascript
function onRouteChange(keywords) {
  window.beOpKeywords = keywords; // array of strings

  if (window.BeOpSDK && typeof BeOpSDK.watch === "function") {
    BeOpSDK.watch();
  }
}

// Example: after router navigates to a PDP
onRouteChange(["pdp", "beauty", "skincare", "brand-a"]);
```

> **note:** The SDK uses the page URL (or canonical meta / `beop:id`) to identify the page. For SPAs, ensure the URL or `beop:id` reflects the current view. See Identify pages *(→ identify-pages)*.


---

## Contextual ad delivery: campaign matching → assets/items

Flow in short:


1. **Page load or context update** → Your site sets keywords (and optional context).
2. **Request** → SDK sends a request to our content server with page URL, slot names, keywords, and any context we support.
3. **Matching** → We select campaigns (and optionally catalog items) that match context and targeting rules.
4. **Response** → We return **assets** (creatives: image, video, link) and/or **items** (catalog products for carousels).
5. **Render** → The SDK (or your code) renders the creative or dynamic carousel in the slot.

**Assets** = creatives (banners, native cards, video). **Items** = rows from your product catalog (e.g. for a “Recommended for you” or “Similar products” carousel).

```
┌─────────────┐     ┌──────────────┐     ┌─────────────────┐     ┌─────────────┐
│ Your page   │────▶│ SDK request  │────▶│ Campaign        │────▶│ Response    │
│ + context   │     │ + keywords   │     │ matching +      │     │ assets /    │
│ + keywords  │     │ + slot names │     │ catalog filter  │     │ items       │
└─────────────┘     └──────────────┘     └─────────────────┘     └─────────────┘
                                                                          │
                                                                          ▼
                                                                   ┌─────────────┐
                                                                   │ Render in   │
                                                                   │ BeOpWidget  │
                                                                   └─────────────┘
```


---

## Dynamic Carousel and product catalog (RMN use cases)

RMNs often show **product carousels** (e.g. “More like this”, “Hair products”, “Beauty deals”). Collective Audience™ hosts your **catalog** and can filter items by the same **keywords/context** you send with the page.

### Catalog hosting

* You **do not** host the live catalog on your side for our carousels. You provide a feed (CSV/XML); we import and store it. See Import your catalog *(→ import-catalog)*.
* We use that catalog to fill **dynamic carousels** and filter by category or other attributes.

### Two ways to drive carousel content


1. **Filtered by page context (automatic)**  
   We use the **keywords** (and context) of the current page to filter the catalog. Example: on a “Hair” page you send `keywords: ["hair", "shampoo"]`; the carousel shows only items that match (e.g. category=hair or relevant tags).
2. **Search refinement (user search inside carousel)**  
   The carousel can show a search box. The user types (e.g. “dry hair”); we refine the visible items using that query. Configure this in the Dynamic carousel *(→ dynamic-carousel (#search))* (Search section) in the Audience Desk™.

### Examples

* **“Hair products” page** → Set `window.beOpKeywords = ["hair", "shampoo", "conditioner"]`. Place a dynamic carousel slot; we filter the catalog by those keywords (e.g. category=hair).
* **“Beauty” category page** → Set `window.beOpKeywords = ["beauty", "skincare", "makeup"]`. Carousel shows beauty items only.

### Carousel integration (concept)

Slots are the same (`BeOpWidget`). The **creative** configured in our back office for that slot can be a **dynamic carousel** tied to your catalog. No extra client-side API is required beyond passing keywords; filtering is done server-side. For configuration (choices, search, geo, exits), see Create dynamic carousels *(→ dynamic-carousel)* and Import your catalog *(→ import-catalog)*.

**Filtered mode (auto by page context):**  
Keywords set on the page are used automatically; ensure the catalog has matching columns (e.g. `categories`, `brand`) and that your keywords align with those values.

**Search refinement mode:**  
Enable the search option in the dynamic carousel in the Desk. Users can type in the carousel to narrow results; we search in the columns configured for that creative.


:::info
Catalog columns (e.g. `categories`, `brand`, `short_text`) are defined at import. Use consistent values in your feed and in the keywords you send so filtering works as expected.

:::


---

## Data sharing and privacy

* **We expect:** Page **context** and **keywords** only (no PII). Examples: page type, category, product_id/SKU, brand, price range bucket, search query.
* **We do not want:** PII such as emails, names, precise location, or exact basket value. Do not send those in keywords or context.
* **Consent:** If your site uses a CMP, add Collective Audience™ to your TCF vendor list (ID **666**). We can run in a consentless mode with limited features. See Consent management *(→ consent-management)*.


---

## Go-live checklist

- [ ] **Integration path** chosen (Standard SDK vs Network SDK) and init (account or network + networkPartnerID) correct.
- [ ] **Placements** created and named (`data-name`) on SRP, PLP, PDP (and others as needed).
- [ ] **Context coverage:** Keywords (and optional context) set for SRP, PLP, PDP; updated on SPA route change.
- [ ] **Catalog** imported and columns aligned with keywords/categories you send. See Import your catalog *(→ import-catalog)*.
- [ ] **QA:** Test at least one SRP, one PLP, one PDP; verify ads or carousel fill when campaigns/items match.
- [ ] **Latency:** First request within your acceptable budget; use diagnostics and network tab to confirm no blocking or repeated failures.
- [ ] **Monitoring:** Check for script errors, failed requests, or empty slots in your usual monitoring/logging.
- [ ] **Fallback:** Decide what to show when there is no match (empty slot or house creative). We do not force a default; your page can handle empty state.


---

## Troubleshooting

| Issue | What to check |
|-------|---------------|
| **No ads returned** | Correct account/network ID; slot names present; keywords/context sent; campaigns live and targeting matching your context; diagnostics and network tab for 4xx/5xx or missing request. |
| **Keywords not taken into account** | `window.beOpKeywords` set **before** SDK init (or before `BeOpSDK.watch()` on SPA); value is an array of strings; no typos in targeting rules. |
| **Carousel empty** | Catalog imported and linked to the creative; keywords overlap catalog categories/brand; no filter too narrow (try broader keywords for testing). |
| **SPA navigation not updating context** | Call `BeOpSDK.watch()` after updating `beOpKeywords` (and context if used); URL or `beop:id` updated so we don’t serve cached content for the previous page. |
| **CORS or blocked requests** | Script loaded from `https://widget.collectiveaudience.co/sdk.js`; no ad-blockers or CSP blocking our domain in testing. |


---

## FAQ (RMN-specific)

**Are you like a DSP?**  
We do authorize users to buy publishers' inventory, but the **placement owner** decides: they can open a placement to external demand and incremental revenues, or keep that inventory for their own use only. We match campaigns (and product catalog) to your inventory using context and keywords; the control over who can monetize each slot stays with the publisher or network.

**Do we need to host the catalog?**  
No. You send a feed (CSV/XML); we host and use it for dynamic carousels. See Import your catalog *(→ import-catalog)*.

**How do we send category info?**  
Via **keywords** only. Set `window.beOpKeywords` to an array of strings (e.g. `["beauty", "skincare"]`) before the SDK runs and on each meaningful page or route change (then call `BeOpSDK.watch()`). The SDK sends them as `kwds` in the serve request. There is no separate structured-context API in the current SDK.

**Can we A/B test different placements or context?**  
A/B testing of creatives and targeting is configured in the Audience Desk™. For custom A/B tests (e.g. different context or slot names), use your own experimentation layer and pass different keywords/slots per variant. For dedicated support, contact your account manager or support.


---

## Next steps and contact

* **Getting started:** [JavaScript SDK](/doc/sdk-installation-en-Kem5GcAHjr) · [Network SDK](/doc/network-sdk-JL2lWlsgrW)
* **Page identity and keywords:** [Identify pages](/doc/identify-pages-jkET6WAfeh)
* **Catalog and carousels:** [Import your catalog](/doc/import-your-catalog-pR8Wsbwels) · [Dynamic carousels](/doc/dynamic-carousels-dMAps1IFF5)
* **Consent:** [Consent management ](/doc/consent-management-6ys6r8Jv7Y)
* **Support:** [support@collectiveaudience.co](mailto:support@collectiveaudience.co) — for account IDs, catalog import, API details for structured context, and go-live checks.