> For the complete documentation index, see [llms.txt](https://developers.panxo.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developers.panxo.ai/integrations/google-tag-manager-+-ad-manager.md).

# Google Tag Manager + Ad Manager

This guide is for publishers who serve ads through Google Ad Manager and do not run their own Prebid wrapper. If you already run Prebid.js on the page, do not use this integration; write to <tech@panxo.ai> and we will set up the standard Panxo Prebid integration (bid adapter plus RTD Module) inside your existing wrapper.

**What Panxo is.** Panxo brings new advertising demand for AI-influenced audiences: visitors who arrive at your site from assistants such as ChatGPT, Perplexity, Claude or Gemini, or who show signals of having been influenced by them. That demand is delivered through the standard Prebid stack: a bid adapter and the Panxo RTD Module, the same technology used by our publishers who run their own Prebid.

**What changes on your site.** Two things, and nothing else:

1. A Custom HTML tag in Google Tag Manager (once per container). That tag activates the Panxo RTD Module on the page.
2. A set of Price Priority line items in Google Ad Manager, created by your ad ops team following this guide (or set up by us, see Annex B).

{% hint style="success" %}
**No-risk guarantees.** Your current ads, your existing Ad Manager configuration and your page load times stay exactly the same. Panxo competes inside Ad Manager as one more demand source: it only wins a slot when its price beats the rest. Panxo never calls refresh: we do not trigger, advance or repeat any ad call; we only leave key-values ready for the ad calls your page already makes. If Panxo does not bid or does not respond, absolutely nothing happens; the page behaves as it does today.
{% endhint %}

### Prerequisites

Before starting, make sure you have:

1. **A Panxo account.** Your publisher account, with your domain added as a property. If you do not have an account yet, write to <publishers@panxo.ai>.
2. **Per-domain activation by Panxo.** Each domain has two distinct identifiers in the Panxo dashboard: the RTD Module siteId and the bid adapter propertyKey. They are different values; the RTD siteId is not the propertyKey. You do not paste either of them into Google Tag Manager (Panxo configures both server-side when the domain is activated), but confirm that both appear in your dashboard, under Integrations, before requesting activation. To activate a domain, write to <publishers@panxo.ai>.
3. **Your ads.txt lines.** The dashboard shows the exact ads.txt lines for each domain (the three DIRECT IDs are unique per domain). They go live in Step 5.

{% hint style="warning" %}
**Network currency.** The rates and buckets in this guide are defined in USD. Line item rates and price buckets are always defined in the currency of your Ad Manager network. If your network operates in USD, everything in this guide applies as-is. If your network operates in any other currency, contact Panxo (<tech@panxo.ai>) before creating any line items: we adapt the buckets and send you the equivalent table in your currency. Do not create line items with the USD values on a network that does not operate in USD.
{% endhint %}

#### Consent (GDPR territories)

The Panxo RTD Module applies geo-scoped consent gating: consent requirements are enforced according to the visitor's territory.

* **Domains with traffic from GDPR territories.** Panxo enables Prebid's consentManagement module (TCF 2.x) server-side for that domain; the Google Tag Manager tag does not change. Panxo is registered with the IAB TCF under **GVL ID 1527**: add vendor 1527 to your CMP's vendor list so that consent for Panxo is collected and passed correctly. Without vendor 1527 in the CMP, TCF consent for Panxo is never granted and Panxo does not bid in those territories.
* **Domains outside GDPR territories** (for example LatAm or the United States) need no CMP for this integration. US privacy is handled separately through GPP/USP passthrough; it requires no configuration on your side.

### How it works

The six steps, in ad ops language:

1. **Activation.** The Google Tag Manager tag activates the Panxo RTD Module on the page. Loading is asynchronous and does not interfere with the loading of your ads.
2. **Detection.** The RTD Module detects whether the visit is AI-influenced. Panxo only participates on those visits.
3. **Parallel auction.** Panxo requests prices from its own demand while the page keeps working normally. No existing ad call is blocked, delayed or conditioned.
4. **Key-values.** The auction result is translated into key-values (`hb_pb`, `hb_adid`, `hb_bidder`, `hb_size`) that are applied to the Google Ad Manager slots. The next ad call your page fires (the first load if the auction arrived in time, a lazy-load or a refresh cycle) carries them automatically.
5. **Competition in Ad Manager.** The Panxo line item matching the bid price competes in Price Priority against the rest of your demand. It wins only if Panxo's price is higher.
6. **Render.** If the Panxo line item wins, the universal creative renders the Panxo ad in that slot. If it does not win, or Panxo did not bid, exactly what would have been served without Panxo is served.

### Step 1: the tag in Google Tag Manager

Create a **Custom HTML** tag with this content, exactly as it appears here:

```html
<script>
  (function () {
    var s = document.createElement('script');
    s.async = true;
    s.src = 'https://assets.panxostack.com/prebid/gtm-loader.js';
    document.head.appendChild(s);
  })();
</script>
```

Tag configuration:

* **Type:** Custom HTML.
* **Trigger:** All Pages.
* **Priorities or sequencing:** none. There is no need to configure firing priority or ordering relative to other tags.

The tag is **identical for every domain and every container**. Nothing is customized per domain: the tag carries no identifiers, and Panxo resolves your domain's configuration server-side.

**Domain activation is controlled server-side by Panxo.** Pasting the tag in the container of a domain that has not been activated yet is safe and inert: the RTD Module does nothing on that domain until Panxo activates it, and in that state it generates no calls or activity of any kind on the page. To activate a domain, write to <publishers@panxo.ai>.

### Step 2: key-values in Ad Manager

In Google Ad Manager, go to **Inventory > Key-values** and create these four keys, all of type **dynamic**:

```
hb_pb
hb_adid
hb_bidder
hb_size
```

| Key-value   | Type    | Purpose                                                          |
| ----------- | ------- | ---------------------------------------------------------------- |
| `hb_pb`     | Dynamic | Bid price (bucket). This is the targeting key for the line items |
| `hb_adid`   | Dynamic | Identifier of the winning ad; the creative uses it to render     |
| `hb_bidder` | Dynamic | Identifies Panxo demand (`panxo`) in targeting                   |
| `hb_size`   | Dynamic | Size of the winning ad; the creative uses it                     |

{% hint style="warning" %}
**Do not create `hb_format` or `hb_env`.** The creative in Step 4 also references `hb_format` and `hb_env`. Those two **must not be created** as key-values: they are standard creative macros that resolve empty for display without affecting the render.
{% endhint %}

### Step 3: order and line items

The rates below are in USD. If your network does not operate in USD, stop here and contact Panxo first (see the network currency note in Prerequisites).

If you operate several domains on the same Ad Manager network, a single set of line items serves all of them. If a domain uses a different network, the same set is replicated on that network.

#### Structure

Create an advertiser of type **Ad network**, named:

```
Panxo
```

Under it, create an order named:

```
Panxo Header Bidding
```

* **Line items:** type **Price Priority**, **no end date**, **unlimited** delivery. One per price bucket; each line item's rate is the bucket value.
* **Targeting per line item:** `hb_pb` = the bucket value **AND** `hb_bidder` = `panxo` (both conditions at once). Including `hb_bidder=panxo` guarantees these line items only capture Panxo demand.
* **Inventory:** run of network (the whole network).

#### The per-line-item pattern (explained once)

All 25 line items follow the same pattern; only the bucket value changes from row to row. Replace `X.XX` with the bucket value from the table below.

Line item name:

```
Panxo HB X.XX
```

Rate (CPM):

```
X.XX
```

Targeting (both conditions at once):

```
hb_pb = X.XX AND hb_bidder = panxo
```

The `hb_bidder` targeting value is always:

```
panxo
```

#### Recommended creation flow

Create the first line item in full (for example the 0.50 one), verify targeting, rate, type and delivery, and from there **duplicate and adjust**: in each copy only the name, the rate and the `hb_pb` value change. Annex B describes how to automate the creation of the 25 line items.

#### Full bucket table (25 line items)

All with type Price Priority, no end date, unlimited delivery, run of network inventory and targeting `hb_pb` = table value **AND** `hb_bidder` = `panxo`.

| Line item (suggested name) | `hb_pb` targeting | Rate (CPM) |
| -------------------------- | ----------------- | ---------- |
| Panxo HB 0.50              | 0.50              | 0.50       |
| Panxo HB 1.00              | 1.00              | 1.00       |
| Panxo HB 1.50              | 1.50              | 1.50       |
| Panxo HB 2.00              | 2.00              | 2.00       |
| Panxo HB 2.50              | 2.50              | 2.50       |
| Panxo HB 3.00              | 3.00              | 3.00       |
| Panxo HB 3.50              | 3.50              | 3.50       |
| Panxo HB 4.00              | 4.00              | 4.00       |
| Panxo HB 4.50              | 4.50              | 4.50       |
| Panxo HB 5.00              | 5.00              | 5.00       |
| Panxo HB 5.50              | 5.50              | 5.50       |
| Panxo HB 6.00              | 6.00              | 6.00       |
| Panxo HB 6.50              | 6.50              | 6.50       |
| Panxo HB 7.00              | 7.00              | 7.00       |
| Panxo HB 7.50              | 7.50              | 7.50       |
| Panxo HB 8.00              | 8.00              | 8.00       |
| Panxo HB 8.50              | 8.50              | 8.50       |
| Panxo HB 9.00              | 9.00              | 9.00       |
| Panxo HB 9.50              | 9.50              | 9.50       |
| Panxo HB 10.00             | 10.00             | 10.00      |
| Panxo HB 11.00             | 11.00             | 11.00      |
| Panxo HB 12.00             | 12.00             | 12.00      |
| Panxo HB 13.00             | 13.00             | 13.00      |
| Panxo HB 14.00             | 14.00             | 14.00      |
| Panxo HB 15.00             | 15.00             | 15.00      |

In total: 20 line items from 0.50 to 10.00 in 0.50 steps, plus 5 line items from 11.00 to 15.00 in 1.00 steps.

**Note on bucket boundaries:** bids between 10.00 and 10.99 fall into the 10.00 bucket (which is why there are no intermediate buckets in that range), and a bid of 15.00 or more emits 15.00. Below 0.50 there is no row: the bid is safely lost (it emits hb\_pb=0.00, a value no line item targets, and your current demand serves as usual). In practice this does not happen, because Panxo's effective floor is $3.

#### Note for the finance team

The line item rate **only decides competition inside Ad Manager**: it is the price with which the Panxo bid competes against the rest of your demand. The actual payment is calculated from Panxo reporting and settled on NET-45 terms, as with any Prebid partner. The revenue Ad Manager reports for these line items **is not the invoice amount**; the billing figure always comes from Panxo reporting.

### Step 4: the creative

Create **one creative of type Third party (Custom creative)** with this code, exactly as it appears here:

```html
<script src="https://cdn.jsdelivr.net/npm/prebid-universal-creative@1.17.2/dist/creative.js"></script>
<!-- Version pinned on purpose: this runs inside the partner's live GAM creatives; @latest could break renders without warning. Bump deliberately. -->
<script>
  var ucTagData = {};
  ucTagData.adServerDomain = "";
  ucTagData.pubUrl = "%%PATTERN:url%%";
  ucTagData.adId = "%%PATTERN:hb_adid%%";
  ucTagData.mediaType = "%%PATTERN:hb_format%%";
  ucTagData.env = "%%PATTERN:hb_env%%";
  ucTagData.size = "%%PATTERN:hb_size%%";
  ucTagData.hbPb = "%%PATTERN:hb_pb%%";
  try { ucTag.renderAd(document, ucTagData); } catch (e) {}
</script>
```

This is Prebid's standard universal creative. The version is pinned on purpose (the comment in the code explains it): this code runs inside your production creatives, and a floating version could break renders without warning.

Configuration:

* **Declared size:** 1x1, with **size overrides** for the eight sizes below.
* **Association:** the creative is associated with **all** the line items from Step 3, with **5 instances per line item** (5 copies of the same creative associated with each line item).

Size overrides:

```
300x250, 728x90, 320x50, 320x100, 300x600, 160x600, 970x250, 970x90
```

**Why 5 instances:** pages often request several slots in a single Ad Manager call (single request). In that mode, the same price bucket can win several slots in the same call (for example, several 300x250s on a homepage). Each simultaneous win needs its own creative instance; with only one, the additional slots would silently fall through to lower-priority demand.

### Step 5: ads.txt

Add these lines to the `ads.txt` file of **each domain**:

```
#PanxoAI
panxo.ai, YOUR_PANXO_ID, DIRECT
bidgx.com, YOUR_BIDGX_ID, DIRECT
kntxy.com, YOUR_KNXTY_ID, DIRECT
rubiconproject.com, 27034, RESELLER, 0bfd66d529a55807
themediagrid.com, FNG4LA, RESELLER, 35d5010d7789b49d
pubmatic.com, 166733, RESELLER, 5d62403b186f2ace
smartadserver.com, 5397, RESELLER, 060d053dcf45cbf3
xandr.com, 17781, RESELLER
```

* The IDs on the first three lines (DIRECT) are **unique per domain**; replace the placeholders with the values shown in your Panxo dashboard, under Integrations, for that domain. The RESELLER lines are fixed and go in as-is on every domain.
* **Verification is automatic:** our system checks ads.txt files daily. As soon as it detects the lines on a domain, monetization for that domain activates on its own. There is nothing to notify us about and nothing else to do.

### Step 6: go-live verification (checklist)

With the tag published, the line items and the creative created and the ads.txt in place, verify in this order:

1. **GTM Preview.** In the container's Preview mode, confirm that the Custom HTML tag fires on All Pages without errors.
2. **Console with `?pbjs_debug=true`.** Open any page of the activated domain adding `?pbjs_debug=true` to the URL and check the browser console. What to look for: messages with the `[panxo-gtm]` prefix (RTD Module activity) and the standard Prebid log showing the auctions and the bids received from the `panxo` bidder on AI-influenced visits.
3. **Google Publisher Console.** Open the Google Publisher Console (for example adding `?google_console=1` to the URL and pressing Ctrl+F10, or running `googletag.openConsole()` in the browser console) and check that the slots show the `hb_pb`, `hb_adid`, `hb_bidder` and `hb_size` key-values when there was a bid.
4. **Request to securepubads.** In the browser's Network tab, locate the call to `securepubads.g.doubleclick.net` and confirm that the `prev_scp` parameter contains `hb_pb=` (and `hb_bidder=panxo`) for the slots with a bid. That parameter carries the per-slot targeting: if `hb_pb` appears there, the key-values are reaching Ad Manager.
5. **Delivery in Ad Manager.** On the line item matching the observed bucket, use delivery diagnostics (Delivery diagnostics / Troubleshoot) to confirm that the line item receives impressions and competes. Remember that volume will be low at first: only AI-influenced traffic participates.

### Frequently asked questions

**Fill on the Panxo line items is low. Is that normal?**

Yes, and it is the expected behavior. Panxo does not bid on all traffic: we only bid on AI-influenced visits, typically 5-25% of total visits depending on each site's profile. The rest of the impressions keeps going entirely to your current demand. Panxo's value is in the price of those visits, not in the volume.

**What happens if Panxo does not respond or goes down?**

Nothing, zero impact. If Panxo does not bid, does not respond in time or the service fails completely, no key-values are applied and Ad Manager serves exactly what it would have served without Panxo. There is no scenario in which a Panxo failure affects the delivery of your ads.

**Does it affect page load speed or performance?**

No. The Panxo RTD Module loads asynchronously, the auction runs in parallel and never blocks or delays your ads. In addition, Panxo never calls refresh: we do not trigger, advance or repeat any ad call; we only leave key-values ready for the ad calls your page already makes.

**How is the integration turned off if needed?**

With a notice to Panxo (<tech@panxo.ai>). Deactivation happens on our side, server-side, and takes effect in under 5 minutes, without touching Google Tag Manager or Ad Manager. The tag can stay published: with the domain deactivated it is completely inert.

### Annex A: first-impression coverage (optional, phase 2)

By design, in phase 1 Panxo never delays anything: if the auction does not arrive before the first ad call, the Panxo bid competes starting with the next refresh or lazy-load. For first-impression slots there is an optional phase 2 that is **not a go-live requirement** and requires coordination with whoever controls the first ad call on your pages (your ad management vendor or your own setup).

The RTD Module exposes `window.panxo.firstAuction`: a promise that **always resolves, at most at 800 ms, no matter what** (a completed auction, a timeout or any error all resolve it; it never hangs). Your first ad call can wait for it:

```js
// Before firing the first ad batch:
var waitForPanxo = (window.panxo && window.panxo.firstAuction) || Promise.resolve();
waitForPanxo.then(function () {
  launchFirstAdBatch(); // your existing function that fires the first ad call
});
```

The result is **first-impression coverage bounded by the 800 ms budget**: when the auction arrives within the budget, the first impression already competes with the Panxo bid; when the connection is slow and it does not arrive, the promise resolves at the deadline and that page behaves as in phase 1. The worst possible case is a single wait of 800 ms before the first ad batch; there are never additional waits.

If you want to activate this phase, write to us and we coordinate the details.

### Annex B: line item automation

Creating 25 line items by hand is perfectly viable with the duplicate-and-adjust flow from Step 3, but there are two alternatives:

1. **Prebid's Line Item Manager.** Open source tool from the Prebid project that automatically generates the line items and the creatives via the Google Ad Manager API: <https://github.com/prebid/line-item-manager>. It is configured with the buckets from the Step 3 table and creates the whole set in minutes.
2. **We set it up for you.** If you grant us trafficker access to your network (or Ad Manager API credentials), our team creates the order, the 25 line items and the creatives, and leaves everything ready for your review and approval. It is the fastest path and has no cost for you.

### Contact

* **Technical support and integration:** <tech@panxo.ai>
* **Partnerships and commercial matters:** <publishers@panxo.ai>
