@the_ro_show/agent-ads-sdk 0.13.0 → 0.13.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/README.md CHANGED
@@ -1,450 +1,242 @@
1
- # AttentionMarket
1
+ # AttentionMarket SDK
2
2
 
3
3
  [![npm version](https://badge.fury.io/js/@the_ro_show%2Fagent-ads-sdk.svg)](https://www.npmjs.com/package/@the_ro_show/agent-ads-sdk)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
- **The ad exchange for the AI era. Monetize your chatbot, AI assistant, or AI agent with one API call.**
6
+ Monetize your AI application with contextual advertising. AttentionMarket matches user intent with relevant sponsored content, enabling you to generate revenue from every conversation.
7
7
 
8
- Works on every platform — iOS, Android, Web, Voice, Discord, WhatsApp, Node.js, Python, and anything else that can make an HTTP request.
9
-
10
- ---
11
-
12
- ## Two Ways To Earn
13
-
14
- ### 1. Show ads in your chatbot or AI assistant
15
- Your app has conversations. Users ask questions. You send us the context, we return a relevant ad, you earn $5–$150 when the user clicks.
16
-
17
- ### 2. List your AI agent on the exchange
18
- You built a specialized AI agent. Other AI agents need what you offer. List it on AttentionMarket and earn every time another agent calls yours.
19
-
20
- ---
21
-
22
- ## Quick Start — 30 Seconds
23
-
24
- Get your API keys at [api.attentionmarket.ai](https://api.attentionmarket.ai), then:
8
+ ## Installation
25
9
 
26
10
  ```bash
27
- curl -X POST https://peruwnbrqkvmrldhpoom.supabase.co/functions/v1/decide \
28
- -H "X-AM-API-Key: am_live_YOUR_KEY" \
29
- -H "Content-Type: application/json" \
30
- -d '{"context": "I need car insurance"}'
31
- ```
32
-
33
- **Response:**
34
- ```json
35
- {
36
- "status": "filled",
37
- "units": [{
38
- "suggestion": {
39
- "title": "Get 20% off car insurance",
40
- "body": "Compare quotes in minutes",
41
- "cta": "Get a Quote",
42
- "tracking_url": "https://..."
43
- }
44
- }]
45
- }
11
+ npm install @the_ro_show/agent-ads-sdk
46
12
  ```
47
13
 
48
- Show `suggestion.title` and `suggestion.body` to your user. When they click, send them to `tracking_url`. That's it — you get paid automatically.
49
-
50
- ---
14
+ ## Quick Start
51
15
 
52
- ## 🚨 CRITICAL: Impression Tracking Required (v0.9.0+)
53
-
54
- **As of v0.9.0, impression tracking is REQUIRED to earn revenue from clicks.**
55
-
56
- When you show an ad to a user, you must track an impression. Clicks without prior impressions will:
57
- - ✅ Still redirect the user to the advertiser (good UX)
58
- - ❌ NOT charge the advertiser
59
- - ❌ NOT credit your earnings
60
-
61
- ### Why This Matters
62
-
63
- This prevents developers from filtering ads client-side and still earning revenue from clicks. It ensures clean data and accurate quality metrics for the advertising exchange.
64
-
65
- ### Using `decideFromContext()` (Recommended)
16
+ ```typescript
17
+ import { AttentionMarketClient } from '@the_ro_show/agent-ads-sdk';
66
18
 
67
- **Good news:** If you're using the SDK's `decideFromContext()` method, impressions are tracked **automatically**. No changes needed.
19
+ const client = new AttentionMarketClient({
20
+ apiKey: 'am_live_YOUR_KEY',
21
+ agentId: 'agt_YOUR_AGENT_ID'
22
+ });
68
23
 
69
- ```typescript
70
- // Impressions tracked automatically ✅
24
+ // Request a contextual ad
71
25
  const ad = await client.decideFromContext({
72
- userMessage: "I need car insurance",
26
+ userMessage: "I'm looking for car insurance",
73
27
  placement: 'sponsored_suggestion'
74
28
  });
75
29
 
76
30
  if (ad) {
77
- // Show ad to user
78
- displayAd(ad);
31
+ console.log(ad.creative.title); // "Get 20% off car insurance"
32
+ console.log(ad.creative.body); // "Compare quotes from top providers"
33
+ console.log(ad.click_url); // Auto-tracked click URL
79
34
  }
80
35
  ```
81
36
 
82
- ### Using Raw HTTP or `decide()` (Manual Tracking Required)
37
+ ## Authentication
83
38
 
84
- If you're making raw HTTP calls or using the low-level `decide()` method, you **must manually track impressions**:
39
+ All API requests require authentication via an API key. Get your keys at [api.attentionmarket.ai](https://api.attentionmarket.ai).
85
40
 
86
- ```typescript
87
- // Step 1: Get ad
88
- const response = await fetch('https://peruwnbrqkvmrldhpoom.supabase.co/functions/v1/decide', {
89
- method: 'POST',
90
- headers: { 'X-AM-API-Key': 'am_live_...' },
91
- body: JSON.stringify({ context: 'I need car insurance' })
92
- });
93
- const data = await response.json();
94
-
95
- if (data.status === 'filled') {
96
- const ad = data.units[0];
97
-
98
- // Step 2: Track impression IMMEDIATELY when showing ad ✅
99
- await fetch('https://peruwnbrqkvmrldhpoom.supabase.co/functions/v1/event', {
100
- method: 'POST',
101
- headers: { 'X-AM-API-Key': 'am_live_...' },
102
- body: JSON.stringify({
103
- event_id: `evt_${crypto.randomUUID()}`,
104
- event_type: 'impression',
105
- occurred_at: new Date().toISOString(),
106
- agent_id: 'YOUR_AGENT_ID',
107
- unit_id: ad.unit_id,
108
- tracking_token: ad.tracking.token
109
- })
110
- });
111
-
112
- // Step 3: Now safe to show ad and earn from clicks
113
- displayAd(ad);
114
- }
115
- ```
116
-
117
- ---
118
-
119
- ## Use Case 1: Monetize Your Chatbot or AI Assistant
120
-
121
- Any app with conversations can earn revenue. Users ask questions with commercial intent every day — insurance, legal help, home services, travel, weddings, software. When they do, you can surface a relevant offer and earn.
122
-
123
- **Platforms this works on:**
41
+ ### API Key Types
124
42
 
125
- | Type | Examples |
126
- |------|---------|
127
- | **AI Assistants** | Mobile AI apps, productivity tools, personal assistants |
128
- | **Chatbots** | Website chat, customer service bots, FAQ bots |
129
- | **Messaging Bots** | WhatsApp, Discord, Telegram, Slack |
130
- | **Voice Assistants** | Alexa Skills, Google Actions, voice apps |
131
- | **AI Companions** | Coaching bots, tutors, entertainment assistants |
43
+ - **Test keys** (`am_test_...`) — Use in development. No charges, test data only.
44
+ - **Live keys** (`am_live_...`) — Use in production. Real advertisers, real revenue.
132
45
 
133
- ### How It Works
46
+ ### For Autonomous AI Agents (Clawdbots)
134
47
 
135
- ```
136
- User sends message → You call our API with context → We return a relevant ad
137
- → You show it → User clicks → You earn $5–$150
138
- ```
48
+ If you're building an autonomous agent with email access, you can set up AttentionMarket programmatically:
139
49
 
140
- ### Platform Examples
141
-
142
- **iOS (Swift)** — drop this into your Xcode project:
143
- ```swift
144
- // Initialize once
145
- let client = AttentionMarketClient(
146
- apiKey: "am_live_YOUR_KEY",
147
- agentId: "agt_YOUR_ID"
148
- )
149
-
150
- // Call after your AI responds
151
- if let ad = await client.getAd(for: userMessage) {
152
- showAdInChat(
153
- title: ad.suggestion.title,
154
- body: ad.suggestion.body,
155
- cta: ad.suggestion.cta,
156
- url: ad.suggestion.trackingUrl
157
- )
158
- }
159
- ```
160
- [Full iOS example →](./examples/ios/AttentionMarketClient.swift)
161
-
162
- ---
163
-
164
- **Android (Kotlin)**:
165
- ```kotlin
166
- val client = AttentionMarketClient(
167
- apiKey = "am_live_YOUR_KEY",
168
- agentId = "agt_YOUR_ID"
169
- )
170
-
171
- client.getAd(context = userMessage)?.let { ad ->
172
- showAdInChat(
173
- title = ad.suggestion.title,
174
- body = ad.suggestion.body,
175
- url = ad.suggestion.trackingUrl
176
- )
177
- }
178
- ```
179
- [Full Android example →](./examples/android/AttentionMarketClient.kt)
50
+ #### Email-Based Autonomous Setup
180
51
 
181
- ---
52
+ 1. **Sign up** at https://api.attentionmarket.ai/api/login
53
+ 2. **Monitor your inbox** for verification email from `noreply@worldview.network`
54
+ 3. **Extract verification link** from email body:
55
+ ```
56
+ https://gleam-start-page.lovable.app/api/verify-email?token={token}
57
+ ```
58
+ 4. **Visit the link** programmatically to verify your account
59
+ 5. **Login and retrieve credentials:**
182
60
 
183
- **Web (JavaScript)** — plain fetch, no dependencies:
184
- ```javascript
185
- const response = await fetch('https://peruwnbrqkvmrldhpoom.supabase.co/functions/v1/decide', {
61
+ ```typescript
62
+ // Login to get session
63
+ const loginResponse = await fetch('https://api.attentionmarket.ai/api/login', {
186
64
  method: 'POST',
187
- headers: {
188
- 'X-AM-API-Key': 'am_live_YOUR_KEY',
189
- 'Content-Type': 'application/json'
190
- },
191
- body: JSON.stringify({ context: userMessage })
65
+ headers: { 'Content-Type': 'application/json' },
66
+ body: JSON.stringify({
67
+ email: 'your-agent@example.com',
68
+ password: 'your_password'
69
+ })
192
70
  });
193
71
 
194
- const data = await response.json();
72
+ const sessionCookie = loginResponse.headers.get('set-cookie');
195
73
 
196
- if (data.status === 'filled') {
197
- const ad = data.units[0];
198
- showAdInChat(ad.suggestion.title, ad.suggestion.body, ad.suggestion.tracking_url);
199
- }
200
- ```
201
- [Full Web example →](./examples/web/attentionmarket.js)
202
-
203
- ---
74
+ // Fetch API credentials
75
+ const credsResponse = await fetch('https://api.attentionmarket.ai/developer-data', {
76
+ headers: { 'Cookie': sessionCookie }
77
+ });
204
78
 
205
- **Node.js (optional SDK)**:
206
- ```bash
207
- npm install @the_ro_show/agent-ads-sdk
208
- ```
209
- ```javascript
210
- import { AttentionMarketClient } from '@the_ro_show/agent-ads-sdk';
79
+ const { api_key, agent_id } = await credsResponse.json();
211
80
 
81
+ // Initialize SDK
212
82
  const client = new AttentionMarketClient({
213
- apiKey: process.env.ATTENTIONMARKET_API_KEY,
214
- agentId: process.env.ATTENTIONMARKET_AGENT_ID
83
+ apiKey: api_key, // am_test_...
84
+ agentId: agent_id // agt_...
215
85
  });
216
-
217
- const ad = await client.decideFromContext({ userMessage });
218
- if (ad) console.log(ad.creative.title, ad.click_url);
219
86
  ```
220
87
 
221
- ---
88
+ **Complete autonomous flow:** Sign up → Verify email → Retrieve credentials → Install SDK → Start earning revenue
222
89
 
223
- ## Use Case 2: List Your AI Agent on the Exchange
90
+ 📖 **[Full Personal AI Agent Setup Guide](https://docs.attentionmarket.ai/personal-agents)**
224
91
 
225
- You built a specialized AI agent — a legal document drafter, a wedding photographer booker, a travel planner, a code reviewer. Other AI assistants encounter users who need exactly what you do every day. List your agent on AttentionMarket and earn every time another agent calls yours.
92
+ ### SDK Configuration
226
93
 
227
- **This is the ad exchange for the agentic world.** Instead of humans clicking ads, AI agents discover and call specialized agents. You pay to be discovered. You earn when your agent gets used.
94
+ ```typescript
95
+ const client = new AttentionMarketClient({
96
+ apiKey: 'am_live_YOUR_KEY', // Required
97
+ agentId: 'agt_YOUR_AGENT_ID', // Required for decideFromContext()
98
+ baseUrl: 'https://peruwnbrqkvmrldhpoom.supabase.co/functions/v1', // Optional: defaults to production
99
+ timeoutMs: 4000, // Optional: request timeout in milliseconds
100
+ maxRetries: 2 // Optional: automatic retry count
101
+ });
102
+ ```
228
103
 
229
- ### How It Works
104
+ ## Core Concepts
230
105
 
231
- ```
232
- General assistant encounters a task it can't handle well
233
- → Calls AttentionMarket with context + response_type: "agent"
234
- → Exchange returns the best specialized agent for that task
235
- → General assistant calls your agent
236
- → You earn per call
237
- ```
106
+ ### Placements
238
107
 
239
- ### Example
108
+ A placement defines where ads appear in your application:
240
109
 
241
- **Developer A** built a general wedding planning AI assistant. A user asks: *"Can you find me a photographer in Austin under $3,000?"*
110
+ - `sponsored_suggestion` Conversational ad in chat flow (most common)
111
+ - `sponsored_block` — Dedicated ad section in UI
112
+ - `sponsored_tool` — AI agent service recommendation
242
113
 
243
- Developer A's assistant calls AttentionMarket:
244
- ```bash
245
- curl -X POST https://peruwnbrqkvmrldhpoom.supabase.co/functions/v1/decide \
246
- -H "X-AM-API-Key: am_live_DEV_A_KEY" \
247
- -H "Content-Type: application/json" \
248
- -d '{
249
- "context": "User needs a wedding photographer in Austin under $3,000",
250
- "response_type": "agent"
251
- }'
252
- ```
114
+ ### Ad Response Format
253
115
 
254
- **AttentionMarket returns Developer B's agent:**
255
- ```json
256
- {
257
- "status": "filled",
258
- "units": [{
259
- "agent_name": "WeddingPhoto AI",
260
- "capability": "Book wedding photographers by location and budget",
261
- "endpoint": "https://weddingphoto.ai/api/v1",
262
- "input_description": "Send: location (string), budget (number), date (string)",
263
- "output_description": "Returns: photographer name, price, booking URL",
264
- "tracking": {
265
- "call_url": "https://.../track-call/token"
266
- }
267
- }]
116
+ ```typescript
117
+ interface AdResponse {
118
+ request_id: string;
119
+ decision_id: string;
120
+ advertiser_id: string;
121
+ ad_type: 'link' | 'recommendation' | 'service';
122
+ payout: number; // Amount earned on conversion (in cents)
123
+
124
+ creative: {
125
+ title: string;
126
+ body: string;
127
+ cta: string;
128
+ };
129
+
130
+ click_url: string; // Tracked click URL (use this)
131
+ tracking_url?: string; // Server-side tracking URL
132
+ tracking_token: string; // For manual event tracking
133
+
134
+ disclosure: {
135
+ label: string;
136
+ explanation: string;
137
+ sponsor_name: string;
138
+ };
268
139
  }
269
140
  ```
270
141
 
271
- **Developer A** calls WeddingPhoto AI, gets a result, passes it back to the user.
272
- **Developer B** earns per call. **Developer A** earns a routing fee.
273
-
274
- ### List Your Agent
275
-
276
- Sign up as an advertiser at [api.attentionmarket.ai](https://api.attentionmarket.ai) and create an **Agent Ad** campaign:
277
-
278
- | Field | Description |
279
- |-------|-------------|
280
- | Agent Name | Display name (e.g. "WeddingPhoto AI") |
281
- | What does your agent do? | Natural language capability description — used for matching |
282
- | Endpoint URL | Where calling agents send requests |
283
- | What to send | Input format description |
284
- | What you return | Output format description |
285
- | Bid per call | What you pay each time your agent is called |
286
-
287
- **Matching is semantic** — describe your agent clearly in plain English and we handle the rest.
142
+ ### Impression Tracking
288
143
 
289
- ---
144
+ **Important:** As of v0.9.0, impression tracking is required to earn revenue from clicks.
290
145
 
291
- ## Revenue Model
146
+ The SDK automatically tracks impressions when using `decideFromContext()`. Clicks without prior impressions will redirect users but will not generate revenue.
292
147
 
293
- ### As a Developer (showing ads or routing to agents)
148
+ If using the raw `decide()` API, you must manually track impressions:
294
149
 
295
- - **70% revenue share** on every click or agent call you facilitate
296
- - Earn $5–$150 per click for ads
297
- - Earn a routing fee per agent-to-agent call
298
- - Monthly payouts via Stripe (minimum $100 balance)
299
- - Free to integrate — no setup fees, no monthly costs
300
-
301
- **Example earnings:**
302
-
303
- | Monthly clicks/calls | Avg payout | Monthly revenue |
304
- |---------------------|------------|-----------------|
305
- | 50 | $50 | $2,500 |
306
- | 200 | $50 | $10,000 |
307
- | 1,000 | $50 | $50,000 |
308
-
309
- ### As an Agent Advertiser (listing your agent)
150
+ ```typescript
151
+ await client.trackImpression({
152
+ agent_id: 'agt_YOUR_AGENT_ID',
153
+ request_id: ad.request_id,
154
+ decision_id: ad.decision_id,
155
+ unit_id: ad._ad.unit_id,
156
+ tracking_token: ad.tracking_token
157
+ });
158
+ ```
310
159
 
311
- - Pay per click (Link Ad) or pay per call (Agent Ad)
312
- - Second-price auction — you bid a max, you often pay less
313
- - Full budget controls — set a cap, campaign pauses when spent
314
- - Track spend and performance in the advertiser dashboard
160
+ ## Developer Controls
315
161
 
316
- ---
162
+ AttentionMarket provides fine-grained controls over ad selection, quality, and revenue optimization.
317
163
 
318
- ## 💰 Earn Bonuses with Feedback (v0.11.0+)
164
+ ### Quality and Brand Safety
319
165
 
320
- Send feedback on ad performance to earn **AI-validated conversion bonuses**.
166
+ #### Minimum Quality Score
321
167
 
322
- ### How It Works
168
+ Filter ads based on historical performance metrics. Quality scores range from 0.0 (worst) to 1.0 (best) and are calculated from click-through rates, conversion rates, and user feedback.
323
169
 
324
- 1. **Show an ad** to your user (impression tracked automatically in v0.9.0+)
325
- 2. **User clicks** You earn base click revenue
326
- 3. **Send user's actual response** → AI analyzes sentiment automatically
327
- 4. **Wait 7 days** System checks if user actually converted
328
- 5. **Get bonus or penalty based on AI prediction:**
329
- - ✅ Correct positive prediction → **+15% bonus**
330
- - ❌ Wrong positive prediction → **-5% penalty**
331
- - 🤷 Neutral/negative → no bonus/penalty (data only)
170
+ ```typescript
171
+ const ad = await client.decideFromContext({
172
+ userMessage: "I need legal help",
173
+ minQualityScore: 0.7 // Only show ads with quality >= 0.7
174
+ });
175
+ ```
332
176
 
333
- ### What's New in v0.11.0
177
+ **Validation:** Must be a number between 0.0 and 1.0.
334
178
 
335
- **Backend now analyzes sentiment for you using AI:**
336
- - You just send the user's actual response text
337
- - No need to classify sentiment yourself
338
- - More consistent quality scores across all developers
339
- - Harder to game (can't spam "positive" on everything)
179
+ **Use cases:**
180
+ - Premium applications: `0.8+` for high-quality experiences only
181
+ - Brand-sensitive contexts: `0.7+` to avoid low-quality advertisers
182
+ - General applications: `0.5+` for balanced quality and fill rate
340
183
 
341
- ### Anti-Fraud Design
184
+ #### Category Filtering
342
185
 
343
- **Why penalties?** To prevent developers from gaming the system.
186
+ Control which advertiser categories can appear using the IAB Content Taxonomy 3.0 (704 categories across 38 top-level verticals).
344
187
 
345
- - AI analyzes actual user responses (not developer claims)
346
- - Spamming fake positive responses = net loss
347
- - Accuracy = net gain (bonus > penalty when right)
348
- - System rewards honest data collection
188
+ ##### Allowed Categories
349
189
 
350
- ### Usage Example
190
+ Whitelist specific categories. Only ads from these categories will be shown.
351
191
 
352
192
  ```typescript
353
- import { AttentionMarketClient } from '@the_ro_show/agent-ads-sdk';
354
-
355
- const client = new AttentionMarketClient({
356
- apiKey: 'am_live_YOUR_KEY'
193
+ // Insurance comparison app: only show insurance ads
194
+ const ad = await client.decideFromContext({
195
+ userMessage: "I need car insurance",
196
+ allowedCategories: [31] // 31 = Auto Insurance (IAB category)
357
197
  });
358
198
 
359
- // Get and show ad
199
+ // Wedding planner: allow wedding + photography + food
360
200
  const ad = await client.decideFromContext({
361
- userMessage: "I need car insurance"
201
+ userMessage: "Help me plan my wedding",
202
+ allowedCategories: [
203
+ 603, // Weddings
204
+ 162, // Photography
205
+ 190 // Restaurants
206
+ ]
362
207
  });
363
-
364
- if (ad) {
365
- // User responds after clicking
366
- const userResponse = "This looks perfect! How do I sign up?";
367
-
368
- await client.sendFeedback({
369
- tracking_token: ad.tracking_token,
370
- user_response: userResponse,
371
- agent_response: "Here's the signup link...",
372
- additional_context: "User spent 2 minutes on offer page"
373
- });
374
-
375
- // AI will detect positive sentiment → +15% bonus if user converts
376
- }
377
208
  ```
378
209
 
379
- ### HTTP Example
210
+ ##### Blocked Categories
380
211
 
381
- ```bash
382
- curl -X POST https://peruwnbrqkvmrldhpoom.supabase.co/functions/v1/feedback \
383
- -H "X-AM-API-Key: am_live_YOUR_KEY" \
384
- -H "Content-Type: application/json" \
385
- -d '{
386
- "tracking_token": "eyJhbGc...",
387
- "user_response": "This looks great! How do I get started?",
388
- "agent_response": "Here is the signup process...",
389
- "additional_context": "User asked 3 follow-up questions"
390
- }'
391
- ```
392
-
393
- **Response:**
394
- ```json
395
- {
396
- "success": true,
397
- "sentiment_detected": "positive",
398
- "potential_bonus": "$1.05",
399
- "potential_penalty": "$0.35",
400
- "message": "Feedback recorded! Detected sentiment: positive. If correct (user converts within 7 days), you'll earn a +15% bonus. Wrong predictions incur a -5% penalty.",
401
- "resolution_date": "2026-02-28T12:00:00Z"
402
- }
403
- ```
404
-
405
- ### When To Send Feedback
406
-
407
- **Good signals to capture:**
408
- - User's actual text responses after clicking
409
- - Your agent's follow-up messages
410
- - Context about how long user engaged with the offer
411
- - Any follow-up questions or actions
412
-
413
- **Best practices:**
414
- - Send actual user text, not summaries
415
- - Include your agent's response for context
416
- - Only send when you have meaningful interaction data
417
- - Quality > quantity
418
-
419
- ---
420
-
421
- ## 🎛️ Developer Controls (v0.12.0+)
422
-
423
- Take control of what ads appear in your app with quality, category, and advertiser filters.
424
-
425
- ### Quality Score Filter
426
-
427
- Only show ads that meet your quality standards:
212
+ Blacklist specific categories. Ads from these categories will never be shown.
428
213
 
429
214
  ```typescript
215
+ // Block all sensitive content (gambling, adult, controversial)
430
216
  const ad = await client.decideFromContext({
431
- userMessage: "I need car insurance",
432
- minQualityScore: 0.8 // Only ads with 0.8+ quality
217
+ userMessage: "Help me with something",
218
+ blockedCategories: [601] // Blocks "Sensitive Topics" + all children
219
+ });
220
+
221
+ // Legal assistant: block competitor law firms
222
+ const ad = await client.decideFromContext({
223
+ userMessage: "I need legal help",
224
+ blockedCategories: [318] // Block "Legal Services"
433
225
  });
434
226
  ```
435
227
 
436
- **Use cases:**
437
- - **Premium apps**: Set 0.9+ for best-in-class ads only
438
- - **General apps**: Set 0.7+ for good balance
439
- - **High-volume apps**: Set 0.5+ to maximize fill rate
228
+ **Parent-child relationships:** Blocking a parent category automatically blocks all subcategories. For example, blocking category `1` (Automotive) blocks Auto Insurance, Auto Repair, Auto Parts, etc.
440
229
 
441
- ### Category Controls (IAB Taxonomy 3.0)
230
+ **Precedence:** If `allowedCategories` is set, `blockedCategories` is ignored.
442
231
 
443
- Filter ads using **IAB Content Taxonomy 3.0** - the industry-standard category system used by Google Ads, Microsoft Ads, and all major ad platforms.
232
+ **Validation rules:**
233
+ - `allowedCategories`: Must be a non-empty array of numbers or strings
234
+ - `blockedCategories`: Must be an array of numbers or strings
235
+ - Empty `allowedCategories: []` is rejected (would block all ads)
444
236
 
445
- **704 categories** across 38 top-level categories with 4-tier hierarchy.
237
+ **Note:** IAB category IDs (numbers) are recommended. Legacy string categories are deprecated and will be removed on 2026-06-01. Use the `getCategories()` API to discover category IDs.
446
238
 
447
- #### Discover Available Categories
239
+ ##### Discovering Categories
448
240
 
449
241
  ```typescript
450
242
  // Get all 38 top-level categories
@@ -466,244 +258,251 @@ insurance.categories.forEach(cat => {
466
258
  // Output: "Automotive > Auto Insurance", "Personal Finance > Insurance", etc.
467
259
  ```
468
260
 
469
- #### Filter by Category
261
+ #### Advertiser Blocklist
470
262
 
471
- Use IAB category IDs for precise control:
263
+ Block specific advertisers by ID (e.g., based on user feedback or competitive conflicts).
472
264
 
473
265
  ```typescript
474
- // Insurance app: Only show insurance ads (by IAB ID)
475
266
  const ad = await client.decideFromContext({
476
- userMessage: "I need car insurance",
477
- allowedCategories: [31] // 31 = Auto Insurance
267
+ userMessage: "I need legal help",
268
+ blockedAdvertisers: ['adv_abc123', 'adv_xyz789']
478
269
  });
270
+ ```
479
271
 
480
- // Block entire "Sensitive Topics" tree (IAB ID: 601)
481
- // This blocks all gambling, adult content, controversial topics, etc.
482
- const ad = await client.decideFromContext({
483
- userMessage: "Help me with something",
484
- blockedCategories: [601] // Blocks parent + all children
485
- });
272
+ **Validation:** Must be an array of non-empty strings (advertiser IDs).
273
+
274
+ Advertiser IDs are included in ad responses as `advertiser_id`.
275
+
276
+ ### Revenue Optimization
277
+
278
+ #### Minimum CPC Filter
279
+
280
+ Only show ads with bids at or above a specified cost-per-click threshold (in cents).
486
281
 
487
- // Wedding planner: Allow wedding + photography + food
282
+ ```typescript
488
283
  const ad = await client.decideFromContext({
489
- userMessage: "Help me plan my wedding",
490
- allowedCategories: [
491
- 603, // Weddings (Personal Celebrations)
492
- 162, // Photography (Hobbies & Interests)
493
- 190 // Restaurants (Food & Drink)
494
- ]
284
+ userMessage: "I need car insurance",
285
+ minCPC: 100 // Only ads bidding >= $1.00 per click
495
286
  });
496
287
  ```
497
288
 
498
- **Parent-Child Relationships:**
499
- - Blocking a parent category blocks ALL child categories automatically
500
- - Example: Blocking `1` (Automotive) blocks Auto Insurance, Auto Repair, etc.
501
- - Great for compliance: Block `601` (Sensitive Topics) to block gambling, adult, controversial in one go
289
+ **Validation:** Must be a non-negative number.
502
290
 
503
- **Note:** If `allowedCategories` is set, `blockedCategories` is ignored.
291
+ **Use cases:**
292
+ - Premium applications: `200+` for $2+ ads only
293
+ - High-value verticals: Filter out low-budget advertisers
294
+ - Revenue targets: Ensure minimum revenue per impression
295
+ - Lower fill rate tolerance: When you'd rather show nothing than a low-value ad
296
+
297
+ **Trade-off:** Higher thresholds = higher revenue per ad but lower fill rate.
504
298
 
505
- ### Advertiser Blocklist
299
+ #### Minimum Relevance Score
506
300
 
507
- Block specific advertisers based on user feedback:
301
+ Only show ads with semantic similarity at or above a threshold. Relevance scores range from 0.0 (unrelated) to 1.0 (perfect match) and are calculated using vector embeddings of user context and advertiser intent.
508
302
 
509
303
  ```typescript
510
304
  const ad = await client.decideFromContext({
511
- userMessage: "I need legal help",
512
- blockedAdvertisers: ['adv_abc123', 'adv_xyz789']
305
+ userMessage: "Help me plan my wedding",
306
+ minRelevanceScore: 0.8 // Only highly relevant ads
513
307
  });
514
308
  ```
515
309
 
516
- Get advertiser IDs from previous ad responses.
310
+ **Validation:** Must be a number between 0.0 and 1.0.
517
311
 
518
- ### Combined Controls
312
+ **Use cases:**
313
+ - Niche applications: `0.8+` for specialized content only (e.g., legal assistant)
314
+ - User experience priority: Filter out loosely related ads
315
+ - Context-sensitive placements: Ensure ads match conversation topic
316
+ - Brand-aligned content: Maintain thematic consistency
519
317
 
520
- Mix and match for precise control:
318
+ **Important:** This filter only applies to campaigns with semantic targeting. Keyword and automatic campaigns are not affected.
319
+
320
+ **Default threshold:** Backend applies a minimum threshold of 0.25 for all semantic campaigns (ads below this are never shown).
321
+
322
+ #### Ranking Strategy
323
+
324
+ Choose how ads are ranked when multiple ads match the request.
521
325
 
522
326
  ```typescript
327
+ // Revenue-optimized (default): highest bid wins
523
328
  const ad = await client.decideFromContext({
524
- userMessage: "I need car insurance",
525
- minQualityScore: 0.8, // High quality only
526
- blockedCategories: ['crypto'], // No crypto ads
527
- allowedCategories: ['insurance', 'finance'] // Relevant only
329
+ userMessage: "I need legal help",
330
+ optimizeFor: 'revenue' // Rank by bid × quality × relevance
528
331
  });
529
- ```
530
-
531
- ### HTTP Example
532
332
 
533
- ```bash
534
- curl -X POST https://peruwnbrqkvmrldhpoom.supabase.co/functions/v1/decide \
535
- -H "X-AM-API-Key: am_live_YOUR_KEY" \
536
- -H "Content-Type: application/json" \
537
- -d '{
538
- "context": "I need car insurance",
539
- "minQualityScore": 0.8,
540
- "blockedCategories": ["crypto", "gambling"]
541
- }'
333
+ // Relevance-optimized: best match wins
334
+ const ad = await client.decideFromContext({
335
+ userMessage: "I need legal help",
336
+ optimizeFor: 'relevance' // Rank by semantic similarity only
337
+ });
542
338
  ```
543
339
 
544
- ### Best Practices
340
+ **Validation:** Must be either `'revenue'` or `'relevance'`.
545
341
 
546
- **Quality Scores:**
547
- - Start with no filter, monitor what you get
548
- - Gradually increase threshold if quality is lacking
549
- - Higher thresholds = fewer ads but better quality
342
+ **How it works (second-price auction):**
550
343
 
551
- **Categories (IAB Taxonomy):**
552
- - Use `getCategories()` to discover the full 704-category taxonomy
553
- - Use `allowedCategories` for specialized apps (wedding planner = `[603]`, legal assistant = `[318]`)
554
- - Use `blockedCategories` for compliance (kids apps = block `[601]` Sensitive Topics)
555
- - Parent categories block all children automatically (block `[1]` Automotive = blocks all 40+ auto subcategories)
556
- - Full taxonomy reference: https://github.com/InteractiveAdvertisingBureau/Taxonomies
344
+ - **Revenue mode:** Winner is ranked by composite score (bid × quality × relevance), pays just enough to beat the next ad's composite score + $0.01
345
+ - **Relevance mode:** Winner is ranked by semantic similarity, pays just enough to beat the next ad in composite score space + $0.01
346
+ - **Price cap:** Winner never pays more than their max bid (auction integrity guaranteed)
347
+ - **Price floor:** Minimum clearing price of $0.25 ensures platform sustainability
557
348
 
558
- **Advertiser Blocking:**
559
- - Collect user feedback on specific ads
560
- - Block advertisers with multiple complaints
561
- - Use sparingly - too many blocks reduce fill rate
349
+ **Use cases:**
350
+ - General applications: `'revenue'` to maximize earnings
351
+ - Niche applications: `'relevance'` to prioritize perfect matches over high bids
352
+ - Premium experiences: Combine with high `minRelevanceScore` + `'relevance'` ranking
562
353
 
563
- ---
354
+ #### Combined Controls
564
355
 
565
- ## API Reference
356
+ Combine multiple controls for precise ad selection:
566
357
 
567
- ### `POST /v1/decide`
358
+ ```typescript
359
+ // Premium legal assistant: high relevance + high bids + category filter
360
+ const ad = await client.decideFromContext({
361
+ userMessage: "I need estate planning help",
362
+ minRelevanceScore: 0.8, // Only highly relevant
363
+ minCPC: 200, // Only $2+ bids
364
+ minQualityScore: 0.7, // Only high-quality advertisers
365
+ optimizeFor: 'relevance', // Best match wins
366
+ allowedCategories: [318] // Legal services only
367
+ });
368
+ ```
568
369
 
569
- **Base URL:** `https://peruwnbrqkvmrldhpoom.supabase.co/functions/v1/decide`
370
+ ## Advanced Features
570
371
 
571
- **Required header:** `X-AM-API-Key: am_live_...` or `am_test_...`
372
+ ### Multi-Turn Conversations
572
373
 
573
- ### Simple Request (recommended)
374
+ Include conversation history for better ad matching:
574
375
 
575
- ```json
576
- {
577
- "context": "I need car insurance"
578
- }
376
+ ```typescript
377
+ const ad = await client.decideFromContext({
378
+ userMessage: "What are my options?",
379
+ conversationHistory: [
380
+ "User: My car insurance is too expensive",
381
+ "Agent: I can help you compare rates",
382
+ "User: What are my options?"
383
+ ]
384
+ });
579
385
  ```
580
386
 
581
- ### Simple Request with Optional Fields
387
+ The SDK automatically limits history to the last 5 messages to prevent token overflow.
582
388
 
583
- ```json
584
- {
585
- "context": "I need car insurance",
586
- "country": "US",
587
- "language": "en",
588
- "platform": "ios"
589
- }
389
+ ### Geographic and Platform Targeting
390
+
391
+ ```typescript
392
+ const ad = await client.decideFromContext({
393
+ userMessage: "I need car insurance",
394
+ country: 'US',
395
+ language: 'en',
396
+ platform: 'ios' // 'web' | 'ios' | 'android' | 'desktop' | 'voice' | 'other'
397
+ });
590
398
  ```
591
399
 
592
- ### Request an Agent Instead of an Ad
400
+ ### Click Tracking
593
401
 
594
- ```json
595
- {
596
- "context": "User needs a wedding photographer in Austin",
597
- "response_type": "agent"
598
- }
402
+ Clicks are automatically tracked when users visit `click_url`. For manual tracking:
403
+
404
+ ```typescript
405
+ await client.trackClick({
406
+ agent_id: 'agt_YOUR_AGENT_ID',
407
+ request_id: ad.request_id,
408
+ decision_id: ad.decision_id,
409
+ unit_id: ad._ad.unit_id,
410
+ tracking_token: ad.tracking_token,
411
+ href: ad.click_url,
412
+ click_context: "User clicked 'Get a Quote' button"
413
+ });
599
414
  ```
600
415
 
601
- ### Advanced Request (full control)
602
-
603
- ```json
604
- {
605
- "request_id": "req_custom_123",
606
- "agent_id": "agt_YOUR_ID",
607
- "placement": "chat_inline",
608
- "opportunity": {
609
- "intent": { "taxonomy": "insurance.auto.quote" },
610
- "context": { "country": "US", "language": "en", "platform": "ios" }
611
- },
612
- "context": "I need car insurance"
613
- }
416
+ Simplified tracking from ad object:
417
+
418
+ ```typescript
419
+ await client.trackClickFromAd(ad, {
420
+ click_context: "User clicked on sponsored suggestion"
421
+ });
614
422
  ```
615
423
 
616
- ### Response: Ad Filled
617
-
618
- ```json
619
- {
620
- "request_id": "req_abc123",
621
- "decision_id": "dec_xyz789",
622
- "status": "filled",
623
- "ttl_ms": 300000,
624
- "units": [{
625
- "unit_id": "unit_123",
626
- "suggestion": {
627
- "title": "Get 20% off car insurance",
628
- "body": "Compare quotes in minutes",
629
- "cta": "Get a Quote",
630
- "tracking_url": "https://.../track-click/...",
631
- "action_url": "https://progressive.com/quote"
632
- },
633
- "tracking": {
634
- "token": "trk_abc",
635
- "impression_url": "https://.../event",
636
- "click_url": "https://.../click/trk_abc"
637
- }
638
- }]
639
- }
424
+ ### Conversion Tracking
425
+
426
+ Track conversions (purchases, signups, etc.) to improve advertiser ROI and your quality score:
427
+
428
+ ```typescript
429
+ await client.track({
430
+ event_id: `evt_${generateUUID()}`,
431
+ event_type: 'conversion',
432
+ occurred_at: new Date().toISOString(),
433
+ agent_id: 'agt_YOUR_AGENT_ID',
434
+ request_id: ad.request_id,
435
+ decision_id: ad.decision_id,
436
+ unit_id: ad._ad.unit_id,
437
+ tracking_token: ad.tracking_token,
438
+ metadata: {
439
+ conversion_value: 99.99,
440
+ conversion_type: 'purchase'
441
+ }
442
+ });
640
443
  ```
641
444
 
642
- ### Response: No Fill
445
+ ## Error Handling
446
+
447
+ The SDK throws errors for invalid configurations and failed requests:
643
448
 
644
- ```json
645
- {
646
- "status": "no_fill",
647
- "units": []
449
+ ```typescript
450
+ try {
451
+ const ad = await client.decideFromContext({
452
+ userMessage: "I need car insurance",
453
+ minQualityScore: -0.5 // Invalid: must be 0.0-1.0
454
+ });
455
+ } catch (error) {
456
+ console.error(error.message);
457
+ // Output: "minQualityScore must be a number between 0.0 and 1.0"
648
458
  }
649
459
  ```
650
460
 
651
- ### Key Response Fields
461
+ ### Validation Errors
652
462
 
653
- | Field | Use |
654
- |-------|-----|
655
- | `status` | `"filled"` or `"no_fill"` |
656
- | `suggestion.title` | Ad headline to show |
657
- | `suggestion.body` | Ad description to show |
658
- | `suggestion.cta` | Button text |
659
- | `suggestion.tracking_url` | **Send users here on click** — tracks automatically |
660
- | `suggestion.action_url` | Direct advertiser URL (display only, no tracking) |
463
+ The SDK validates all parameters before making API calls. Common validation errors:
661
464
 
662
- **Always use `tracking_url` for clicks.** This is how clicks are tracked and how you get paid.
465
+ - `minQualityScore must be a number between 0.0 and 1.0`
466
+ - `minCPC must be a non-negative number (cost-per-click in cents)`
467
+ - `minRelevanceScore must be a number between 0.0 and 1.0`
468
+ - `optimizeFor must be either "revenue" or "relevance"`
469
+ - `allowedCategories cannot be empty (would block all ads). Use blockedCategories to exclude specific categories, or omit to allow all.`
470
+ - `blockedAdvertisers must contain non-empty strings (advertiser IDs)`
663
471
 
664
- ### HTTP Status Codes
472
+ ### HTTP Errors
665
473
 
666
- | Code | Meaning |
667
- |------|---------|
668
- | `200` | Success — check `status` field |
669
- | `400` | Bad request — missing `context` field |
670
- | `401` | Invalid API key |
671
- | `429` | Rate limit exceeded (1,000 req/min) — retry after 60s |
672
- | `500` | Server error — retry with backoff |
474
+ The API returns standard HTTP status codes:
673
475
 
674
- ---
476
+ - `400 Bad Request` — Invalid parameters (see error message for details)
477
+ - `401 Unauthorized` — Missing or invalid API key
478
+ - `429 Too Many Requests` — Rate limit exceeded
479
+ - `500 Internal Server Error` — Server error (contact support if persistent)
675
480
 
676
- ## Testing
481
+ ## Rate Limits
677
482
 
678
- Use your test key (`am_test_...`) during development — same API, no real money moves.
483
+ - **Per IP:** 60 requests per minute
484
+ - **Per API key:** 100 requests per minute
679
485
 
680
- **Validate your setup:**
681
- ```bash
682
- node test-integration.js # Tests connectivity and credentials
683
- node validate-production.js # Full production readiness check (4/4 ✅)
684
- ```
685
-
686
- Download: [test-integration.js](./test-integration.js) | [validate-production.js](./validate-production.js)
486
+ Rate limits are enforced to prevent abuse and ensure fair resource allocation. If you need higher limits, contact support.
687
487
 
688
- ---
488
+ ## Testing
689
489
 
690
- ## Get Started
490
+ Use test API keys (`am_test_...`) for development and testing. Test keys:
691
491
 
692
- 1. **Sign up** at [api.attentionmarket.ai](https://api.attentionmarket.ai) — get your test key, live key, and agent ID
693
- 2. **Test it** run the curl above with your test key
694
- 3. **Integrate** add 5 lines to your existing chatbot or assistant
695
- 4. **Go live** swap test key for live key, start earning
492
+ - Return test ads with realistic data
493
+ - Do not charge advertisers
494
+ - Do not generate real revenue
495
+ - Have the same rate limits as live keys
696
496
 
697
- ---
497
+ Switch to live keys (`am_live_...`) when deploying to production.
698
498
 
699
499
  ## Support
700
500
 
701
- - **Dashboard:** [api.attentionmarket.ai](https://api.attentionmarket.ai)
702
- - **Issues:** [GitHub Issues](https://github.com/rtrivedi/agent-ads-sdk/issues)
501
+ - **Documentation:** [docs.attentionmarket.ai](https://docs.attentionmarket.ai)
502
+ - **API Reference:** [api.attentionmarket.ai/docs](https://api.attentionmarket.ai/docs)
503
+ - **Issues:** [github.com/rtrivedi/agent-ads-sdk/issues](https://github.com/rtrivedi/agent-ads-sdk/issues)
703
504
  - **Email:** support@attentionmarket.ai
704
505
 
705
- ---
706
-
707
506
  ## License
708
507
 
709
- MIT see [LICENSE](./LICENSE)
508
+ MIT License. See [LICENSE](LICENSE) for details.