@mixpeek/prebid 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -1
- package/ENDPOINTS.md +21 -27
- package/QUICKSTART.md +5 -5
- package/README.md +225 -333
- package/dist/mixpeekContextAdapter.js +1 -1
- package/dist/mixpeekContextAdapter.js.map +1 -1
- package/docs/MIGRATION_V2.md +4 -4
- package/docs/api-reference.md +1 -1
- package/docs/health-check.md +5 -5
- package/docs/integration-guide.md +5 -5
- package/package.json +10 -4
- package/src/api/mixpeekClient.js +133 -32
- package/src/config/constants.js +13 -8
- package/src/modules/mixpeekContextAdapter.js +55 -3
package/README.md
CHANGED
|
@@ -1,439 +1,331 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
|
+
<p align="center">
|
|
3
|
+
<img src="assets/header.png" alt="Mixpeek Multimodal Contextual Targeting Adapter" />
|
|
4
|
+
</p>
|
|
5
|
+
|
|
6
|
+
# Mixpeek RTD (Real-Time Data) Adapter for Prebid.js
|
|
7
|
+
|
|
8
|
+
**This is a Real-Time Data (RTD) module for Prebid.js that replaces cookie-based targeting with sub-100ms contextual signals.**
|
|
2
9
|
|
|
3
10
|
[](https://www.npmjs.com/package/@mixpeek/prebid)
|
|
4
11
|
[](https://www.npmjs.com/package/@mixpeek/prebid)
|
|
5
12
|
[](https://opensource.org/licenses/Apache-2.0)
|
|
6
|
-
[](https://www.npmjs.com/package/@mixpeek/prebid)
|
|
7
13
|
[](https://bundlephobia.com/package/@mixpeek/prebid)
|
|
8
|
-
[](https://github.com/mixpeek/prebid)
|
|
10
|
-
[](https://github.com/mixpeek/prebid/pulls)
|
|
14
|
+
[](https://www.npmjs.com/package/@mixpeek/prebid)
|
|
11
15
|
|
|
12
|
-
|
|
16
|
+
---
|
|
13
17
|
|
|
14
|
-
|
|
18
|
+
## What This Is
|
|
15
19
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
- **Graceful Fallbacks**: Never blocks the auction
|
|
20
|
+
| Type | Status |
|
|
21
|
+
|------|--------|
|
|
22
|
+
| **RTD Module** (bid enrichment) | Supported |
|
|
23
|
+
| Bidder Adapter | Not a bidder |
|
|
24
|
+
| Analytics Adapter | Not analytics |
|
|
25
|
+
| Identity Module | Not identity |
|
|
23
26
|
|
|
24
|
-
|
|
27
|
+
**SSPs and DSPs can immediately consume these signals via `ortb2.site.content` and `ortb2Imp.ext.data` without custom integration.**
|
|
25
28
|
|
|
26
|
-
|
|
29
|
+
---
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
## Who This Is For
|
|
32
|
+
|
|
33
|
+
- **Publishers** migrating off cookie-based targeting
|
|
34
|
+
- **SSPs** looking for contextual + adjacency signals
|
|
35
|
+
- **DSPs** pricing inventory using OpenRTB 2.6 context
|
|
36
|
+
|
|
37
|
+
Used in Mixpeek production demos and SSP pilots.
|
|
38
|
+
|
|
39
|
+
---
|
|
31
40
|
|
|
32
|
-
|
|
41
|
+
## Why Use This
|
|
42
|
+
|
|
43
|
+
1. **Ad Adjacency Awareness** - Competitive separation, no repeat creatives
|
|
44
|
+
2. **Privacy-First Contextual Targeting** - No cookies, no user tracking
|
|
45
|
+
3. **IAB Taxonomy Classification** - Content categorization (v3.0)
|
|
46
|
+
4. **Brand Safety Scoring** - Real-time sentiment analysis
|
|
47
|
+
5. **Multimodal Analysis** - Text, image, video content understanding
|
|
48
|
+
6. **Sub-100ms RTD Performance** - Optimized for header bidding latency
|
|
49
|
+
|
|
50
|
+
**Graceful Failure:** If Mixpeek is unavailable or times out, auctions proceed normally. The module never blocks bid requests.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Minimal Setup (Copy-Paste Ready)
|
|
33
55
|
|
|
34
56
|
```javascript
|
|
35
|
-
// 1. Include the Mixpeek RTD module
|
|
36
57
|
import '@mixpeek/prebid'
|
|
37
58
|
|
|
38
|
-
// 2. Configure Mixpeek as an RTD provider
|
|
39
59
|
pbjs.setConfig({
|
|
40
60
|
realTimeData: {
|
|
41
|
-
auctionDelay: 250, // Max time to wait for contextual data (ms)
|
|
42
61
|
dataProviders: [{
|
|
43
62
|
name: 'mixpeek',
|
|
44
|
-
waitForIt: true, // Wait for Mixpeek before starting auction
|
|
45
63
|
params: {
|
|
46
|
-
apiKey: '
|
|
47
|
-
collectionId: '
|
|
48
|
-
|
|
49
|
-
// Use development server (temporary)
|
|
50
|
-
endpoint: 'https://server-xb24.onrender.com',
|
|
51
|
-
// Or production: endpoint: 'https://api.mixpeek.com',
|
|
52
|
-
|
|
53
|
-
namespace: 'your-namespace', // optional
|
|
54
|
-
featureExtractors: ['taxonomy', 'brand-safety'],
|
|
55
|
-
mode: 'page', // 'page', 'video', or 'auto'
|
|
56
|
-
timeout: 5000, // ms - higher for dev server
|
|
57
|
-
cacheTTL: 300 // seconds
|
|
64
|
+
apiKey: 'YOUR_API_KEY',
|
|
65
|
+
collectionId: 'YOUR_COLLECTION_ID',
|
|
66
|
+
namespace: 'YOUR_NAMESPACE'
|
|
58
67
|
}
|
|
59
68
|
}]
|
|
60
69
|
}
|
|
61
70
|
})
|
|
62
|
-
|
|
63
|
-
// 3. The RTD module automatically enriches bid requests!
|
|
64
|
-
pbjs.requestBids({
|
|
65
|
-
adUnits: [...],
|
|
66
|
-
bidsBackHandler: function(bids) {
|
|
67
|
-
// Bids now include Mixpeek contextual data in ortb2
|
|
68
|
-
}
|
|
69
|
-
})
|
|
70
71
|
```
|
|
71
72
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
1. **Mixpeek Account**: Sign up at [mixpeek.com](https://mixpeek.com/start)
|
|
75
|
-
2. **API Key**: Generate an API key in your Mixpeek dashboard
|
|
76
|
-
3. **Collection**: Create a collection with feature extractors configured
|
|
77
|
-
4. **Prebid.js**: Version 6.0.0 or higher
|
|
73
|
+
That's it. The RTD module automatically enriches all bid requests.
|
|
78
74
|
|
|
79
|
-
|
|
75
|
+
---
|
|
80
76
|
|
|
81
|
-
|
|
77
|
+
## Installation
|
|
82
78
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
| `realTimeData.dataProviders[].name` | string | ✅ | - | Must be `'mixpeek'` |
|
|
87
|
-
| `realTimeData.dataProviders[].waitForIt` | boolean | ❌ | false | Wait for Mixpeek before starting auction |
|
|
88
|
-
|
|
89
|
-
### Mixpeek Parameters
|
|
79
|
+
```bash
|
|
80
|
+
npm install @mixpeek/prebid
|
|
81
|
+
```
|
|
90
82
|
|
|
91
|
-
|
|
92
|
-
|--------|------|----------|---------|-------------|
|
|
93
|
-
| `params.apiKey` | string | ✅ | - | Your Mixpeek API key |
|
|
94
|
-
| `params.collectionId` | string | ✅ | - | Mixpeek collection ID for document processing |
|
|
95
|
-
| `params.endpoint` | string | ❌ | `https://server-xb24.onrender.com` | Mixpeek API endpoint (dev server default) |
|
|
96
|
-
| `params.namespace` | string | ❌ | - | Optional namespace for data isolation |
|
|
97
|
-
| `params.featureExtractors` | array | ❌ | `['taxonomy']` | Feature extractors to use (taxonomy, brand-safety, etc.) |
|
|
98
|
-
| `params.mode` | string | ❌ | `auto` | Content mode: `page`, `video`, `image`, or `auto` |
|
|
99
|
-
| `params.timeout` | number | ❌ | 250 | API request timeout in milliseconds |
|
|
100
|
-
| `params.cacheTTL` | number | ❌ | 300 | Cache TTL in seconds |
|
|
101
|
-
| `params.enableCache` | boolean | ❌ | `true` | Enable local caching |
|
|
102
|
-
| `params.debug` | boolean | ❌ | `false` | Enable debug logging |
|
|
103
|
-
| `params.batchSize` | number | ❌ | 1 | Number of concurrent requests |
|
|
104
|
-
| `params.retryAttempts` | number | ❌ | 2 | Number of retry attempts on failure |
|
|
83
|
+
## Prerequisites
|
|
105
84
|
|
|
106
|
-
|
|
85
|
+
1. **Mixpeek Account** - Sign up at [mixpeek.com](https://mixpeek.com/start)
|
|
86
|
+
2. **API Key** - Generate in your Mixpeek dashboard
|
|
87
|
+
3. **Collection** - Create a collection with feature extractors
|
|
88
|
+
4. **Namespace** - Your namespace ID (format: `ns_xxxxx`)
|
|
89
|
+
5. **Prebid.js** - Version 6.0.0 or higher
|
|
107
90
|
|
|
108
|
-
|
|
91
|
+
---
|
|
109
92
|
|
|
110
|
-
|
|
111
|
-
```javascript
|
|
112
|
-
{
|
|
113
|
-
"ortb2": {
|
|
114
|
-
"site": {
|
|
115
|
-
"content": {
|
|
116
|
-
"cat": ["IAB19-11"], // IAB Content Categories
|
|
117
|
-
"cattax": 6, // IAB Content Taxonomy v3.0
|
|
118
|
-
"genre": "Technology - AI", // Human-readable category
|
|
119
|
-
"keywords": "ai,technology,ml", // Extracted keywords
|
|
120
|
-
"language": "en", // Content language
|
|
121
|
-
"title": "Article Title", // Page title
|
|
122
|
-
"url": "https://example.com", // Page URL
|
|
123
|
-
"ext": {
|
|
124
|
-
"data": {
|
|
125
|
-
"mixpeek": {
|
|
126
|
-
"score": 0.94, // Confidence score
|
|
127
|
-
"brandSafety": 0.98, // Brand safety score
|
|
128
|
-
"sentiment": "positive", // Content sentiment
|
|
129
|
-
"embeddingId": "emb_abc123" // Embedding ID
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
```
|
|
93
|
+
## Full Configuration
|
|
138
94
|
|
|
139
|
-
### Impression-Level Data (`ortb2Imp.ext.data`)
|
|
140
95
|
```javascript
|
|
141
|
-
|
|
142
|
-
// Current page context
|
|
143
|
-
"hb_mixpeek_taxonomy": "IAB19-11", // Primary IAB taxonomy code
|
|
144
|
-
"hb_mixpeek_category": "Technology > AI", // Human-readable category
|
|
145
|
-
"hb_mixpeek_node": "node_tech_ai", // Taxonomy node ID
|
|
146
|
-
"hb_mixpeek_path": "tech/ai/ml", // Hierarchical path
|
|
147
|
-
"hb_mixpeek_score": "0.94", // Confidence score
|
|
148
|
-
"hb_mixpeek_safety": "0.98", // Brand safety score
|
|
149
|
-
"hb_mixpeek_keywords": "AI,ML,tech", // Extracted keywords
|
|
150
|
-
"hb_mixpeek_embed": "emb_abc123", // Embedding ID for retrieval
|
|
151
|
-
|
|
152
|
-
// Previous ad context (adjacency awareness)
|
|
153
|
-
"hb_mixpeek_prev_creative": "12345", // Last creative ID shown
|
|
154
|
-
"hb_mixpeek_prev_bidder": "appnexus", // Last bidder that won
|
|
155
|
-
"hb_mixpeek_prev_adunit": "sidebar-1", // Last ad unit code
|
|
156
|
-
"hb_mixpeek_prev_cat": "IAB18-1,IAB12-3" // Last ad categories
|
|
157
|
-
}
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
## 🎥 Usage Examples
|
|
161
|
-
|
|
162
|
-
### Page Context (Articles, Blogs)
|
|
96
|
+
import '@mixpeek/prebid'
|
|
163
97
|
|
|
164
|
-
```javascript
|
|
165
98
|
pbjs.setConfig({
|
|
166
99
|
realTimeData: {
|
|
167
|
-
auctionDelay: 250,
|
|
100
|
+
auctionDelay: 250, // Max wait for contextual data (ms)
|
|
168
101
|
dataProviders: [{
|
|
169
102
|
name: 'mixpeek',
|
|
170
|
-
waitForIt: true,
|
|
103
|
+
waitForIt: true, // Wait for Mixpeek before auction
|
|
171
104
|
params: {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
105
|
+
// Required
|
|
106
|
+
apiKey: 'YOUR_API_KEY',
|
|
107
|
+
collectionId: 'YOUR_COLLECTION_ID',
|
|
108
|
+
namespace: 'YOUR_NAMESPACE', // e.g., 'ns_abc123'
|
|
109
|
+
|
|
110
|
+
// Optional
|
|
111
|
+
endpoint: 'https://api.mixpeek.com', // Default
|
|
112
|
+
mode: 'auto', // 'page', 'video', 'image', or 'auto'
|
|
113
|
+
timeout: 250, // API timeout in ms
|
|
114
|
+
cacheTTL: 300, // Cache TTL in seconds
|
|
115
|
+
enableCache: true, // Enable local caching
|
|
116
|
+
debug: false // Enable debug logging
|
|
176
117
|
}
|
|
177
118
|
}]
|
|
178
119
|
}
|
|
179
120
|
})
|
|
180
|
-
```
|
|
181
121
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
auctionDelay: 300, // Longer delay for video processing
|
|
188
|
-
dataProviders: [{
|
|
189
|
-
name: 'mixpeek',
|
|
190
|
-
waitForIt: true,
|
|
191
|
-
params: {
|
|
192
|
-
apiKey: 'sk_your_api_key',
|
|
193
|
-
collectionId: 'col_videos',
|
|
194
|
-
mode: 'video',
|
|
195
|
-
videoSelector: '#main-video', // CSS selector for video element
|
|
196
|
-
featureExtractors: ['taxonomy', 'scene-detection']
|
|
197
|
-
}
|
|
198
|
-
}]
|
|
122
|
+
// Bids are automatically enriched with contextual data
|
|
123
|
+
pbjs.requestBids({
|
|
124
|
+
adUnits: [...],
|
|
125
|
+
bidsBackHandler: function(bids) {
|
|
126
|
+
// Bids now include Mixpeek contextual data in ortb2
|
|
199
127
|
}
|
|
200
128
|
})
|
|
201
129
|
```
|
|
202
130
|
|
|
203
|
-
|
|
131
|
+
---
|
|
204
132
|
|
|
205
|
-
|
|
206
|
-
pbjs.setConfig({
|
|
207
|
-
realTimeData: {
|
|
208
|
-
auctionDelay: 250,
|
|
209
|
-
dataProviders: [{
|
|
210
|
-
name: 'mixpeek',
|
|
211
|
-
waitForIt: true,
|
|
212
|
-
params: {
|
|
213
|
-
apiKey: 'sk_your_api_key',
|
|
214
|
-
collectionId: 'col_mixed',
|
|
215
|
-
mode: 'auto', // Automatically detects page, video, or image content
|
|
216
|
-
featureExtractors: ['taxonomy', 'brand-safety', 'clustering']
|
|
217
|
-
}
|
|
218
|
-
}]
|
|
219
|
-
}
|
|
220
|
-
})
|
|
221
|
-
```
|
|
133
|
+
## Ad Adjacency Awareness (Key Feature)
|
|
222
134
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
```mermaid
|
|
226
|
-
sequenceDiagram
|
|
227
|
-
participant Publisher
|
|
228
|
-
participant Prebid
|
|
229
|
-
participant MixpeekAdapter
|
|
230
|
-
participant MixpeekAPI
|
|
231
|
-
participant SSP
|
|
232
|
-
|
|
233
|
-
Publisher->>Prebid: Request Bids
|
|
234
|
-
Prebid->>MixpeekAdapter: beforeRequestBids event
|
|
235
|
-
MixpeekAdapter->>MixpeekAdapter: Extract page/video content
|
|
236
|
-
MixpeekAdapter->>MixpeekAdapter: Check cache
|
|
237
|
-
alt Cache Miss
|
|
238
|
-
MixpeekAdapter->>MixpeekAPI: POST /collections/{id}/documents
|
|
239
|
-
MixpeekAPI->>MixpeekAPI: Process with feature extractors
|
|
240
|
-
MixpeekAPI-->>MixpeekAdapter: Return enrichments
|
|
241
|
-
MixpeekAdapter->>MixpeekAdapter: Cache result
|
|
242
|
-
end
|
|
243
|
-
MixpeekAdapter->>Prebid: Inject contextual key-values
|
|
244
|
-
Prebid->>SSP: Send enriched bid request
|
|
245
|
-
SSP-->>Prebid: Return bids
|
|
246
|
-
Prebid-->>Publisher: Render ad
|
|
247
|
-
```
|
|
135
|
+
The adapter automatically tracks previously served ads to enable:
|
|
248
136
|
|
|
249
|
-
|
|
137
|
+
- **Competitive Separation** - Avoid showing competing brands consecutively
|
|
138
|
+
- **Creative Frequency** - Prevent the same ad from showing repeatedly
|
|
139
|
+
- **Category Diversity** - Improve ad variety for better user experience
|
|
250
140
|
|
|
251
|
-
|
|
252
|
-
# Run all tests
|
|
253
|
-
npm test
|
|
141
|
+
### How SSPs/DSPs Use It
|
|
254
142
|
|
|
255
|
-
|
|
256
|
-
|
|
143
|
+
```javascript
|
|
144
|
+
// Data automatically injected into ortb2Imp.ext.data:
|
|
145
|
+
{
|
|
146
|
+
"hb_mixpeek_prev_creative": "12345", // Last creative ID
|
|
147
|
+
"hb_mixpeek_prev_bidder": "appnexus", // Last winning bidder
|
|
148
|
+
"hb_mixpeek_prev_adunit": "sidebar-1", // Last ad unit
|
|
149
|
+
"hb_mixpeek_prev_cat": "IAB18-1,IAB12-3" // Last ad categories
|
|
150
|
+
}
|
|
257
151
|
|
|
258
|
-
|
|
259
|
-
|
|
152
|
+
// DSP can use for competitive separation:
|
|
153
|
+
if (prevCategories.includes('IAB18-1') && currentAd.category === 'IAB18-1') {
|
|
154
|
+
// Don't show competing fashion ads back-to-back
|
|
155
|
+
}
|
|
260
156
|
```
|
|
261
157
|
|
|
262
|
-
|
|
158
|
+
### Privacy Safe
|
|
263
159
|
|
|
264
|
-
|
|
160
|
+
- No user tracking or identifiers
|
|
161
|
+
- Only ad metadata stored (< 200 bytes)
|
|
162
|
+
- Session-scoped, localStorage with memory fallback
|
|
163
|
+
- GDPR/CCPA compliant (contextual, not behavioral)
|
|
265
164
|
|
|
266
|
-
|
|
267
|
-
pbjs.setConfig({
|
|
268
|
-
realTimeData: {
|
|
269
|
-
auctionDelay: 250,
|
|
270
|
-
dataProviders: [{
|
|
271
|
-
name: 'mixpeek',
|
|
272
|
-
waitForIt: true,
|
|
273
|
-
params: {
|
|
274
|
-
apiKey: 'sk_your_api_key',
|
|
275
|
-
collectionId: 'col_custom',
|
|
276
|
-
customExtractors: [
|
|
277
|
-
{
|
|
278
|
-
feature_extractor_id: 'sentiment-analyzer',
|
|
279
|
-
payload: {
|
|
280
|
-
model: 'sentiment-v2',
|
|
281
|
-
threshold: 0.7
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
]
|
|
285
|
-
}
|
|
286
|
-
}]
|
|
287
|
-
}
|
|
288
|
-
})
|
|
289
|
-
```
|
|
165
|
+
---
|
|
290
166
|
|
|
291
|
-
|
|
167
|
+
## OpenRTB 2.6 Output
|
|
168
|
+
|
|
169
|
+
### Site-Level Data (`ortb2.site.content`)
|
|
292
170
|
|
|
293
171
|
```javascript
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
172
|
+
{
|
|
173
|
+
"site": {
|
|
174
|
+
"content": {
|
|
175
|
+
"cat": ["IAB19-11"], // IAB Content Categories
|
|
176
|
+
"cattax": 6, // IAB Taxonomy v3.0
|
|
177
|
+
"genre": "Technology", // Human-readable category
|
|
178
|
+
"keywords": "ai,technology,ml", // Extracted keywords
|
|
179
|
+
"language": "en", // Content language
|
|
180
|
+
"ext": {
|
|
181
|
+
"data": {
|
|
182
|
+
"mixpeek": {
|
|
183
|
+
"score": 0.94, // Classification confidence
|
|
184
|
+
"brandSafety": 0.98, // Brand safety score
|
|
185
|
+
"sentiment": "positive" // Content sentiment
|
|
186
|
+
}
|
|
306
187
|
}
|
|
307
|
-
}
|
|
188
|
+
}
|
|
308
189
|
}
|
|
309
|
-
}
|
|
190
|
+
}
|
|
310
191
|
}
|
|
311
192
|
```
|
|
312
193
|
|
|
313
|
-
###
|
|
194
|
+
### Impression-Level Keys (`ortb2Imp.ext.data`)
|
|
314
195
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
196
|
+
| Key | Description | Example |
|
|
197
|
+
|-----|-------------|---------|
|
|
198
|
+
| `hb_mixpeek_category` | Content category | `"Technology"` |
|
|
199
|
+
| `hb_mixpeek_score` | Classification confidence | `"0.94"` |
|
|
200
|
+
| `hb_mixpeek_safety` | Brand safety score | `"0.98"` |
|
|
201
|
+
| `hb_mixpeek_keywords` | Extracted keywords | `"AI,ML,tech"` |
|
|
202
|
+
| `hb_mixpeek_sentiment` | Content sentiment | `"positive"` |
|
|
203
|
+
| `hb_mixpeek_prev_creative` | Last creative ID | `"12345"` |
|
|
204
|
+
| `hb_mixpeek_prev_bidder` | Last winning bidder | `"appnexus"` |
|
|
205
|
+
| `hb_mixpeek_prev_cat` | Last ad categories | `"IAB18-1"` |
|
|
320
206
|
|
|
321
|
-
|
|
322
|
-
console.error('Mixpeek context error:', error)
|
|
323
|
-
// Custom error handling
|
|
324
|
-
})
|
|
325
|
-
```
|
|
207
|
+
---
|
|
326
208
|
|
|
327
|
-
##
|
|
209
|
+
## How It Works
|
|
328
210
|
|
|
329
|
-
|
|
211
|
+
```
|
|
212
|
+
USER → WEBSITE → PREBID.JS
|
|
213
|
+
│
|
|
214
|
+
├──→ MIXPEEK RTD Module
|
|
215
|
+
│ (Extract page content)
|
|
216
|
+
│ (Get previous ad info)
|
|
217
|
+
│ ↓
|
|
218
|
+
│ Returns: categories, keywords, sentiment
|
|
219
|
+
│ ↓
|
|
220
|
+
(enrich bid request with ortb2)
|
|
221
|
+
│
|
|
222
|
+
├──→ SSP 1 ──→ DSPs (use contextual signals)
|
|
223
|
+
├──→ SSP 2 ──→ DSPs (use adjacency data)
|
|
224
|
+
└──→ SSP N
|
|
225
|
+
|
|
226
|
+
(collect bids)
|
|
227
|
+
│
|
|
228
|
+
▼
|
|
229
|
+
AD SERVER
|
|
230
|
+
│
|
|
231
|
+
▼
|
|
232
|
+
RELEVANT AD
|
|
233
|
+
```
|
|
330
234
|
|
|
331
|
-
|
|
332
|
-
- **Frequency Capping**: Build frequency cap rules based on previous impressions
|
|
333
|
-
- **Competitive Separation**: Avoid showing competing brands consecutively
|
|
334
|
-
- **Enhanced User Experience**: Improve ad diversity and relevance
|
|
235
|
+
---
|
|
335
236
|
|
|
336
|
-
|
|
237
|
+
## Content Modes
|
|
337
238
|
|
|
338
|
-
|
|
339
|
-
2. **Lightweight Storage**: Data is stored in memory + localStorage (privacy-safe, no PII)
|
|
340
|
-
3. **Targeting Keys**: Previous ad data is automatically injected into subsequent bid requests
|
|
239
|
+
### Page Context (Default)
|
|
341
240
|
|
|
342
|
-
|
|
241
|
+
```javascript
|
|
242
|
+
params: {
|
|
243
|
+
mode: 'page', // Analyze article/page content
|
|
244
|
+
// ...
|
|
245
|
+
}
|
|
246
|
+
```
|
|
343
247
|
|
|
344
|
-
|
|
345
|
-
|-------|-------------|---------|
|
|
346
|
-
| `creativeId` | Winning creative ID | `"12345"` |
|
|
347
|
-
| `bidder` | Winning bidder code | `"appnexus"` |
|
|
348
|
-
| `adUnitCode` | Ad unit that served the ad | `"sidebar-1"` |
|
|
349
|
-
| `categories` | IAB categories of the ad | `["IAB18-1", "IAB12-3"]` |
|
|
350
|
-
| `timestamp` | When the ad was served | `1697123456789` |
|
|
248
|
+
### Video Context
|
|
351
249
|
|
|
352
|
-
|
|
250
|
+
```javascript
|
|
251
|
+
params: {
|
|
252
|
+
mode: 'video',
|
|
253
|
+
videoSelector: '#main-video', // CSS selector
|
|
254
|
+
// ...
|
|
255
|
+
}
|
|
256
|
+
```
|
|
353
257
|
|
|
354
|
-
|
|
258
|
+
### Auto-Detection
|
|
355
259
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
260
|
+
```javascript
|
|
261
|
+
params: {
|
|
262
|
+
mode: 'auto', // Automatically detect content type
|
|
263
|
+
// ...
|
|
264
|
+
}
|
|
265
|
+
```
|
|
360
266
|
|
|
361
|
-
|
|
267
|
+
---
|
|
362
268
|
|
|
363
|
-
|
|
269
|
+
## Event Callbacks
|
|
364
270
|
|
|
365
271
|
```javascript
|
|
366
|
-
//
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
// Example: Competitive separation
|
|
372
|
-
const prevCategories = bidRequest.ortb2Imp.ext.data.hb_mixpeek_prev_cat?.split(',') || []
|
|
373
|
-
if (prevCategories.includes('IAB18-1') && currentAd.category === 'IAB18-1') {
|
|
374
|
-
// Don't show competing fashion ads back-to-back
|
|
375
|
-
}
|
|
376
|
-
```
|
|
272
|
+
// Context ready
|
|
273
|
+
pbjs.onEvent('mixpeekContextReady', function(context) {
|
|
274
|
+
console.log('Category:', context.taxonomy?.label)
|
|
275
|
+
console.log('Keywords:', context.keywords)
|
|
276
|
+
})
|
|
377
277
|
|
|
378
|
-
|
|
278
|
+
// Error handling
|
|
279
|
+
pbjs.onEvent('mixpeekContextError', function(error) {
|
|
280
|
+
// Errors don't block auction (graceful degradation)
|
|
281
|
+
console.error('Mixpeek error:', error)
|
|
282
|
+
})
|
|
379
283
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
284
|
+
// Cache hit
|
|
285
|
+
pbjs.onEvent('mixpeekContextCached', function(data) {
|
|
286
|
+
console.log('Using cached context')
|
|
287
|
+
})
|
|
288
|
+
```
|
|
385
289
|
|
|
386
|
-
|
|
290
|
+
---
|
|
387
291
|
|
|
388
|
-
|
|
292
|
+
## Testing
|
|
389
293
|
|
|
390
|
-
```
|
|
391
|
-
|
|
294
|
+
```bash
|
|
295
|
+
# Run all tests
|
|
296
|
+
npm test
|
|
392
297
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
298
|
+
# Run with live API (requires credentials)
|
|
299
|
+
MIXPEEK_API_KEY=your_key \
|
|
300
|
+
MIXPEEK_NAMESPACE=ns_xxxxx \
|
|
301
|
+
MIXPEEK_COLLECTION_ID=col_xxxxx \
|
|
302
|
+
npm run test:live
|
|
396
303
|
|
|
397
|
-
|
|
398
|
-
|
|
304
|
+
# Coverage report
|
|
305
|
+
npm run test:coverage
|
|
399
306
|
```
|
|
400
307
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
- **No PII**: The adapter never sends user identifiers or cookies
|
|
404
|
-
- **Content-Only**: Only page/video content is analyzed
|
|
405
|
-
- **HTTPS**: All API calls use TLS encryption
|
|
406
|
-
- **API Key Safety**: Store API keys securely (environment variables, server-side rendering)
|
|
407
|
-
- **GDPR/CCPA Compliant**: Contextual targeting doesn't require user consent
|
|
308
|
+
---
|
|
408
309
|
|
|
409
|
-
##
|
|
310
|
+
## Documentation
|
|
410
311
|
|
|
411
|
-
### User Guides
|
|
412
312
|
- [Quick Start](QUICKSTART.md) - Get running in 5 minutes
|
|
413
|
-
- [Integration Guide](docs/integration-guide.md) - Step-by-step
|
|
414
|
-
- [API Reference](docs/api-reference.md) - Complete API
|
|
313
|
+
- [Integration Guide](docs/integration-guide.md) - Step-by-step setup
|
|
314
|
+
- [API Reference](docs/api-reference.md) - Complete API docs
|
|
415
315
|
- [Testing Guide](TESTING.md) - How to test the adapter
|
|
416
|
-
- [Endpoint Configuration](ENDPOINTS.md) - Configure API endpoints
|
|
417
|
-
- [Health Check](docs/health-check.md) - Health check configuration
|
|
418
316
|
|
|
419
|
-
|
|
420
|
-
- [Mixpeek API Docs](https://docs.mixpeek.com) - Platform documentation
|
|
421
|
-
- [Internal Planning](tasks/) - Gap analysis & implementation plans (internal)
|
|
317
|
+
---
|
|
422
318
|
|
|
423
|
-
##
|
|
319
|
+
## Support
|
|
424
320
|
|
|
425
|
-
- **Email**: support@mixpeek.com
|
|
426
|
-
- **GitHub Issues**: [Create an issue](https://github.com/mixpeek/prebid/issues)
|
|
427
321
|
- **Documentation**: [docs.mixpeek.com](https://docs.mixpeek.com)
|
|
428
|
-
- **
|
|
429
|
-
|
|
430
|
-
## 📄 License
|
|
431
|
-
|
|
432
|
-
Apache 2.0 - see [LICENSE](LICENSE) file for details.
|
|
322
|
+
- **GitHub Issues**: [Create an issue](https://github.com/mixpeek/prebid/issues)
|
|
323
|
+
- **Email**: support@mixpeek.com
|
|
433
324
|
|
|
434
|
-
|
|
325
|
+
---
|
|
435
326
|
|
|
436
|
-
|
|
327
|
+
## License
|
|
437
328
|
|
|
438
|
-
|
|
329
|
+
Apache 2.0 - see [LICENSE](LICENSE)
|
|
439
330
|
|
|
331
|
+
Built by [Mixpeek](https://mixpeek.com) | Integrates with [Prebid.js](https://prebid.org)
|