From 6187766139670733b22341db3716829d7dc84cdf Mon Sep 17 00:00:00 2001 From: paywalls-mike Date: Sun, 1 Mar 2026 16:22:18 -0800 Subject: [PATCH 1/4] Paywalls RTD Provider: add module and analytics adapter docs Companion docs for prebid/Prebid.js PR #14541. - dev-docs/modules/paywallsRtdProvider.md: RTD module docs with refactored 3-scope ORTB2 (site/user/imp), bare domain iss, jws, mstk, pvtk - dev-docs/analytics/paywalls.md: Analytics adapter docs (gtag, dataLayer, callback output modes, sampling, activity controls) --- dev-docs/analytics/paywalls.md | 152 +++++++++++++++++ dev-docs/modules/paywallsRtdProvider.md | 218 ++++++++++++++++++++++++ 2 files changed, 370 insertions(+) create mode 100644 dev-docs/analytics/paywalls.md create mode 100644 dev-docs/modules/paywallsRtdProvider.md diff --git a/dev-docs/analytics/paywalls.md b/dev-docs/analytics/paywalls.md new file mode 100644 index 0000000000..cfea1425fc --- /dev/null +++ b/dev-docs/analytics/paywalls.md @@ -0,0 +1,152 @@ +--- +layout: analytics +title: Paywalls +description: Paywalls Analytics Adapter +modulecode: paywallsAnalyticsAdapter +prebid_member: false +--- + +#### Overview + +The Paywalls Analytics Adapter emits [VAI (Validated Actor Inventory)](https://paywalls.net/docs/publishers/vai) classification on each Prebid auction. VAI helps publishers distinguish **human traffic** and **AI agents** from **non-human automation** (sharing/preview bots, search crawlers, AI training scrapers, etc.), enabling them to segment and analyze performance by traffic class (yield, fill, viewability, buyer outcomes) in their existing analytics stack (GA4, GTM / dataLayer, or a custom callback). + +Two key-value pairs are emitted per auction: + +{: .table .table-bordered .table-striped } + +| Key | Example | Description | +| :-- | :------ | :---------- | +| `vai_vat` | `HUMAN` | Validated Actor Type — `HUMAN`, `AI_AGENT`, `SHARING`, or `OTHER` | +| `vai_act` | `ACT-1` | Actor Confidence Tier — `ACT-1`, `ACT-2`, or `ACT-3` | + +If VAI is unavailable, both values are `UNKNOWN`. + +{: .alert.alert-info :} +The companion [Paywalls RTD Provider](/dev-docs/modules/paywallsRtdProvider.html) injects VAI into ORTB2 and GAM targeting. The analytics adapter independently reads the same `window.__PW_VAI__` global and routes classification to your analytics tool of choice. + +#### Build + +```bash +gulp build --modules=paywallsAnalyticsAdapter +``` + +Or with the recommended RTD provider: + +```bash +gulp build --modules=rtdModule,paywallsRtdProvider,paywallsAnalyticsAdapter +``` + +#### Analytics Options + +{: .table .table-bordered .table-striped } + +| Name | Type | Scope | Description | Default | +| :--- | :--- | :---- | :---------- | :------ | +| provider | `String` | Required | Must be `'paywalls'` | — | +| options.output | `String` | Optional | Output mode: `'gtag'`, `'dataLayer'`, or `'callback'` | `'callback'` | +| options.scriptUrl | `String` | Optional | URL of the VAI loader script | `'/pw/vai.js'` | +| options.samplingRate | `Number` | Optional | Fraction of page views that emit analytics (0.0–1.0) | `1.0` | +| options.callback | `Function` | Optional | Called with the metrics object when output is `'callback'` | `null` | + +#### Example Configuration + +##### gtag (Google Analytics 4) + +```javascript +pbjs.enableAnalytics([{ + provider: 'paywalls', + options: { + output: 'gtag' + } +}]); +``` + +Fires a GA4 event via the global `gtag()` function: + +```javascript +gtag('event', 'vai_auction', { vai_vat: 'HUMAN', vai_act: 'ACT-1' }); +``` + +##### dataLayer (Google Tag Manager) + +```javascript +pbjs.enableAnalytics([{ + provider: 'paywalls', + options: { + output: 'dataLayer' + } +}]); +``` + +Pushes to the GTM `dataLayer` array: + +```javascript +window.dataLayer.push({ + event: 'vai_auction', + vai_vat: 'HUMAN', + vai_act: 'ACT-1' +}); +``` + +In GTM, create a Custom Event trigger on `vai_auction` to route the data to any tag. + +##### callback (Custom Function) + +```javascript +pbjs.enableAnalytics([{ + provider: 'paywalls', + options: { + output: 'callback', + callback: function (metrics) { + console.log(metrics); + // { vai_vat: 'HUMAN', vai_act: 'ACT-1' } + } + } +}]); +``` + +#### Sampling + +Set `samplingRate` to control cost. The decision is made once per page load — all auctions on that page either emit or don't. + +```javascript +pbjs.enableAnalytics([{ + provider: 'paywalls', + options: { + samplingRate: 0.1 // emit on ~10% of page views + } +}]); +``` + +#### Activity Controls + +The adapter uses `loadExternalScript` to inject `vai.js`. If your activity configuration restricts external scripts, allow the `paywalls` component: + +```javascript +pbjs.setConfig({ + allowActivities: { + loadExternalScript: { + default: false, + rules: [{ + condition: function (params) { + return params.componentName === 'paywalls'; + }, + allow: true + }] + } + } +}); +``` + +#### Privacy + +VAI does not collect, store, or transmit user IDs, cookies, fingerprints, or PII. Classification is based on aggregate session-level behavioral signals processed entirely in the browser. + +#### Registration + +For questions or integration help, contact [engineering@paywalls.net](mailto:engineering@paywalls.net). + +#### Further Reading + +* [VAI Documentation](https://paywalls.net/docs/publishers/vai) +* [Paywalls RTD Provider](/dev-docs/modules/paywallsRtdProvider.html) diff --git a/dev-docs/modules/paywallsRtdProvider.md b/dev-docs/modules/paywallsRtdProvider.md new file mode 100644 index 0000000000..f89e800e22 --- /dev/null +++ b/dev-docs/modules/paywallsRtdProvider.md @@ -0,0 +1,218 @@ +--- +layout: page_v2 +title: Paywalls RTD Provider +display_name: Paywalls RTD Provider +description: VAI (Validated Actor Inventory) classification for Prebid.js — enriches ORTB2 and GAM targeting with actor-type and confidence signals. +page_type: module +module_type: rtd +module_code: paywallsRtdProvider +enable_download: true +vendor_specific: true +sidebarType: 1 +--- + +# Paywalls RTD Provider +{:.no_toc} + +The Paywalls RTD module integrates [VAI (Validated Actor Inventory)](https://paywalls.net/docs/publishers/vai) into Prebid.js. VAI helps publishers distinguish **human traffic** and **AI agents** from **non-human automation** (sharing/preview bots, search crawlers, AI training scrapers, etc.) so they can make better-informed economic decisions about their inventory. + +Each page impression is classified by **actor type** (`vat`) and **confidence tier** (`act`), producing a cryptographically signed assertion that SSPs and DSPs can independently verify via a standard JWKS endpoint. + +* TOC +{:toc} + +## Overview + +The module automates VAI loading, timing, and signal injection: + +* **ORTB2 enrichment** — VAI signals are split across `site.ext.vai` (domain provenance), `user.ext.vai` (actor classification + signed assertion), and `imp[].ext.vai` (pageview correlation), available to all ORTB2-native bid adapters. +* **GAM targeting** — `vai_vat` and `vai_act` key-value pairs are set per ad unit for Google Ad Manager line item targeting. +* **Graceful degradation** — if VAI is unavailable or times out, the auction proceeds normally without enrichment. + +## Integration + +Build the Paywalls RTD module into your Prebid.js package: + +```bash +gulp build --modules=rtdModule,paywallsRtdProvider +``` + +{: .alert.alert-info :} +The global RTD module (`rtdModule`) is a required dependency. + +## Configuration + +This module is configured as part of the `realTimeData.dataProviders` object: + +```javascript +pbjs.setConfig({ + realTimeData: { + auctionDelay: 500, + dataProviders: [ + { + name: 'paywalls', + waitForIt: true, + params: { + scriptUrl: '/pw/vai.js' + } + } + ] + } +}); +``` + +### Parameter Descriptions + +{: .table .table-bordered .table-striped } + +| Name | Type | Scope | Description | Default | +| :--- | :--- | :---- | :---------- | :------ | +| name | `String` | Required | RTD sub module name | Always `'paywalls'` | +| waitForIt | `Boolean` | Optional | Should be `true` when `auctionDelay` is set | `false` | +| params | `Object` | Optional | Provider configuration | `{}` | +| params.scriptUrl | `String` | Optional | URL of the VAI loader script | `'/pw/vai.js'` | +| params.waitForIt | `Number` | Optional | Max ms to wait for VAI before releasing the auction (distinct from the Boolean `waitForIt` above) | `100` | + +### Hosting Modes + +VAI supports two hosting modes for the loader script: + +* **Publisher-hosted** (preferred) — The script is served from the publisher's own domain via a CDN or server integration. Use the default relative path `'/pw/vai.js'`. This keeps requests same-origin, avoids CORS, and ensures the assertion's `dom` claim matches the inventory domain. +* **Paywalls-hosted** — The script is served from `https://paywalls.net/pw/vai.js`. Set `scriptUrl` to the full URL. This requires paywalls.net configuration before usage. **Note:** The domain provenance claim (`dom`) will reflect `paywalls.net` rather than the inventory domain, which may affect SSP verification and buyer trust. + +## Output + +### ORTB2 — `site.ext.vai` (Domain Provenance) + +Fields that describe the assertion context. The `dom` value can be cryptographically verified through the signed `jws` in `user.ext.vai`. + +```json +{ + "site": { + "ext": { + "vai": { + "iss": "paywalls.net", + "dom": "example.com" + } + } + } +} +``` + +{: .table .table-bordered .table-striped } + +| Field | Description | +| :---- | :---------- | +| `iss` | Issuer — bare domain (e.g. `paywalls.net`) | +| `dom` | Domain the assertion covers | + +### ORTB2 — `user.ext.vai` (Actor Classification) + +Fields that describe the classified actor and the signed assertion: + +```json +{ + "user": { + "ext": { + "vai": { + "iss": "paywalls.net", + "mstk": "01J4X9K2ABCDEF01234567", + "vat": "HUMAN", + "act": "ACT-1", + "jws": "eyJhbGciOiJFZERTQSIs..." + } + } + } +} +``` + +{: .table .table-bordered .table-striped } + +| Field | Description | +| :---- | :---------- | +| `iss` | Issuer — bare domain (e.g. `paywalls.net`) | +| `vat` | Validated Actor Type — one of `HUMAN`, `AI_AGENT`, `SHARING`, `OTHER` | +| `act` | Actor Confidence Tier — one of `ACT-1`, `ACT-2`, `ACT-3` | +| `mstk` | Micro-session token — unique per assertion | +| `jws` | Full JWS (compact serialization) for SSP/DSP verification | + +### ORTB2 — `imp[].ext.vai` (Pageview Correlation) + +Set on each ad unit’s `ortb2Imp` when `pvtk` is available from the VAI payload: + +```json +{ + "imp": [{ + "ext": { + "vai": { + "pvtk": "01J4X9K2ABCDEF01234567/3" + } + } + }] +} +``` + +{: .table .table-bordered .table-striped } + +| Field | Description | +| :---- | :---------- | +| `pvtk` | Pageview token — client-derived, unsigned; correlates impressions within a pageview using the `mstk` root | + +### GAM Targeting + +The module sets key-value pairs on every ad unit for Google Ad Manager targeting: + +{: .table .table-bordered .table-striped } + +| Key | Example Value | Description | +| :-- | :------------ | :---------- | +| `vai_vat` | `HUMAN` | Actor type | +| `vai_act` | `ACT-1` | Confidence tier | + +These are available via `pbjs.getAdserverTargeting()` and are compatible with standard GPT integration. + +## Activity Controls + +The module uses `loadExternalScript` to inject `vai.js`. If your activity configuration denies external scripts by default, explicitly allow the `paywalls` component: + +```javascript +pbjs.setConfig({ + allowActivities: { + loadExternalScript: { + default: false, + rules: [ + { + condition: function (params) { + return params.componentName === 'paywalls'; + }, + allow: true + } + ] + } + } +}); +``` + +## Privacy + +* **No user identifiers** — VAI does not collect, store, or transmit user IDs, cookies, or fingerprints. +* **No PII** — The classification is based on aggregate session-level behavioral signals, not personal data. +* **Browser-side only** — All signal extraction runs in the browser; no data leaves the page except the classification result. +* **Signed assertions** — SSPs can independently verify the `jws` via the JWKS endpoint pulled from the JWS header (typically `https://example.com/pw/jwks.json`). + +## How It Works + +1. **`init()`** — Checks `window.__PW_VAI__` and `localStorage` for an existing VAI payload. If present and unexpired, caches it for immediate use. +2. **`getBidRequestData()`** — If cached VAI exists, merges ORTB2 and calls back immediately (fast path). Otherwise, injects `vai.js` via `loadExternalScript`, sets up a callback hook, and polls until timeout (slow path). On timeout, the auction proceeds without enrichment. +3. **`getTargetingData()`** — Returns `{ vai_vat, vai_act }` for each ad unit from the current VAI payload. + +## Support + +For questions or integration help, contact [engineering@paywalls.net](mailto:engineering@paywalls.net). + +## Further Reading + +* [VAI Documentation](https://paywalls.net/docs/publishers/vai) +* [Paywalls Analytics Adapter](/dev-docs/analytics/paywalls.html) +* [Prebid RTD Module Documentation](/dev-docs/add-rtd-submodule.html) +* [How Bid Adapters Should Read First Party Data](/features/firstPartyData.html#how-bid-adapters-should-read-first-party-data) From b01d40d2d22e2ad98cea961009a8d3ef1b162a8a Mon Sep 17 00:00:00 2001 From: paywalls-mike Date: Tue, 10 Mar 2026 15:07:58 -0700 Subject: [PATCH 2/4] Paywalls RTD Provider: rename params.waitForIt to params.timeout --- dev-docs/modules/paywallsRtdProvider.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-docs/modules/paywallsRtdProvider.md b/dev-docs/modules/paywallsRtdProvider.md index f89e800e22..d618c12d1c 100644 --- a/dev-docs/modules/paywallsRtdProvider.md +++ b/dev-docs/modules/paywallsRtdProvider.md @@ -71,7 +71,7 @@ pbjs.setConfig({ | waitForIt | `Boolean` | Optional | Should be `true` when `auctionDelay` is set | `false` | | params | `Object` | Optional | Provider configuration | `{}` | | params.scriptUrl | `String` | Optional | URL of the VAI loader script | `'/pw/vai.js'` | -| params.waitForIt | `Number` | Optional | Max ms to wait for VAI before releasing the auction (distinct from the Boolean `waitForIt` above) | `100` | +| params.timeout | `Number` | Optional | Max ms to wait for VAI before releasing the auction | `100` | ### Hosting Modes From 0bfe1f67f7fb6cc97e960c624a5be1f097f61c07 Mon Sep 17 00:00:00 2001 From: paywalls-mike Date: Wed, 11 Mar 2026 13:30:18 -0700 Subject: [PATCH 3/4] Paywalls RTD Provider: update ORTB2 paths to ext.data.vai per FPD convention --- dev-docs/modules/paywallsRtdProvider.md | 30 ++++++++++++++----------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/dev-docs/modules/paywallsRtdProvider.md b/dev-docs/modules/paywallsRtdProvider.md index d618c12d1c..d57ed94ff5 100644 --- a/dev-docs/modules/paywallsRtdProvider.md +++ b/dev-docs/modules/paywallsRtdProvider.md @@ -25,7 +25,7 @@ Each page impression is classified by **actor type** (`vat`) and **confidence ti The module automates VAI loading, timing, and signal injection: -* **ORTB2 enrichment** — VAI signals are split across `site.ext.vai` (domain provenance), `user.ext.vai` (actor classification + signed assertion), and `imp[].ext.vai` (pageview correlation), available to all ORTB2-native bid adapters. +* **ORTB2 enrichment** — VAI signals are split across `site.ext.data.vai` (domain provenance), `user.ext.data.vai` (actor classification + signed assertion), and `imp[].ext.vai` (pageview correlation), available to all ORTB2-native bid adapters. * **GAM targeting** — `vai_vat` and `vai_act` key-value pairs are set per ad unit for Google Ad Manager line item targeting. * **Graceful degradation** — if VAI is unavailable or times out, the auction proceeds normally without enrichment. @@ -82,17 +82,19 @@ VAI supports two hosting modes for the loader script: ## Output -### ORTB2 — `site.ext.vai` (Domain Provenance) +### ORTB2 — `site.ext.data.vai` (Domain Provenance) -Fields that describe the assertion context. The `dom` value can be cryptographically verified through the signed `jws` in `user.ext.vai`. +Fields that describe the assertion context. The `dom` value can be cryptographically verified through the signed `jws` in `user.ext.data.vai`. ```json { "site": { "ext": { - "vai": { - "iss": "paywalls.net", - "dom": "example.com" + "data": { + "vai": { + "iss": "paywalls.net", + "dom": "example.com" + } } } } @@ -106,7 +108,7 @@ Fields that describe the assertion context. The `dom` value can be cryptographic | `iss` | Issuer — bare domain (e.g. `paywalls.net`) | | `dom` | Domain the assertion covers | -### ORTB2 — `user.ext.vai` (Actor Classification) +### ORTB2 — `user.ext.data.vai` (Actor Classification) Fields that describe the classified actor and the signed assertion: @@ -114,12 +116,14 @@ Fields that describe the classified actor and the signed assertion: { "user": { "ext": { - "vai": { - "iss": "paywalls.net", - "mstk": "01J4X9K2ABCDEF01234567", - "vat": "HUMAN", - "act": "ACT-1", - "jws": "eyJhbGciOiJFZERTQSIs..." + "data": { + "vai": { + "iss": "paywalls.net", + "mstk": "01J4X9K2ABCDEF01234567", + "vat": "HUMAN", + "act": "ACT-1", + "jws": "eyJhbGciOiJFZERTQSIs..." + } } } } From 82722ff43f745c551c8042d2aa509180e78361a1 Mon Sep 17 00:00:00 2001 From: paywalls-mike Date: Fri, 13 Mar 2026 10:59:36 -0700 Subject: [PATCH 4/4] Paywalls modules docs: remove script injection, add Prerequisites RTD Provider (paywallsRtdProvider.md): - Add Prerequisites section with vai.js script tag guidance - Remove scriptUrl, timeout, waitForIt params - Remove Activity Controls and Hosting Modes sections - Simplify How It Works and configuration Analytics Adapter (paywalls.md): - Add Prerequisites section with vai.js script tag guidance - Remove scriptUrl param - Remove Activity Controls section --- dev-docs/analytics/paywalls.md | 32 +++++-------- dev-docs/modules/paywallsRtdProvider.md | 64 ++++++++----------------- 2 files changed, 30 insertions(+), 66 deletions(-) diff --git a/dev-docs/analytics/paywalls.md b/dev-docs/analytics/paywalls.md index cfea1425fc..a2d8a79539 100644 --- a/dev-docs/analytics/paywalls.md +++ b/dev-docs/analytics/paywalls.md @@ -24,6 +24,17 @@ If VAI is unavailable, both values are `UNKNOWN`. {: .alert.alert-info :} The companion [Paywalls RTD Provider](/dev-docs/modules/paywallsRtdProvider.html) injects VAI into ORTB2 and GAM targeting. The analytics adapter independently reads the same `window.__PW_VAI__` global and routes classification to your analytics tool of choice. +#### Prerequisites + +Load vai.js **before** Prebid.js initializes so that `window.__PW_VAI__` is populated when auctions run: + +```html + + +``` + +The adapter does **not** inject vai.js itself. See [VAI Documentation](https://paywalls.net/docs/publishers/vai) for setup details. + #### Build ```bash @@ -44,7 +55,6 @@ gulp build --modules=rtdModule,paywallsRtdProvider,paywallsAnalyticsAdapter | :--- | :--- | :---- | :---------- | :------ | | provider | `String` | Required | Must be `'paywalls'` | — | | options.output | `String` | Optional | Output mode: `'gtag'`, `'dataLayer'`, or `'callback'` | `'callback'` | -| options.scriptUrl | `String` | Optional | URL of the VAI loader script | `'/pw/vai.js'` | | options.samplingRate | `Number` | Optional | Fraction of page views that emit analytics (0.0–1.0) | `1.0` | | options.callback | `Function` | Optional | Called with the metrics object when output is `'callback'` | `null` | @@ -118,26 +128,6 @@ pbjs.enableAnalytics([{ }]); ``` -#### Activity Controls - -The adapter uses `loadExternalScript` to inject `vai.js`. If your activity configuration restricts external scripts, allow the `paywalls` component: - -```javascript -pbjs.setConfig({ - allowActivities: { - loadExternalScript: { - default: false, - rules: [{ - condition: function (params) { - return params.componentName === 'paywalls'; - }, - allow: true - }] - } - } -}); -``` - #### Privacy VAI does not collect, store, or transmit user IDs, cookies, fingerprints, or PII. Classification is based on aggregate session-level behavioral signals processed entirely in the browser. diff --git a/dev-docs/modules/paywallsRtdProvider.md b/dev-docs/modules/paywallsRtdProvider.md index d57ed94ff5..674835d2c6 100644 --- a/dev-docs/modules/paywallsRtdProvider.md +++ b/dev-docs/modules/paywallsRtdProvider.md @@ -23,11 +23,23 @@ Each page impression is classified by **actor type** (`vat`) and **confidence ti ## Overview -The module automates VAI loading, timing, and signal injection: +The module reads VAI classification from `window.__PW_VAI__` (populated by the publisher's vai.js script) and injects it into bid requests and ad server targeting: * **ORTB2 enrichment** — VAI signals are split across `site.ext.data.vai` (domain provenance), `user.ext.data.vai` (actor classification + signed assertion), and `imp[].ext.vai` (pageview correlation), available to all ORTB2-native bid adapters. * **GAM targeting** — `vai_vat` and `vai_act` key-value pairs are set per ad unit for Google Ad Manager line item targeting. -* **Graceful degradation** — if VAI is unavailable or times out, the auction proceeds normally without enrichment. +* **Graceful degradation** — if VAI is unavailable (publisher did not load vai.js, or it returned an invalid/expired response), the auction proceeds normally without enrichment. + +## Prerequisites + +Load vai.js **before** Prebid.js initializes so that `window.__PW_VAI__` is populated when the RTD module runs: + +```html + + +``` + +{: .alert.alert-warning :} +The module does **not** inject vai.js itself. Publishers must add the script tag to their page. See [VAI Documentation](https://paywalls.net/docs/publishers/vai) for setup details. ## Integration @@ -47,14 +59,9 @@ This module is configured as part of the `realTimeData.dataProviders` object: ```javascript pbjs.setConfig({ realTimeData: { - auctionDelay: 500, dataProviders: [ { - name: 'paywalls', - waitForIt: true, - params: { - scriptUrl: '/pw/vai.js' - } + name: 'paywalls' } ] } @@ -68,17 +75,6 @@ pbjs.setConfig({ | Name | Type | Scope | Description | Default | | :--- | :--- | :---- | :---------- | :------ | | name | `String` | Required | RTD sub module name | Always `'paywalls'` | -| waitForIt | `Boolean` | Optional | Should be `true` when `auctionDelay` is set | `false` | -| params | `Object` | Optional | Provider configuration | `{}` | -| params.scriptUrl | `String` | Optional | URL of the VAI loader script | `'/pw/vai.js'` | -| params.timeout | `Number` | Optional | Max ms to wait for VAI before releasing the auction | `100` | - -### Hosting Modes - -VAI supports two hosting modes for the loader script: - -* **Publisher-hosted** (preferred) — The script is served from the publisher's own domain via a CDN or server integration. Use the default relative path `'/pw/vai.js'`. This keeps requests same-origin, avoids CORS, and ensures the assertion's `dom` claim matches the inventory domain. -* **Paywalls-hosted** — The script is served from `https://paywalls.net/pw/vai.js`. Set `scriptUrl` to the full URL. This requires paywalls.net configuration before usage. **Note:** The domain provenance claim (`dom`) will reflect `paywalls.net` rather than the inventory domain, which may affect SSP verification and buyer trust. ## Output @@ -175,27 +171,11 @@ The module sets key-value pairs on every ad unit for Google Ad Manager targeting These are available via `pbjs.getAdserverTargeting()` and are compatible with standard GPT integration. -## Activity Controls - -The module uses `loadExternalScript` to inject `vai.js`. If your activity configuration denies external scripts by default, explicitly allow the `paywalls` component: +## How It Works -```javascript -pbjs.setConfig({ - allowActivities: { - loadExternalScript: { - default: false, - rules: [ - { - condition: function (params) { - return params.componentName === 'paywalls'; - }, - allow: true - } - ] - } - } -}); -``` +1. **`init()`** — Always returns `true`; the module is always available. +2. **`getBidRequestData()`** — Reads `window.__PW_VAI__`. If a valid, unexpired payload is found, merges ORTB2 fragments (`site.ext.data.vai`, `user.ext.data.vai`, `imp[].ext.vai`) and calls back immediately. If VAI is absent or invalid, calls back without enrichment. +3. **`getTargetingData()`** — Returns `{ vai_vat, vai_act }` for each ad unit from the current VAI payload. ## Privacy @@ -204,12 +184,6 @@ pbjs.setConfig({ * **Browser-side only** — All signal extraction runs in the browser; no data leaves the page except the classification result. * **Signed assertions** — SSPs can independently verify the `jws` via the JWKS endpoint pulled from the JWS header (typically `https://example.com/pw/jwks.json`). -## How It Works - -1. **`init()`** — Checks `window.__PW_VAI__` and `localStorage` for an existing VAI payload. If present and unexpired, caches it for immediate use. -2. **`getBidRequestData()`** — If cached VAI exists, merges ORTB2 and calls back immediately (fast path). Otherwise, injects `vai.js` via `loadExternalScript`, sets up a callback hook, and polls until timeout (slow path). On timeout, the auction proceeds without enrichment. -3. **`getTargetingData()`** — Returns `{ vai_vat, vai_act }` for each ad unit from the current VAI payload. - ## Support For questions or integration help, contact [engineering@paywalls.net](mailto:engineering@paywalls.net).