@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 +245 -549
- package/dist/core.cjs +3 -0
- package/dist/core.cjs.map +1 -0
- package/dist/core.d.cts +640 -0
- package/dist/core.d.ts +638 -0
- package/dist/core.js +3 -0
- package/dist/core.js.map +1 -0
- package/dist/express.cjs +2 -2
- package/dist/express.cjs.map +1 -1
- package/dist/express.d.cts +3 -3
- package/dist/express.d.ts +3 -3
- package/dist/express.js +2 -2
- package/dist/express.js.map +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +578 -3
- package/dist/index.d.ts +578 -3
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/langchain.cjs +3 -0
- package/dist/langchain.cjs.map +1 -0
- package/dist/langchain.d.cts +290 -0
- package/dist/langchain.d.ts +290 -0
- package/dist/langchain.js +3 -0
- package/dist/langchain.js.map +1 -0
- package/dist/middleware.cjs +2 -2
- package/dist/middleware.cjs.map +1 -1
- package/dist/middleware.d.cts +3 -3
- package/dist/middleware.d.ts +3 -3
- package/dist/middleware.js +2 -2
- package/dist/middleware.js.map +1 -1
- package/dist/next.cjs +2 -2
- package/dist/next.cjs.map +1 -1
- package/dist/next.d.cts +3 -3
- package/dist/next.d.ts +3 -3
- package/dist/next.js +2 -2
- package/dist/next.js.map +1 -1
- package/dist/{types-D8mMON4v.d.ts → types-B2qwDadD.d.ts} +1 -1
- package/dist/{types-92iVqLtE.d.cts → types-Bo8SiUdl.d.cts} +1 -1
- package/package.json +23 -1
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @drip-sdk/node
|
|
2
2
|
|
|
3
|
-
The official Node.js SDK for **Drip** - Usage
|
|
3
|
+
The official Node.js SDK for **Drip** - Usage tracking and cost attribution for metered infrastructure.
|
|
4
4
|
|
|
5
|
-
Drip
|
|
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
|
[](https://www.npmjs.com/package/@drip-sdk/node)
|
|
8
8
|
[](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
|
-
|
|
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
|
-
|
|
18
|
+
For most use cases, import the **Core SDK** - a simplified API focused on two concepts:
|
|
29
19
|
|
|
30
|
-
|
|
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
|
-
|
|
34
|
-
import { withDrip } from '@drip-sdk/node/next';
|
|
24
|
+
import { Drip } from '@drip-sdk/node/core';
|
|
35
25
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
32
|
+
### Track Usage
|
|
47
33
|
|
|
48
34
|
```typescript
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
###
|
|
66
|
-
|
|
67
|
-
For more control, use the SDK directly:
|
|
44
|
+
### Record Agent Runs
|
|
68
45
|
|
|
69
46
|
```typescript
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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(
|
|
91
|
-
|
|
59
|
+
console.log(result.summary);
|
|
60
|
+
// Output: "Research Agent: 3 events recorded (2.5s)"
|
|
92
61
|
```
|
|
93
62
|
|
|
94
|
-
|
|
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
|
-
|
|
102
|
-
|
|
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
|
-
|
|
105
|
-
timeout: 30000,
|
|
106
|
-
});
|
|
107
|
-
```
|
|
79
|
+
---
|
|
108
80
|
|
|
109
|
-
##
|
|
81
|
+
## Full SDK
|
|
110
82
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
#### Create a Customer
|
|
83
|
+
For billing, webhooks, and advanced features, use the full SDK:
|
|
114
84
|
|
|
115
85
|
```typescript
|
|
116
|
-
|
|
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
|
-
|
|
88
|
+
const drip = new Drip({ apiKey: process.env.DRIP_API_KEY! });
|
|
124
89
|
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
97
|
+
## Quick Start (Full SDK)
|
|
98
|
+
|
|
99
|
+
### Track Usage
|
|
130
100
|
|
|
131
101
|
```typescript
|
|
132
|
-
|
|
133
|
-
const { data: customers } = await drip.listCustomers();
|
|
102
|
+
import { Drip } from '@drip-sdk/node';
|
|
134
103
|
|
|
135
|
-
|
|
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
|
-
|
|
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
|
-
|
|
145
|
-
const balance = await drip.getBalance('
|
|
146
|
-
console.log(`
|
|
114
|
+
// Check accumulated usage
|
|
115
|
+
const balance = await drip.getBalance('cus_123');
|
|
116
|
+
console.log(`Total usage: $${balance.totalUsageUsd}`);
|
|
147
117
|
```
|
|
148
118
|
|
|
149
|
-
###
|
|
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
|
-
|
|
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(
|
|
159
|
-
|
|
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
|
-
###
|
|
169
|
-
|
|
170
|
-
#### Record Usage and Charge
|
|
138
|
+
### View Execution Traces
|
|
171
139
|
|
|
172
140
|
```typescript
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
|
|
182
|
-
console.log(
|
|
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
|
-
|
|
189
|
-
|
|
190
|
-
```typescript
|
|
191
|
-
const charge = await drip.getCharge('chg_abc123');
|
|
192
|
-
console.log(`Status: ${charge.status}`);
|
|
193
|
-
```
|
|
149
|
+
## Use Cases
|
|
194
150
|
|
|
195
|
-
|
|
151
|
+
### RPC Providers
|
|
196
152
|
|
|
197
153
|
```typescript
|
|
198
|
-
//
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
-
|
|
168
|
+
### API Companies
|
|
210
169
|
|
|
211
170
|
```typescript
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
193
|
+
await drip.emitEvent({
|
|
194
|
+
runId: run.id,
|
|
195
|
+
eventType: 'ocr.process',
|
|
196
|
+
quantity: 5,
|
|
197
|
+
units: 'pages',
|
|
231
198
|
});
|
|
232
199
|
|
|
233
|
-
|
|
234
|
-
|
|
200
|
+
await drip.emitEvent({
|
|
201
|
+
runId: run.id,
|
|
202
|
+
eventType: 'llm.summarize',
|
|
235
203
|
model: 'gpt-4',
|
|
236
|
-
|
|
237
|
-
|
|
204
|
+
inputTokens: 10000,
|
|
205
|
+
outputTokens: 500,
|
|
238
206
|
});
|
|
239
207
|
|
|
240
|
-
|
|
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
|
-
|
|
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
|
-
|
|
263
|
-
metadata: { model: 'gpt-4', endpoint: '/v1/chat' },
|
|
213
|
+
### Usage & Billing
|
|
264
214
|
|
|
265
|
-
|
|
266
|
-
|
|
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
|
-
|
|
269
|
-
onAdd: (quantity, total) => console.log(`Added ${quantity}, total: ${total}`),
|
|
270
|
-
});
|
|
271
|
-
```
|
|
224
|
+
### Execution Logging
|
|
272
225
|
|
|
273
|
-
|
|
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
|
-
|
|
237
|
+
### Customer Management
|
|
276
238
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
314
|
-
|
|
315
|
-
|
|
259
|
+
| Method | Description |
|
|
260
|
+
|--------|-------------|
|
|
261
|
+
| `estimateFromUsage(params)` | Estimate cost from usage data |
|
|
262
|
+
| `estimateFromHypothetical(params)` | Estimate from hypothetical usage |
|
|
316
263
|
|
|
317
|
-
###
|
|
264
|
+
### Other
|
|
318
265
|
|
|
319
|
-
|
|
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
|
-
|
|
272
|
+
## Streaming Meter (LLM Token Streaming)
|
|
322
273
|
|
|
323
|
-
|
|
274
|
+
For LLM token streaming, accumulate usage locally and charge once:
|
|
324
275
|
|
|
325
276
|
```typescript
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
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
|
-
|
|
347
|
-
|
|
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
|
-
|
|
366
|
-
|
|
287
|
+
// Single API call at end
|
|
288
|
+
await meter.flush();
|
|
367
289
|
```
|
|
368
290
|
|
|
369
|
-
|
|
291
|
+
## Framework Middleware
|
|
370
292
|
|
|
371
|
-
|
|
293
|
+
### Next.js
|
|
372
294
|
|
|
373
295
|
```typescript
|
|
374
|
-
|
|
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
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
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
|
-
|
|
394
|
-
|
|
395
|
-
```typescript
|
|
396
|
-
await drip.deleteWebhook('wh_abc123');
|
|
397
|
-
```
|
|
398
|
-
|
|
399
|
-
#### Verify Webhook Signatures
|
|
306
|
+
### Express
|
|
400
307
|
|
|
401
308
|
```typescript
|
|
402
|
-
import
|
|
403
|
-
import { Drip } from '@drip-sdk/node';
|
|
309
|
+
import { dripMiddleware } from '@drip-sdk/node/express';
|
|
404
310
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
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
|
-
|
|
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
|
-
|
|
476
|
-
|
|
477
|
-
|
|
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
|
-
|
|
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(`
|
|
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
|
-
|
|
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
|
-
|
|
347
|
+
```typescript
|
|
348
|
+
import { Drip } from '@drip-sdk/node';
|
|
537
349
|
|
|
538
|
-
|
|
350
|
+
const drip = new Drip({ apiKey: process.env.DRIP_API_KEY! });
|
|
539
351
|
|
|
540
|
-
|
|
352
|
+
// Create a billable charge
|
|
541
353
|
const result = await drip.charge({
|
|
542
|
-
customerId: '
|
|
354
|
+
customerId: 'cus_123',
|
|
543
355
|
meter: 'api_calls',
|
|
544
|
-
quantity:
|
|
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
|
-
|
|
570
|
-
|
|
571
|
-
|
|
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
|
-
|
|
363
|
+
// Query charges
|
|
364
|
+
const charge = await drip.getCharge(chargeId);
|
|
365
|
+
const charges = await drip.listCharges({ customerId: 'cus_123' });
|
|
632
366
|
|
|
633
|
-
|
|
367
|
+
// Cost estimation
|
|
368
|
+
await drip.estimateFromUsage({ customerId, startDate, endDate });
|
|
369
|
+
await drip.estimateFromHypothetical({ items: [...] });
|
|
634
370
|
|
|
635
|
-
|
|
636
|
-
|
|
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
|
-
|
|
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
|
-
|
|
377
|
+
Full TypeScript support with exported types:
|
|
659
378
|
|
|
660
379
|
```typescript
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
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
|
-
|
|
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
|
-
|
|
392
|
+
- Node.js 18.0.0 or higher
|
|
696
393
|
|
|
697
394
|
## Links
|
|
698
395
|
|
|
699
|
-
- [
|
|
700
|
-
- [
|
|
701
|
-
- [npm
|
|
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)
|