@the_ro_show/agent-ads-sdk 0.13.1 → 0.13.3

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,243 @@
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 defaults to production Supabase endpoint
99
+ // Only override if self-hosting or using different environment
100
+ timeoutMs: 4000, // Optional: request timeout in milliseconds
101
+ maxRetries: 2 // Optional: automatic retry count
102
+ });
103
+ ```
228
104
 
229
- ### How It Works
105
+ ## Core Concepts
230
106
 
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
- ```
107
+ ### Placements
238
108
 
239
- ### Example
109
+ A placement defines where ads appear in your application:
240
110
 
241
- **Developer A** built a general wedding planning AI assistant. A user asks: *"Can you find me a photographer in Austin under $3,000?"*
111
+ - `sponsored_suggestion` Conversational ad in chat flow (most common)
112
+ - `sponsored_block` — Dedicated ad section in UI
113
+ - `sponsored_tool` — AI agent service recommendation
242
114
 
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
- ```
115
+ ### Ad Response Format
253
116
 
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
- }]
117
+ ```typescript
118
+ interface AdResponse {
119
+ request_id: string;
120
+ decision_id: string;
121
+ advertiser_id: string;
122
+ ad_type: 'link' | 'recommendation' | 'service';
123
+ payout: number; // Amount earned on conversion (in cents)
124
+
125
+ creative: {
126
+ title: string;
127
+ body: string;
128
+ cta: string;
129
+ };
130
+
131
+ click_url: string; // Tracked click URL (use this)
132
+ tracking_url?: string; // Server-side tracking URL
133
+ tracking_token: string; // For manual event tracking
134
+
135
+ disclosure: {
136
+ label: string;
137
+ explanation: string;
138
+ sponsor_name: string;
139
+ };
268
140
  }
269
141
  ```
270
142
 
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.
143
+ ### Impression Tracking
288
144
 
289
- ---
145
+ **Important:** As of v0.9.0, impression tracking is required to earn revenue from clicks.
290
146
 
291
- ## Revenue Model
147
+ The SDK automatically tracks impressions when using `decideFromContext()`. Clicks without prior impressions will redirect users but will not generate revenue.
292
148
 
293
- ### As a Developer (showing ads or routing to agents)
149
+ If using the raw `decide()` API, you must manually track impressions:
294
150
 
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)
151
+ ```typescript
152
+ await client.trackImpression({
153
+ agent_id: 'agt_YOUR_AGENT_ID',
154
+ request_id: ad.request_id,
155
+ decision_id: ad.decision_id,
156
+ unit_id: ad._ad.unit_id,
157
+ tracking_token: ad.tracking_token
158
+ });
159
+ ```
310
160
 
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
161
+ ## Developer Controls
315
162
 
316
- ---
163
+ AttentionMarket provides fine-grained controls over ad selection, quality, and revenue optimization.
317
164
 
318
- ## 💰 Earn Bonuses with Feedback (v0.11.0+)
165
+ ### Quality and Brand Safety
319
166
 
320
- Send feedback on ad performance to earn **AI-validated conversion bonuses**.
167
+ #### Minimum Quality Score
321
168
 
322
- ### How It Works
169
+ 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
170
 
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)
171
+ ```typescript
172
+ const ad = await client.decideFromContext({
173
+ userMessage: "I need legal help",
174
+ minQualityScore: 0.7 // Only show ads with quality >= 0.7
175
+ });
176
+ ```
332
177
 
333
- ### What's New in v0.11.0
178
+ **Validation:** Must be a number between 0.0 and 1.0.
334
179
 
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)
180
+ **Use cases:**
181
+ - Premium applications: `0.8+` for high-quality experiences only
182
+ - Brand-sensitive contexts: `0.7+` to avoid low-quality advertisers
183
+ - General applications: `0.5+` for balanced quality and fill rate
340
184
 
341
- ### Anti-Fraud Design
185
+ #### Category Filtering
342
186
 
343
- **Why penalties?** To prevent developers from gaming the system.
187
+ Control which advertiser categories can appear using the IAB Content Taxonomy 3.0 (704 categories across 38 top-level verticals).
344
188
 
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
189
+ ##### Allowed Categories
349
190
 
350
- ### Usage Example
191
+ Whitelist specific categories. Only ads from these categories will be shown.
351
192
 
352
193
  ```typescript
353
- import { AttentionMarketClient } from '@the_ro_show/agent-ads-sdk';
354
-
355
- const client = new AttentionMarketClient({
356
- apiKey: 'am_live_YOUR_KEY'
194
+ // Insurance comparison app: only show insurance ads
195
+ const ad = await client.decideFromContext({
196
+ userMessage: "I need car insurance",
197
+ allowedCategories: [31] // 31 = Auto Insurance (IAB category)
357
198
  });
358
199
 
359
- // Get and show ad
200
+ // Wedding planner: allow wedding + photography + food
360
201
  const ad = await client.decideFromContext({
361
- userMessage: "I need car insurance"
202
+ userMessage: "Help me plan my wedding",
203
+ allowedCategories: [
204
+ 603, // Weddings
205
+ 162, // Photography
206
+ 190 // Restaurants
207
+ ]
362
208
  });
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
- ```
378
-
379
- ### HTTP Example
380
-
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
209
  ```
404
210
 
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
211
+ ##### Blocked Categories
412
212
 
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:
213
+ Blacklist specific categories. Ads from these categories will never be shown.
428
214
 
429
215
  ```typescript
216
+ // Block all sensitive content (gambling, adult, controversial)
430
217
  const ad = await client.decideFromContext({
431
- userMessage: "I need car insurance",
432
- minQualityScore: 0.8 // Only ads with 0.8+ quality
218
+ userMessage: "Help me with something",
219
+ blockedCategories: [601] // Blocks "Sensitive Topics" + all children
220
+ });
221
+
222
+ // Legal assistant: block competitor law firms
223
+ const ad = await client.decideFromContext({
224
+ userMessage: "I need legal help",
225
+ blockedCategories: [318] // Block "Legal Services"
433
226
  });
434
227
  ```
435
228
 
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
229
+ **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
230
 
441
- ### Category Controls (IAB Taxonomy 3.0)
231
+ **Precedence:** If `allowedCategories` is set, `blockedCategories` is ignored.
442
232
 
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.
233
+ **Validation rules:**
234
+ - `allowedCategories`: Must be a non-empty array of numbers or strings
235
+ - `blockedCategories`: Must be an array of numbers or strings
236
+ - Empty `allowedCategories: []` is rejected (would block all ads)
444
237
 
445
- **704 categories** across 38 top-level categories with 4-tier hierarchy.
238
+ **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
239
 
447
- #### Discover Available Categories
240
+ ##### Discovering Categories
448
241
 
449
242
  ```typescript
450
243
  // Get all 38 top-level categories
@@ -466,55 +259,9 @@ insurance.categories.forEach(cat => {
466
259
  // Output: "Automotive > Auto Insurance", "Personal Finance > Insurance", etc.
467
260
  ```
468
261
 
469
- #### Filter by Category
262
+ #### Advertiser Blocklist
470
263
 
471
- Use IAB category IDs for precise control:
472
-
473
- ```typescript
474
- // Insurance app: Only show insurance ads (by IAB ID)
475
- const ad = await client.decideFromContext({
476
- userMessage: "I need car insurance",
477
- allowedCategories: [31] // 31 = Auto Insurance
478
- });
479
-
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
- });
486
-
487
- // Wedding planner: Allow wedding + photography + food
488
- 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
- ]
495
- });
496
- ```
497
-
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
502
-
503
- **Important:**
504
- - If `allowedCategories` is set, `blockedCategories` is ignored
505
- - IAB category IDs (numbers) are **recommended** - legacy string categories are deprecated (support ends 2026-06-01)
506
- - Use `getCategories()` API to discover category IDs
507
- - Empty `allowedCategories: []` is rejected (would block all ads - use `blockedCategories` instead)
508
-
509
- **Validation rules:**
510
- - `minQualityScore`: Must be 0.0-1.0
511
- - `allowedCategories`: Must be non-empty array of strings or numbers
512
- - `blockedCategories`: Must be array of strings or numbers
513
- - `blockedAdvertisers`: Must be array of non-empty strings (advertiser IDs)
514
-
515
- ### Advertiser Blocklist
516
-
517
- Block specific advertisers based on user feedback:
264
+ Block specific advertisers by ID (e.g., based on user feedback or competitive conflicts).
518
265
 
519
266
  ```typescript
520
267
  const ad = await client.decideFromContext({
@@ -523,292 +270,240 @@ const ad = await client.decideFromContext({
523
270
  });
524
271
  ```
525
272
 
526
- Get advertiser IDs from previous ad responses.
273
+ **Validation:** Must be an array of non-empty strings (advertiser IDs).
527
274
 
528
- ## 💰 Revenue Optimization (v0.13.1+)
275
+ Advertiser IDs are included in ad responses as `advertiser_id`.
529
276
 
530
- Control revenue vs. relevance tradeoffs with bid and relevance filters.
277
+ ### Revenue Optimization
531
278
 
532
- ### Minimum CPC Filter
279
+ #### Minimum CPC Filter
533
280
 
534
- Only show ads above a minimum bid threshold:
281
+ Only show ads with bids at or above a specified cost-per-click threshold (in cents).
535
282
 
536
283
  ```typescript
537
284
  const ad = await client.decideFromContext({
538
285
  userMessage: "I need car insurance",
539
- minCPC: 100 // Only ads bidding ≥$1.00 per click
286
+ minCPC: 100 // Only ads bidding >= $1.00 per click
540
287
  });
541
288
  ```
542
289
 
290
+ **Validation:** Must be a non-negative number.
291
+
543
292
  **Use cases:**
544
- - **Premium apps**: `minCPC: 200` for $2+ ads only
545
- - **High-value verticals**: Filter out low-budget advertisers
546
- - **Revenue targets**: Ensure minimum revenue per impression
293
+ - Premium applications: `200+` for $2+ ads only
294
+ - High-value verticals: Filter out low-budget advertisers
295
+ - Revenue targets: Ensure minimum revenue per impression
296
+ - Lower fill rate tolerance: When you'd rather show nothing than a low-value ad
297
+
298
+ **Trade-off:** Higher thresholds = higher revenue per ad but lower fill rate.
547
299
 
548
- ### Minimum Relevance Score
300
+ #### Minimum Relevance Score
549
301
 
550
- Only show ads that are semantically relevant:
302
+ 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.
551
303
 
552
304
  ```typescript
553
305
  const ad = await client.decideFromContext({
554
306
  userMessage: "Help me plan my wedding",
555
- minRelevanceScore: 0.8 // Only highly relevant ads (0.0-1.0)
307
+ minRelevanceScore: 0.8 // Only highly relevant ads
556
308
  });
557
309
  ```
558
310
 
311
+ **Validation:** Must be a number between 0.0 and 1.0.
312
+
559
313
  **Use cases:**
560
- - **Niche apps**: Legal assistant = `0.8+` for specialized content only
561
- - **User experience**: Filter out loosely related ads
562
- - **Context matching**: Ensure ads match conversation topic
563
- - **Default threshold**: Backend uses 0.25 minimum
314
+ - Niche applications: `0.8+` for specialized content only (e.g., legal assistant)
315
+ - User experience priority: Filter out loosely related ads
316
+ - Context-sensitive placements: Ensure ads match conversation topic
317
+ - Brand-aligned content: Maintain thematic consistency
318
+
319
+ **Important:** This filter only applies to campaigns with semantic targeting. Keyword and automatic campaigns are not affected.
564
320
 
565
- ### Ranking Strategy
321
+ **Default threshold:** Backend applies a minimum threshold of 0.25 for all semantic campaigns (ads below this are never shown).
566
322
 
567
- Choose how ads are ranked:
323
+ #### Ranking Strategy
324
+
325
+ Choose how ads are ranked when multiple ads match the request.
568
326
 
569
327
  ```typescript
570
- // Default: Revenue-optimized (highest bid wins)
328
+ // Revenue-optimized (default): highest bid wins
571
329
  const ad = await client.decideFromContext({
572
330
  userMessage: "I need legal help",
573
331
  optimizeFor: 'revenue' // Rank by bid × quality × relevance
574
332
  });
575
333
 
576
- // Alternative: Relevance-optimized (best match wins)
334
+ // Relevance-optimized: best match wins
577
335
  const ad = await client.decideFromContext({
578
336
  userMessage: "I need legal help",
579
337
  optimizeFor: 'relevance' // Rank by semantic similarity only
580
338
  });
581
339
  ```
582
340
 
583
- **Use cases:**
584
- - **General apps**: `'revenue'` to maximize earnings
585
- - **Niche apps**: `'relevance'` to prioritize perfect matches over high bids
586
- - **Premium experiences**: Combine with high `minRelevanceScore` + `'relevance'` ranking
341
+ **Validation:** Must be either `'revenue'` or `'relevance'`.
587
342
 
588
343
  **How it works (second-price auction):**
589
- - **Revenue mode**: Winner ranked by composite score (bid × quality × relevance), pays just enough to beat next ad's composite score + $0.01
590
- - **Relevance mode**: Winner ranked by semantic similarity, pays just enough to beat next ad in the composite score space + $0.01
591
- - **Always capped**: Winner never pays more than their max bid (auction integrity guaranteed)
592
- - **Floor price**: Minimum $0.25 clearing price ensures platform revenue
593
344
 
594
- **Important notes:**
595
- - `minRelevanceScore` only applies to campaigns with semantic targeting
596
- - For keyword/automatic campaigns, relevance filter has no effect
597
- - Validation errors return HTTP 400 with clear messages
345
+ - **Revenue mode:** Winner is ranked by composite score (bid × quality × relevance), pays just enough to beat the next ad's composite score + $0.01
346
+ - **Relevance mode:** Winner is ranked by semantic similarity, pays just enough to beat the next ad in composite score space + $0.01
347
+ - **Price cap:** Winner never pays more than their max bid (auction integrity guaranteed)
348
+ - **Price floor:** Minimum clearing price of $0.25 ensures platform sustainability
349
+
350
+ **Use cases:**
351
+ - General applications: `'revenue'` to maximize earnings
352
+ - Niche applications: `'relevance'` to prioritize perfect matches over high bids
353
+ - Premium experiences: Combine with high `minRelevanceScore` + `'relevance'` ranking
354
+
355
+ #### Combined Controls
598
356
 
599
- ### Combined Revenue Controls
357
+ Combine multiple controls for precise ad selection:
600
358
 
601
359
  ```typescript
602
- // Premium legal assistant: High relevance + high bids
360
+ // Premium legal assistant: high relevance + high bids + category filter
603
361
  const ad = await client.decideFromContext({
604
362
  userMessage: "I need estate planning help",
605
- minRelevanceScore: 0.8, // Only highly relevant
606
- minCPC: 200, // Only $2+ bids
607
- optimizeFor: 'relevance', // Best match wins
608
- allowedCategories: [318] // Legal services only (IAB)
363
+ minRelevanceScore: 0.8, // Only highly relevant
364
+ minCPC: 200, // Only $2+ bids
365
+ minQualityScore: 0.7, // Only high-quality advertisers
366
+ optimizeFor: 'relevance', // Best match wins
367
+ allowedCategories: [318] // Legal services only
609
368
  });
369
+ ```
610
370
 
611
- // General chatbot: Maximize revenue
371
+ ## Advanced Features
372
+
373
+ ### Multi-Turn Conversations
374
+
375
+ Include conversation history for better ad matching:
376
+
377
+ ```typescript
612
378
  const ad = await client.decideFromContext({
613
- userMessage: "Help me with something",
614
- optimizeFor: 'revenue', // Highest bid wins
615
- minQualityScore: 0.7 // Decent quality threshold
379
+ userMessage: "What are my options?",
380
+ conversationHistory: [
381
+ "User: My car insurance is too expensive",
382
+ "Agent: I can help you compare rates",
383
+ "User: What are my options?"
384
+ ]
616
385
  });
617
386
  ```
618
387
 
619
- ### Combined Controls (All Phases)
388
+ The SDK automatically limits history to the last 5 messages to prevent token overflow.
620
389
 
621
- Mix and match for precise control:
390
+ ### Geographic and Platform Targeting
622
391
 
623
392
  ```typescript
624
393
  const ad = await client.decideFromContext({
625
394
  userMessage: "I need car insurance",
626
- // Phase 1: Quality & Brand Safety
627
- minQualityScore: 0.8, // High quality only
628
- allowedCategories: [31, 398], // Auto + Personal Finance Insurance (IAB)
629
- // Phase 2: Revenue Optimization
630
- minCPC: 50, // $0.50+ bids only
631
- minRelevanceScore: 0.7, // Highly relevant only
632
- optimizeFor: 'revenue' // Highest bid wins
395
+ country: 'US',
396
+ language: 'en',
397
+ platform: 'ios' // 'web' | 'ios' | 'android' | 'desktop' | 'voice' | 'other'
633
398
  });
634
399
  ```
635
400
 
636
- ### HTTP Example
637
-
638
- ```bash
639
- curl -X POST https://peruwnbrqkvmrldhpoom.supabase.co/functions/v1/decide \
640
- -H "X-AM-API-Key: am_live_YOUR_KEY" \
641
- -H "Content-Type: application/json" \
642
- -d '{
643
- "context": "I need car insurance",
644
- "minQualityScore": 0.8,
645
- "blockedCategories": ["crypto", "gambling"]
646
- }'
647
- ```
648
-
649
- ### Best Practices
650
-
651
- **Quality Scores:**
652
- - Start with no filter, monitor what you get
653
- - Gradually increase threshold if quality is lacking
654
- - Higher thresholds = fewer ads but better quality
655
-
656
- **Categories (IAB Taxonomy):**
657
- - Use `getCategories()` to discover the full 704-category taxonomy
658
- - Use `allowedCategories` for specialized apps (wedding planner = `[603]`, legal assistant = `[318]`)
659
- - Use `blockedCategories` for compliance (kids apps = block `[601]` Sensitive Topics)
660
- - Parent categories block all children automatically (block `[1]` Automotive = blocks all 40+ auto subcategories)
661
- - Full taxonomy reference: https://github.com/InteractiveAdvertisingBureau/Taxonomies
662
-
663
- **Advertiser Blocking:**
664
- - Collect user feedback on specific ads
665
- - Block advertisers with multiple complaints
666
- - Use sparingly - too many blocks reduce fill rate
667
-
668
- ---
669
-
670
- ## API Reference
671
-
672
- ### `POST /v1/decide`
673
-
674
- **Base URL:** `https://peruwnbrqkvmrldhpoom.supabase.co/functions/v1/decide`
401
+ ### Click Tracking
675
402
 
676
- **Required header:** `X-AM-API-Key: am_live_...` or `am_test_...`
403
+ Clicks are automatically tracked when users visit `click_url`. For manual tracking:
677
404
 
678
- ### Simple Request (recommended)
679
-
680
- ```json
681
- {
682
- "context": "I need car insurance"
683
- }
405
+ ```typescript
406
+ await client.trackClick({
407
+ agent_id: 'agt_YOUR_AGENT_ID',
408
+ request_id: ad.request_id,
409
+ decision_id: ad.decision_id,
410
+ unit_id: ad._ad.unit_id,
411
+ tracking_token: ad.tracking_token,
412
+ href: ad.click_url,
413
+ click_context: "User clicked 'Get a Quote' button"
414
+ });
684
415
  ```
685
416
 
686
- ### Simple Request with Optional Fields
417
+ Simplified tracking from ad object:
687
418
 
688
- ```json
689
- {
690
- "context": "I need car insurance",
691
- "country": "US",
692
- "language": "en",
693
- "platform": "ios"
694
- }
419
+ ```typescript
420
+ await client.trackClickFromAd(ad, {
421
+ click_context: "User clicked on sponsored suggestion"
422
+ });
695
423
  ```
696
424
 
697
- ### Request an Agent Instead of an Ad
425
+ ### Conversion Tracking
698
426
 
699
- ```json
700
- {
701
- "context": "User needs a wedding photographer in Austin",
702
- "response_type": "agent"
703
- }
704
- ```
427
+ Track conversions (purchases, signups, etc.) to improve advertiser ROI and your quality score:
705
428
 
706
- ### Advanced Request (full control)
707
-
708
- ```json
709
- {
710
- "request_id": "req_custom_123",
711
- "agent_id": "agt_YOUR_ID",
712
- "placement": "chat_inline",
713
- "opportunity": {
714
- "intent": { "taxonomy": "insurance.auto.quote" },
715
- "context": { "country": "US", "language": "en", "platform": "ios" }
716
- },
717
- "context": "I need car insurance"
718
- }
429
+ ```typescript
430
+ await client.track({
431
+ event_id: `evt_${generateUUID()}`,
432
+ event_type: 'conversion',
433
+ occurred_at: new Date().toISOString(),
434
+ agent_id: 'agt_YOUR_AGENT_ID',
435
+ request_id: ad.request_id,
436
+ decision_id: ad.decision_id,
437
+ unit_id: ad._ad.unit_id,
438
+ tracking_token: ad.tracking_token,
439
+ metadata: {
440
+ conversion_value: 99.99,
441
+ conversion_type: 'purchase'
442
+ }
443
+ });
719
444
  ```
720
445
 
721
- ### Response: Ad Filled
722
-
723
- ```json
724
- {
725
- "request_id": "req_abc123",
726
- "decision_id": "dec_xyz789",
727
- "status": "filled",
728
- "ttl_ms": 300000,
729
- "units": [{
730
- "unit_id": "unit_123",
731
- "suggestion": {
732
- "title": "Get 20% off car insurance",
733
- "body": "Compare quotes in minutes",
734
- "cta": "Get a Quote",
735
- "tracking_url": "https://.../track-click/...",
736
- "action_url": "https://progressive.com/quote"
737
- },
738
- "tracking": {
739
- "token": "trk_abc",
740
- "impression_url": "https://.../event",
741
- "click_url": "https://.../click/trk_abc"
742
- }
743
- }]
744
- }
745
- ```
446
+ ## Error Handling
746
447
 
747
- ### Response: No Fill
448
+ The SDK throws errors for invalid configurations and failed requests:
748
449
 
749
- ```json
750
- {
751
- "status": "no_fill",
752
- "units": []
450
+ ```typescript
451
+ try {
452
+ const ad = await client.decideFromContext({
453
+ userMessage: "I need car insurance",
454
+ minQualityScore: -0.5 // Invalid: must be 0.0-1.0
455
+ });
456
+ } catch (error) {
457
+ console.error(error.message);
458
+ // Output: "minQualityScore must be a number between 0.0 and 1.0"
753
459
  }
754
460
  ```
755
461
 
756
- ### Key Response Fields
462
+ ### Validation Errors
757
463
 
758
- | Field | Use |
759
- |-------|-----|
760
- | `status` | `"filled"` or `"no_fill"` |
761
- | `suggestion.title` | Ad headline to show |
762
- | `suggestion.body` | Ad description to show |
763
- | `suggestion.cta` | Button text |
764
- | `suggestion.tracking_url` | **Send users here on click** — tracks automatically |
765
- | `suggestion.action_url` | Direct advertiser URL (display only, no tracking) |
464
+ The SDK validates all parameters before making API calls. Common validation errors:
766
465
 
767
- **Always use `tracking_url` for clicks.** This is how clicks are tracked and how you get paid.
466
+ - `minQualityScore must be a number between 0.0 and 1.0`
467
+ - `minCPC must be a non-negative number (cost-per-click in cents)`
468
+ - `minRelevanceScore must be a number between 0.0 and 1.0`
469
+ - `optimizeFor must be either "revenue" or "relevance"`
470
+ - `allowedCategories cannot be empty (would block all ads). Use blockedCategories to exclude specific categories, or omit to allow all.`
471
+ - `blockedAdvertisers must contain non-empty strings (advertiser IDs)`
768
472
 
769
- ### HTTP Status Codes
473
+ ### HTTP Errors
770
474
 
771
- | Code | Meaning |
772
- |------|---------|
773
- | `200` | Success — check `status` field |
774
- | `400` | Bad request — missing `context` field |
775
- | `401` | Invalid API key |
776
- | `429` | Rate limit exceeded (1,000 req/min) — retry after 60s |
777
- | `500` | Server error — retry with backoff |
475
+ The API returns standard HTTP status codes:
778
476
 
779
- ---
477
+ - `400 Bad Request` — Invalid parameters (see error message for details)
478
+ - `401 Unauthorized` — Missing or invalid API key
479
+ - `429 Too Many Requests` — Rate limit exceeded
480
+ - `500 Internal Server Error` — Server error (contact support if persistent)
780
481
 
781
- ## Testing
482
+ ## Rate Limits
782
483
 
783
- Use your test key (`am_test_...`) during development — same API, no real money moves.
484
+ - **Per IP:** 60 requests per minute
485
+ - **Per API key:** 100 requests per minute
784
486
 
785
- **Validate your setup:**
786
- ```bash
787
- node test-integration.js # Tests connectivity and credentials
788
- node validate-production.js # Full production readiness check (4/4 ✅)
789
- ```
790
-
791
- Download: [test-integration.js](./test-integration.js) | [validate-production.js](./validate-production.js)
487
+ Rate limits are enforced to prevent abuse and ensure fair resource allocation. If you need higher limits, contact support.
792
488
 
793
- ---
489
+ ## Testing
794
490
 
795
- ## Get Started
491
+ Use test API keys (`am_test_...`) for development and testing. Test keys:
796
492
 
797
- 1. **Sign up** at [api.attentionmarket.ai](https://api.attentionmarket.ai) — get your test key, live key, and agent ID
798
- 2. **Test it** run the curl above with your test key
799
- 3. **Integrate** add 5 lines to your existing chatbot or assistant
800
- 4. **Go live** swap test key for live key, start earning
493
+ - Return test ads with realistic data
494
+ - Do not charge advertisers
495
+ - Do not generate real revenue
496
+ - Have the same rate limits as live keys
801
497
 
802
- ---
498
+ Switch to live keys (`am_live_...`) when deploying to production.
803
499
 
804
500
  ## Support
805
501
 
806
- - **Dashboard:** [api.attentionmarket.ai](https://api.attentionmarket.ai)
807
- - **Issues:** [GitHub Issues](https://github.com/rtrivedi/agent-ads-sdk/issues)
502
+ - **Documentation:** [docs.attentionmarket.ai](https://docs.attentionmarket.ai)
503
+ - **API Reference:** [api.attentionmarket.ai/docs](https://api.attentionmarket.ai/docs)
504
+ - **Issues:** [github.com/rtrivedi/agent-ads-sdk/issues](https://github.com/rtrivedi/agent-ads-sdk/issues)
808
505
  - **Email:** support@attentionmarket.ai
809
506
 
810
- ---
811
-
812
507
  ## License
813
508
 
814
- MIT see [LICENSE](./LICENSE)
509
+ MIT License. See [LICENSE](LICENSE) for details.