Opt-Out Advertising Integration
Connect Upod with Opt-Out Advertising to generate personalized opt-out advertising
This integration shows how to use Upod with Opt-Out Advertising to generate personalized opt-out advertising. Users log in with Upod, and you can use their anonymized profile data to serve relevant ads without using cookies or tracking.
When to Use This
- You want to serve personalized ads without cookies
- You need to comply with privacy regulations (GDPR, etc.)
- You want to use anonymized user data for ad targeting
- You're working with Opt-Out Advertising network
Prerequisites
Before starting, you need to:
- Register as a new OIDC client - You must register your application as a new OIDC client by contacting us via email. We'll register your
client_idandredirect_uri(s)for you. - Have access to Opt-Out Advertising network
- Understand how to pass audience parameters to your ad server
Implementation Overview
The integration requires:
- Loading the Upod client library and Opt-Out Advertising script
- Initializing the ad server
- Initializing the Upod client
- Handling user authentication
- Fetching anonymized user profile data
- Passing profile data to the ad server
- Rendering personalized ads based on user data
Step 1: Load the Client Library
Include the Upod client library and Opt-Out Advertising script:
<script src="https://cdn.optoutadvertising.com/script/ootag.v2.min.js"></script>
<script src="https://cdn.upod.eu/client/bundle.browser.js"></script>Step 2: Initialize the Ad Server
Initialize your opt-out ad server:
var ootag = ootag || {};
ootag.queue = ootag.queue || [];
ootag.queue.push(function () {
ootag.initializeOo({
publisher: YOUR_PUBLISHER_ID,
alwaysNoConsent: 1, // Always use opt-out mode
consentTimeOutMS: 500,
noRequestsOnPageLoad: 1, // Don't load ads until we have user data
});
});Step 3: Initialize the Upod Client
Initialize the Upod client:
const client = upod.initialize({
authority: 'https://account.upod.eu',
client_id: 'your-client-id', // Obtained by contacting us
redirect_uri: location.origin + location.pathname,
post_logout_redirect_uri: location.origin + location.pathname,
language: 'en',
});Step 4: Define Ad Slots
Define your ad slots with the opt-out ad server:
ootag.queue.push(function () {
ootag.defineSlot({
adSlot: 'YOUR_AD_SLOT_ID',
targetId: 'ad-container',
emptyCallback: () => {
// Handle empty ad slot
const adContainer = document.getElementById('ad-container');
if (adContainer) adContainer.classList.add('is-hidden');
},
});
});Step 5: Handle Authentication and Fetch Profile
After the user logs in, fetch their profile and pass data to the ad server:
// Handle OIDC callback
client.handleCallbackIfNeeded().then(() => {
client.isAuthenticated().then(loggedIn => {
if (loggedIn) {
loadUserProfileAndAds();
} else {
// Show login button or generic ads
showGenericAds();
}
});
});
// Listen for login events
client.onLogin(() => {
loadUserProfileAndAds();
});
// Listen for logout events
client.onLogout(() => {
showGenericAds();
});
function loadUserProfileAndAds() {
client
.fetch({ path: '/storage/profile', method: 'get' })
.then(profile => {
console.log('User profile:', profile);
// Extract profile data
const gender = profile.gender || 'unknown';
const ageRange = profile.ageRange || null;
const city = profile.residence || 'unknown';
// Pass audience parameters to ad server
queueOotagParameters(gender, ageRange, city);
// Show personalized content based on profile
personalizeContent(profile);
// Trigger ad requests
ootag.queue.push(function () {
ootag.makeRequests(true);
});
})
.catch(error => {
console.error('Failed to fetch profile:', error);
showGenericAds();
});
}
function queueOotagParameters(gender, ageRange, city) {
ootag.queue.push(function () {
ootag.addParameter('gender', gender);
ootag.addParameter('age_bracket', formatAgeRange(ageRange));
ootag.addParameter('city', city);
});
}
function formatAgeRange(ageRange) {
if (!ageRange) return 'unknown';
if (ageRange[1] === null) {
return `${ageRange[0]}+`;
}
return `${ageRange[0]}-${ageRange[1]}`;
}Step 6: Personalize Content
Use the profile data to personalize your website content:
function personalizeContent(profile) {
const gender = profile.gender || 'other';
const residence = profile.residence;
// Update content based on gender
if (gender === 'male') {
// Show male-targeted content
} else if (gender === 'female') {
// Show female-targeted content
}
// Update content based on residence
if (residence) {
// Show location-specific content
updateLocationContent(residence);
}
}Complete Example
Here's a complete example based on our opt-out advertising example:
<!DOCTYPE html>
<html>
<head>
<title>My Website with Opt-Out Ads</title>
<script src="https://cdn.optoutadvertising.com/script/ootag.v2.min.js"></script>
<script src="https://cdn.upod.eu/client/bundle.browser.js"></script>
<style>
.ad-container {
min-height: 250px;
width: 300px;
background: white;
border-radius: 8px;
padding: 1rem;
}
.ad-container.is-hidden {
visibility: hidden;
}
.login-button {
padding: 12px 24px;
background: #4a90e2;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
}
</style>
</head>
<body>
<header>
<h1>My Website</h1>
<button
id="login-button"
class="login-button"
style="display: none;"
>
Login with Upod
</button>
<button
id="logout-button"
style="display: none;"
onclick="client.logout()"
>
Logout
</button>
</header>
<main>
<div id="content">
<!-- Your content here -->
</div>
<aside>
<div id="ad-container" class="ad-container">
<div id="YOUR_AD_SLOT_ID"></div>
</div>
</aside>
</main>
<script>
// Initialize opt-out ad server
var ootag = ootag || {};
ootag.queue = ootag.queue || [];
ootag.queue.push(function () {
ootag.initializeOo({
publisher: YOUR_PUBLISHER_ID,
alwaysNoConsent: 1,
consentTimeOutMS: 500,
noRequestsOnPageLoad: 1,
});
});
ootag.queue.push(function () {
ootag.defineSlot({
adSlot: 'YOUR_AD_SLOT_ID',
targetId: 'YOUR_AD_SLOT_ID',
emptyCallback: () => {
const adContainer =
document.getElementById('ad-container');
if (adContainer) adContainer.classList.add('is-hidden');
},
});
});
// Initialize Upod client
const client = upod.initialize({
authority: 'https://account.upod.eu',
client_id: 'your-client-id', // Obtained by contacting us
redirect_uri: location.origin + location.pathname,
post_logout_redirect_uri: location.origin + location.pathname,
language: 'en',
});
// Login button handler
document
.getElementById('login-button')
.addEventListener('click', () => {
client.login();
});
// Handle OIDC callback
client.handleCallbackIfNeeded().then(() => {
client.isAuthenticated().then(loggedIn => {
updateUI(loggedIn);
if (loggedIn) {
loadUserProfileAndAds();
} else {
showGenericAds();
}
});
});
// Listen for login
client.onLogin(() => {
updateUI(true);
loadUserProfileAndAds();
});
// Listen for logout
client.onLogout(() => {
updateUI(false);
showGenericAds();
});
function updateUI(loggedIn) {
document.getElementById('login-button').style.display = loggedIn
? 'none'
: 'block';
document.getElementById('logout-button').style.display =
loggedIn ? 'block' : 'none';
}
function loadUserProfileAndAds() {
client
.fetch({ path: '/storage/profile', method: 'get' })
.then(profile => {
const gender = profile.gender || 'unknown';
const ageRange = profile.ageRange || null;
const city = profile.residence || 'unknown';
// Pass parameters to ad server
queueOotagParameters(gender, ageRange, city);
// Personalize content
personalizeContent(profile);
// Show ad container and trigger requests
document
.getElementById('ad-container')
.classList.remove('is-hidden');
ootag.queue.push(function () {
ootag.makeRequests(true);
});
})
.catch(error => {
console.error('Failed to fetch profile:', error);
showGenericAds();
});
}
function queueOotagParameters(gender, ageRange, city) {
ootag.queue.push(function () {
ootag.addParameter('gender', gender);
ootag.addParameter('age_bracket', formatAgeRange(ageRange));
ootag.addParameter('city', city);
});
}
function formatAgeRange(ageRange) {
if (!ageRange) return 'unknown';
if (ageRange[1] === null) {
return `${ageRange[0]}+`;
}
return `${ageRange[0]}-${ageRange[1]}`;
}
function personalizeContent(profile) {
// Personalize your content based on profile
const content = document.getElementById('content');
if (profile.gender) {
content.innerHTML = `Showing content for ${profile.gender} in ${profile.residence || 'your area'}`;
}
}
function showGenericAds() {
// Show generic ads or hide ad container
document
.getElementById('ad-container')
.classList.add('is-hidden');
}
</script>
</body>
</html>Available Profile Data
The user profile contains anonymized data:
- gender:
'male','female', or'other' - ageRange:
[min, max]or[min, null]for open-ended ranges - residence: City name (e.g.,
'Amsterdam','Rotterdam')
Passing Data to Ad Servers
Different ad servers may require different parameter formats. Common approaches:
- Query parameters: Add as URL parameters to ad requests
- Custom parameters: Use the ad server's parameter API (like
ootag.addParameter()) - Header values: Some servers accept audience data in headers
Check your opt-out ad server's documentation for the correct format.
Benefits
- ✅ Privacy-compliant advertising
- ✅ Personalized ads without cookies
- ✅ Uses anonymized user data
- ✅ Works with opt-out ad networks
- ✅ Better user experience with relevant ads
Best Practices
- Always handle errors: If profile fetch fails, show generic ads
- Respect user privacy: Only use anonymized data, never personal identifiers
- Graceful degradation: Show generic content if user is not logged in
- Cache profile data: Use the client library's built-in caching (controlled by
cache_ttlsetting) - Update on logout: Clear personalized content when user logs out
Next Steps
- Contact us to register your OIDC client
- See our live opt-out advertising example
- Review the anonymized profile data documentation to understand available user data
- Integrate with Opt-Out Advertising network