@mixpeek/prebid 1.0.0 → 1.0.1
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 +211 -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,317 @@
|
|
|
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
|
+
---
|
|
25
28
|
|
|
26
|
-
|
|
29
|
+
## Why Use This
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
1. **Ad Adjacency Awareness** - Competitive separation, no repeat creatives
|
|
32
|
+
2. **Privacy-First Contextual Targeting** - No cookies, no user tracking
|
|
33
|
+
3. **IAB Taxonomy Classification** - Content categorization (v3.0)
|
|
34
|
+
4. **Brand Safety Scoring** - Real-time sentiment analysis
|
|
35
|
+
5. **Multimodal Analysis** - Text, image, video content understanding
|
|
36
|
+
6. **Sub-100ms RTD Performance** - Optimized for header bidding latency
|
|
37
|
+
|
|
38
|
+
---
|
|
31
39
|
|
|
32
|
-
|
|
40
|
+
## Minimal Setup (Copy-Paste Ready)
|
|
33
41
|
|
|
34
42
|
```javascript
|
|
35
|
-
// 1. Include the Mixpeek RTD module
|
|
36
43
|
import '@mixpeek/prebid'
|
|
37
44
|
|
|
38
|
-
// 2. Configure Mixpeek as an RTD provider
|
|
39
45
|
pbjs.setConfig({
|
|
40
46
|
realTimeData: {
|
|
41
|
-
auctionDelay: 250, // Max time to wait for contextual data (ms)
|
|
42
47
|
dataProviders: [{
|
|
43
48
|
name: 'mixpeek',
|
|
44
|
-
waitForIt: true, // Wait for Mixpeek before starting auction
|
|
45
49
|
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
|
|
50
|
+
apiKey: 'YOUR_API_KEY',
|
|
51
|
+
collectionId: 'YOUR_COLLECTION_ID',
|
|
52
|
+
namespace: 'YOUR_NAMESPACE'
|
|
58
53
|
}
|
|
59
54
|
}]
|
|
60
55
|
}
|
|
61
56
|
})
|
|
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
57
|
```
|
|
71
58
|
|
|
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
|
|
59
|
+
That's it. The RTD module automatically enriches all bid requests.
|
|
78
60
|
|
|
79
|
-
|
|
61
|
+
---
|
|
80
62
|
|
|
81
|
-
|
|
63
|
+
## Installation
|
|
82
64
|
|
|
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
|
|
65
|
+
```bash
|
|
66
|
+
npm install @mixpeek/prebid
|
|
67
|
+
```
|
|
90
68
|
|
|
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 |
|
|
69
|
+
## Prerequisites
|
|
105
70
|
|
|
106
|
-
|
|
71
|
+
1. **Mixpeek Account** - Sign up at [mixpeek.com](https://mixpeek.com/start)
|
|
72
|
+
2. **API Key** - Generate in your Mixpeek dashboard
|
|
73
|
+
3. **Collection** - Create a collection with feature extractors
|
|
74
|
+
4. **Namespace** - Your namespace ID (format: `ns_xxxxx`)
|
|
75
|
+
5. **Prebid.js** - Version 6.0.0 or higher
|
|
107
76
|
|
|
108
|
-
|
|
77
|
+
---
|
|
109
78
|
|
|
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
|
-
```
|
|
79
|
+
## Full Configuration
|
|
138
80
|
|
|
139
|
-
### Impression-Level Data (`ortb2Imp.ext.data`)
|
|
140
81
|
```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)
|
|
82
|
+
import '@mixpeek/prebid'
|
|
163
83
|
|
|
164
|
-
```javascript
|
|
165
84
|
pbjs.setConfig({
|
|
166
85
|
realTimeData: {
|
|
167
|
-
auctionDelay: 250,
|
|
86
|
+
auctionDelay: 250, // Max wait for contextual data (ms)
|
|
168
87
|
dataProviders: [{
|
|
169
88
|
name: 'mixpeek',
|
|
170
|
-
waitForIt: true,
|
|
89
|
+
waitForIt: true, // Wait for Mixpeek before auction
|
|
171
90
|
params: {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
91
|
+
// Required
|
|
92
|
+
apiKey: 'YOUR_API_KEY',
|
|
93
|
+
collectionId: 'YOUR_COLLECTION_ID',
|
|
94
|
+
namespace: 'YOUR_NAMESPACE', // e.g., 'ns_abc123'
|
|
95
|
+
|
|
96
|
+
// Optional
|
|
97
|
+
endpoint: 'https://api.mixpeek.com', // Default
|
|
98
|
+
mode: 'auto', // 'page', 'video', 'image', or 'auto'
|
|
99
|
+
timeout: 250, // API timeout in ms
|
|
100
|
+
cacheTTL: 300, // Cache TTL in seconds
|
|
101
|
+
enableCache: true, // Enable local caching
|
|
102
|
+
debug: false // Enable debug logging
|
|
176
103
|
}
|
|
177
104
|
}]
|
|
178
105
|
}
|
|
179
106
|
})
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
### Video Context (Pre-roll, Mid-roll)
|
|
183
107
|
|
|
184
|
-
|
|
185
|
-
pbjs.
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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
|
-
}]
|
|
108
|
+
// Bids are automatically enriched with contextual data
|
|
109
|
+
pbjs.requestBids({
|
|
110
|
+
adUnits: [...],
|
|
111
|
+
bidsBackHandler: function(bids) {
|
|
112
|
+
// Bids now include Mixpeek contextual data in ortb2
|
|
199
113
|
}
|
|
200
114
|
})
|
|
201
115
|
```
|
|
202
116
|
|
|
203
|
-
|
|
117
|
+
---
|
|
204
118
|
|
|
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
|
-
```
|
|
119
|
+
## Ad Adjacency Awareness (Key Feature)
|
|
222
120
|
|
|
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
|
-
```
|
|
121
|
+
The adapter automatically tracks previously served ads to enable:
|
|
248
122
|
|
|
249
|
-
|
|
123
|
+
- **Competitive Separation** - Avoid showing competing brands consecutively
|
|
124
|
+
- **Creative Frequency** - Prevent the same ad from showing repeatedly
|
|
125
|
+
- **Category Diversity** - Improve ad variety for better user experience
|
|
250
126
|
|
|
251
|
-
|
|
252
|
-
# Run all tests
|
|
253
|
-
npm test
|
|
127
|
+
### How SSPs/DSPs Use It
|
|
254
128
|
|
|
255
|
-
|
|
256
|
-
|
|
129
|
+
```javascript
|
|
130
|
+
// Data automatically injected into ortb2Imp.ext.data:
|
|
131
|
+
{
|
|
132
|
+
"hb_mixpeek_prev_creative": "12345", // Last creative ID
|
|
133
|
+
"hb_mixpeek_prev_bidder": "appnexus", // Last winning bidder
|
|
134
|
+
"hb_mixpeek_prev_adunit": "sidebar-1", // Last ad unit
|
|
135
|
+
"hb_mixpeek_prev_cat": "IAB18-1,IAB12-3" // Last ad categories
|
|
136
|
+
}
|
|
257
137
|
|
|
258
|
-
|
|
259
|
-
|
|
138
|
+
// DSP can use for competitive separation:
|
|
139
|
+
if (prevCategories.includes('IAB18-1') && currentAd.category === 'IAB18-1') {
|
|
140
|
+
// Don't show competing fashion ads back-to-back
|
|
141
|
+
}
|
|
260
142
|
```
|
|
261
143
|
|
|
262
|
-
|
|
144
|
+
### Privacy Safe
|
|
263
145
|
|
|
264
|
-
|
|
146
|
+
- No user tracking or identifiers
|
|
147
|
+
- Only ad metadata stored (< 200 bytes)
|
|
148
|
+
- Session-scoped, localStorage with memory fallback
|
|
149
|
+
- GDPR/CCPA compliant (contextual, not behavioral)
|
|
265
150
|
|
|
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
|
-
```
|
|
151
|
+
---
|
|
290
152
|
|
|
291
|
-
|
|
153
|
+
## OpenRTB 2.6 Output
|
|
154
|
+
|
|
155
|
+
### Site-Level Data (`ortb2.site.content`)
|
|
292
156
|
|
|
293
157
|
```javascript
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
158
|
+
{
|
|
159
|
+
"site": {
|
|
160
|
+
"content": {
|
|
161
|
+
"cat": ["IAB19-11"], // IAB Content Categories
|
|
162
|
+
"cattax": 6, // IAB Taxonomy v3.0
|
|
163
|
+
"genre": "Technology", // Human-readable category
|
|
164
|
+
"keywords": "ai,technology,ml", // Extracted keywords
|
|
165
|
+
"language": "en", // Content language
|
|
166
|
+
"ext": {
|
|
167
|
+
"data": {
|
|
168
|
+
"mixpeek": {
|
|
169
|
+
"score": 0.94, // Classification confidence
|
|
170
|
+
"brandSafety": 0.98, // Brand safety score
|
|
171
|
+
"sentiment": "positive" // Content sentiment
|
|
172
|
+
}
|
|
306
173
|
}
|
|
307
|
-
}
|
|
174
|
+
}
|
|
308
175
|
}
|
|
309
|
-
}
|
|
176
|
+
}
|
|
310
177
|
}
|
|
311
178
|
```
|
|
312
179
|
|
|
313
|
-
###
|
|
180
|
+
### Impression-Level Keys (`ortb2Imp.ext.data`)
|
|
314
181
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
182
|
+
| Key | Description | Example |
|
|
183
|
+
|-----|-------------|---------|
|
|
184
|
+
| `hb_mixpeek_category` | Content category | `"Technology"` |
|
|
185
|
+
| `hb_mixpeek_score` | Classification confidence | `"0.94"` |
|
|
186
|
+
| `hb_mixpeek_safety` | Brand safety score | `"0.98"` |
|
|
187
|
+
| `hb_mixpeek_keywords` | Extracted keywords | `"AI,ML,tech"` |
|
|
188
|
+
| `hb_mixpeek_sentiment` | Content sentiment | `"positive"` |
|
|
189
|
+
| `hb_mixpeek_prev_creative` | Last creative ID | `"12345"` |
|
|
190
|
+
| `hb_mixpeek_prev_bidder` | Last winning bidder | `"appnexus"` |
|
|
191
|
+
| `hb_mixpeek_prev_cat` | Last ad categories | `"IAB18-1"` |
|
|
320
192
|
|
|
321
|
-
|
|
322
|
-
console.error('Mixpeek context error:', error)
|
|
323
|
-
// Custom error handling
|
|
324
|
-
})
|
|
325
|
-
```
|
|
193
|
+
---
|
|
326
194
|
|
|
327
|
-
##
|
|
195
|
+
## How It Works
|
|
328
196
|
|
|
329
|
-
|
|
197
|
+
```
|
|
198
|
+
USER → WEBSITE → PREBID.JS
|
|
199
|
+
│
|
|
200
|
+
├──→ MIXPEEK RTD Module
|
|
201
|
+
│ (Extract page content)
|
|
202
|
+
│ (Get previous ad info)
|
|
203
|
+
│ ↓
|
|
204
|
+
│ Returns: categories, keywords, sentiment
|
|
205
|
+
│ ↓
|
|
206
|
+
(enrich bid request with ortb2)
|
|
207
|
+
│
|
|
208
|
+
├──→ SSP 1 ──→ DSPs (use contextual signals)
|
|
209
|
+
├──→ SSP 2 ──→ DSPs (use adjacency data)
|
|
210
|
+
└──→ SSP N
|
|
211
|
+
|
|
212
|
+
(collect bids)
|
|
213
|
+
│
|
|
214
|
+
▼
|
|
215
|
+
AD SERVER
|
|
216
|
+
│
|
|
217
|
+
▼
|
|
218
|
+
RELEVANT AD
|
|
219
|
+
```
|
|
330
220
|
|
|
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
|
|
221
|
+
---
|
|
335
222
|
|
|
336
|
-
|
|
223
|
+
## Content Modes
|
|
337
224
|
|
|
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
|
|
225
|
+
### Page Context (Default)
|
|
341
226
|
|
|
342
|
-
|
|
227
|
+
```javascript
|
|
228
|
+
params: {
|
|
229
|
+
mode: 'page', // Analyze article/page content
|
|
230
|
+
// ...
|
|
231
|
+
}
|
|
232
|
+
```
|
|
343
233
|
|
|
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` |
|
|
234
|
+
### Video Context
|
|
351
235
|
|
|
352
|
-
|
|
236
|
+
```javascript
|
|
237
|
+
params: {
|
|
238
|
+
mode: 'video',
|
|
239
|
+
videoSelector: '#main-video', // CSS selector
|
|
240
|
+
// ...
|
|
241
|
+
}
|
|
242
|
+
```
|
|
353
243
|
|
|
354
|
-
|
|
244
|
+
### Auto-Detection
|
|
355
245
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
246
|
+
```javascript
|
|
247
|
+
params: {
|
|
248
|
+
mode: 'auto', // Automatically detect content type
|
|
249
|
+
// ...
|
|
250
|
+
}
|
|
251
|
+
```
|
|
360
252
|
|
|
361
|
-
|
|
253
|
+
---
|
|
362
254
|
|
|
363
|
-
|
|
255
|
+
## Event Callbacks
|
|
364
256
|
|
|
365
257
|
```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
|
-
```
|
|
258
|
+
// Context ready
|
|
259
|
+
pbjs.onEvent('mixpeekContextReady', function(context) {
|
|
260
|
+
console.log('Category:', context.taxonomy?.label)
|
|
261
|
+
console.log('Keywords:', context.keywords)
|
|
262
|
+
})
|
|
377
263
|
|
|
378
|
-
|
|
264
|
+
// Error handling
|
|
265
|
+
pbjs.onEvent('mixpeekContextError', function(error) {
|
|
266
|
+
// Errors don't block auction (graceful degradation)
|
|
267
|
+
console.error('Mixpeek error:', error)
|
|
268
|
+
})
|
|
379
269
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
270
|
+
// Cache hit
|
|
271
|
+
pbjs.onEvent('mixpeekContextCached', function(data) {
|
|
272
|
+
console.log('Using cached context')
|
|
273
|
+
})
|
|
274
|
+
```
|
|
385
275
|
|
|
386
|
-
|
|
276
|
+
---
|
|
387
277
|
|
|
388
|
-
|
|
278
|
+
## Testing
|
|
389
279
|
|
|
390
|
-
```
|
|
391
|
-
|
|
280
|
+
```bash
|
|
281
|
+
# Run all tests
|
|
282
|
+
npm test
|
|
392
283
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
284
|
+
# Run with live API (requires credentials)
|
|
285
|
+
MIXPEEK_API_KEY=your_key \
|
|
286
|
+
MIXPEEK_NAMESPACE=ns_xxxxx \
|
|
287
|
+
MIXPEEK_COLLECTION_ID=col_xxxxx \
|
|
288
|
+
npm run test:live
|
|
396
289
|
|
|
397
|
-
|
|
398
|
-
|
|
290
|
+
# Coverage report
|
|
291
|
+
npm run test:coverage
|
|
399
292
|
```
|
|
400
293
|
|
|
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
|
|
294
|
+
---
|
|
408
295
|
|
|
409
|
-
##
|
|
296
|
+
## Documentation
|
|
410
297
|
|
|
411
|
-
### User Guides
|
|
412
298
|
- [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
|
|
299
|
+
- [Integration Guide](docs/integration-guide.md) - Step-by-step setup
|
|
300
|
+
- [API Reference](docs/api-reference.md) - Complete API docs
|
|
415
301
|
- [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
302
|
|
|
419
|
-
|
|
420
|
-
- [Mixpeek API Docs](https://docs.mixpeek.com) - Platform documentation
|
|
421
|
-
- [Internal Planning](tasks/) - Gap analysis & implementation plans (internal)
|
|
303
|
+
---
|
|
422
304
|
|
|
423
|
-
##
|
|
305
|
+
## Support
|
|
424
306
|
|
|
425
|
-
- **Email**: support@mixpeek.com
|
|
426
|
-
- **GitHub Issues**: [Create an issue](https://github.com/mixpeek/prebid/issues)
|
|
427
307
|
- **Documentation**: [docs.mixpeek.com](https://docs.mixpeek.com)
|
|
428
|
-
- **
|
|
429
|
-
|
|
430
|
-
## 📄 License
|
|
431
|
-
|
|
432
|
-
Apache 2.0 - see [LICENSE](LICENSE) file for details.
|
|
308
|
+
- **GitHub Issues**: [Create an issue](https://github.com/mixpeek/prebid/issues)
|
|
309
|
+
- **Email**: support@mixpeek.com
|
|
433
310
|
|
|
434
|
-
|
|
311
|
+
---
|
|
435
312
|
|
|
436
|
-
|
|
313
|
+
## License
|
|
437
314
|
|
|
438
|
-
|
|
315
|
+
Apache 2.0 - see [LICENSE](LICENSE)
|
|
439
316
|
|
|
317
|
+
Built by [Mixpeek](https://mixpeek.com) | Integrates with [Prebid.js](https://prebid.org)
|