Skip to main content

How to Configure Smaato Unified Bidding in Google Ad Manager (GAM)

 In-app header bidding is notoriously fragile. You spend weeks configuring Prebid Server, integrating the SDKs into Android and iOS, and setting up Line Items in Google Ad Manager (GAM), only to face the most frustrating metric in Ad Tech: High Bid Rate, Low Render Rate.

If you are integrating Smaato via Prebid (Unified Bidding), you have likely encountered a specific, silent failure mode. You see the bid win in the console logs, the GAM Line Item matches, but the ad slot remains blank or defaults to a fallback.

Often, this is due to an Adspace ID Collision. This occurs when the creative cached in Prebid Server is retrieved by a GAM creative wrapper that lacks the specific Smaato Adspace context required to render it, or when static Line Item configurations conflict with dynamic bid parameters.

This guide details the root cause of this collision and provides a production-grade implementation to resolve it using dynamic macro injection and Universal Creatives.

The Root Cause: Why Static Creatives Fail in Dynamic Auctions

To understand the fix, we must look at the data flow of an in-app header bidding auction.

  1. The Auction: The App (via Prebid SDK) calls Prebid Server. Prebid Server calls Smaato. Smaato responds with a bid and an Adspace ID (e.g., 1300293).
  2. The Cache: Prebid Server caches this bid payload and returns a UUID (e.g., 392f40-a...) and a price key (e.g., hb_pb: 1.20) to the app.
  3. The Request: The App calls GAM, passing hb_pb and hb_uuid as Key-Values.
  4. The Break: GAM selects a Line Item matching $1.20.

Here is where the architecture fails.

Most publishers configure static creatives. They create a Line Item for Smaato and paste a generic Prebid creative script. However, Smaato’s rendering logic often requires strict validation of the adspaceId against the dimension of the slot.

If you have a single GAM Line Item targeting multiple sizes (e.g., 320x50 and 300x250) to reduce bloat, but your creative code doesn't dynamically inject the specific adspaceId or cache_id associated with that exact auction instance, the render fails. The Smaato SDK (or Prebid renderer) attempts to match a generic request against a specific cached asset and rejects the "collision" of IDs.

The Fix: Dynamic Creative Construction

We will move away from static associations. Instead, we will configure GAM to treat the winning bid as a dynamic payload, injecting the Prebid Cache ID directly into the rendering logic using GAM Macros.

1. Prebid Server Stored Request Configuration

First, ensure your Stored Requests in Prebid Server are passing the correct parameters. While this is likely already set, verify that your imp object allows for flexible asset retrieval.

{
  "id": "impl_1",
  "banner": {
    "format": [
      { "w": 320, "h": 50 },
      { "w": 300, "h": 250 }
    ]
  },
  "ext": {
    "prebid": {
      "bidder": {
        "smaato": {
          "publisherId": "YOUR_PUBLISHER_ID",
          "adspaceId": "YOUR_ADSPACE_ID" // This must match the format above
        }
      }
    }
  }
}

2. Google Ad Manager Line Item Setup

You do not need separate Line Items for every Smaato Adspace ID. That is unmaintainable.

  1. Create a set of Price Priority Line Items (e.g., $0.50 to $20.00).
  2. Target standard Prebid Key-Values: hb_pbhb_source = smaato (optional but recommended for reporting).
  3. Crucially: Do not target specific sizes in the creative placeholder if you are using high-density setups. Allow the creative to handle the rendering size based on the winning bid.

3. The Universal Creative Code

This is the most critical part. We will use a standard Third-party creative in GAM, but we will wrap the Prebid Universal Creative code in a way that guarantees the hb_cache_id (UUID) is captured correctly.

Standard Prebid creatives often fail to capture the hb_cache_id if it isn't passed explicitly in the targeting map. We will force the injection using GAM's %%PATTERN:key%% macro.

Create a Native Style or Third-party creative in GAM containing the following code.

<!-- 
  Universal Prebid Creative for Smaato Unified Bidding 
  Handles dynamic cache retrieval to prevent Adspace ID collisions.
-->

<script>
  (function() {
    var win = window;
    var doc = document;
    
    // 1. Extract GAM Macros
    // %%PATTERN:hb_uuid%% pulls the specific cache ID associated with this impression
    // %%PATTERN:hb_format%% helps distinguish video vs display
    var cacheId = '%%PATTERN:hb_uuid%%'; 
    var format = '%%PATTERN:hb_format%%';
    
    // 2. Define the Prebid Universal Creative (PUC) Render Config
    var renderConfig = {
      cacheId: cacheId,
      format: format || 'banner',
      // If Smaato requires specific cache host configuration:
      cacheHost: 'prebid-server.rubiconproject.com', // Replace with your PBS host
      cachePath: '/cache',
      
      // 3. Callback for Render Failure
      onRenderFailed: function(reason) {
        console.error('Smaato/Prebid Render Failed:', reason);
        // Optional: Trigger a passback or collapse the slot
        if (win.parent && win.parent.postMessage) {
            win.parent.postMessage('collapseSlot', '*');
        }
      }
    };

    // 4. Load Prebid Universal Creative Script
    // Ideally, host this uc.js on your own CDN for version control
    var script = doc.createElement('script');
    script.async = true;
    script.type = 'text/javascript';
    script.src = 'https://cdn.jsdelivr.net/npm/prebid-universal-creative@latest/dist/creative.min.js';

    script.onload = function() {
      try {
        if (win.ucTag && typeof win.ucTag.renderAd === 'function') {
          win.ucTag.renderAd(doc, renderConfig);
        } else {
           console.error('Prebid Universal Creative library loaded but ucTag is missing.');
        }
      } catch (e) {
        console.error('Error executing Prebid render:', e);
      }
    };

    doc.head.appendChild(script);
  })();
</script>

4. Configuring the Keys in GAM

For the code above to work, GAM must be aware of the hb_uuid key.

  1. Go to Inventory > Key-values.
  2. Ensure hb_uuid is defined.
  3. Important: Make sure "Report on values" is checked or that the key is defined as dynamic so GAM accepts the variable passing from the SDK.
  4. In your Line Item targeting, you generally target hb_pb. You do not need to target hb_uuid explicitly in the Line Item, but the Key-Value must be present in the ad request sent by the mobile SDK for the %%PATTERN:hb_uuid%% macro to expand.

Deep Dive: Why This Solves the Collision

The "Collision" happens because of ambiguity. When the app requests an ad, it might send parameters for multiple Adspace IDs (e.g., a banner and an MREC on the same screen).

If you use a static creative script that attempts to look up a bid based on ad unit path or size, it might grab the wrong cached object.

By using %%PATTERN:hb_uuid%%, we bypass the logic of "guessing" which bid won.

  1. The Prebid SDK receives the bid response and stores it in the GAM request targeting parameters (kw_uuid=...).
  2. GAM selects the line item.
  3. When GAM constructs the HTML for the creative, it physically replaces %%PATTERN:hb_uuid%% with the literal string of the UUID (e.g., 392f40-a...).
  4. The JavaScript that runs in the WebView requests that exact UUID from Prebid Server's cache.
  5. The cache returns the exact Smaato payload that won, containing the correct, validated Adspace ID.

This creates a hard link between the auction winner and the rendered creative, eliminating ID collisions.

Common Pitfalls and Edge Cases

1. The "Vast" XML Trap

If you are running Smaato Video, the setup differs. %%PATTERN:hb_uuid%% works for Display. For video, you must use a VAST redirect creative in GAM. The redirect URL should be constructed dynamically: https://prebid-server.example.com/cache?uuid=%%PATTERN:hb_uuid%%

2. HTTPS/SSL Mixed Content

Android 9+ and iOS 14+ are aggressive about blocking mixed content. Ensure your Prebid Server cache host is strictly https. If Smaato returns an HTTP beacon inside their creative payload, it may be blocked by the WebView. You may need a mechanism in Prebid Server (via Stored Request config) to force secure creatives:

"ext": { "prebid": { "cache": { "bids": { "secure": 1 } } } }

3. SafeFrame Compatibility

The code snippet provided above injects a script tag. If your GAM Creative settings have "Serve into a SafeFrame" checked, ensure the Prebid Universal Creative (uc.js) is compatible with cross-origin communication. The snippet provided handles standard iframe isolation, but if you require the ad to expand (MRAID), you must uncheck SafeFrame or configure the apiframe permissions correctly in GAM.

Conclusion

Setting up Smaato Unified Bidding requires looking past standard documentation. The complexity lies not in the bid request, but in the creative rendering pathway.

By moving logic out of static Line Item configurations and into dynamic Creative Macros, you reduce Line Item count, eliminate Adspace ID collisions, and significantly increase render rates. This approach treats the creative as a dumb pipe that simply renders whatever payload the auction determined was the winner.