@gravity-ai/api 0.1.2 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,18 +17,44 @@ import { Client } from '@gravity-ai/api';
17
17
 
18
18
  const client = new Client('your-api-key');
19
19
 
20
- const ad = await client.getAd({
20
+ const response = await client.getAd({
21
21
  messages: [
22
22
  { role: 'user', content: 'What are some good hiking trails?' },
23
23
  { role: 'assistant', content: 'Here are some popular trails...' }
24
- ]
24
+ ],
25
+ sessionId: 'session-123', // Recommended
26
+ userId: 'user-456', // Recommended
25
27
  });
26
28
 
29
+ if (response) {
30
+ const ad = response.ads[0];
31
+ console.log(ad.adText);
32
+ }
33
+ ```
34
+
35
+ ## Migrating from v0
36
+
37
+ If you're upgrading from a previous version, the `getAd()` response format has changed:
38
+
39
+ ```typescript
40
+ // Before (v0)
41
+ const ad = await client.getAd({ messages });
27
42
  if (ad) {
28
43
  console.log(ad.adText);
29
44
  }
45
+
46
+ // After (v1)
47
+ const response = await client.getAd({ messages, sessionId: '...' });
48
+ if (response) {
49
+ const ad = response.ads[0];
50
+ console.log(ad.adText);
51
+ }
30
52
  ```
31
53
 
54
+ The response is now an object with an `ads` array instead of a single ad object.
55
+
56
+ ---
57
+
32
58
  ## Client Configuration
33
59
 
34
60
  ### Basic Initialization
@@ -61,29 +87,43 @@ const client = new Client('your-api-key', {
61
87
  | `excludedTopics` | `string[]` | `[]` | Topics to exclude from ad matching |
62
88
  | `relevancy` | `number \| null` | `null` | Minimum relevancy score (0-1) |
63
89
 
64
- ## Fetching Ads
90
+ ---
91
+
92
+ ## `getAd()` — Fetch Contextual Ads
65
93
 
66
- ### Basic Request
94
+ Fetch ads based on conversation context. Requires `messages` array.
67
95
 
68
96
  ```typescript
69
- const ad = await client.getAd({
97
+ const response = await client.getAd({
70
98
  messages: [
71
99
  { role: 'user', content: 'I need help finding a new laptop.' },
72
100
  { role: 'assistant', content: 'What is your budget?' }
73
- ]
101
+ ],
102
+ sessionId: 'session-123', // Recommended
103
+ userId: 'user-456', // Recommended
104
+ numAds: 1, // 1-3, default 1
74
105
  });
106
+
107
+ if (response) {
108
+ const ad = response.ads[0];
109
+ console.log(ad.adText);
110
+ }
75
111
  ```
76
112
 
77
113
  ### Full Request with All Options
78
114
 
79
115
  ```typescript
80
- const ad = await client.getAd({
116
+ const response = await client.getAd({
81
117
  // Required: conversation messages
82
118
  messages: [
83
119
  { role: 'user', content: 'I need help finding a new laptop.' },
84
120
  { role: 'assistant', content: 'What is your budget?' }
85
121
  ],
86
122
 
123
+ // Recommended: session/user tracking (improves ad relevance & revenue)
124
+ sessionId: 'session-123',
125
+ userId: 'user-456',
126
+
87
127
  // Optional: user information for targeting
88
128
  user: {
89
129
  uid: 'user-123', // Unique user identifier
@@ -101,27 +141,66 @@ const ad = await client.getAd({
101
141
  ifa: 'device-ad-id', // Advertising identifier
102
142
  },
103
143
 
104
- // Optional: additional targeting context
105
- excludedTopics: ['politics'], // Override client-level exclusions
106
- relevancy: 0.8, // Override client-level relevancy
144
+ // Optional: ad request settings
145
+ excludedTopics: ['politics'], // Topics to exclude
146
+ relevancy: 0.8, // Min relevancy threshold (0-1)
147
+ numAds: 1, // Number of ads (1-3)
148
+ testAd: false, // Return test ad when true
107
149
 
108
- // Optional: custom fields (open-ended)
150
+ // Optional: custom fields (open-ended, passed to matching)
109
151
  interests: ['coding', 'apple', 'software development'],
110
152
  summary: 'User wants a laptop for software development',
111
153
  });
112
154
  ```
113
155
 
114
- ### Request Parameters
156
+ ---
157
+
158
+ ## Request Parameters
115
159
 
116
160
  | Parameter | Type | Required | Description |
117
161
  |-----------|------|----------|-------------|
118
- | `messages` | `MessageObject[]` | | Conversation history |
119
- | `user` | `UserObject` | - | User targeting info |
162
+ | `messages` | `MessageObject[]` | Yes | Conversation history |
163
+ | `sessionId` | `string` | Recommended | Session identifier for tracking |
164
+ | `userId` | `string` | Recommended | User identifier for frequency capping |
120
165
  | `device` | `DeviceObject` | - | Device/location info |
166
+ | `user` | `UserObject` | - | User targeting info |
121
167
  | `excludedTopics` | `string[]` | - | Topics to exclude |
122
168
  | `relevancy` | `number` | - | Min relevancy (0-1) |
123
- | `apiKey` | `string` | - | Override client API key |
124
- | `[key: string]` | `any` | - | Custom fields allowed |
169
+ | `numAds` | `number` | - | Number of ads (1-3, default 1) |
170
+ | `testAd` | `boolean` | - | Return test ad when true |
171
+
172
+ ---
173
+
174
+ ## Response Types
175
+
176
+ ### AdResponse
177
+
178
+ Returned by `getAd()`.
179
+
180
+ ```typescript
181
+ interface AdResponse {
182
+ ads: Ad[]; // Array of ads
183
+ numAds: number; // Number of ads returned
184
+ totalPayout?: number; // Total payout across all ads
185
+ }
186
+
187
+ interface Ad {
188
+ adText: string; // Ad copy text
189
+ adId: string; // Unique ad identifier
190
+ title?: string; // Ad title
191
+ brandName?: string; // Brand name
192
+ brandImage?: string; // Brand logo URL
193
+ url?: string; // Landing page URL
194
+ favicon?: string; // Favicon URL
195
+ impUrl?: string; // Impression tracking URL
196
+ clickUrl?: string; // Click-through URL
197
+ payout?: number; // Payout amount
198
+ }
199
+ ```
200
+
201
+ ---
202
+
203
+ ## Common Types
125
204
 
126
205
  ### Message Object
127
206
 
@@ -155,49 +234,14 @@ interface DeviceObject {
155
234
  }
156
235
  ```
157
236
 
158
- ## Ad Response
159
-
160
- The `getAd` method returns an `AdResponse` object or `null` if no relevant ad is found.
161
-
162
- ```typescript
163
- interface AdResponse {
164
- adText: string; // The ad copy to display
165
- impUrl?: string; // Impression tracking URL (fire on view)
166
- clickUrl?: string; // Click-through URL
167
- payout?: number; // Payout amount
168
- }
169
- ```
170
-
171
- ### Handling the Response
172
-
173
- ```typescript
174
- const ad = await client.getAd({ messages });
175
-
176
- if (ad) {
177
- // Display the ad
178
- console.log('Ad:', ad.adText);
179
-
180
- // Track impression (fire pixel)
181
- if (ad.impUrl) {
182
- fetch(ad.impUrl); // or use an image beacon
183
- }
184
-
185
- // Handle clicks
186
- if (ad.clickUrl) {
187
- // Navigate user to ad.clickUrl on click
188
- }
189
- } else {
190
- // No relevant ad found - show fallback or nothing
191
- console.log('No ad available');
192
- }
193
- ```
237
+ ---
194
238
 
195
239
  ## Error Handling
196
240
 
197
241
  The client handles errors gracefully and returns `null` on failure. Errors are logged to the console.
198
242
 
199
243
  ```typescript
200
- const ad = await client.getAd({ messages });
244
+ const response = await client.getAd({ messages, sessionId: '...' });
201
245
 
202
246
  // Returns null on:
203
247
  // - Network errors
@@ -205,7 +249,7 @@ const ad = await client.getAd({ messages });
205
249
  // - No relevant ad (204)
206
250
  // - Invalid response data
207
251
 
208
- if (!ad) {
252
+ if (!response) {
209
253
  // Handle gracefully - don't break the user experience
210
254
  }
211
255
  ```
@@ -216,7 +260,14 @@ Full TypeScript support with exported types:
216
260
 
217
261
  ```typescript
218
262
  import { Client, ClientParams } from '@gravity-ai/api';
219
- import type { AdParams, AdResponse, MessageObject, DeviceObject, UserObject } from '@gravity-ai/api';
263
+ import type {
264
+ AdParams,
265
+ Ad,
266
+ AdResponse,
267
+ MessageObject,
268
+ DeviceObject,
269
+ UserObject,
270
+ } from '@gravity-ai/api';
220
271
  ```
221
272
 
222
273
  ## Using with React
@@ -237,13 +288,25 @@ function ChatApp() {
237
288
  const [ad, setAd] = useState(null);
238
289
 
239
290
  useEffect(() => {
240
- client.getAd({ messages }).then(setAd);
291
+ client.getAd({
292
+ messages,
293
+ sessionId: 'session-123',
294
+ userId: 'user-456',
295
+ }).then(res => setAd(res?.ads[0] || null));
241
296
  }, [messages]);
242
297
 
243
298
  return <AdBanner ad={ad} theme="dark" />;
244
299
  }
245
300
  ```
246
301
 
302
+ ## Best Practices
303
+
304
+ 1. **Always include `sessionId` and `userId`** — These directly impact publisher revenue through better frequency capping and ad relevance.
305
+
306
+ 2. **Handle null responses gracefully** — Don't break the user experience when no ad is available.
307
+
308
+ 3. **Fire impression url** — Use the `impUrl` when the ad becomes visible to properly track impressions.
309
+
247
310
  ## License
248
311
 
249
312
  MIT
package/dist/index.d.mts CHANGED
@@ -83,6 +83,8 @@ interface UserObject {
83
83
  * { role: 'user', content: 'What laptop should I buy?' },
84
84
  * { role: 'assistant', content: 'What is your budget?' }
85
85
  * ],
86
+ * sessionId: 'session-123',
87
+ * userId: 'user-456',
86
88
  * user: { gender: 'male', age: '25-34' },
87
89
  * device: { ip: '1.2.3.4', country: 'US', ua: 'Mozilla/5.0...' },
88
90
  * excludedTopics: ['politics'],
@@ -91,10 +93,12 @@ interface UserObject {
91
93
  * ```
92
94
  */
93
95
  interface AdParams {
94
- /** Override the client's API key for this request */
95
- apiKey?: string;
96
96
  /** Array of conversation messages for contextual targeting (required) */
97
97
  messages: MessageObject[];
98
+ /** Session identifier for tracking user sessions */
99
+ sessionId?: string;
100
+ /** Unique user identifier */
101
+ userId?: string;
98
102
  /** Device and location information */
99
103
  device?: DeviceObject;
100
104
  /** User demographic and interest data */
@@ -103,43 +107,67 @@ interface AdParams {
103
107
  excludedTopics?: string[];
104
108
  /** Minimum relevancy score threshold (0-1). Higher = more relevant but fewer ads */
105
109
  relevancy?: number | null;
110
+ /** Number of ads to return (1-3, default 1) */
111
+ numAds?: number;
112
+ /** Returns a test ad when true */
113
+ testAd?: boolean;
106
114
  /**
107
115
  * Additional custom fields for publisher-specific targeting
108
116
  * @description Any additional key-value pairs will be passed to the API
109
117
  */
110
118
  [key: string]: unknown;
111
119
  }
120
+ /**
121
+ * Single ad object in responses.
122
+ * @description Contains all ad creative and tracking data.
123
+ */
124
+ interface Ad {
125
+ /** The advertisement copy text */
126
+ adText: string;
127
+ /** Unique ad identifier */
128
+ adId: string;
129
+ /** Ad title */
130
+ title?: string;
131
+ /** Brand/advertiser name */
132
+ brandName?: string;
133
+ /** Brand logo image URL */
134
+ brandImage?: string;
135
+ /** Landing page URL */
136
+ url?: string;
137
+ /** Favicon URL */
138
+ favicon?: string;
139
+ /** Impression tracking URL */
140
+ impUrl?: string;
141
+ /** Click-through tracking URL */
142
+ clickUrl?: string;
143
+ /** Payout amount in USD */
144
+ payout?: number;
145
+ }
112
146
  /**
113
147
  * Response from the Gravity API containing ad data
114
148
  * @description Returned by getAd() when a relevant advertisement is found
115
149
  * @example
116
150
  * ```typescript
117
- * const ad: AdResponse = {
118
- * adText: 'Check out our amazing laptops!',
119
- * impUrl: 'https://tracking.example.com/imp?id=123',
120
- * clickUrl: 'https://example.com/laptops',
121
- * payout: 0.50
151
+ * const response: AdResponse = {
152
+ * ads: [{
153
+ * adText: 'Check out our amazing laptops!',
154
+ * adId: 'ad-123',
155
+ * impUrl: 'https://tracking.example.com/imp?id=123',
156
+ * clickUrl: 'https://example.com/laptops',
157
+ * payout: 0.50
158
+ * }],
159
+ * numAds: 1,
160
+ * totalPayout: 0.50
122
161
  * };
123
162
  * ```
124
163
  */
125
164
  interface AdResponse {
126
- /** The advertisement copy text to display to the user */
127
- adText: string;
128
- /**
129
- * Impression tracking URL - fire this when the ad is viewed
130
- * @description Should be loaded (e.g., via image beacon) when ad becomes visible
131
- */
132
- impUrl?: string;
133
- /**
134
- * Click-through URL - navigate user here when ad is clicked
135
- * @description The destination page when the user clicks the ad
136
- */
137
- clickUrl?: string;
138
- /**
139
- * Payout amount in USD for this ad impression
140
- * @description The revenue earned for displaying this ad
141
- */
142
- payout?: number;
165
+ /** Array of ad objects */
166
+ ads: Ad[];
167
+ /** Number of ads returned */
168
+ numAds: number;
169
+ /** Total payout across all ads */
170
+ totalPayout?: number;
143
171
  }
144
172
  /**
145
173
  * Error response structure from the Gravity API
@@ -153,6 +181,80 @@ interface ApiErrorResponse {
153
181
  /** HTTP status code */
154
182
  statusCode?: number;
155
183
  }
184
+ /**
185
+ * Base fields shared across all ad requests.
186
+ */
187
+ interface AdRequestBase {
188
+ /** Session identifier for tracking user sessions */
189
+ sessionId?: string;
190
+ /** Unique user identifier */
191
+ userId?: string;
192
+ /** Device and location information */
193
+ device?: DeviceObject;
194
+ /** User demographic and interest data */
195
+ user?: UserObject;
196
+ /** Topics to exclude from ad matching */
197
+ excludedTopics?: string[];
198
+ /** Minimum relevancy score threshold (0-1) */
199
+ relevancy?: number | null;
200
+ /** Number of ads to return (1-3, default 1) */
201
+ numAds?: number;
202
+ /** Returns a test ad when true */
203
+ testAd?: boolean;
204
+ /** Additional custom fields */
205
+ [key: string]: unknown;
206
+ }
207
+ /**
208
+ * POST /api/v1/ad/summary
209
+ * @description Requires queryString for summary-based targeting.
210
+ */
211
+ interface SummaryAdParams extends AdRequestBase {
212
+ /** Search/summary query string (required) */
213
+ queryString: string;
214
+ }
215
+ /**
216
+ * POST /api/v1/ad/non-contextual
217
+ * @description No context required - returns ads without context matching.
218
+ */
219
+ interface NonContextualAdParams extends AdRequestBase {
220
+ }
221
+ /**
222
+ * POST /api/v1/bid
223
+ * @description Two-phase bid request. Returns bid price and bidId.
224
+ * @note Does NOT extend AdRequestBaseV1 - has its own field set.
225
+ */
226
+ interface BidParams {
227
+ /** Array of conversation messages (required) */
228
+ messages: MessageObject[];
229
+ /** Session identifier */
230
+ sessionId?: string;
231
+ /** Unique user identifier */
232
+ userId?: string;
233
+ /** Chat/conversation identifier */
234
+ chatId?: string;
235
+ /** Device and location information */
236
+ device?: DeviceObject;
237
+ }
238
+ /**
239
+ * POST /api/v1/render
240
+ * @description Two-phase render request using cached bid context.
241
+ */
242
+ interface RenderParams {
243
+ /** Bid identifier from the bid phase (required) */
244
+ bidId: string;
245
+ /** Realized price to charge (required) */
246
+ realizedPrice: number;
247
+ }
248
+ /**
249
+ * Bid response.
250
+ * @description Returned by the bid endpoint.
251
+ */
252
+ interface BidResponse {
253
+ /** Clearing price (CPM) */
254
+ bid: number;
255
+ /** Bid identifier for the render phase */
256
+ bidId: string;
257
+ }
156
258
 
157
259
  /**
158
260
  * Configuration options for the Gravity API Client
@@ -197,13 +299,15 @@ interface ClientParams {
197
299
  *
198
300
  * const client = new Client('your-api-key');
199
301
  *
200
- * const ad = await client.getAd({
302
+ * const response = await client.getAd({
201
303
  * messages: [
202
304
  * { role: 'user', content: 'What laptop should I buy?' }
203
- * ]
305
+ * ],
306
+ * sessionId: 'session-123',
204
307
  * });
205
308
  *
206
- * if (ad) {
309
+ * if (response) {
310
+ * const ad = response.ads[0];
207
311
  * console.log(ad.adText);
208
312
  * }
209
313
  * ```
@@ -252,7 +356,7 @@ declare class Client {
252
356
  /**
253
357
  * Request a contextually relevant advertisement
254
358
  *
255
- * @description Fetches an ad based on the provided conversation context and targeting parameters.
359
+ * @description Fetches ads based on the provided conversation context and targeting parameters.
256
360
  * Returns `null` if no relevant ad is available or if an error occurs.
257
361
  *
258
362
  * @param params - Ad request parameters including conversation messages
@@ -260,18 +364,27 @@ declare class Client {
260
364
  *
261
365
  * @example Basic request
262
366
  * ```typescript
263
- * const ad = await client.getAd({
367
+ * const response = await client.getAd({
264
368
  * messages: [
265
369
  * { role: 'user', content: 'I need a new laptop for programming' },
266
370
  * { role: 'assistant', content: 'What is your budget range?' }
267
- * ]
371
+ * ],
372
+ * sessionId: 'session-123',
373
+ * userId: 'user-456',
268
374
  * });
375
+ *
376
+ * if (response) {
377
+ * const ad = response.ads[0];
378
+ * console.log(ad.adText);
379
+ * }
269
380
  * ```
270
381
  *
271
382
  * @example Full request with targeting
272
383
  * ```typescript
273
- * const ad = await client.getAd({
384
+ * const response = await client.getAd({
274
385
  * messages: [...],
386
+ * sessionId: 'session-123',
387
+ * userId: 'user-456',
275
388
  * user: {
276
389
  * uid: 'user-123',
277
390
  * gender: 'male',
@@ -289,9 +402,10 @@ declare class Client {
289
402
  *
290
403
  * @example Handling the response
291
404
  * ```typescript
292
- * const ad = await client.getAd({ messages });
405
+ * const response = await client.getAd({ messages, sessionId: '...' });
293
406
  *
294
- * if (ad) {
407
+ * if (response) {
408
+ * const ad = response.ads[0];
295
409
  * // Display the ad
296
410
  * showAd(ad.adText);
297
411
  *
@@ -303,6 +417,47 @@ declare class Client {
303
417
  * ```
304
418
  */
305
419
  getAd(params: AdParams): Promise<AdResponse | null>;
420
+ /**
421
+ * Request summary-based advertisements
422
+ *
423
+ * @description Fetches ads based on a search/summary query string.
424
+ * Returns null if no relevant ad is available or on error.
425
+ *
426
+ * @param params - Request parameters including queryString
427
+ * @returns Promise resolving to AdResponse or null if no ad available
428
+ */
429
+ summaryAd(params: SummaryAdParams): Promise<AdResponse | null>;
430
+ /**
431
+ * Request non-contextual advertisements
432
+ *
433
+ * @description Fetches ads without context matching. Useful for brand awareness placements.
434
+ * Returns null if no ad is available or on error.
435
+ *
436
+ * @param params - Optional request parameters
437
+ * @returns Promise resolving to AdResponse or null if no ad available
438
+ */
439
+ nonContextualAd(params?: NonContextualAdParams): Promise<AdResponse | null>;
440
+ /**
441
+ * Request a bid price for contextual ad placement
442
+ *
443
+ * @description First phase of two-phase ad flow. Returns bid price and bidId.
444
+ * Use the bidId with render() to generate the actual ad creative.
445
+ * Returns null if no bid is available or on error.
446
+ *
447
+ * @param params - Request parameters including messages array
448
+ * @returns Promise resolving to BidResponse or null if no bid available
449
+ */
450
+ bid(params: BidParams): Promise<BidResponse | null>;
451
+ /**
452
+ * Render an ad from a cached bid
453
+ *
454
+ * @description Second phase of two-phase ad flow. Generates ad creative using cached bid context.
455
+ * Returns null if bid expired (404) or on error. Bid expires after 60 seconds.
456
+ *
457
+ * @param params - Request parameters including bidId and realizedPrice
458
+ * @returns Promise resolving to AdResponse or null if bid expired/error
459
+ */
460
+ render(params: RenderParams): Promise<AdResponse | null>;
306
461
  /**
307
462
  * Handle and log API errors
308
463
  *
@@ -317,4 +472,4 @@ declare class Client {
317
472
  private handleError;
318
473
  }
319
474
 
320
- export { type AdParams, type AdResponse, type ApiErrorResponse, Client, type ClientParams };
475
+ export { type Ad, type AdParams, type AdRequestBase, type AdResponse, type ApiErrorResponse, type BidParams, type BidResponse, Client, type ClientParams, type DeviceObject, type Gender, type MessageObject, type NonContextualAdParams, type RenderParams, type Role, type SummaryAdParams, type UserObject };
package/dist/index.d.ts CHANGED
@@ -83,6 +83,8 @@ interface UserObject {
83
83
  * { role: 'user', content: 'What laptop should I buy?' },
84
84
  * { role: 'assistant', content: 'What is your budget?' }
85
85
  * ],
86
+ * sessionId: 'session-123',
87
+ * userId: 'user-456',
86
88
  * user: { gender: 'male', age: '25-34' },
87
89
  * device: { ip: '1.2.3.4', country: 'US', ua: 'Mozilla/5.0...' },
88
90
  * excludedTopics: ['politics'],
@@ -91,10 +93,12 @@ interface UserObject {
91
93
  * ```
92
94
  */
93
95
  interface AdParams {
94
- /** Override the client's API key for this request */
95
- apiKey?: string;
96
96
  /** Array of conversation messages for contextual targeting (required) */
97
97
  messages: MessageObject[];
98
+ /** Session identifier for tracking user sessions */
99
+ sessionId?: string;
100
+ /** Unique user identifier */
101
+ userId?: string;
98
102
  /** Device and location information */
99
103
  device?: DeviceObject;
100
104
  /** User demographic and interest data */
@@ -103,43 +107,67 @@ interface AdParams {
103
107
  excludedTopics?: string[];
104
108
  /** Minimum relevancy score threshold (0-1). Higher = more relevant but fewer ads */
105
109
  relevancy?: number | null;
110
+ /** Number of ads to return (1-3, default 1) */
111
+ numAds?: number;
112
+ /** Returns a test ad when true */
113
+ testAd?: boolean;
106
114
  /**
107
115
  * Additional custom fields for publisher-specific targeting
108
116
  * @description Any additional key-value pairs will be passed to the API
109
117
  */
110
118
  [key: string]: unknown;
111
119
  }
120
+ /**
121
+ * Single ad object in responses.
122
+ * @description Contains all ad creative and tracking data.
123
+ */
124
+ interface Ad {
125
+ /** The advertisement copy text */
126
+ adText: string;
127
+ /** Unique ad identifier */
128
+ adId: string;
129
+ /** Ad title */
130
+ title?: string;
131
+ /** Brand/advertiser name */
132
+ brandName?: string;
133
+ /** Brand logo image URL */
134
+ brandImage?: string;
135
+ /** Landing page URL */
136
+ url?: string;
137
+ /** Favicon URL */
138
+ favicon?: string;
139
+ /** Impression tracking URL */
140
+ impUrl?: string;
141
+ /** Click-through tracking URL */
142
+ clickUrl?: string;
143
+ /** Payout amount in USD */
144
+ payout?: number;
145
+ }
112
146
  /**
113
147
  * Response from the Gravity API containing ad data
114
148
  * @description Returned by getAd() when a relevant advertisement is found
115
149
  * @example
116
150
  * ```typescript
117
- * const ad: AdResponse = {
118
- * adText: 'Check out our amazing laptops!',
119
- * impUrl: 'https://tracking.example.com/imp?id=123',
120
- * clickUrl: 'https://example.com/laptops',
121
- * payout: 0.50
151
+ * const response: AdResponse = {
152
+ * ads: [{
153
+ * adText: 'Check out our amazing laptops!',
154
+ * adId: 'ad-123',
155
+ * impUrl: 'https://tracking.example.com/imp?id=123',
156
+ * clickUrl: 'https://example.com/laptops',
157
+ * payout: 0.50
158
+ * }],
159
+ * numAds: 1,
160
+ * totalPayout: 0.50
122
161
  * };
123
162
  * ```
124
163
  */
125
164
  interface AdResponse {
126
- /** The advertisement copy text to display to the user */
127
- adText: string;
128
- /**
129
- * Impression tracking URL - fire this when the ad is viewed
130
- * @description Should be loaded (e.g., via image beacon) when ad becomes visible
131
- */
132
- impUrl?: string;
133
- /**
134
- * Click-through URL - navigate user here when ad is clicked
135
- * @description The destination page when the user clicks the ad
136
- */
137
- clickUrl?: string;
138
- /**
139
- * Payout amount in USD for this ad impression
140
- * @description The revenue earned for displaying this ad
141
- */
142
- payout?: number;
165
+ /** Array of ad objects */
166
+ ads: Ad[];
167
+ /** Number of ads returned */
168
+ numAds: number;
169
+ /** Total payout across all ads */
170
+ totalPayout?: number;
143
171
  }
144
172
  /**
145
173
  * Error response structure from the Gravity API
@@ -153,6 +181,80 @@ interface ApiErrorResponse {
153
181
  /** HTTP status code */
154
182
  statusCode?: number;
155
183
  }
184
+ /**
185
+ * Base fields shared across all ad requests.
186
+ */
187
+ interface AdRequestBase {
188
+ /** Session identifier for tracking user sessions */
189
+ sessionId?: string;
190
+ /** Unique user identifier */
191
+ userId?: string;
192
+ /** Device and location information */
193
+ device?: DeviceObject;
194
+ /** User demographic and interest data */
195
+ user?: UserObject;
196
+ /** Topics to exclude from ad matching */
197
+ excludedTopics?: string[];
198
+ /** Minimum relevancy score threshold (0-1) */
199
+ relevancy?: number | null;
200
+ /** Number of ads to return (1-3, default 1) */
201
+ numAds?: number;
202
+ /** Returns a test ad when true */
203
+ testAd?: boolean;
204
+ /** Additional custom fields */
205
+ [key: string]: unknown;
206
+ }
207
+ /**
208
+ * POST /api/v1/ad/summary
209
+ * @description Requires queryString for summary-based targeting.
210
+ */
211
+ interface SummaryAdParams extends AdRequestBase {
212
+ /** Search/summary query string (required) */
213
+ queryString: string;
214
+ }
215
+ /**
216
+ * POST /api/v1/ad/non-contextual
217
+ * @description No context required - returns ads without context matching.
218
+ */
219
+ interface NonContextualAdParams extends AdRequestBase {
220
+ }
221
+ /**
222
+ * POST /api/v1/bid
223
+ * @description Two-phase bid request. Returns bid price and bidId.
224
+ * @note Does NOT extend AdRequestBaseV1 - has its own field set.
225
+ */
226
+ interface BidParams {
227
+ /** Array of conversation messages (required) */
228
+ messages: MessageObject[];
229
+ /** Session identifier */
230
+ sessionId?: string;
231
+ /** Unique user identifier */
232
+ userId?: string;
233
+ /** Chat/conversation identifier */
234
+ chatId?: string;
235
+ /** Device and location information */
236
+ device?: DeviceObject;
237
+ }
238
+ /**
239
+ * POST /api/v1/render
240
+ * @description Two-phase render request using cached bid context.
241
+ */
242
+ interface RenderParams {
243
+ /** Bid identifier from the bid phase (required) */
244
+ bidId: string;
245
+ /** Realized price to charge (required) */
246
+ realizedPrice: number;
247
+ }
248
+ /**
249
+ * Bid response.
250
+ * @description Returned by the bid endpoint.
251
+ */
252
+ interface BidResponse {
253
+ /** Clearing price (CPM) */
254
+ bid: number;
255
+ /** Bid identifier for the render phase */
256
+ bidId: string;
257
+ }
156
258
 
157
259
  /**
158
260
  * Configuration options for the Gravity API Client
@@ -197,13 +299,15 @@ interface ClientParams {
197
299
  *
198
300
  * const client = new Client('your-api-key');
199
301
  *
200
- * const ad = await client.getAd({
302
+ * const response = await client.getAd({
201
303
  * messages: [
202
304
  * { role: 'user', content: 'What laptop should I buy?' }
203
- * ]
305
+ * ],
306
+ * sessionId: 'session-123',
204
307
  * });
205
308
  *
206
- * if (ad) {
309
+ * if (response) {
310
+ * const ad = response.ads[0];
207
311
  * console.log(ad.adText);
208
312
  * }
209
313
  * ```
@@ -252,7 +356,7 @@ declare class Client {
252
356
  /**
253
357
  * Request a contextually relevant advertisement
254
358
  *
255
- * @description Fetches an ad based on the provided conversation context and targeting parameters.
359
+ * @description Fetches ads based on the provided conversation context and targeting parameters.
256
360
  * Returns `null` if no relevant ad is available or if an error occurs.
257
361
  *
258
362
  * @param params - Ad request parameters including conversation messages
@@ -260,18 +364,27 @@ declare class Client {
260
364
  *
261
365
  * @example Basic request
262
366
  * ```typescript
263
- * const ad = await client.getAd({
367
+ * const response = await client.getAd({
264
368
  * messages: [
265
369
  * { role: 'user', content: 'I need a new laptop for programming' },
266
370
  * { role: 'assistant', content: 'What is your budget range?' }
267
- * ]
371
+ * ],
372
+ * sessionId: 'session-123',
373
+ * userId: 'user-456',
268
374
  * });
375
+ *
376
+ * if (response) {
377
+ * const ad = response.ads[0];
378
+ * console.log(ad.adText);
379
+ * }
269
380
  * ```
270
381
  *
271
382
  * @example Full request with targeting
272
383
  * ```typescript
273
- * const ad = await client.getAd({
384
+ * const response = await client.getAd({
274
385
  * messages: [...],
386
+ * sessionId: 'session-123',
387
+ * userId: 'user-456',
275
388
  * user: {
276
389
  * uid: 'user-123',
277
390
  * gender: 'male',
@@ -289,9 +402,10 @@ declare class Client {
289
402
  *
290
403
  * @example Handling the response
291
404
  * ```typescript
292
- * const ad = await client.getAd({ messages });
405
+ * const response = await client.getAd({ messages, sessionId: '...' });
293
406
  *
294
- * if (ad) {
407
+ * if (response) {
408
+ * const ad = response.ads[0];
295
409
  * // Display the ad
296
410
  * showAd(ad.adText);
297
411
  *
@@ -303,6 +417,47 @@ declare class Client {
303
417
  * ```
304
418
  */
305
419
  getAd(params: AdParams): Promise<AdResponse | null>;
420
+ /**
421
+ * Request summary-based advertisements
422
+ *
423
+ * @description Fetches ads based on a search/summary query string.
424
+ * Returns null if no relevant ad is available or on error.
425
+ *
426
+ * @param params - Request parameters including queryString
427
+ * @returns Promise resolving to AdResponse or null if no ad available
428
+ */
429
+ summaryAd(params: SummaryAdParams): Promise<AdResponse | null>;
430
+ /**
431
+ * Request non-contextual advertisements
432
+ *
433
+ * @description Fetches ads without context matching. Useful for brand awareness placements.
434
+ * Returns null if no ad is available or on error.
435
+ *
436
+ * @param params - Optional request parameters
437
+ * @returns Promise resolving to AdResponse or null if no ad available
438
+ */
439
+ nonContextualAd(params?: NonContextualAdParams): Promise<AdResponse | null>;
440
+ /**
441
+ * Request a bid price for contextual ad placement
442
+ *
443
+ * @description First phase of two-phase ad flow. Returns bid price and bidId.
444
+ * Use the bidId with render() to generate the actual ad creative.
445
+ * Returns null if no bid is available or on error.
446
+ *
447
+ * @param params - Request parameters including messages array
448
+ * @returns Promise resolving to BidResponse or null if no bid available
449
+ */
450
+ bid(params: BidParams): Promise<BidResponse | null>;
451
+ /**
452
+ * Render an ad from a cached bid
453
+ *
454
+ * @description Second phase of two-phase ad flow. Generates ad creative using cached bid context.
455
+ * Returns null if bid expired (404) or on error. Bid expires after 60 seconds.
456
+ *
457
+ * @param params - Request parameters including bidId and realizedPrice
458
+ * @returns Promise resolving to AdResponse or null if bid expired/error
459
+ */
460
+ render(params: RenderParams): Promise<AdResponse | null>;
306
461
  /**
307
462
  * Handle and log API errors
308
463
  *
@@ -317,4 +472,4 @@ declare class Client {
317
472
  private handleError;
318
473
  }
319
474
 
320
- export { type AdParams, type AdResponse, type ApiErrorResponse, Client, type ClientParams };
475
+ export { type Ad, type AdParams, type AdRequestBase, type AdResponse, type ApiErrorResponse, type BidParams, type BidResponse, Client, type ClientParams, type DeviceObject, type Gender, type MessageObject, type NonContextualAdParams, type RenderParams, type Role, type SummaryAdParams, type UserObject };
package/dist/index.js CHANGED
@@ -76,7 +76,7 @@ var Client = class {
76
76
  /**
77
77
  * Request a contextually relevant advertisement
78
78
  *
79
- * @description Fetches an ad based on the provided conversation context and targeting parameters.
79
+ * @description Fetches ads based on the provided conversation context and targeting parameters.
80
80
  * Returns `null` if no relevant ad is available or if an error occurs.
81
81
  *
82
82
  * @param params - Ad request parameters including conversation messages
@@ -84,18 +84,27 @@ var Client = class {
84
84
  *
85
85
  * @example Basic request
86
86
  * ```typescript
87
- * const ad = await client.getAd({
87
+ * const response = await client.getAd({
88
88
  * messages: [
89
89
  * { role: 'user', content: 'I need a new laptop for programming' },
90
90
  * { role: 'assistant', content: 'What is your budget range?' }
91
- * ]
91
+ * ],
92
+ * sessionId: 'session-123',
93
+ * userId: 'user-456',
92
94
  * });
95
+ *
96
+ * if (response) {
97
+ * const ad = response.ads[0];
98
+ * console.log(ad.adText);
99
+ * }
93
100
  * ```
94
101
  *
95
102
  * @example Full request with targeting
96
103
  * ```typescript
97
- * const ad = await client.getAd({
104
+ * const response = await client.getAd({
98
105
  * messages: [...],
106
+ * sessionId: 'session-123',
107
+ * userId: 'user-456',
99
108
  * user: {
100
109
  * uid: 'user-123',
101
110
  * gender: 'male',
@@ -113,9 +122,10 @@ var Client = class {
113
122
  *
114
123
  * @example Handling the response
115
124
  * ```typescript
116
- * const ad = await client.getAd({ messages });
125
+ * const response = await client.getAd({ messages, sessionId: '...' });
117
126
  *
118
- * if (ad) {
127
+ * if (response) {
128
+ * const ad = response.ads[0];
119
129
  * // Display the ad
120
130
  * showAd(ad.adText);
121
131
  *
@@ -130,26 +140,134 @@ var Client = class {
130
140
  try {
131
141
  const body = {
132
142
  ...params,
133
- // Use request-level excludedTopics, or fall back to client-level
134
143
  excludedTopics: params.excludedTopics ?? this.excludedTopics,
135
- // Use request-level relevancy, or fall back to client-level
136
144
  relevancy: params.relevancy ?? this.relevancy
137
145
  };
138
- const response = await this.axios.post("/ad", body);
146
+ const response = await this.axios.post("/api/v1/ad/contextual", body);
147
+ if (response.status === 204) {
148
+ return null;
149
+ }
150
+ if (response.data && response.data.ads && response.data.ads.length > 0) {
151
+ return response.data;
152
+ }
153
+ return null;
154
+ } catch (error) {
155
+ this.handleError(error, "getAd");
156
+ return null;
157
+ }
158
+ }
159
+ // ===========================================================================
160
+ // Alternative Ad Methods (for advanced use cases)
161
+ // ===========================================================================
162
+ /**
163
+ * Request summary-based advertisements
164
+ *
165
+ * @description Fetches ads based on a search/summary query string.
166
+ * Returns null if no relevant ad is available or on error.
167
+ *
168
+ * @param params - Request parameters including queryString
169
+ * @returns Promise resolving to AdResponse or null if no ad available
170
+ */
171
+ async summaryAd(params) {
172
+ try {
173
+ const body = {
174
+ ...params,
175
+ excludedTopics: params.excludedTopics ?? this.excludedTopics,
176
+ relevancy: params.relevancy ?? this.relevancy
177
+ };
178
+ const response = await this.axios.post("/api/v1/ad/summary", body);
179
+ if (response.status === 204) {
180
+ return null;
181
+ }
182
+ if (response.data && response.data.ads && response.data.ads.length > 0) {
183
+ return response.data;
184
+ }
185
+ return null;
186
+ } catch (error) {
187
+ this.handleError(error, "summaryAd");
188
+ return null;
189
+ }
190
+ }
191
+ /**
192
+ * Request non-contextual advertisements
193
+ *
194
+ * @description Fetches ads without context matching. Useful for brand awareness placements.
195
+ * Returns null if no ad is available or on error.
196
+ *
197
+ * @param params - Optional request parameters
198
+ * @returns Promise resolving to AdResponse or null if no ad available
199
+ */
200
+ async nonContextualAd(params = {}) {
201
+ try {
202
+ const body = {
203
+ ...params,
204
+ excludedTopics: params.excludedTopics ?? this.excludedTopics
205
+ };
206
+ const response = await this.axios.post("/api/v1/ad/non-contextual", body);
207
+ if (response.status === 204) {
208
+ return null;
209
+ }
210
+ if (response.data && response.data.ads && response.data.ads.length > 0) {
211
+ return response.data;
212
+ }
213
+ return null;
214
+ } catch (error) {
215
+ this.handleError(error, "nonContextualAd");
216
+ return null;
217
+ }
218
+ }
219
+ /**
220
+ * Request a bid price for contextual ad placement
221
+ *
222
+ * @description First phase of two-phase ad flow. Returns bid price and bidId.
223
+ * Use the bidId with render() to generate the actual ad creative.
224
+ * Returns null if no bid is available or on error.
225
+ *
226
+ * @param params - Request parameters including messages array
227
+ * @returns Promise resolving to BidResponse or null if no bid available
228
+ */
229
+ async bid(params) {
230
+ try {
231
+ const response = await this.axios.post("/api/v1/bid", params);
139
232
  if (response.status === 204) {
140
233
  return null;
141
234
  }
142
- if (response.data && response.data.adText) {
235
+ if (response.data && response.data.data) {
143
236
  return {
144
- adText: response.data.adText,
145
- impUrl: response.data.impUrl,
146
- clickUrl: response.data.clickUrl,
147
- payout: response.data.payout
237
+ bid: response.data.data.bid,
238
+ bidId: response.data.data.bidId
148
239
  };
149
240
  }
150
241
  return null;
151
242
  } catch (error) {
152
- this.handleError(error, "getAd");
243
+ this.handleError(error, "bid");
244
+ return null;
245
+ }
246
+ }
247
+ /**
248
+ * Render an ad from a cached bid
249
+ *
250
+ * @description Second phase of two-phase ad flow. Generates ad creative using cached bid context.
251
+ * Returns null if bid expired (404) or on error. Bid expires after 60 seconds.
252
+ *
253
+ * @param params - Request parameters including bidId and realizedPrice
254
+ * @returns Promise resolving to AdResponse or null if bid expired/error
255
+ */
256
+ async render(params) {
257
+ try {
258
+ const response = await this.axios.post("/api/v1/render", params);
259
+ if (response.status === 204) {
260
+ return null;
261
+ }
262
+ if (response.data && response.data.ads && response.data.ads.length > 0) {
263
+ return response.data;
264
+ }
265
+ return null;
266
+ } catch (error) {
267
+ if (import_axios.default.isAxiosError(error) && error.response?.status === 404) {
268
+ return null;
269
+ }
270
+ this.handleError(error, "render");
153
271
  return null;
154
272
  }
155
273
  }
package/dist/index.mjs CHANGED
@@ -40,7 +40,7 @@ var Client = class {
40
40
  /**
41
41
  * Request a contextually relevant advertisement
42
42
  *
43
- * @description Fetches an ad based on the provided conversation context and targeting parameters.
43
+ * @description Fetches ads based on the provided conversation context and targeting parameters.
44
44
  * Returns `null` if no relevant ad is available or if an error occurs.
45
45
  *
46
46
  * @param params - Ad request parameters including conversation messages
@@ -48,18 +48,27 @@ var Client = class {
48
48
  *
49
49
  * @example Basic request
50
50
  * ```typescript
51
- * const ad = await client.getAd({
51
+ * const response = await client.getAd({
52
52
  * messages: [
53
53
  * { role: 'user', content: 'I need a new laptop for programming' },
54
54
  * { role: 'assistant', content: 'What is your budget range?' }
55
- * ]
55
+ * ],
56
+ * sessionId: 'session-123',
57
+ * userId: 'user-456',
56
58
  * });
59
+ *
60
+ * if (response) {
61
+ * const ad = response.ads[0];
62
+ * console.log(ad.adText);
63
+ * }
57
64
  * ```
58
65
  *
59
66
  * @example Full request with targeting
60
67
  * ```typescript
61
- * const ad = await client.getAd({
68
+ * const response = await client.getAd({
62
69
  * messages: [...],
70
+ * sessionId: 'session-123',
71
+ * userId: 'user-456',
63
72
  * user: {
64
73
  * uid: 'user-123',
65
74
  * gender: 'male',
@@ -77,9 +86,10 @@ var Client = class {
77
86
  *
78
87
  * @example Handling the response
79
88
  * ```typescript
80
- * const ad = await client.getAd({ messages });
89
+ * const response = await client.getAd({ messages, sessionId: '...' });
81
90
  *
82
- * if (ad) {
91
+ * if (response) {
92
+ * const ad = response.ads[0];
83
93
  * // Display the ad
84
94
  * showAd(ad.adText);
85
95
  *
@@ -94,26 +104,134 @@ var Client = class {
94
104
  try {
95
105
  const body = {
96
106
  ...params,
97
- // Use request-level excludedTopics, or fall back to client-level
98
107
  excludedTopics: params.excludedTopics ?? this.excludedTopics,
99
- // Use request-level relevancy, or fall back to client-level
100
108
  relevancy: params.relevancy ?? this.relevancy
101
109
  };
102
- const response = await this.axios.post("/ad", body);
110
+ const response = await this.axios.post("/api/v1/ad/contextual", body);
111
+ if (response.status === 204) {
112
+ return null;
113
+ }
114
+ if (response.data && response.data.ads && response.data.ads.length > 0) {
115
+ return response.data;
116
+ }
117
+ return null;
118
+ } catch (error) {
119
+ this.handleError(error, "getAd");
120
+ return null;
121
+ }
122
+ }
123
+ // ===========================================================================
124
+ // Alternative Ad Methods (for advanced use cases)
125
+ // ===========================================================================
126
+ /**
127
+ * Request summary-based advertisements
128
+ *
129
+ * @description Fetches ads based on a search/summary query string.
130
+ * Returns null if no relevant ad is available or on error.
131
+ *
132
+ * @param params - Request parameters including queryString
133
+ * @returns Promise resolving to AdResponse or null if no ad available
134
+ */
135
+ async summaryAd(params) {
136
+ try {
137
+ const body = {
138
+ ...params,
139
+ excludedTopics: params.excludedTopics ?? this.excludedTopics,
140
+ relevancy: params.relevancy ?? this.relevancy
141
+ };
142
+ const response = await this.axios.post("/api/v1/ad/summary", body);
143
+ if (response.status === 204) {
144
+ return null;
145
+ }
146
+ if (response.data && response.data.ads && response.data.ads.length > 0) {
147
+ return response.data;
148
+ }
149
+ return null;
150
+ } catch (error) {
151
+ this.handleError(error, "summaryAd");
152
+ return null;
153
+ }
154
+ }
155
+ /**
156
+ * Request non-contextual advertisements
157
+ *
158
+ * @description Fetches ads without context matching. Useful for brand awareness placements.
159
+ * Returns null if no ad is available or on error.
160
+ *
161
+ * @param params - Optional request parameters
162
+ * @returns Promise resolving to AdResponse or null if no ad available
163
+ */
164
+ async nonContextualAd(params = {}) {
165
+ try {
166
+ const body = {
167
+ ...params,
168
+ excludedTopics: params.excludedTopics ?? this.excludedTopics
169
+ };
170
+ const response = await this.axios.post("/api/v1/ad/non-contextual", body);
171
+ if (response.status === 204) {
172
+ return null;
173
+ }
174
+ if (response.data && response.data.ads && response.data.ads.length > 0) {
175
+ return response.data;
176
+ }
177
+ return null;
178
+ } catch (error) {
179
+ this.handleError(error, "nonContextualAd");
180
+ return null;
181
+ }
182
+ }
183
+ /**
184
+ * Request a bid price for contextual ad placement
185
+ *
186
+ * @description First phase of two-phase ad flow. Returns bid price and bidId.
187
+ * Use the bidId with render() to generate the actual ad creative.
188
+ * Returns null if no bid is available or on error.
189
+ *
190
+ * @param params - Request parameters including messages array
191
+ * @returns Promise resolving to BidResponse or null if no bid available
192
+ */
193
+ async bid(params) {
194
+ try {
195
+ const response = await this.axios.post("/api/v1/bid", params);
103
196
  if (response.status === 204) {
104
197
  return null;
105
198
  }
106
- if (response.data && response.data.adText) {
199
+ if (response.data && response.data.data) {
107
200
  return {
108
- adText: response.data.adText,
109
- impUrl: response.data.impUrl,
110
- clickUrl: response.data.clickUrl,
111
- payout: response.data.payout
201
+ bid: response.data.data.bid,
202
+ bidId: response.data.data.bidId
112
203
  };
113
204
  }
114
205
  return null;
115
206
  } catch (error) {
116
- this.handleError(error, "getAd");
207
+ this.handleError(error, "bid");
208
+ return null;
209
+ }
210
+ }
211
+ /**
212
+ * Render an ad from a cached bid
213
+ *
214
+ * @description Second phase of two-phase ad flow. Generates ad creative using cached bid context.
215
+ * Returns null if bid expired (404) or on error. Bid expires after 60 seconds.
216
+ *
217
+ * @param params - Request parameters including bidId and realizedPrice
218
+ * @returns Promise resolving to AdResponse or null if bid expired/error
219
+ */
220
+ async render(params) {
221
+ try {
222
+ const response = await this.axios.post("/api/v1/render", params);
223
+ if (response.status === 204) {
224
+ return null;
225
+ }
226
+ if (response.data && response.data.ads && response.data.ads.length > 0) {
227
+ return response.data;
228
+ }
229
+ return null;
230
+ } catch (error) {
231
+ if (axios.isAxiosError(error) && error.response?.status === 404) {
232
+ return null;
233
+ }
234
+ this.handleError(error, "render");
117
235
  return null;
118
236
  }
119
237
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ai/api",
3
- "version": "0.1.2",
3
+ "version": "1.0.2",
4
4
  "description": "Gravity JS SDK for retrieving targeted advertisements",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",