Brand Store (Web)

A button on the product detail page that redirects users to the brand's store

Brand Store (Web)

A button on the OOP (product detail page) displayed below the product that redirects users directly to the brand's official store page.

What Is Brand Store?

Brand Store is a sponsored button that appears on product detail pages. When a user clicks it, they are redirected to the brand's (manufacturer's) official store where they can purchase the product directly.

Key concept: The ad server tells you IF a campaign is active for a given brand. Your shop decides WHETHER to display the button based on your own business logic.

How It Works

What the Ad Server Provides

Based on the manufacturer_id you pass, the ad server checks if there is an active campaign for this brand (manufacturer). If yes, it returns:

  • Confirmation that a sponsored ad exists
  • Click tracking URL for revenue attribution
  • Brand logo URL in the image field of the creative

Important: The ad server provides tracking information and the brand logo URL. Price and store URL still come from YOUR shop system.

What Your Shop Must Check

Before displaying the button, your shop should verify:

  1. Brand has a store page - Does this manufacturer have a brand shop in your system?
  2. Product offer exists - Does the brand's store have an offer for the current product?
  3. Price is competitive - Is the brand's offer cheaper than (or equal to) the current best price?

Display logic: Only show the Brand Store button if all conditions are met: active campaign (from ad server) + brand shop exists + competitive offer (from your shop).

Data Sources

SourceData
Ad ServerWhether campaign is active, click tracking URL, brand logo URL
Your ShopBrand shop URL, price, shop name, offer availability

Data Flow

  1. Pass manufacturer_id to ad server via fetchNativeAd() or bidder request
  2. Ad server returns tracking info IF campaign is active for this brand
  3. Your shop checks: Does brand have a store? Is offer available and competitive?
  4. If all conditions met → render button with your shop data + ad server tracking

Integration Flow Diagram

sequenceDiagram
    participant User as Shop User
    participant Page as Shop Page
    participant ShopAPI as Shop API/Database
    participant DAS as Ring DAS

    User->>Page: 1. Request product page
    Page->>DAS: 2. Fetch Ad (fetchNativeAd / bidder)
    DAS-->>Page: Response with tracking URLs (if campaign active)

    Note over Page: 3. Check if ad exists

    Page->>ShopAPI: 4. Fetch brand store data
    ShopAPI-->>Page: Brand shop URL, price, availability

    Note over Page: 5. Verify conditions:<br/>- Brand shop exists?<br/>- Offer available?<br/>- Price competitive?<br/>(Logo comes from ad response)

    alt All conditions met
        Page->>User: 6. Display Brand Store button
        Note over Page: Fire impression tracking
        User->>Page: 7. Click button
        Page->>DAS: Fire click tracking
        Page->>User: Redirect to brand store
    else Conditions not met
        Note over Page: Hide ad slot
    end

Live Demo

See the working implementation:


Frontend Integration (CSR)

Step 1: Load the SDK

Configure the SDK with your Ring DAS credentials. You will receive these values from your Ring DAS account manager.

ParameterDescription
targetSite and area identifier (see details below)
tidYour Ring DAS tenant ID (format: EA-XXXXXXX)
dsainfoSet to 1 to enable DSA (Digital Services Act) transparency info in responses

Target format: site/area

  • site - Your website identifier assigned by Ring DAS, UPPERCASE (e.g., DEMO_PAGE)
  • area - Page type context where the ad appears, UPPERCASE: HOMEPAGE, PRODUCT_DETAIL, LISTING, SEARCH
  • Mobile prefix - For mobile pages, add M_ prefix to site (e.g., M_DEMO_PAGE/PRODUCT_DETAIL)

Examples:

  • Desktop product page: DEMO_PAGE/PRODUCT_DETAIL
  • Mobile product page: M_DEMO_PAGE/PRODUCT_DETAIL
  • Desktop homepage: DEMO_PAGE/HOMEPAGE
<script>
    dlApi = {
        target: "DEMO_PAGE/PRODUCT_DETAIL", // Format: SITE/AREA. For mobile: M_SITE/AREA
        tid: 'EA-<<NETWORK_ID>>',        // your tenant ID - provided by Ring DAS
        dsainfo: 1,               // enable DSA transparency info
        cmd: []                   // command queue
    };
</script>
<!-- SDK URL - provided by your Ring DAS account manager -->
<script src="https://<<DAS_CDN>>/<<NETWORK_ID>>/build/dlApi/minit.boot.min.js" async></script>

The SDK script URL (minit.boot.min.js) may differ depending on your integration. Your Ring DAS account manager will provide the correct URL for your setup.

Step 2: Configure Consent

As soon as possible after embedding the script, you should pass consent information. This determines whether personalized ads are served and controls the script's behavior.

dlApi.cmd.push(function (dlApi) {
    dlApi.consent({npa: 0});  // 0 = consent given, 1 = no consent
});
ParameterValueDescription
npa0Consent given - personalized ads enabled
npa1No consent - non-personalized ads only

Step 3: Set Targeting Context

Set the manufacturer ID BEFORE fetching ads. This determines which brand's ad to display.

Key-ValueDescription
manufacturer_idBrand/manufacturer ID - determines which brand's ad to display
IPPage View ID — constant identifier for the current page load
IVPage View Unique ID — changes on every SPA view transition; equals IP on traditional pages
TABIDBrowser Tab Identifier — stable ID per browser tab session
dlApi.cmd.push(function (dlApi) {
    dlApi.addKeyValue('manufacturer_id', '12345678'); // determines which brand's ad to display
    dlApi.addKeyValue('IP', '202603041502158687149647');   // page view ID
    dlApi.addKeyValue('IV', '202603041502158687149647');   // page view unique ID (changes on SPA transitions)
    dlApi.addKeyValue('TABID', 'tab-7f3e2a');              // browser tab identifier
});

Why this matters: The manufacturer_id tells the ad server which brand's campaign to check. For additional targeting options like category_ids and main_category_id, see Set Targeting Context.

Step 4: Fetch the Ad

ParameterDescription
slotFixed value product-button for this format
divContainer element ID - required for viewability measurement
tplCodeFixed value 1746213/Banner-Standard for Brand Store
asyncRenderSet to true to manually control when impression is counted
dlApi.cmd.push(function (dlApi) {
    dlApi.fetchNativeAd({
        slot: 'product-button',               // fixed value for Brand Store
        div: 'ad-brand-store',                // container ID for viewability tracking
        tplCode: '1746213/Banner-Standard',   // fixed value for Brand Store
        asyncRender: true                     // manually count impression with ad.render()
    }).then(async function (ad) {
        if (!ad) {
            console.log('No ad available');
            return;
        }

        // Ad exists - NOW fetch your shop data
        // The ad server only provides tracking - YOU provide display data
        const shopData = await fetchShopDataFromYourSystem('12345678');

        // Render with YOUR shop data + ad tracking
        renderBrandStoreButton(ad, shopData);
    }).catch(function (err) {
        console.error('Ad could not be loaded:', err);
    });

    // Trigger ad fetch
    dlApi.fetch();
});

Implementation note: The onclick handler shown below is an example. Your shop is responsible for implementing click tracking - simply fire a tracking pixel on product click using the constructed tracking URL.

Example code: The rendering function below is provided as a documentation example. Adapt it to your shop's templating system and styling requirements.

Step 5: Fetch Your Shop Data

The ad server tells you IF an ad exists and provides the brand logo. You must fetch price and URL data from your own system.

/**
 * Fetch brand/product data from YOUR shop system
 * @param {string} manufacturerId - Brand identifier used in targeting
 * @returns {Promise<Object>} - Shop data for rendering
 */
async function fetchShopDataFromYourSystem(manufacturerId) {
    // Replace with your actual product/brand API
    const brandInfo = await yourBrandAPI.get(manufacturerId);

    return {
        productUrl: brandInfo.storeUrl,       // URL to brand's store page
        shopName: brandInfo.name,             // Brand display name
        price: brandInfo.currentOffer.price   // Current price/offer
        // Note: brand logo comes from ad.fields?.image (AD SERVER)
    };
}

Your responsibility: The function above is a template. Replace yourBrandAPI.get() with your actual database query or API call.

Step 6: Render with Your Shop Data

/**
 * Render the Brand Store button
 * @param {Object} ad - Ad response from Ring DAS (provides tracking + brand logo)
 * @param {Object} shopData - Your shop data (provides price and store URL)
 */
function renderBrandStoreButton(ad, shopData) {
    // ─────────────────────────────────────────────────────────────
    // DATA FROM AD SERVER (tracking + DSA info + brand logo)
    // ─────────────────────────────────────────────────────────────
    const clickUrl = ad.meta.adclick;
    const dsaInfo = ad.dsa;  // { behalf, paid, adrender }
    const logoUrl = ad.fields?.image;  // Brand logo from AD SERVER

    // ─────────────────────────────────────────────────────────────
    // DATA FROM YOUR SHOP (display info)
    // shopData comes from fetchShopDataFromYourSystem()
    // ─────────────────────────────────────────────────────────────
    // shopData.productUrl - URL to brand's store page
    // shopData.shopName   - Brand display name
    // shopData.price      - Current price/offer

    // Count impressions manually
    ad.render();

    // Render button combining SHOP data (price/URL) + AD SERVER data (tracking + logo)
    const container = document.getElementById('ad-brand-store');
    container.innerHTML =
        '<span class="ad-label">Ad</span>' +
        '<a href="' + shopData.productUrl + '" ' +          // ← YOUR SHOP
           'target="_blank" ' +
           'rel="noopener nofollow sponsored" ' +
           'onclick="new Image().src=\'' + clickUrl + '\';">' +  // ← AD SERVER
            'At <img src="' + logoUrl + '" alt="' + shopData.shopName + '"> ' +  // ← AD SERVER (logo)
            'for ' + shopData.price +                        // ← YOUR SHOP
        '</a>';
}

Backend Integration (SSR)

For server-side rendering, fetch ad data from the bidder before rendering the page.

Required: The SDK from Step 1 (Frontend) must still be loaded on the page for viewability tracking via registerBidResponse().

Step 1: Fetch Ad from Bidder

Make a GET request to the Ring DAS bidder endpoint. The bidder URL will be provided by your Ring DAS account manager.

Request Configuration

ParameterDescription
BIDDER_URLBidder endpoint URL - provided by your Ring DAS account manager
NETWORK_IDYour Ring DAS network ID (without EA- prefix)
tmaxRequest timeout in milliseconds
ext.srcOptional. Set to s2s for server-to-server requests
ext.is_non_prebid_requestSet to true to receive the response in the correct server-to-server format (not Prebid.js format)

Placement Parameters

These parameters define WHERE the ad appears and WHICH format to serve.

ParameterDescription
site.idYour site identifier in Ring DAS
site.ext.areaPage type context, UPPERCASE: HOMEPAGE, PRODUCT_DETAIL, LISTING, SEARCH
imp[].tagidFixed value product-button for this format

Targeting Parameters

ParameterDescription
ext.keyvalues.manufacturer_idManufacturer identifier for targeting
ext.keyvalues.main_category_idMain product category ID — primary category for ad matching and reporting
ext.keyvalues.page_product_idProduct ID currently viewed — used for reporting purposes
ext.keyvalues.page_product_nameProduct name currently viewed — used for reporting purposes
ext.keyvalues.IPPage View ID — constant for the lifetime of a page load. See API Parameters Reference
ext.keyvalues.IVPage View Unique ID — changes on every SPA view transition; equals IP on traditional pages. See API Parameters Reference
ext.keyvalues.TABIDBrowser Tab Identifier — stable ID per browser tab session. See API Parameters Reference
ext.keyvalues.ab_variantA/B test variant identifier — string identifying which experiment variant the user is in (optional)

Consent & Privacy Parameters

ParameterDescription
user.ext.npaConsent flag: false = consent given, true = no consent
regs.gdprGDPR applies flag: 1 = GDPR applies, 0 = does not apply
regs.gppGPP consent string (optional) - TCF-compliant consent string from your CMP
regs.ext.dsaSet to 1 to request DSA transparency info in response

Consent handling: You can pass consent via user.ext.npa (simple flag) OR via regs.gpp (TCF-compliant string). If you have a TCF-compliant CMP, use the GPP string for more granular consent information.

User Identifier

Important for backend integration: Pass a user identifier consistently per user. This enables frequency capping, personalization, and other targeting features. Without an identifier, each request is treated as a new user.

ParameterDescription
user.eidsOpenRTB Extended Identity Array — pass user identifiers with per-ID GDPR consent metadata. Supports session-level (atype: 500, no consent required) and device-level (atype: 1, consent required) identifiers. See API Parameters Reference for full field reference.
user.ext.ids.luLocal user ID from the ea_uuid cookie on your shop's domain (set by Ring DAS). Must follow the alphanumeric format (e.g., 202408221036415499301131).

User identifiers:

  • user.eids — Pass your own session and/or tracking identifiers with GDPR consent metadata. Provide your session ID as atype: 500 (no consent required) and your device/tracking ID as atype: 1 (consent required).
  • user.ext.ids.lu — The value from the ea_uuid cookie set by Ring DAS. Used as Ring DAS's internal session identifier.

Both can be sent simultaneously. See API Parameters Reference for the full user.eids field reference.

// Node.js backend
const NETWORK_ID = `${NETWORK_ID}`;  // your Ring DAS network ID
const BIDDER_URL = `https://das.idealo.com/${NETWORK_ID}/bid`;  // provided by your account manager

// User identifiers — replace with actual values from your system
const SESSION_ID = 'your-session-id';      // session-level identifier (no GDPR consent required)
const TRACKING_ID = 'your-tracking-id';    // device-level tracking identifier (GDPR consent required)

const requestBody = {
    id: Math.random().toString(16).substring(2, 15),
    imp: [{
        id: 'imp-1',
        tagid: 'product-button',                    // fixed value for Brand Store
        secure: 1,
        native: { request: "{}" }
    }],
    site: {
        id: 'DEMO_PAGE',                            // your site ID (UPPERCASE)
        ext: {
            area: 'PRODUCT_DETAIL'                  // page type (UPPERCASE): HOMEPAGE, PRODUCT_DETAIL, LISTING, SEARCH
        }

    },
    user: {
        eids: [{
            source: 'your-domain.com',
            inserter: 'your-domain.com',
            uids: [
                {
                    id: SESSION_ID,
                    atype: 500,
                    ext: { id_type: 'session', consent_required: false }
                },
                {
                    id: TRACKING_ID,
                    atype: 1,
                    ext: { id_type: 'tracking', consent_required: true }
                }
            ]
        }],
        ext: {
            npa: false,                             // false = consent given, true = no consent
            ids: {
                lu: '202408221036415499301131'      // Ring DAS internal session ID (from ea_uuid cookie)
            }
        }
    },
    ext: {
        network: NETWORK_ID,
        keyvalues: {
            manufacturer_id: '12345678',            // manufacturer ID for targeting
            main_category_id: 101,                  // main product category ID
            page_product_id: '987654',              // product ID for reporting
            page_product_name: 'Example Product Name', // product name for reporting
            IP: '202603041502158687149647',          // page view ID
            IV: '202603041502158687149647',          // page view unique ID
            TABID: 'tab-7f3e2a',                    // browser tab identifier
            ab_variant: 'variant-b',                // optional: A/B test variant identifier
        },
        is_non_prebid_request: true,
        src: 's2s'                                  // optional: indicates server-to-server request
    },
    regs: {
        gdpr: 1,                                    // 1 = GDPR applies, 0 = does not apply
        gpp: 'YOUR_GPP_STRING',                     // optional: TCF-compliant consent string from CMP
        ext: {
            dsa: 1                                  // request DSA transparency info
        }
    },
    tmax: 1000                                      // request timeout in milliseconds
};

// Send as GET request with body in data= parameter
const encodedData = encodeURIComponent(JSON.stringify(requestBody));
const response = await fetch(`${BIDDER_URL}?data=${encodedData}`);

// Check for no ad available (204 No Content)
if (response.status === 204) {
    // No ad available for this request
    console.log('No ad available');
}

const bidResponse = await response.json();

No ad available: When no ad matches the request criteria, the server returns HTTP 204 No Content with an empty response body. Always check the status code before parsing JSON.

Step 2: Parse Response

const bid = bidResponse?.seatbid?.[0]?.bid?.[0];
if (!bid) {
    // Fallback check - no bid in response
    return null;
}

// Parse adm (it's a JSON string)
const adm = JSON.parse(bid.adm);
// Construct click tracking URL (from AD SERVER)
const clickUrl = adm.meta.adclick;
// DSA transparency info (from AD SERVER, requires regs.ext.dsa: 1 in request)
const dsaInfo = bid.ext?.dsa;  // { behalf, paid, adrender }

Step 2.5: Fetch Your Shop Data

Before rendering, fetch the product/brand details from your database:

// ─────────────────────────────────────────────────────────────
// FETCH DATA FROM YOUR SHOP DATABASE
// The ad server provides tracking + brand logo - YOU provide price and URL
// ─────────────────────────────────────────────────────────────
const shopData = await db.brands.findOne({
    where: { manufacturerId: '12345678' },
    select: {
        storeUrl: true,
        name: true,
        currentPrice: true
        // Note: brand logo comes from adm.fields?.image (AD SERVER)
    }
});

// Now you have both data sources:
// - clickUrl, dsaInfo, adm.fields?.image (from AD SERVER - tracking + logo)
// - shopData (from YOUR DATABASE - price and store URL)

Your responsibility: Replace the database query above with your actual ORM or API call. The ad server does NOT provide price or store URL — your shop provides those. The brand logo comes from adm.fields?.image.

Step 3: Render HTML + Register Response

The href links to your shop's product page. The ad tracking is attached only to onclick - it fires a tracking pixel without affecting navigation.

<!-- Render with AD SERVER logo + YOUR shop price/URL -->
<div id="ad-brand-store">
    <span class="ad-label">Ad</span>
    <a href="<%= shopData.productUrl %>"
       onclick="new Image().src='<%= clickUrl %>';">
        At <img src="<%= adm.fields?.image %>"> for <%= shopData.price %>
    </a>
</div>

<!-- Register for viewability and impression tracking -->
<script>
    dlApi.cmd.push(function () {
        // Arguments: raw bid response object, container element ID
        dlApi.registerBidResponse(<%- JSON.stringify(adm) %>, 'ad-brand-store');
    });
</script>
⚠️

registerBidResponse() counts impressions. Only call it when the ad is rendered — never for empty slots. For multi-slot patterns, see Register Bid Response.


What the Ad Server Returns

FieldDescription
ad.meta.adclick / adm.meta.adclickClick tracking base URL
ad.meta.adid / adm.meta.adidAd identifier
ad.fields?.image / adm.fields?.imageBrand logo URL — use directly to display the brand logo
ad.dsa.behalf / bid.ext?.dsa.behalfAdvertiser name (DSA compliance, requires dsainfo: 1)
ad.dsa.paid / bid.ext?.dsa.paidEntity that paid for the ad (DSA compliance)
ad.dsa.adrender / bid.ext?.dsa.adrenderAd render information (DSA compliance)

The ad server does NOT return price or store URLs. Your shop provides those. The brand logo comes from ad.fields?.image (frontend) or adm.fields?.image (backend).


Preview & Test Your Ads

Ad Preview & Debug Mode serves two distinct audiences — developers integrating the format and business stakeholders managing live campaigns.

For developers: Test backend integrations and verify ad rendering without running live campaigns. Use query string overrides (test_site, test_area, test_kwrd) to simulate different targeting contexts and adbeta to force a specific creative from any line item.

For advertisers and campaign managers: Preview a specific creative on a real publisher page before campaign launch — or verify it after changes. No development tools or server access required. Share a single URL (with the right query parameters) and anyone opening that link sees the exact creative that will run in production.

See Ad Preview & Debug Mode for the full parameter reference, backend implementation guide, and end-to-end tracking with X-ADP-EVENT-TRACK-ID.


Caching Considerations

🚧

Important: Improper caching can significantly reduce ad revenue and break tracking.

Do NOT cache:

  • Tracking pixels - Caching impression/click pixels prevents accurate counting and drastically reduces revenue
  • API responses - Caching bid responses prevents real-time optimization, budget pacing, and frequency capping

Safe to cache:

  • Static assets (CSS, JS, images from your shop)
  • Product data from your own database

Related


What’s Next

Learn how to integrate the Branded Products format