@drip-sdk/node 1.0.10 → 1.1.0

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,8 +1,8 @@
1
1
  # @drip-sdk/node
2
2
 
3
- The official Node.js SDK for **Drip** - Usage-based billing for AI agents.
3
+ The official Node.js SDK for **Drip** - Usage tracking and cost attribution for metered infrastructure.
4
4
 
5
- Drip enables real-time, per-request billing using USDC on blockchain. Perfect for AI APIs, compute platforms, and any service with variable usage patterns.
5
+ Drip is the system of record for usage. We capture high-frequency metering for RPC providers, API companies, and AI agents - with cost attribution, execution traces, and real-time analytics.
6
6
 
7
7
  [![npm version](https://badge.fury.io/js/@drip-sdk/node.svg)](https://www.npmjs.com/package/@drip-sdk/node)
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
@@ -13,690 +13,386 @@ Drip enables real-time, per-request billing using USDC on blockchain. Perfect fo
13
13
  npm install @drip-sdk/node
14
14
  ```
15
15
 
16
- ```bash
17
- yarn add @drip-sdk/node
18
- ```
19
-
20
- ```bash
21
- pnpm add @drip-sdk/node
22
- ```
23
-
24
- ## Quick Start
25
-
26
- ### One-Liner Integration (Recommended)
16
+ ## Core SDK (Recommended for Pilots)
27
17
 
28
- The fastest way to add billing to your API:
18
+ For most use cases, import the **Core SDK** - a simplified API focused on two concepts:
29
19
 
30
- #### Next.js App Router
20
+ 1. **Usage Tracking** - Record metered usage events
21
+ 2. **Execution Logging** - Log agent runs with detailed event traces
31
22
 
32
23
  ```typescript
33
- // app/api/generate/route.ts
34
- import { withDrip } from '@drip-sdk/node/next';
24
+ import { Drip } from '@drip-sdk/node/core';
35
25
 
36
- export const POST = withDrip({
37
- meter: 'api_calls',
38
- quantity: 1,
39
- }, async (req, { charge, customerId }) => {
40
- // Your handler - payment already verified!
41
- console.log(`Charged ${charge.charge.amountUsdc} USDC to ${customerId}`);
42
- return Response.json({ result: 'success' });
43
- });
26
+ const drip = new Drip({ apiKey: process.env.DRIP_API_KEY! });
27
+
28
+ // Verify connection
29
+ await drip.ping();
44
30
  ```
45
31
 
46
- #### Express
32
+ ### Track Usage
47
33
 
48
34
  ```typescript
49
- import express from 'express';
50
- import { dripMiddleware } from '@drip-sdk/node/express';
51
-
52
- const app = express();
53
-
54
- app.use('/api/paid', dripMiddleware({
35
+ // Track any metered usage (no billing - just recording)
36
+ await drip.trackUsage({
37
+ customerId: 'cus_123',
55
38
  meter: 'api_calls',
56
39
  quantity: 1,
57
- }));
58
-
59
- app.post('/api/paid/generate', (req, res) => {
60
- console.log(`Charged: ${req.drip.charge.charge.amountUsdc} USDC`);
61
- res.json({ success: true });
40
+ metadata: { endpoint: '/v1/generate', method: 'POST' },
62
41
  });
63
42
  ```
64
43
 
65
- ### Manual Integration
66
-
67
- For more control, use the SDK directly:
44
+ ### Record Agent Runs
68
45
 
69
46
  ```typescript
70
- import { Drip } from '@drip-sdk/node';
71
-
72
- // Initialize the client
73
- const drip = new Drip({
74
- apiKey: process.env.DRIP_API_KEY!,
75
- });
76
-
77
- // Create a customer
78
- const customer = await drip.createCustomer({
79
- onchainAddress: '0x1234567890abcdef...',
80
- externalCustomerId: 'user_123',
81
- });
82
-
83
- // Record usage and charge
84
- const result = await drip.charge({
85
- customerId: customer.id,
86
- meter: 'api_calls',
87
- quantity: 100,
47
+ // Record a complete agent execution with one call
48
+ const result = await drip.recordRun({
49
+ customerId: 'cus_123',
50
+ workflow: 'research-agent',
51
+ events: [
52
+ { eventType: 'llm.call', model: 'gpt-4', inputTokens: 500, outputTokens: 1200 },
53
+ { eventType: 'tool.call', name: 'web-search', duration: 1500 },
54
+ { eventType: 'llm.call', model: 'gpt-4', inputTokens: 200, outputTokens: 800 },
55
+ ],
56
+ status: 'COMPLETED',
88
57
  });
89
58
 
90
- console.log(`Charged ${result.charge.amountUsdc} USDC`);
91
- console.log(`TX: ${result.charge.txHash}`);
59
+ console.log(result.summary);
60
+ // Output: "Research Agent: 3 events recorded (2.5s)"
92
61
  ```
93
62
 
94
- ## Configuration
95
-
96
- ```typescript
97
- const drip = new Drip({
98
- // Required: Your Drip API key
99
- apiKey: 'drip_live_abc123...',
63
+ ### Core SDK Methods
100
64
 
101
- // Optional: API base URL (for staging/development)
102
- baseUrl: 'https://api.drip.dev/v1',
65
+ | Method | Description |
66
+ |--------|-------------|
67
+ | `ping()` | Verify API connection |
68
+ | `createCustomer(params)` | Create a customer |
69
+ | `getCustomer(customerId)` | Get customer details |
70
+ | `listCustomers(options)` | List all customers |
71
+ | `trackUsage(params)` | Record metered usage |
72
+ | `recordRun(params)` | Log complete agent run (simplified) |
73
+ | `startRun(params)` | Start execution trace |
74
+ | `emitEvent(params)` | Log event within run |
75
+ | `emitEventsBatch(params)` | Batch log events |
76
+ | `endRun(runId, params)` | Complete execution trace |
77
+ | `getRunTimeline(runId)` | Get execution timeline |
103
78
 
104
- // Optional: Request timeout in milliseconds (default: 30000)
105
- timeout: 30000,
106
- });
107
- ```
79
+ ---
108
80
 
109
- ## API Reference
81
+ ## Full SDK
110
82
 
111
- ### Customer Management
112
-
113
- #### Create a Customer
83
+ For billing, webhooks, and advanced features, use the full SDK:
114
84
 
115
85
  ```typescript
116
- const customer = await drip.createCustomer({
117
- onchainAddress: '0x1234567890abcdef...',
118
- externalCustomerId: 'user_123', // Your internal user ID
119
- metadata: { plan: 'pro' },
120
- });
121
- ```
86
+ import { Drip } from '@drip-sdk/node';
122
87
 
123
- #### Get a Customer
88
+ const drip = new Drip({ apiKey: process.env.DRIP_API_KEY! });
124
89
 
125
- ```typescript
126
- const customer = await drip.getCustomer('cust_abc123');
90
+ // All Core SDK methods plus:
91
+ // - charge(), getBalance(), getCharge(), listCharges()
92
+ // - createWebhook(), listWebhooks(), deleteWebhook()
93
+ // - estimateFromUsage(), estimateFromHypothetical()
94
+ // - checkout(), and more
127
95
  ```
128
96
 
129
- #### List Customers
97
+ ## Quick Start (Full SDK)
98
+
99
+ ### Track Usage
130
100
 
131
101
  ```typescript
132
- // List all customers
133
- const { data: customers } = await drip.listCustomers();
102
+ import { Drip } from '@drip-sdk/node';
134
103
 
135
- // With filters
136
- const { data: activeCustomers } = await drip.listCustomers({
137
- status: 'ACTIVE',
138
- limit: 50,
139
- });
140
- ```
104
+ const drip = new Drip({ apiKey: process.env.DRIP_API_KEY! });
141
105
 
142
- #### Get Customer Balance
106
+ // Track any metered usage
107
+ await drip.trackUsage({
108
+ customerId: 'cus_123',
109
+ meter: 'api_calls',
110
+ quantity: 1,
111
+ metadata: { endpoint: '/v1/generate', method: 'POST' },
112
+ });
143
113
 
144
- ```typescript
145
- const balance = await drip.getBalance('cust_abc123');
146
- console.log(`Balance: ${balance.balanceUSDC} USDC`);
114
+ // Check accumulated usage
115
+ const balance = await drip.getBalance('cus_123');
116
+ console.log(`Total usage: $${balance.totalUsageUsd}`);
147
117
  ```
148
118
 
149
- ### Meters (Usage Types)
150
-
151
- #### List Available Meters
152
-
153
- Discover what meter names are valid for charging. Meters are defined by your pricing plans.
119
+ ### Log Agent Runs (AI/Agent API)
154
120
 
155
121
  ```typescript
156
- const { data: meters } = await drip.listMeters();
122
+ // Record a complete agent execution with one call
123
+ const result = await drip.recordRun({
124
+ customerId: 'cus_123',
125
+ workflow: 'research-agent',
126
+ events: [
127
+ { eventType: 'llm.call', model: 'gpt-4', inputTokens: 500, outputTokens: 1200 },
128
+ { eventType: 'tool.call', name: 'web-search', duration: 1500 },
129
+ { eventType: 'llm.call', model: 'gpt-4', inputTokens: 200, outputTokens: 800 },
130
+ ],
131
+ status: 'COMPLETED',
132
+ });
157
133
 
158
- console.log('Available meters:');
159
- for (const meter of meters) {
160
- console.log(` ${meter.meter}: $${meter.unitPriceUsd}/unit`);
161
- }
162
- // Output:
163
- // api_calls: $0.001/unit
164
- // tokens: $0.00001/unit
165
- // compute_seconds: $0.01/unit
134
+ console.log(result.summary);
135
+ // Output: "Research Agent: 3 events recorded (2.5s)"
166
136
  ```
167
137
 
168
- ### Charging & Usage
169
-
170
- #### Record Usage and Charge
138
+ ### View Execution Traces
171
139
 
172
140
  ```typescript
173
- const result = await drip.charge({
174
- customerId: 'cust_abc123',
175
- meter: 'api_calls',
176
- quantity: 100,
177
- idempotencyKey: 'req_unique_123', // Prevents duplicate charges
178
- metadata: { endpoint: '/v1/chat' },
179
- });
141
+ // Get detailed timeline of an agent run
142
+ const timeline = await drip.getRunTimeline(runId);
180
143
 
181
- if (result.success) {
182
- console.log(`Charge ID: ${result.charge.id}`);
183
- console.log(`Amount: ${result.charge.amountUsdc} USDC`);
184
- console.log(`TX Hash: ${result.charge.txHash}`);
144
+ for (const event of timeline.events) {
145
+ console.log(`${event.eventType}: ${event.duration}ms`);
185
146
  }
186
147
  ```
187
148
 
188
- #### Get Charge Details
189
-
190
- ```typescript
191
- const charge = await drip.getCharge('chg_abc123');
192
- console.log(`Status: ${charge.status}`);
193
- ```
149
+ ## Use Cases
194
150
 
195
- #### List Charges
151
+ ### RPC Providers
196
152
 
197
153
  ```typescript
198
- // List all charges
199
- const { data: charges } = await drip.listCharges();
200
-
201
- // Filter by customer and status
202
- const { data: customerCharges } = await drip.listCharges({
203
- customerId: 'cust_abc123',
204
- status: 'CONFIRMED',
205
- limit: 50,
154
+ // Track per-method usage with chain and latency
155
+ await drip.trackUsage({
156
+ customerId: apiKeyOwner,
157
+ meter: 'rpc_calls',
158
+ quantity: 1,
159
+ metadata: {
160
+ method: 'eth_call',
161
+ chain: 'ethereum',
162
+ latencyMs: 45,
163
+ cacheHit: false,
164
+ },
206
165
  });
207
166
  ```
208
167
 
209
- #### Check Charge Status
168
+ ### API Companies
210
169
 
211
170
  ```typescript
212
- const status = await drip.getChargeStatus('chg_abc123');
213
- if (status.status === 'CONFIRMED') {
214
- console.log('Charge confirmed on-chain!');
215
- }
171
+ // Track API usage with endpoint attribution
172
+ await drip.trackUsage({
173
+ customerId: 'cus_123',
174
+ meter: 'api_calls',
175
+ quantity: 1,
176
+ metadata: {
177
+ endpoint: '/v1/embeddings',
178
+ tokens: 1500,
179
+ model: 'text-embedding-3-small',
180
+ },
181
+ });
216
182
  ```
217
183
 
218
- ### Streaming Meter
219
-
220
- For LLM token streaming and other high-frequency metering scenarios, use the streaming meter to accumulate usage locally and charge once at the end:
184
+ ### AI Agents
221
185
 
222
186
  ```typescript
223
- import { Drip } from '@drip-sdk/node';
224
-
225
- const drip = new Drip({ apiKey: process.env.DRIP_API_KEY! });
187
+ // Track agent execution with detailed events
188
+ const run = await drip.startRun({
189
+ customerId: 'cus_123',
190
+ workflowSlug: 'document-processor',
191
+ });
226
192
 
227
- // Create a stream meter for a customer
228
- const meter = drip.createStreamMeter({
229
- customerId: 'cust_abc123',
230
- meter: 'tokens',
193
+ await drip.emitEvent({
194
+ runId: run.id,
195
+ eventType: 'ocr.process',
196
+ quantity: 5,
197
+ units: 'pages',
231
198
  });
232
199
 
233
- // Stream from your LLM provider
234
- const stream = await openai.chat.completions.create({
200
+ await drip.emitEvent({
201
+ runId: run.id,
202
+ eventType: 'llm.summarize',
235
203
  model: 'gpt-4',
236
- messages: [{ role: 'user', content: 'Hello!' }],
237
- stream: true,
204
+ inputTokens: 10000,
205
+ outputTokens: 500,
238
206
  });
239
207
 
240
- // Accumulate tokens as they stream
241
- for await (const chunk of stream) {
242
- const tokens = chunk.usage?.completion_tokens ?? 1;
243
- meter.add(tokens); // Accumulates locally, no API call
244
- yield chunk;
245
- }
246
-
247
- // Single charge at end of stream
248
- const result = await meter.flush();
249
- console.log(`Charged ${result.charge.amountUsdc} USDC for ${result.quantity} tokens`);
208
+ await drip.endRun(run.id, { status: 'COMPLETED' });
250
209
  ```
251
210
 
252
- #### Stream Meter Options
253
-
254
- ```typescript
255
- const meter = drip.createStreamMeter({
256
- customerId: 'cust_abc123',
257
- meter: 'tokens',
258
-
259
- // Optional: Custom idempotency key
260
- idempotencyKey: 'stream_req_123',
211
+ ## Full SDK API Reference
261
212
 
262
- // Optional: Metadata attached to the charge
263
- metadata: { model: 'gpt-4', endpoint: '/v1/chat' },
213
+ ### Usage & Billing
264
214
 
265
- // Optional: Auto-flush when threshold reached
266
- flushThreshold: 10000, // Flush every 10k tokens
215
+ | Method | Description |
216
+ |--------|-------------|
217
+ | `trackUsage(params)` | Record metered usage (no billing) |
218
+ | `charge(params)` | Create a billable charge |
219
+ | `getBalance(customerId)` | Get balance and usage summary |
220
+ | `getCharge(chargeId)` | Get charge details |
221
+ | `listCharges(options)` | List all charges |
222
+ | `getChargeStatus(chargeId)` | Get charge status |
267
223
 
268
- // Optional: Callback on each add
269
- onAdd: (quantity, total) => console.log(`Added ${quantity}, total: ${total}`),
270
- });
271
- ```
224
+ ### Execution Logging
272
225
 
273
- #### Handling Partial Failures
226
+ | Method | Description |
227
+ |--------|-------------|
228
+ | `recordRun(params)` | Log complete agent run (simplified) |
229
+ | `startRun(params)` | Start execution trace |
230
+ | `emitEvent(params)` | Log event within run |
231
+ | `emitEventsBatch(params)` | Batch log events |
232
+ | `endRun(runId, params)` | Complete execution trace |
233
+ | `getRunTimeline(runId)` | Get execution timeline |
234
+ | `createWorkflow(params)` | Create a workflow |
235
+ | `listWorkflows()` | List all workflows |
274
236
 
275
- If the stream fails mid-way, you can still charge for what was delivered:
237
+ ### Customer Management
276
238
 
277
- ```typescript
278
- const meter = drip.createStreamMeter({
279
- customerId: 'cust_abc123',
280
- meter: 'tokens',
281
- });
239
+ | Method | Description |
240
+ |--------|-------------|
241
+ | `createCustomer(params)` | Create a customer |
242
+ | `getCustomer(customerId)` | Get customer details |
243
+ | `listCustomers(options)` | List all customers |
282
244
 
283
- try {
284
- for await (const chunk of stream) {
285
- meter.add(chunk.tokens);
286
- yield chunk;
287
- }
288
- await meter.flush();
289
- } catch (error) {
290
- // Charge for tokens delivered before failure
291
- if (meter.total > 0) {
292
- await meter.flush();
293
- }
294
- throw error;
295
- }
296
- ```
245
+ ### Webhooks
297
246
 
298
- #### Multiple Meters in One Request
247
+ | Method | Description |
248
+ |--------|-------------|
249
+ | `createWebhook(params)` | Create webhook endpoint |
250
+ | `listWebhooks()` | List all webhooks |
251
+ | `getWebhook(webhookId)` | Get webhook details |
252
+ | `deleteWebhook(webhookId)` | Delete a webhook |
253
+ | `testWebhook(webhookId)` | Test a webhook |
254
+ | `rotateWebhookSecret(webhookId)` | Rotate webhook secret |
255
+ | `Drip.verifyWebhookSignature()` | Verify webhook signature |
299
256
 
300
- ```typescript
301
- const tokenMeter = drip.createStreamMeter({ customerId, meter: 'tokens' });
302
- const toolMeter = drip.createStreamMeter({ customerId, meter: 'tool_calls' });
303
-
304
- for await (const chunk of agentStream) {
305
- if (chunk.type === 'token') {
306
- tokenMeter.add(1);
307
- } else if (chunk.type === 'tool_call') {
308
- toolMeter.add(1);
309
- }
310
- yield chunk;
311
- }
257
+ ### Cost Estimation
312
258
 
313
- // Flush all meters
314
- await Promise.all([tokenMeter.flush(), toolMeter.flush()]);
315
- ```
259
+ | Method | Description |
260
+ |--------|-------------|
261
+ | `estimateFromUsage(params)` | Estimate cost from usage data |
262
+ | `estimateFromHypothetical(params)` | Estimate from hypothetical usage |
316
263
 
317
- ### Run Tracking (Simplified API)
264
+ ### Other
318
265
 
319
- Track agent executions with a single API call instead of multiple separate calls.
266
+ | Method | Description |
267
+ |--------|-------------|
268
+ | `checkout(params)` | Create checkout session (fiat on-ramp) |
269
+ | `listMeters()` | List available meters |
270
+ | `ping()` | Verify API connection |
320
271
 
321
- #### Record a Complete Run
272
+ ## Streaming Meter (LLM Token Streaming)
322
273
 
323
- The `recordRun()` method combines workflow creation, run tracking, event emission, and completion into one call:
274
+ For LLM token streaming, accumulate usage locally and charge once:
324
275
 
325
276
  ```typescript
326
- // Before: 4+ separate API calls
327
- const workflow = await drip.createWorkflow({ name: 'My Agent', slug: 'my_agent' });
328
- const run = await drip.startRun({ customerId, workflowId: workflow.id });
329
- await drip.emitEvent({ runId: run.id, eventType: 'step1', ... });
330
- await drip.emitEvent({ runId: run.id, eventType: 'step2', ... });
331
- await drip.endRun(run.id, { status: 'COMPLETED' });
332
-
333
- // After: 1 call with recordRun()
334
- const result = await drip.recordRun({
335
- customerId: 'cust_123',
336
- workflow: 'my_agent', // Auto-creates workflow if it doesn't exist
337
- events: [
338
- { eventType: 'agent.start', description: 'Started processing' },
339
- { eventType: 'tool.ocr', quantity: 3, units: 'pages', costUnits: 0.15 },
340
- { eventType: 'tool.validate', quantity: 1, costUnits: 0.05 },
341
- { eventType: 'agent.complete', description: 'Finished successfully' },
342
- ],
343
- status: 'COMPLETED',
277
+ const meter = drip.createStreamMeter({
278
+ customerId: 'cus_123',
279
+ meter: 'tokens',
344
280
  });
345
281
 
346
- console.log(result.summary);
347
- // Output: "✓ My Agent: 4 events recorded (250ms)"
348
- ```
349
-
350
- #### Record a Failed Run
351
-
352
- ```typescript
353
- const result = await drip.recordRun({
354
- customerId: 'cust_123',
355
- workflow: 'prescription_intake',
356
- events: [
357
- { eventType: 'agent.start', description: 'Started processing' },
358
- { eventType: 'error', description: 'OCR failed: image too blurry' },
359
- ],
360
- status: 'FAILED',
361
- errorMessage: 'OCR processing failed',
362
- errorCode: 'OCR_QUALITY_ERROR',
363
- });
282
+ for await (const chunk of llmStream) {
283
+ meter.add(chunk.tokens);
284
+ yield chunk;
285
+ }
364
286
 
365
- console.log(result.summary);
366
- // Output: "✗ Prescription Intake: 2 events recorded (150ms)"
287
+ // Single API call at end
288
+ await meter.flush();
367
289
  ```
368
290
 
369
- ### Webhooks
291
+ ## Framework Middleware
370
292
 
371
- #### Create a Webhook
293
+ ### Next.js
372
294
 
373
295
  ```typescript
374
- const webhook = await drip.createWebhook({
375
- url: 'https://api.yourapp.com/webhooks/drip',
376
- events: ['charge.succeeded', 'charge.failed', 'customer.balance.low'],
377
- description: 'Main webhook endpoint',
378
- });
379
-
380
- // IMPORTANT: Save the secret securely!
381
- console.log(`Webhook secret: ${webhook.secret}`);
382
- ```
383
-
384
- #### List Webhooks
296
+ import { withDrip } from '@drip-sdk/node/next';
385
297
 
386
- ```typescript
387
- const { data: webhooks } = await drip.listWebhooks();
388
- webhooks.forEach((wh) => {
389
- console.log(`${wh.url}: ${wh.stats?.successfulDeliveries} successful`);
298
+ export const POST = withDrip({
299
+ meter: 'api_calls',
300
+ quantity: 1,
301
+ }, async (req, { customerId }) => {
302
+ return Response.json({ result: 'success' });
390
303
  });
391
304
  ```
392
305
 
393
- #### Delete a Webhook
394
-
395
- ```typescript
396
- await drip.deleteWebhook('wh_abc123');
397
- ```
398
-
399
- #### Verify Webhook Signatures
306
+ ### Express
400
307
 
401
308
  ```typescript
402
- import express from 'express';
403
- import { Drip } from '@drip-sdk/node';
309
+ import { dripMiddleware } from '@drip-sdk/node/express';
404
310
 
405
- const app = express();
406
-
407
- app.post(
408
- '/webhooks/drip',
409
- express.raw({ type: 'application/json' }),
410
- (req, res) => {
411
- const isValid = Drip.verifyWebhookSignature(
412
- req.body.toString(),
413
- req.headers['x-drip-signature'] as string,
414
- process.env.DRIP_WEBHOOK_SECRET!,
415
- );
416
-
417
- if (!isValid) {
418
- return res.status(401).send('Invalid signature');
419
- }
420
-
421
- const event = JSON.parse(req.body.toString());
422
-
423
- switch (event.type) {
424
- case 'charge.succeeded':
425
- console.log('Charge succeeded:', event.data.charge_id);
426
- break;
427
- case 'charge.failed':
428
- console.log('Charge failed:', event.data.failure_reason);
429
- break;
430
- case 'customer.balance.low':
431
- console.log('Low balance alert for:', event.data.customer_id);
432
- break;
433
- }
434
-
435
- res.status(200).send('OK');
436
- },
437
- );
311
+ app.use('/api', dripMiddleware({
312
+ meter: 'api_calls',
313
+ quantity: 1,
314
+ }));
438
315
  ```
439
316
 
440
- ### Available Webhook Events
441
-
442
- | Event | Description |
443
- | ---------------------------- | ------------------------------------ |
444
- | `charge.succeeded` | Charge confirmed on-chain |
445
- | `charge.failed` | Charge failed |
446
- | `customer.balance.low` | Customer balance below threshold |
447
- | `customer.deposit.confirmed` | Deposit confirmed on-chain |
448
- | `customer.withdraw.confirmed`| Withdrawal confirmed |
449
- | `customer.usage_cap.reached` | Usage cap hit |
450
- | `customer.created` | New customer created |
451
- | `usage.recorded` | Usage event recorded |
452
- | `transaction.created` | Transaction initiated |
453
- | `transaction.confirmed` | Transaction confirmed on-chain |
454
- | `transaction.failed` | Transaction failed |
455
-
456
- ## TypeScript Usage
457
-
458
- The SDK is written in TypeScript and includes full type definitions.
317
+ ## LangChain Integration
459
318
 
460
319
  ```typescript
461
- import {
462
- Drip,
463
- DripConfig,
464
- DripError,
465
- Customer,
466
- Charge,
467
- ChargeResult,
468
- ChargeStatus,
469
- Webhook,
470
- WebhookEventType,
471
- StreamMeter,
472
- StreamMeterOptions,
473
- } from '@drip-sdk/node';
320
+ import { DripCallbackHandler } from '@drip-sdk/node/langchain';
474
321
 
475
- // All types are available for use
476
- const config: DripConfig = {
477
- apiKey: process.env.DRIP_API_KEY!,
478
- };
479
-
480
- const drip = new Drip(config);
481
-
482
- // Type-safe responses
483
- const customer: Customer = await drip.getCustomer('cust_abc123');
484
- const result: ChargeResult = await drip.charge({
485
- customerId: customer.id,
486
- meter: 'api_calls',
487
- quantity: 100,
322
+ const handler = new DripCallbackHandler({
323
+ drip,
324
+ customerId: 'cus_123',
488
325
  });
326
+
327
+ // Automatically tracks all LLM calls and tool usage
328
+ await agent.invoke({ input: '...' }, { callbacks: [handler] });
489
329
  ```
490
330
 
491
331
  ## Error Handling
492
332
 
493
- The SDK throws `DripError` for API errors:
494
-
495
333
  ```typescript
496
334
  import { Drip, DripError } from '@drip-sdk/node';
497
335
 
498
336
  try {
499
- const result = await drip.charge({
500
- customerId: 'cust_abc123',
501
- meter: 'api_calls',
502
- quantity: 100,
503
- });
337
+ await drip.trackUsage({ ... });
504
338
  } catch (error) {
505
339
  if (error instanceof DripError) {
506
- console.error(`API Error: ${error.message}`);
507
- console.error(`Status Code: ${error.statusCode}`);
508
- console.error(`Error Code: ${error.code}`);
509
-
510
- switch (error.code) {
511
- case 'INSUFFICIENT_BALANCE':
512
- // Handle low balance
513
- break;
514
- case 'CUSTOMER_NOT_FOUND':
515
- // Handle missing customer
516
- break;
517
- case 'RATE_LIMITED':
518
- // Handle rate limiting
519
- break;
520
- }
340
+ console.error(`Error: ${error.message} (${error.code})`);
521
341
  }
522
342
  }
523
343
  ```
524
344
 
525
- ### Common Error Codes
526
-
527
- | Code | Description |
528
- | ---------------------- | ---------------------------------------- |
529
- | `INSUFFICIENT_BALANCE` | Customer doesn't have enough balance |
530
- | `CUSTOMER_NOT_FOUND` | Customer ID doesn't exist |
531
- | `DUPLICATE_CUSTOMER` | Customer already exists |
532
- | `INVALID_API_KEY` | API key is invalid or revoked |
533
- | `RATE_LIMITED` | Too many requests |
534
- | `TIMEOUT` | Request timed out |
345
+ ## Billing (Full SDK Only)
535
346
 
536
- ## Idempotency
347
+ ```typescript
348
+ import { Drip } from '@drip-sdk/node';
537
349
 
538
- Use idempotency keys to safely retry requests:
350
+ const drip = new Drip({ apiKey: process.env.DRIP_API_KEY! });
539
351
 
540
- ```typescript
352
+ // Create a billable charge
541
353
  const result = await drip.charge({
542
- customerId: 'cust_abc123',
354
+ customerId: 'cus_123',
543
355
  meter: 'api_calls',
544
- quantity: 100,
545
- idempotencyKey: `req_${requestId}`, // Unique per request
546
- });
547
-
548
- // Retrying with the same key returns the original result
549
- const retry = await drip.charge({
550
- customerId: 'cust_abc123',
551
- meter: 'api_calls',
552
- quantity: 100,
553
- idempotencyKey: `req_${requestId}`, // Same key = same result
356
+ quantity: 1,
554
357
  });
555
- ```
556
-
557
- ## CommonJS Usage
558
-
559
- The SDK supports both ESM and CommonJS:
560
-
561
- ```javascript
562
- // ESM
563
- import { Drip } from '@drip-sdk/node';
564
-
565
- // CommonJS
566
- const { Drip } = require('@drip-sdk/node');
567
- ```
568
358
 
569
- ## Requirements
570
-
571
- - Node.js 18.0.0 or higher
572
- - Native `fetch` support (included in Node.js 18+)
573
-
574
- ## Middleware Reference (withDrip)
575
-
576
- ### Configuration Options
577
-
578
- | Option | Type | Default | Description |
579
- |--------|------|---------|-------------|
580
- | `meter` | `string` | **required** | Usage meter to charge (must match pricing plan) |
581
- | `quantity` | `number \| (req) => number` | **required** | Quantity to charge (static or dynamic) |
582
- | `apiKey` | `string` | `DRIP_API_KEY` | Drip API key |
583
- | `baseUrl` | `string` | `DRIP_API_URL` | Drip API base URL |
584
- | `customerResolver` | `'header' \| 'query' \| function` | `'header'` | How to identify customers |
585
- | `skipInDevelopment` | `boolean` | `false` | Skip charging in dev mode |
586
- | `metadata` | `object \| function` | `undefined` | Custom metadata for charges |
587
- | `onCharge` | `function` | `undefined` | Callback after successful charge |
588
- | `onError` | `function` | `undefined` | Custom error handler |
589
-
590
- ### How It Works
591
-
592
- ```
593
- ┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
594
- │ Your API │ │ withDrip │ │ Drip Backend │
595
- │ (Next/Express)│───▶│ Middleware │───▶│ API │
596
- └─────────────────┘ └──────────────────┘ └─────────────────┘
597
- │ │ │
598
- ▼ ▼ ▼
599
- 1. Request 2. Resolve 3. Check balance
600
- arrives customer & charge
601
- │ │ │
602
- ▼ ▼ ▼
603
- 6. Response 5. Pass to 4. Return result
604
- returned handler or 402
605
- ```
606
-
607
- ### x402 Payment Flow
608
-
609
- When a customer has insufficient balance, the middleware returns `402 Payment Required`:
610
-
611
- ```
612
- HTTP/1.1 402 Payment Required
613
- X-Payment-Required: true
614
- X-Payment-Amount: 0.01
615
- X-Payment-Recipient: 0x...
616
- X-Payment-Usage-Id: 0x...
617
- X-Payment-Expires: 1704110400
618
- X-Payment-Nonce: abc123
619
-
620
- {
621
- "error": "Payment required",
622
- "code": "PAYMENT_REQUIRED",
623
- "paymentRequest": { ... },
624
- "instructions": {
625
- "step1": "Sign the payment request with your session key using EIP-712",
626
- "step2": "Retry the request with X-Payment-* headers"
627
- }
628
- }
629
- ```
359
+ // Get customer balance
360
+ const balance = await drip.getBalance('cus_123');
361
+ console.log(`Balance: $${balance.balanceUsdc}`);
630
362
 
631
- ### Advanced Usage
363
+ // Query charges
364
+ const charge = await drip.getCharge(chargeId);
365
+ const charges = await drip.listCharges({ customerId: 'cus_123' });
632
366
 
633
- #### Dynamic Quantity
367
+ // Cost estimation
368
+ await drip.estimateFromUsage({ customerId, startDate, endDate });
369
+ await drip.estimateFromHypothetical({ items: [...] });
634
370
 
635
- ```typescript
636
- export const POST = withDrip({
637
- meter: 'tokens',
638
- quantity: async (req) => {
639
- const body = await req.json();
640
- return body.maxTokens ?? 100;
641
- },
642
- }, handler);
371
+ // Checkout (fiat on-ramp)
372
+ await drip.checkout({ customerId: 'cus_123', amountUsd: 5000 });
643
373
  ```
644
374
 
645
- #### Custom Customer Resolution
646
-
647
- ```typescript
648
- export const POST = withDrip({
649
- meter: 'api_calls',
650
- quantity: 1,
651
- customerResolver: (req) => {
652
- const token = req.headers.get('authorization')?.split(' ')[1];
653
- return decodeJWT(token).customerId;
654
- },
655
- }, handler);
656
- ```
375
+ ## TypeScript Support
657
376
 
658
- #### Factory Pattern
377
+ Full TypeScript support with exported types:
659
378
 
660
379
  ```typescript
661
- // lib/drip.ts
662
- import { createWithDrip } from '@drip-sdk/node/next';
663
-
664
- export const withDrip = createWithDrip({
665
- apiKey: process.env.DRIP_API_KEY,
666
- baseUrl: process.env.DRIP_API_URL,
667
- });
668
-
669
- // app/api/generate/route.ts
670
- import { withDrip } from '@/lib/drip';
671
- export const POST = withDrip({ meter: 'api_calls', quantity: 1 }, handler);
380
+ import type {
381
+ Customer,
382
+ Charge,
383
+ ChargeResult,
384
+ TrackUsageParams,
385
+ RunResult,
386
+ Webhook,
387
+ } from '@drip-sdk/node';
672
388
  ```
673
389
 
674
- ### What's Included vs. Missing
675
-
676
- | Feature | Status | Description |
677
- |---------|--------|-------------|
678
- | Next.js App Router | ✅ | `withDrip` wrapper |
679
- | Express Middleware | ✅ | `dripMiddleware` |
680
- | x402 Payment Flow | ✅ | Automatic 402 handling |
681
- | Dynamic Quantity | ✅ | Function-based pricing |
682
- | Customer Resolution | ✅ | Header, query, or custom |
683
- | Idempotency | ✅ | Built-in or custom keys |
684
- | Dev Mode Skip | ✅ | Skip in development |
685
- | Metadata | ✅ | Attach to charges |
686
- | TypeScript | ✅ | Full type definitions |
687
- | Streaming Meter | ✅ | For LLM token streams |
688
-
689
- ## Contributing
690
-
691
- Contributions are welcome! Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines.
692
-
693
- ## License
390
+ ## Requirements
694
391
 
695
- MIT - see [LICENSE](./LICENSE)
392
+ - Node.js 18.0.0 or higher
696
393
 
697
394
  ## Links
698
395
 
699
- - [GitHub Repository](https://github.com/MichaelLevin5908/drip-sdk)
700
- - [Issue Tracker](https://github.com/MichaelLevin5908/drip-sdk/issues)
701
- - [npm Package](https://www.npmjs.com/package/@drip-sdk/node)
702
- - [Documentation](https://docs.drip.dev)
396
+ - [Documentation](https://docs.drippay.dev)
397
+ - [GitHub](https://github.com/MichaelLevin5908/drip)
398
+ - [npm](https://www.npmjs.com/package/@drip-sdk/node)