diff --git a/dev-docs/analytics/paywalls.md b/dev-docs/analytics/paywalls.md new file mode 100644 index 0000000000..a2d8a79539 --- /dev/null +++ b/dev-docs/analytics/paywalls.md @@ -0,0 +1,142 @@ +--- +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. + +#### 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 +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.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 + } +}]); +``` + +#### 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..674835d2c6 --- /dev/null +++ b/dev-docs/modules/paywallsRtdProvider.md @@ -0,0 +1,196 @@ +--- +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 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 (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 + +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: { + dataProviders: [ + { + name: 'paywalls' + } + ] + } +}); +``` + +### Parameter Descriptions + +{: .table .table-bordered .table-striped } + +| Name | Type | Scope | Description | Default | +| :--- | :--- | :---- | :---------- | :------ | +| name | `String` | Required | RTD sub module name | Always `'paywalls'` | + +## Output + +### 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.data.vai`. + +```json +{ + "site": { + "ext": { + "data": { + "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.data.vai` (Actor Classification) + +Fields that describe the classified actor and the signed assertion: + +```json +{ + "user": { + "ext": { + "data": { + "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. + +## How It Works + +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 + +* **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`). + +## 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)