Your Company

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:

  1. 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_id and redirect_uri(s) for you.
  2. Have access to Opt-Out Advertising network
  3. Understand how to pass audience parameters to your ad server

Implementation Overview

The integration requires:

  1. Loading the Upod client library and Opt-Out Advertising script
  2. Initializing the ad server
  3. Initializing the Upod client
  4. Handling user authentication
  5. Fetching anonymized user profile data
  6. Passing profile data to the ad server
  7. 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:

  1. Query parameters: Add as URL parameters to ad requests
  2. Custom parameters: Use the ad server's parameter API (like ootag.addParameter())
  3. 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

  1. Always handle errors: If profile fetch fails, show generic ads
  2. Respect user privacy: Only use anonymized data, never personal identifiers
  3. Graceful degradation: Show generic content if user is not logged in
  4. Cache profile data: Use the client library's built-in caching (controlled by cache_ttl setting)
  5. Update on logout: Clear personalized content when user logs out

Next Steps