Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
e2812ea
Create datamage RTD integration
leiforion Feb 17, 2026
035b2a6
remove test.html files
leiforion Feb 18, 2026
67456d8
update example.html
leiforion Feb 18, 2026
5ab88c8
Merge branch 'master' into rtd/datamage-contextual-provider
leiforion Feb 18, 2026
af7634a
siplify architecture, adding back in linting, improve load
leiforion Feb 23, 2026
5b319d4
Merge branch 'master' into rtd/datamage-contextual-provider
leiforion Feb 23, 2026
923ca7e
fixed caching issues and bot comments from original pr
leiforion Feb 23, 2026
427757c
fix issues identified by bot
leiforion Feb 23, 2026
0345761
replace dep GPT api calls to modern format
leiforion Feb 27, 2026
fe0b255
Merge branch 'master' into rtd/datamage-contextual-provider
leiforion Feb 27, 2026
0334499
update btoa to support non typical url encodings, strip common tracki…
leiforion Feb 27, 2026
6b35e9a
remove misc local test files
leiforion Feb 27, 2026
80f2d75
fix fetchPromise transient failur potential
leiforion Feb 27, 2026
f255dae
reduce key value size and unusued keys in payload to gam and ortb
leiforion Feb 28, 2026
d383867
update Rtd spec file naming
leiforion Feb 28, 2026
3d12e1a
Merge branch 'master' into rtd/datamage-contextual-provider
leiforion Mar 9, 2026
4ce2874
update prebid js path in example
leiforion Mar 11, 2026
3c0a2fd
Merge branch 'master' into rtd/datamage-contextual-provider
leiforion Mar 11, 2026
f04b940
Merge branch 'master' into rtd/datamage-contextual-provider
leiforion Mar 20, 2026
4c7cd78
include datamageRtdprovider in submodules json
leiforion Mar 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 143 additions & 0 deletions integrationExamples/realTimeData/datamageRtdProvider_example.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8" />
<title>OpsMage Prebid Test Page</title>

<script async src="https://securepubads.g.doubleclick.net/tag/js/gpt.js" crossorigin="anonymous"></script>
<script>
window.googletag = window.googletag || { cmd: [] };
window.gptSlot = null;

googletag.cmd.push(function () {
googletag.pubads().disableInitialLoad();

// Save slot reference so we can refresh JUST this slot
window.gptSlot = googletag.defineSlot(
'/50536250/designcon',
[[300, 250]],
'div-gpt-ad-1771335974713-0'
).addService(googletag.pubads());

googletag.pubads().enableSingleRequest();
googletag.enableServices();

// Display once (no request yet because disableInitialLoad)
googletag.display('div-gpt-ad-1771335974713-0');
});

// Helper function to verify targeting in the console
function dumpGptTargeting(label) {
googletag.cmd.push(function () {
const pubads = googletag.pubads();
const keys = pubads.getTargetingKeys();
const pubadsMap = {};
keys.forEach(k => { pubadsMap[k] = pubads.getTargeting(k); });

console.log(label, 'PUBADS targeting:', pubadsMap);

if (window.gptSlot && typeof window.gptSlot.getTargetingMap === 'function') {
console.log(label, 'SLOT targeting:', window.gptSlot.getTargetingMap());
}
});
}
</script>

<script async src="build/dev/prebid.js"></script>

<script>
var pbjs = window.pbjs = window.pbjs || {};
pbjs.que = pbjs.que || [];

var PREBID_TIMEOUT = 1500;

var adUnits = [{
code: 'div-gpt-ad-1771335974713-0',
mediaTypes: { banner: { sizes: [[300, 250]] } },
bids: [{
bidder: 'appnexus',
params: { placementId: 1234567 }
}]
}];

pbjs.que.push(function () {
pbjs.setConfig({
debug: true,

consentManagement: {
allowAuctionWithoutConsent: true,
gdpr: {
cmpApi: 'static',
timeout: 0,
consentData: {
getTCData: {
tcString: 'CPAa+2APAa+2AAOACBENC1CoAP_AAH_AAAAAAwwxgAAAAA',
gdprApplies: true,
purpose: { consents: { 1: true, 2: true, 3: true, 4: true, 5: true } },
vendor: { consents: { 1: true }, legitimateInterests: { 1: true } }
}
}
},
usp: { cmpApi: 'static', timeout: 0, consentData: { getUSPData: { uspString: '1---' } } }
},

allowActivities: {
accessDevice: { rules: [{ action: 'allow' }] },
enrichUfpd: { rules: [{ action: 'allow' }] },
enrichEids: { rules: [{ action: 'allow' }] },
transmitTid: { rules: [{ action: 'allow' }] }
},

realTimeData: {
auctionDelay: 1000, // Prebid will wait up to 1000ms for Datamage to fetch data
dataProviders: [{
name: "datamage",
waitForIt: true, // <--- CRITICAL FIX: Forces Prebid to wait for this module
params: {
api_key: '8328309832',
selector: 'article',
fetch_timeout_ms: 2500
}
}]
}
});

pbjs.addAdUnits(adUnits);

pbjs.requestBids({
bidsBackHandler: function () {
// Push standard header bidding keys (like hb_pb) to GPT
pbjs.setTargetingForGPTAsync();

// Dump targeting to verify Datamage was successfully pushed via setConfig
dumpGptTargeting('[before refresh]');

// Refresh only our specific slot
googletag.cmd.push(function () {
if (window.gptSlot) {
googletag.pubads().refresh([window.gptSlot]);
} else {
// Fallback
googletag.pubads().refresh();
}
});
},
timeout: PREBID_TIMEOUT
});
});
</script>
</head>

<body>
<h2>OpsMage Prebid Test Page</h2>
<div id="div-gpt-ad-1771335974713-0" style="width:300px; height:250px;"></div>
<article>The tech world is currently buzzing over the highly anticipated market debut of fakeDSP, a trailblazing
startup poised to redefine the landscape of digital signal processing. Leveraging proprietary neuromorphic
algorithms and quantum-ready architecture, fakeDSP promises to accelerate real-time data synthesis by speeds
previously thought impossible. With early analysts calling it a definitive disruptor in both the
telecommunications and audio-engineering sectors, the company’s entrance signifies a major leap forward in how
complex signals are analyzed and reconstructed in the AI era.</article>
</body>

</html>
90 changes: 90 additions & 0 deletions modules/datamageRrdProvider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@

# DataMage RTD Submodule

DataMage provides real-time contextual classification (IAB Categories, Sentiment, Brands, Locations, Public Figures, Restricted Categories, and related IDs) that can be used to enrich demand signals and Google Ad Manager targeting.

## What it does

DataMage automatically supports two outcomes in a Prebid + GAM setup without requiring any custom glue-code on the page:

1. **Passes data to Google Ad Manager (Direct GPT targeting)**

* The moment Prebid initializes, DataMage fetches classification for the current page and automatically pushes the targeting keys directly to GPT using the modern `googletag.setConfig({ targeting: ... })` API at the page level.
* This ensures the data is available for all ad slots and works **even if there are no bids** or if the auction times out.

2. **Passes data to bidders (ORTB2 enrichment)**

* Using a memoized cache from the initial fetch, DataMage seamlessly inserts the contextual results into the bid request using OpenRTB (`ortb2Fragments.global.site.content.data`), allowing bidders to receive the enriched signals instantly.

## Keys provided

DataMage automatically maps and provides the following targeting keys (when available in the API response):

* `om_iab_cat_ids`
* `om_iab_cats`
* `om_brand_ids`
* `om_sentiment_ids`
* `om_location_ids`
* `om_public_figure_ids`
* `om_restricted_cat_ids`
* `om_restricted_cats`
* `om_ops_mage_data_id`
* `om_res_score_bucket`
* `om_res_score` (only when present)

## Prebid config

```javascript
pbjs.setConfig({
realTimeData: {
auctionDelay: 1000, // Gives the module time to fetch data before bids are sent, suggested minimum 1000
dataProviders: [{
name: "datamage",
waitForIt: true, // CRITICAL: Forces Prebid to wait for the module to fetch data before resolving the auction
params: {
api_key: "YOUR_API_KEY",
selector: "article",
fetch_timeout_ms: 2500
}
}]
}
});

```

## GAM set up requirements

Because DataMage automatically injects targeting globally via `setConfig`, your page implementation only requires a standard Prebid setup.

To ensure DataMage key-values are included in your GAM requests:

1. Call `googletag.pubads().disableInitialLoad()` before your ad requests.
2. Define your slots and call `googletag.enableServices()`.
3. Run `pbjs.requestBids(...)`.
4. Inside the `bidsBackHandler` callback:
* Call `pbjs.setTargetingForGPTAsync()` (to set standard Prebid `hb_` pricing keys).
* Call `googletag.pubads().refresh()` to trigger the GAM request.



GAM will automatically combine the standard Prebid slot-level pricing keys with the page-level DataMage contextual keys.

*Note that you will need a real API key provisioned by DataMage to use this module in production.*

### Example:

```javascript
pbjs.requestBids({
bidsBackHandler: function () {
// Push standard header bidding keys to GPT
pbjs.setTargetingForGPTAsync();

// Refresh the ad slots. Datamage page-level keys are already injected!
googletag.cmd.push(function () {
googletag.pubads().refresh();
});
},
timeout: 1500
});

```
Loading