# JavaScript SDK

The SDK is the script that manages widgets on your page. It's in charge of getting content (whether editorial or sponsored) and place it correctly in your pages.

* It initialises and identifies the page as yours
* It calls our content server to check if there's content to display
* It watches the page for changes, so that if a new slot is available, it can be filled

### Installation

Add this code right after the opening `<body>`, and replace `YOUR_ACCOUNT_ID` with your actual account id.

```markup
<script>
  window.beOpAsyncInit = function () {
    BeOpSDK.init({
      account: "YOUR_ACCOUNT_ID", // Replace YOUR_ACCOUNT_ID with your account ID from Audience Desk™ → Settings
    });
    BeOpSDK.watch();
  };
</script>
<script async src="https://widget.collectiveaudience.co/sdk.js"></script>
```

> the `beOpAsyncInit` is where you identify your page using the `init()` function, and the `watch()` tells the SDK to look for slots, the script below is the actual SDK, which is loaded asynchronously (meaning it doesn't block your page)

### Installing on Wordpress

It's possible that your Wordpress setup concatenates scripts, which prevents you from running an up-to-date Collective Audience™ SDK and creates unexpected bugs.

For that, you can use the following SDK:

```markup
<script>
  (function (window, document, script) {
    window.beOpAsyncInit = function () {
      BeOpSDK.init({
        account: "YOUR_ACCOUNT_ID", // Replace YOUR_ACCOUNT_ID with your account ID from Audience Desk™ → Settings
      });
      BeOpSDK.watch();
    };
    var firstScript = document.querySelector(script);
    var script = document.createElement(script);
    script.async = true;
    script.src = "https://widget.collectiveaudience.co/sdk.js";
    firstScript.parentNode.insertBefore(script, firstScript);
  })(window, document, "script");
</script>
```

## Declaring slots

You can declare slots, or *places where widgets should appear* by simply putting the following tag where you want them on your pages:

```markup
<div class="BeOpWidget"></div>
```

In order to ensure your slots are correctly filled, **we highly recommend naming them**, this way if the slot order in your page is altered, the SDK will still be able to identify them.

Example:

```markup
<div class="BeOpWidget" data-name="below-article"></div>
<div class="BeOpWidget" data-name="sidebar"></div>
<div class="BeOpWidget" data-name="homepage"></div>
```

You can specify if a slot is **strictly** for your own editorial content or for sponsored content too:

```markup
<!-- only editorial content -->
<div class="BeOpWidget" data-my-content="1"></div>
<!-- default: accept both (if your account is configured to accept ads) -->
<div class="BeOpWidget"></div>
```

## Browser support

**We support all modern browsers.**

Following [Microsoft's own products](https://techcommunity.microsoft.com/t5/microsoft-365-blog/microsoft-365-apps-say-farewell-to-internet-explorer-11-and/ba-p/1591666), we've discontinued Internet Explorer support.

## Need to check if you installed the SDK correctly?

Come and check the Diagnostics script *(→ running-diagnostics)*!

## API Reference

### `BeOpSDK.init(config)`

Initialises the SDK with your account.

```javascript
BeOpSDK.init({
  account: "abcdef1234567890abcdef12",
});
```

* `config.account`: your Collective Audience™ account ID
* `config.canAccessStorage` (default: `true`): set to false to prevent Collective Audience™ from storing a technical cookie in the absence of a TCF consent platform
* `config.refreshExistingSlots` (default: `true`): set to false if your website loads new articles with an infinite scroll mechanism
* `config.prefillInformations`: provide user information to ease the form/optin filling process (useful if your user is logged in on your account).

  You can provide the elements like follows (all keys are optional):  

  ```javascript
  BeOpSDK.init({
    account: "abcdef1234567890abcdef12",
    prefillInformations: {
      firstName: "Foo",
      lastName: "Bar",
      email: "test@test.com",
      phoneNumber: "+336...",
    },
  });
  ```

### `BeOpSDK.watch()`

Parses and watches the page for available slots.

### `BeOpSDK.parse()`

Parses the page for tags

> **caution:** **Deprecated**: use `BeOpSDK.watch()`, which is reliable even when pages aren't fully loaded or blocked by a longer loading

### `BeOpSDK.pushEvent(event)`

Send an event to Collective Audience™'s server.

```javascript
BeOpSDK.pushEvent({
  eventName: "impression",
  eventValue: "foo",
  slotName: "my-slot",
  currency: "EUR",
});
```

* `event.eventName`: the name of the event. For now, only `impression` is supported
* `event.eventValue`: the value of the event. You can pass any string here
* `event.slotName`: the name of the slot where the event comes from
* `event.currency` (default: *Computed from the account owning the slot*): the currency of the event. Only `EUR` and `USD` are supported for now

### `BeOpSDK.subscribeToWidgetEvents(callback)`

Lets you react to widget events on your page, for example to reload other ads or to adapt a slot when no ad is delivered.

> **caution:** This function must be called inside `beOpAsyncInit`, before `BeOpSDK.watch()`. Calls made elsewhere are ignored.

```javascript
window.beOpAsyncInit = function () {
  BeOpSDK.init({ account: "YOUR_ACCOUNT_ID" });
  BeOpSDK.subscribeToWidgetEvents((event, slotName, voteInformation) => {
    switch (event) {
      case "ad":
        console.log("A sponsored content will be displayed in " + slotName);
        break;
      case "no-ad":
        console.log("No sponsored content for " + slotName);
        break;
      case "complete":
        console.log("A content has been completed in " + slotName);
        break;
    }
  });
  BeOpSDK.watch();
};
```

The callback receives:

* `event`: the event name (see the list below)
* `slotName`: the name of the slot (`data-name`) the event comes from
* `voteInformation`: only set on the `vote` event, `undefined` otherwise

#### Available events

| Event | When it fires |
|-------|---------------|
| `content_received` | The content server returned a content (editorial or sponsored) for the slot. |
| `ad`  | The content returned for the slot is sponsored. Fired right after `content_received`. |
| `no-ad` | No sponsored content for the slot. Fired when the content server returned nothing for the slot, or when it returned an editorial content (in that case `content_received` fires too). |
| `content_enter_view` | The widget enters the viewport. |
| `content_leave_view` | The widget leaves the viewport. |
| `visible_impression` | The widget meets the visible impression criteria. |
| `vote` | The user answered a question. See `voteInformation` below. |
| `signup` | The user signed up from the widget. |
| `login` | The user logged in from the widget. |
| `complete` | The user completed the creative. |
| `final_page_view` | The final page of the creative is displayed. |
| `sharing` | The user shared the creative. |
| `comment` | The user posted a comment. |

`ad` and `no-ad` fire once per slot, a few milliseconds after the content server response. They are the recommended way to trigger a passback or to collapse a slot when there is no ad.

> **caution:** If the content server call fails (network error, timeout), no `ad` or `no-ad` event is sent. If your page depends on these events, add a fallback timer on your side.

The `voteInformation` object has the following shape:

```json
{
  // the question text
  "question_text": string,
  // the text content of the choices the user picked
  "choices": Array<string>,
  // the index of the choices the user picked
  "choice_indexes": Array<int>,
  // whether the user answered correctly (only set in a quiz)
  "choice_valid": bool | void,
  // is the creative over with this question?
  "complete": bool,
}
```

## Fullsize mode

You might want to have a Collective Audience™ slot to take the whole viewport (for instance in a mobile iframe). To that end, you can add a `data-expand` attribute to make a slot fill the window.

```markup
<div class="BeOpWidget" data-expand></div>
```

  

If you want to be able to have a close button to appear once the creative is complete, add the following right after the `BeOpSDK.init` call:

```javascript
BeOpSDK.onPressClose((slotName) => {
  // notify the webview to close itself
});
```

Optionally, you can pass a `timeout` param to trigger the button appearance in case of inactivity (defaults to 10s):

```javascript
// show the button after a 5s inactivity
BeOpSDK.onPressClose((slotName) => {
  /* ... */
}, 5000);
```

  

> **caution:** The close button will not appear unless you provide your `onPressClose` event listener.

## SDK API inventory

This section lists all **init parameters** and **public functions** available in the current SDK. Parameters or functions not described in detail above may be documented in other pages or reserved for specific use cases.

### `BeOpSDK.init(config)` – parameters

| Parameter | Type | Documented above | More details |
|-----------|------|------------------|--------------|
| `account` | string | ✅                | Your publisher account ID (use with **publisher** path). |
| `network` | string | —                | Your network account ID (use with **network** path). See Network SDK *(→ network-sdk)*. |
| `networkPartnerID` | string | —                | Optional site/placement identifier for reporting. See Network SDK *(→ network-sdk)*. |
| `networkPublisherID` | string | —                | Optional per-slot publisher override (network path). See Network SDK *(→ network-sdk)*. |
| `canAccessStorage` | boolean | ✅                | Default `true`. Cookie storage in absence of TCF. |
| `refreshExistingSlots` | boolean | ✅                | Default `true`. Set `false` for infinite scroll. |
| `prefillInformations` | object | ✅                | `firstName`, `lastName`, `email`, `phoneNumber`. |
| `pageUrl` | string | —                | Override page URL used for content requests (e.g. canonical URL). |
| `onContentReceive` | function | —                | Callback when content is received. |
| `onHeightChange` | function | —                | Callback when widget height changes (e.g. iframe resize). |
| `siteUserID` | string | —                | Optional site-specific user identifier. |
| `waitForTCF` | boolean | —                | Wait for TCF consent before init. See Consent management *(→ consent-management)*. |
| `waitForEIDS` | boolean | —                | Wait for EIDs before init (default `true`). |
| `gdpr`    | string | —                | Programmatic GDPR flag (e.g. `"0"` / `"1"`). |
| `gdprConsent` | string | —                | TC string when no CMP is present. |
| `bpsegs`  | array of string | —                | Publisher first-party segments. See Network SDK *(→ network-sdk)* (Prebid). |
| `clickUrlPrefix` | string | —                | Optional prefix for click URLs. |
| `prebidVersion` | string | —                | Prebid version when using header bidding. |

### Public functions

| Function | Documented above | More details |
|----------|------------------|--------------|
| `BeOpSDK.init(config)` | ✅                | See API Reference above. |
| `BeOpSDK.watch(rootElement?)` | ✅                | Parses and watches for slots; triggers content fetch. |
| `BeOpSDK.parse(rootElement?)` | ✅ (deprecated)   | Use `watch()` instead. |
| `BeOpSDK.pushEvent(config)` | ✅                | `eventName`, `eventValue`, `eventLabel`, `slotName`, `currency`. Only `impression` is supported for `eventName`. |
| `BeOpSDK.onPressClose(callback, timeout?)` | ✅                | Fullsize mode: close button after creative complete. |
| `BeOpSDK.subscribeToWidgetEvents(callback)` | ✅                | Subscribe to widget events (`ad`, `no-ad`, `vote`, `complete`…). See API Reference above. |
| `BeOpSDK.getLoginStatus(callback)` | —                | Get current login status and user ID (e.g. CrossConnect). |

### Page keywords (not in `init`)

Keywords are **not** passed in `init()`. Set `**window.beOpKeywords**` (array of strings) **before** the SDK runs; the SDK reads it at request time and sends it as `kwds` to the content server. See Identify pages – Page keywords *(→ identify-pages (#page-keywords))* and Retail Media Network – Passing context *(→ retail-media-network (#passing-context-to-maximize-performance))*.