> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apostra.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Set up TMP with Prebid

> Read your Prebid.js orgId with get on the seller, install the Apostra module in Prebid.js or Prebid Server, wire the targeting keys into Google Ad Manager, and confirm real-time targeting is serving on your inventory.

## Overview

This is the install path for publishers who run **Google Ad Manager with
Prebid** — the established way to connect a storefront to
[real-time targeting (TMP)](https://docs.apostra.com/v2/storefront/tmp/overview.md). You add Apostra
module to your existing Prebid setup; on each ad request it calls the hosted
TMP Router and returns targeting key-values that your GAM line items match
against. No new infrastructure, no change to how you sell — the module
enriches the requests you already serve.

Installing takes three steps: add the module with your credentials, make sure
the targeting keys reach GAM, and verify end-to-end. Most publishers complete
the page-side work in an afternoon.

> [!NOTE]
>
> Run your own ad stack or sales agent instead of GAM/Prebid? That's the
> self-hosted router path — see the
> [TMP overview](https://docs.apostra.com/v2/storefront/tmp/overview.md#how-you-connect--one-router-two-ways)
> and talk to Apostra about onboarding.

## Before you start

* **You run Prebid.js 10.9.0 or later.** Apostra RTD provider ships in
  the Prebid.js tree from 10.9.0; any version before that (10.8.x and
  below, including all of 9.x) does not include it and cannot build it.
  If you're on an older wrapper, plan the Prebid upgrade first.
* **Your storefront is set up** with your GAM connected as an inventory
  source (see [inventory sources](https://docs.apostra.com/v3/seller-connect-ad-server.md)).
* **You have your Apostra credentials.** Prebid.js needs your **org ID**;
  Prebid Server needs an **API key**.
  * **Prebid.js `orgId`**: self-serve. [Read it](https://docs.apostra.com/v3/seller-prebid-setup.md#read-your-prebidjs-orgid)
    from your Seller Account with `get`. It's a URL specific to your
    storefront; use exactly the string Apostra returns.
  * **Prebid Server `auth_key`**: issued by Apostra during TMP onboarding —
    ask Apostra in chat, or your Apostra team, to get it issued.
* **You know who runs your Prebid.** If a wrapper vendor manages your header
  bidding, the install below is theirs to make — send them this page and your
  credentials, and stay on the thread for the [GAM half](https://docs.apostra.com/v3/seller-prebid-setup.md#configure-google-ad-manager),
  which is yours.

## Which integration — Prebid.js or Prebid Server?

| | **Prebid.js (client-side)** | **Prebid Server** |
| - | - | - |
| Where it runs | In the browser, inside your existing wrapper | In your Prebid Server deployment |
| Install shape | Add a module to your Prebid.js build + a config block | Compile the module in + a YAML config block |
| Credential | `orgId` | `auth_key` (your Apostra API key) |
| Best when | You run a standard Prebid.js wrapper on-page | You already operate Prebid Server, or manage many properties server-side |

Both call the same hosted router and produce the same targeting keys. If
you're unsure, client-side Prebid.js is the shorter path.

## Read your Prebid.js orgId

Call `get` on your Seller Account with `include: ["prebid"]` and no `id`:

```json theme={null}
{ "kind": "seller", "include": ["prebid"] }
```

The seller comes back with a `prebid` object:

```json theme={null}
{
  "prebid": {
    "orgId": "https://esa.example.com/seller/acme-media/mcp",
    "configured": true
  }
}
```

| Field | Meaning |
| - | - |
| `orgId` | The value to paste into the `orgId` parameter of the Prebid.js module config. It is your storefront's canonical sales agent URL. It is `null` until TMP setup completes. |
| `configured` | `true` when your storefront has a sales agent URL the router can use. `false` means setup is not finished: no managed sales agent source is active and no third-party sales agent connection exists. Finish storefront setup, then read it again. |

The `orgId` is worked out from your storefront's sales agent, so if that
changes, read it again and update your Prebid.js config. A default `get` of the seller does not include `prebid`, and
the include is refused on any other `kind`, on a read that names an `id`, and
for a buyer account. If Apostra cannot work the value out, the call fails with
`SERVICE_UNAVAILABLE` and returns no `prebid` object, so you never copy a guess.
Retry the same call. The Prebid Server `auth_key` is a different credential and
is not part of this read.

## Install the module

> [tab] Prebid.js (client-side)
>
> **1. Include the module in your Prebid.js build**
>
> The module is the **Apostra RTD provider** (`scope3RtdProvider`) —
> a standard Prebid Real-Time Data module, built into your bundle like
> any other:
>
> ```bash theme={null}
> gulp build --modules=scope3RtdProvider,rtdModule,...yourOtherModules
> ```
>
> Full reference:
> [docs.prebid.org — Scope3 RTD provider](https://docs.prebid.org/dev-docs/modules/scope3RtdProvider.html).
>
> **2. Add the configuration**
>
> ```javascript theme={null}
> pbjs.setConfig({
>   realTimeData: {
>     auctionDelay: 100,
>     dataProviders: [{
>       name: "scope3",
>       waitForIt: true,
>       params: {
>         orgId: "YOUR_ORG_ID" // the prebid.orgId you read above
>       }
>     }]
>   }
> });
> ```
>
> | Parameter | Required | Default | What it does |
> | - | - | - | - |
> | `orgId` | Yes | — | Your Apostra org ID — use exactly the `prebid.orgId` value you read from your Seller Account |
> | `endpoint` | No | `https://prebid.scope3.com/prebid` | The hosted router endpoint |
> | `timeout` | No | `1000` (ms) | Max time to wait for the router's response |
> | `bidders` | No | all auction bidders | Restrict which bidders receive the signals |
> | `includeKey` | No | `axei` | Targeting key for include segments |
> | `excludeKey` | No | `axex` | Targeting key for exclude segments |
> | `macroKey` | No | `axem` | Targeting key for creative macros |
> | `cacheEnabled` / `cacheTtl` | No | `true` / `300000` (ms) | Response caching |
>
> **3. Send targeting to GAM only after the auction ends**
>
> The module writes its keys (`axei`, `axex`, `axem`) onto your ad
> units at **auction end**. If your page calls
> `setTargetingForGPTAsync()` earlier, the keys silently never reach
> GAM.
>
> ```javascript theme={null}
> pbjs.onEvent('auctionEnd', function () {
>   pbjs.setTargetingForGPTAsync();
> });
> ```
>
> Calling it inside `bidsBackHandler` (which runs after the auction)
> is equally correct. If your wrapper uses a `setTimeout` fallback,
> make sure it cannot fire before the auction completes.
>
> > [!WARNING]
> >
> > The auction-end timing above is the most common install mistake — the
> > page looks fine, the module calls the router successfully, and GAM
> > still never sees the keys. If you hand the install to a wrapper
> > vendor, include this step verbatim.

> [tab] Prebid Server
>
> **1. Build with the module enabled**
>
> The module ships with Prebid Server (Go) behind a build tag:
>
> ```bash theme={null}
> go build -tags scope3_rtd
> ```
>
> Full reference:
> [docs.prebid.org — Scope3 real-time data module](https://docs.prebid.org/prebid-server/pbs-modules/scope3-real-time-data.html).
>
> **2. Add the configuration**
>
> ```yaml theme={null}
> hooks:
>   enabled: true
>   modules:
>     scope3:
>       rtd:
>         enabled: true
>         auth_key: ${SCOPE3_API_KEY} # issued by Apostra
>         timeout_ms: 1000
>         cache_ttl_seconds: 60
>         add_to_targeting: true
> ```
>
> | Parameter | Required | Default | What it does |
> | - | - | - | - |
> | `enabled` | Yes | — | Turns the module on |
> | `auth_key` | Yes | — | Your Apostra API key |
> | `timeout_ms` | No | `1000` | Router request timeout |
> | `cache_ttl_seconds` | No | `60` | Response cache window |
> | `add_to_targeting` | No | `false` | Writes the segments as GAM targeting keys |
>
> The module also needs its hooks registered on the
> `/openrtb2/auction` endpoint (entrypoint, raw auction request, and
> auction response stages) — the module reference shows the full
> `host_execution_plan` block.
>
> > [!WARNING]
> >
> > `add_to_targeting` defaults to **`false`**. For TMP campaigns to
> > deliver through GAM line items, set it to **`true`** — without it the
> > module runs but your ad server never receives the targeting keys.

## Declare which products run the module

Once the module is installed, tell Apostra which of your products serve
inventory through it. This is per product and self-serve. Pass
`prebidIntegrationActive: true` (or `false`) on any `save_wholesale_product`
call. Omit the field to leave the current state unchanged. See
[Declare the Scope3 Prebid module on a product](https://docs.apostra.com/v3/seller-prebid-toggle.md) for the
call, the result and its limits.

When on, the line item a buyer's media buy creates on that product is gated
on a package-ID targeting key that only matches traffic the module has
enriched. When off, that key is skipped and the line item serves without
that gate.

> [!WARNING]
>
> Only turn it on for products whose inventory actually runs the module. If
> it's on for a product where the module is not installed, the resulting
> line items get targeting that nothing populates and will not serve.

> [!NOTE]
>
> This flow manages ESA-hosted wholesale products. If your storefront routes
> to a third-party sales agent, declare the same flag on your own agent's
> `get_products` response — Apostra does not manage those products.

Flipping the flag applies to media buys forwarded after the change. Existing
active buys on that product are not retroactively gated; a separate backfill
sweep for active buys is on our roadmap.

## Configure Google Ad Manager

The module puts key-values on your ad requests; GAM needs the matching keys
and line items. If your storefront runs a
[GAM sales agent](https://docs.apostra.com/v3/seller-connect-ad-server.md), most of this is
automated:

* **Targeting keys** — the custom targeting keys (`axei`, `axex`, `axem`)
  are created by the **segment keys** task in your sales agent's setup panel.
  Complete it once; it's listed under your inventory source's setup tasks.
* **Line items** — when a buyer's media buy executes, your sales agent
  creates the GAM order and line item targeting the campaign's `axei` value
  automatically. You don't traffic these by hand; you'll see them appear
  under the advertiser your sales agent manages.
* **Authorize the creative tracker domain** — in GAM, authorize
  `https://ping.apostra.com` for creatives (Google's validation of the
  domain is in progress, so today this is a one-time manual authorization).

> [!WARNING]
>
> Skipping the tracker-domain authorization is the most common reason a
> correctly-installed setup serves nothing: GAM blocks the creative with a
> domain error, and impressions stay at zero until the domain is authorized.

> [!NOTE]
>
> If your consent management platform maintains a vendor allowlist (e.g.
> Didomi), add `ping.apostra.com` as a custom vendor so consent tooling
> never blocks the tracker.

## Verify it's working

**1. Check the module is firing**

Load a page with `?pbjs_debug=true` and confirm in the browser's network
panel that a request goes to `prebid.scope3.com/prebid` and returns
`200`.

**2. Check the keys reach GAM**

On the same page, inspect the GAM ad request and confirm the `axei` key
appears in the request's custom targeting (for video/instream, check the
`cust_params` of the video ad call — the keys must ride there too).

**3. Check delivery end-to-end**

Once a TMP-targeted media buy is live, its line item in GAM should show
impressions against the `axei` targeting. In chat, ask Apostra to check
your TMP setup — it can confirm your enrollment state and flag anything
missing.

## Troubleshooting

| Symptom | Likely cause | Fix |
| - | - | - |
| Build fails on `scope3RtdProvider`, or the module never fires at all | Prebid.js older than 10.9.0 — the module isn't in your tree | Upgrade Prebid.js to 10.9.0+; the module ships in-tree from that release |
| Module calls the router, but no `axei` in GAM requests | `setTargetingForGPTAsync()` runs before auction end | Move it to `auctionEnd` / `bidsBackHandler` (see install step 3) |
| Display works, video doesn't | Keys aren't reaching the video call's `cust_params` | Wire the same targeting keys into your video player's GAM ad-call parameters |
| Line item exists but zero impressions | Creative blocked on the tracker domain | Authorize `https://ping.apostra.com` in GAM (see above) |
| Campaign under-delivers | Line-item price vs. your floors, or goal-type pacing behavior | Check the line item's CPM clears your GAM floor rules; note GAM does not pace price-priority line items with lifetime goals |

Still stuck? Ask Apostra in chat — describe what you're seeing and it will help
diagnose or flag Apostra team with the details.

## Related

> [card] [Real-time targeting (TMP)](https://docs.apostra.com/v2/storefront/tmp/overview.md)
>
> What TMP is, the capabilities it unlocks, and the two connection paths.

> [card] [Frequency caps](https://docs.apostra.com/v2/guides/frequency-caps.md)
>
> How buyer-side, cross-publisher caps work on TMP-connected storefronts.

> [card] [Inventory sources](https://docs.apostra.com/v3/seller-connect-ad-server.md)
>
> Connecting your GAM and managing your sales agent's setup tasks.

> [card] [Apostra RTD provider reference](https://docs.prebid.org/dev-docs/modules/scope3RtdProvider.html)
>
> The module's full parameter reference on docs.prebid.org.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.