VAI Busy Ops Guide
This page is the fastest way to put VAI on a page using a script tag.
Publishers can integrate VAI in two modes:
- Paywalls-hosted VAI (easier): load VAI directly from
https://paywalls.net/. - Publisher-hosted VAI (additional integration): serve VAI from the same origin as your inventory domain (typically via CDN integration). This is also the approach used for cryptographically signed domain provenance that buyers can verify in RTB.
In both modes, your wrapper / ad stack reads VAI and passes it into:
- RTB (e.g. OpenRTB via Prebid ORTB2)
- Ad server tags (key-values / targeting as supported)
If you need the full details (claims, signing, JWKS, and verification), see the VAI technical spec.
Mode 1: Paywalls-hosted VAI (easiest)
Script URL
Use the Paywalls-hosted script base:
<script src="https://paywalls.net/pw/vai.js"></script>
Option A: simplest script tag (blocking, no hook)
<!-- 1) Load VAI (Paywalls-hosted) -->
<script src="https://paywalls.net/pw/vai.js"></script>
<!-- 2) Read window.__PW_VAI__ and pass it into your stack -->
<script>
(function () {
var vai = window.__PW_VAI__;
if (!vai) return;
// Example: Prebid ORTB2
if (window.pbjs && pbjs.setConfig) {
pbjs.setConfig({
ortb2: {
site: {
ext: {
pw_vai: vai
}
}
}
});
}
})();
</script>
Option B: hook-based setup (recommended for tag managers)
<script>
// Define the hook first
window.__PW_VAI_HOOK__ = function (vai) {
if (window.pbjs && pbjs.setConfig) {
pbjs.setConfig({
ortb2: {
site: {
ext: {
pw_vai: vai
}
}
}
});
}
// Optional: expose it for debugging
window.__PW_VAI__ = vai;
};
</script>
<script src="https://paywalls.net/pw/vai.js"></script>
Quick verification
- In DevTools Console, confirm
window.__PW_VAI__is defined. - Confirm
window.__PW_VAI__.vatandwindow.__PW_VAI__.actare present.
Common issues
- CSP blocks the script: ensure your CSP allows
https://paywalls.netinscript-src. - Ordering issues (tag manager): use the hook approach and define
window.__PW_VAI_HOOK__before loading the script.
Mode 2: Publisher-hosted VAI (same-origin + domain provenance)
Use this mode when you want VAI to be served from your inventory domain (same-origin), and when you want buyers to be able to verify publisher-signed domain provenance via the assertion + your jwks.json.
This mode typically requires CDN integration so VAI endpoints are present on your public surface.
Prerequisites (must already be live)
Before you touch page code, confirm these endpoints work on your inventory domain:
GET /pw/vai.jsreturns JavaScript that setswindow.__PW_VAI__.GET /pw/vai.jsonreturns the same data as JSON.GET /pw/jwks.jsonreturns public keys so partners can verify theassertion.
Important operational requirements:
- These endpoints should be same-origin (avoid third-party script hosts for VAI).
vai.jsandvai.jsonshould be not cacheable (e.g.Cache-Control: private, no-store, max-age=0) because assertions are short-lived.- Don’t vary HTML by VAI (keep VAI cache-friendly; VAI is metadata, not page personalization).
If you don’t have these endpoints yet, start with CDN integration and then follow the VAI technical spec.
Option A: simplest script tag (blocking, no hook)
Use this when you control the HTML and can load VAI before your wrapper reads it.
<!-- 1) Load VAI (same-origin) -->
<script src="/pw/vai.js"></script>
<!-- 2) Read window.__PW_VAI__ and pass it into your stack -->
<script>
(function () {
var vai = window.__PW_VAI__;
if (!vai) return;
// Example: Prebid ORTB2
if (window.pbjs && pbjs.setConfig) {
pbjs.setConfig({
ortb2: {
site: {
ext: {
pw_vai: vai
}
}
}
});
}
})();
</script>
Notes:
- Don’t add
asyncordeferto the/pw/vai.jstag if your next script assumes VAI is already present. - If your environment can’t guarantee ordering (common with tag managers), use the hook approach below.
Option B: hook-based setup (recommended for tag managers)
If you need strict ordering without relying on script execution timing, define a hook before loading /pw/vai.js.
<script>
// 1) Define the hook first
window.__PW_VAI_HOOK__ = function (vai) {
// Example: Prebid ORTB2
if (window.pbjs && pbjs.setConfig) {
pbjs.setConfig({
ortb2: {
site: {
ext: {
pw_vai: vai
}
}
}
});
}
// Optional: expose it for debugging
window.__PW_VAI__ = vai;
};
</script>
<!-- 2) Load VAI (same-origin) -->
<script src="/pw/vai.js"></script>
Notes:
- The hook should be a simple function (no dependencies) and should be defined as early as possible.
- If both the hook and
window.__PW_VAI__are supported by your/pw/vai.jsimplementation, you can use either downstream.
Quick verification checklist
In a browser on the inventory domain:
- Open
https://YOUR_DOMAIN/pw/vai.jsonand confirm:audis"vai"dommatches the page hostnameexpis in the future and short-livedassertionis present
- Open the page and check in DevTools Console:
window.__PW_VAI__is definedwindow.__PW_VAI__.vatandwindow.__PW_VAI__.actlook reasonable
- Open
https://YOUR_DOMAIN/pw/jwks.jsonand confirm:- It returns a
keysarray and includes thekidreferenced by the VAI object
- It returns a
Common issues (fast fixes)
/pw/vai.jsis 404: the endpoint isn’t deployed on the public surface. Fix routing/edge deployment.- CSP blocks the script: ensure your CSP allows same-origin scripts (typically
script-src 'self' ...). - VAI loads but doesn’t update: check caching headers;
vai.jsmust not be cached. dommismatch: VAI must be bound to the inventory domain; ensure you’re testing on the real host.- Hook not called: make sure
window.__PW_VAI_HOOK__is defined before the/pw/vai.jsscript tag.