@drip-sdk/node 1.1.0 → 1.1.1
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 +159 -293
- package/dist/core.cjs +2 -2
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +165 -43
- package/dist/core.d.ts +165 -43
- package/dist/core.js +2 -2
- package/dist/core.js.map +1 -1
- package/dist/express.cjs +2 -2
- package/dist/express.cjs.map +1 -1
- 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 +171 -54
- package/dist/index.d.ts +171 -54
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/langchain.cjs +2 -2
- package/dist/langchain.cjs.map +1 -1
- package/dist/langchain.d.cts +2 -2
- package/dist/langchain.d.ts +2 -2
- package/dist/langchain.js +2 -2
- package/dist/langchain.js.map +1 -1
- package/dist/middleware.cjs +2 -2
- package/dist/middleware.cjs.map +1 -1
- 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.js +2 -2
- package/dist/next.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,332 +1,241 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Drip SDK (Node.js)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Drip is a lightweight SDK for **usage tracking and execution logging** in systems where cost is tied to computation — AI agents, APIs, background jobs, and infra workloads.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This **Core SDK** is designed for pilots: it records *what ran* and *how much it used*, without handling billing or balances.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
**One line to start tracking:** `await drip.trackUsage({ customerId, meter, quantity })`
|
|
8
|
+
|
|
9
|
+
[](https://www.npmjs.com/package/@drip-sdk/node)
|
|
8
10
|
[](https://opensource.org/licenses/MIT)
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 60-Second Quickstart (Core SDK)
|
|
15
|
+
|
|
16
|
+
### 1. Install
|
|
11
17
|
|
|
12
18
|
```bash
|
|
13
19
|
npm install @drip-sdk/node
|
|
14
20
|
```
|
|
15
21
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
For most use cases, import the **Core SDK** - a simplified API focused on two concepts:
|
|
22
|
+
### 2. Set your API key
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
```typescript
|
|
24
|
-
import { Drip } from '@drip-sdk/node/core';
|
|
25
|
-
|
|
26
|
-
const drip = new Drip({ apiKey: process.env.DRIP_API_KEY! });
|
|
24
|
+
```bash
|
|
25
|
+
# Secret key — full API access (server-side only, never expose publicly)
|
|
26
|
+
export DRIP_API_KEY=sk_test_...
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
# Or public key — read/write access for usage, customers, billing (safe for client-side)
|
|
29
|
+
export DRIP_API_KEY=pk_test_...
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
### Track
|
|
32
|
+
### 3. Track usage (one line)
|
|
33
33
|
|
|
34
34
|
```typescript
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
quantity: 1,
|
|
40
|
-
metadata: { endpoint: '/v1/generate', method: 'POST' },
|
|
41
|
-
});
|
|
35
|
+
import { drip } from '@drip-sdk/node';
|
|
36
|
+
|
|
37
|
+
// Track usage - that's it
|
|
38
|
+
await drip.trackUsage({ customerId: 'cust_123', meter: 'api_calls', quantity: 1 });
|
|
42
39
|
```
|
|
43
40
|
|
|
44
|
-
|
|
41
|
+
The `drip` singleton reads `DRIP_API_KEY` from your environment automatically.
|
|
45
42
|
|
|
46
|
-
|
|
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',
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
console.log(result.summary);
|
|
60
|
-
// Output: "Research Agent: 3 events recorded (2.5s)"
|
|
61
|
-
```
|
|
43
|
+
### Alternative: Explicit Configuration
|
|
62
44
|
|
|
63
|
-
|
|
45
|
+
```typescript
|
|
46
|
+
import { Drip } from '@drip-sdk/node';
|
|
64
47
|
|
|
65
|
-
|
|
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 |
|
|
48
|
+
// Auto-reads DRIP_API_KEY from environment
|
|
49
|
+
const drip = new Drip();
|
|
78
50
|
|
|
79
|
-
|
|
51
|
+
// Or pass config explicitly with a secret key (full access)
|
|
52
|
+
const drip = new Drip({ apiKey: 'sk_test_...' });
|
|
80
53
|
|
|
81
|
-
|
|
54
|
+
// Or with a public key (safe for client-side, limited scope)
|
|
55
|
+
const drip = new Drip({ apiKey: 'pk_test_...' });
|
|
56
|
+
```
|
|
82
57
|
|
|
83
|
-
|
|
58
|
+
### Full Example
|
|
84
59
|
|
|
85
60
|
```typescript
|
|
86
|
-
import {
|
|
87
|
-
|
|
88
|
-
|
|
61
|
+
import { drip } from '@drip-sdk/node';
|
|
62
|
+
|
|
63
|
+
async function main() {
|
|
64
|
+
// Verify connectivity
|
|
65
|
+
await drip.ping();
|
|
66
|
+
|
|
67
|
+
// Record usage
|
|
68
|
+
await drip.trackUsage({
|
|
69
|
+
customerId: 'customer_123',
|
|
70
|
+
meter: 'llm_tokens',
|
|
71
|
+
quantity: 842,
|
|
72
|
+
metadata: { model: 'gpt-4o-mini' },
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Record an execution lifecycle
|
|
76
|
+
await drip.recordRun({
|
|
77
|
+
customerId: 'customer_123',
|
|
78
|
+
workflow: 'research-agent',
|
|
79
|
+
events: [
|
|
80
|
+
{ eventType: 'llm.call', model: 'gpt-4', inputTokens: 500, outputTokens: 1200 },
|
|
81
|
+
{ eventType: 'tool.call', name: 'web-search', duration: 1500 },
|
|
82
|
+
],
|
|
83
|
+
status: 'COMPLETED',
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
console.log('Usage + run recorded');
|
|
87
|
+
}
|
|
89
88
|
|
|
90
|
-
|
|
91
|
-
// - charge(), getBalance(), getCharge(), listCharges()
|
|
92
|
-
// - createWebhook(), listWebhooks(), deleteWebhook()
|
|
93
|
-
// - estimateFromUsage(), estimateFromHypothetical()
|
|
94
|
-
// - checkout(), and more
|
|
89
|
+
main();
|
|
95
90
|
```
|
|
96
91
|
|
|
97
|
-
|
|
92
|
+
**Expected result:**
|
|
93
|
+
- No errors
|
|
94
|
+
- Events appear in your Drip dashboard within seconds
|
|
98
95
|
|
|
99
|
-
|
|
96
|
+
---
|
|
100
97
|
|
|
101
|
-
|
|
102
|
-
import { Drip } from '@drip-sdk/node';
|
|
98
|
+
## Core Concepts (2-minute mental model)
|
|
103
99
|
|
|
104
|
-
|
|
100
|
+
| Concept | Description |
|
|
101
|
+
|---------|-------------|
|
|
102
|
+
| `customerId` | The end user, API key, or account you're attributing usage to |
|
|
103
|
+
| `meter` | What you're measuring (tokens, requests, seconds, rows, etc.) |
|
|
104
|
+
| `quantity` | Numeric usage for that meter |
|
|
105
|
+
| `run` | A single execution or request lifecycle (success / failure / duration) |
|
|
105
106
|
|
|
106
|
-
|
|
107
|
-
await drip.trackUsage({
|
|
108
|
-
customerId: 'cus_123',
|
|
109
|
-
meter: 'api_calls',
|
|
110
|
-
quantity: 1,
|
|
111
|
-
metadata: { endpoint: '/v1/generate', method: 'POST' },
|
|
112
|
-
});
|
|
107
|
+
**Status values:** `PENDING` | `RUNNING` | `COMPLETED` | `FAILED`
|
|
113
108
|
|
|
114
|
-
|
|
115
|
-
const balance = await drip.getBalance('cus_123');
|
|
116
|
-
console.log(`Total usage: $${balance.totalUsageUsd}`);
|
|
117
|
-
```
|
|
109
|
+
**Event schema:** Payloads are schema-flexible. Drip stores events as structured JSON and does not enforce a fixed event taxonomy.
|
|
118
110
|
|
|
119
|
-
|
|
111
|
+
Drip is append-only and idempotent-friendly. You can safely retry events.
|
|
120
112
|
|
|
121
|
-
|
|
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
|
-
});
|
|
113
|
+
---
|
|
133
114
|
|
|
134
|
-
|
|
135
|
-
// Output: "Research Agent: 3 events recorded (2.5s)"
|
|
136
|
-
```
|
|
115
|
+
## Idempotency Keys
|
|
137
116
|
|
|
138
|
-
|
|
117
|
+
Every mutating SDK method (`charge`, `trackUsage`, `emitEvent`) accepts an optional `idempotencyKey` parameter. The server uses this key to deduplicate requests — if two requests share the same key, only the first is processed.
|
|
139
118
|
|
|
140
|
-
|
|
141
|
-
// Get detailed timeline of an agent run
|
|
142
|
-
const timeline = await drip.getRunTimeline(runId);
|
|
119
|
+
`recordRun` generates idempotency keys internally for its batch events (using `externalRunId` when provided, otherwise deterministic keys).
|
|
143
120
|
|
|
144
|
-
|
|
145
|
-
console.log(`${event.eventType}: ${event.duration}ms`);
|
|
146
|
-
}
|
|
147
|
-
```
|
|
121
|
+
### Auto-generated keys (default)
|
|
148
122
|
|
|
149
|
-
|
|
123
|
+
When you omit `idempotencyKey`, the SDK generates one automatically. The auto key is:
|
|
150
124
|
|
|
151
|
-
|
|
125
|
+
- **Unique per call** — two separate calls with identical parameters produce different keys (a monotonic counter ensures this).
|
|
126
|
+
- **Stable across retries** — the key is generated once and reused for all retry attempts of that call, so network retries are safely deduplicated.
|
|
127
|
+
- **Deterministic** — no randomness; keys are reproducible for debugging.
|
|
152
128
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
quantity: 1,
|
|
159
|
-
metadata: {
|
|
160
|
-
method: 'eth_call',
|
|
161
|
-
chain: 'ethereum',
|
|
162
|
-
latencyMs: 45,
|
|
163
|
-
cacheHit: false,
|
|
164
|
-
},
|
|
165
|
-
});
|
|
166
|
-
```
|
|
129
|
+
This means you get **free retry safety** with zero configuration.
|
|
130
|
+
|
|
131
|
+
> **Note:** `wrapApiCall` generates a time-based key when no explicit `idempotencyKey` is provided. Pass your own key if you need deterministic deduplication with `wrapApiCall`.
|
|
132
|
+
|
|
133
|
+
### When to pass explicit keys
|
|
167
134
|
|
|
168
|
-
|
|
135
|
+
Pass your own `idempotencyKey` when you need **application-level deduplication** — e.g., to guarantee that a specific business operation is billed exactly once, even across process restarts:
|
|
169
136
|
|
|
170
137
|
```typescript
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
customerId: 'cus_123',
|
|
138
|
+
await drip.charge({
|
|
139
|
+
customerId: 'cust_123',
|
|
174
140
|
meter: 'api_calls',
|
|
175
141
|
quantity: 1,
|
|
176
|
-
|
|
177
|
-
endpoint: '/v1/embeddings',
|
|
178
|
-
tokens: 1500,
|
|
179
|
-
model: 'text-embedding-3-small',
|
|
180
|
-
},
|
|
142
|
+
idempotencyKey: `order_${orderId}_charge`, // your business-level key
|
|
181
143
|
});
|
|
182
144
|
```
|
|
183
145
|
|
|
184
|
-
|
|
146
|
+
Common patterns:
|
|
147
|
+
- `order_${orderId}` — one charge per order
|
|
148
|
+
- `run_${runId}_step_${stepIndex}` — one charge per pipeline step
|
|
149
|
+
- `invoice_${invoiceId}` — one charge per invoice
|
|
185
150
|
|
|
186
|
-
|
|
187
|
-
// Track agent execution with detailed events
|
|
188
|
-
const run = await drip.startRun({
|
|
189
|
-
customerId: 'cus_123',
|
|
190
|
-
workflowSlug: 'document-processor',
|
|
191
|
-
});
|
|
151
|
+
### StreamMeter
|
|
192
152
|
|
|
193
|
-
|
|
194
|
-
runId: run.id,
|
|
195
|
-
eventType: 'ocr.process',
|
|
196
|
-
quantity: 5,
|
|
197
|
-
units: 'pages',
|
|
198
|
-
});
|
|
153
|
+
`StreamMeter` also auto-generates idempotency keys per flush. If you provide an `idempotencyKey` in the options, each flush appends a counter (`_flush_0`, `_flush_1`, etc.) to keep multi-flush scenarios safe.
|
|
199
154
|
|
|
200
|
-
|
|
201
|
-
runId: run.id,
|
|
202
|
-
eventType: 'llm.summarize',
|
|
203
|
-
model: 'gpt-4',
|
|
204
|
-
inputTokens: 10000,
|
|
205
|
-
outputTokens: 500,
|
|
206
|
-
});
|
|
155
|
+
---
|
|
207
156
|
|
|
208
|
-
|
|
209
|
-
```
|
|
157
|
+
## API Key Types
|
|
210
158
|
|
|
211
|
-
|
|
159
|
+
Drip issues two key types per API key pair. Each has different access scopes:
|
|
212
160
|
|
|
213
|
-
|
|
161
|
+
| Key Type | Prefix | Access | Use In |
|
|
162
|
+
|----------|--------|--------|--------|
|
|
163
|
+
| **Secret Key** | `sk_live_` / `sk_test_` | Full API access (all endpoints) | Server-side only |
|
|
164
|
+
| **Public Key** | `pk_live_` / `pk_test_` | Usage tracking, customers, billing, analytics, sessions | Client-side safe |
|
|
214
165
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
| `getCharge(chargeId)` | Get charge details |
|
|
221
|
-
| `listCharges(options)` | List all charges |
|
|
222
|
-
| `getChargeStatus(chargeId)` | Get charge status |
|
|
166
|
+
### What public keys **can** access
|
|
167
|
+
- Usage tracking (`trackUsage`, `recordRun`, `startRun`, `emitEvent`, etc.)
|
|
168
|
+
- Customer management (`createCustomer`, `getCustomer`, `listCustomers`)
|
|
169
|
+
- Billing & charges (`charge`, `getBalance`, `listCharges`, etc.)
|
|
170
|
+
- Pricing plans, sessions, analytics, usage caps, refunds
|
|
223
171
|
|
|
224
|
-
###
|
|
172
|
+
### What public keys **cannot** access (secret key required)
|
|
173
|
+
- Webhook management (`createWebhook`, `listWebhooks`, `deleteWebhook`, etc.)
|
|
174
|
+
- API key management (create, rotate, revoke keys)
|
|
175
|
+
- Feature flag management
|
|
225
176
|
|
|
226
|
-
|
|
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 |
|
|
177
|
+
The SDK detects your key type automatically and will throw a `DripError` with code `PUBLIC_KEY_NOT_ALLOWED` (HTTP 403) if you attempt a secret-key-only operation with a public key.
|
|
236
178
|
|
|
237
|
-
|
|
179
|
+
```typescript
|
|
180
|
+
const drip = new Drip({ apiKey: 'pk_test_...' });
|
|
181
|
+
console.log(drip.keyType); // 'public'
|
|
238
182
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
183
|
+
// This works fine
|
|
184
|
+
await drip.trackUsage({ customerId: 'cust_123', meter: 'api_calls', quantity: 1 });
|
|
185
|
+
|
|
186
|
+
// This throws DripError(403, 'PUBLIC_KEY_NOT_ALLOWED')
|
|
187
|
+
await drip.createWebhook({ url: '...', events: ['charge.succeeded'] });
|
|
188
|
+
```
|
|
244
189
|
|
|
245
|
-
|
|
190
|
+
---
|
|
246
191
|
|
|
247
|
-
|
|
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 |
|
|
192
|
+
## SDK Variants
|
|
256
193
|
|
|
257
|
-
|
|
194
|
+
| Variant | Description |
|
|
195
|
+
|---------|-------------|
|
|
196
|
+
| **Core SDK** (recommended for pilots) | Usage tracking + execution logging only |
|
|
197
|
+
| **Full SDK** | Includes billing, balances, and workflows (for later stages) |
|
|
258
198
|
|
|
259
|
-
|
|
260
|
-
|--------|-------------|
|
|
261
|
-
| `estimateFromUsage(params)` | Estimate cost from usage data |
|
|
262
|
-
| `estimateFromHypothetical(params)` | Estimate from hypothetical usage |
|
|
199
|
+
---
|
|
263
200
|
|
|
264
|
-
|
|
201
|
+
## Core SDK Methods
|
|
265
202
|
|
|
266
203
|
| Method | Description |
|
|
267
204
|
|--------|-------------|
|
|
268
|
-
| `checkout(params)` | Create checkout session (fiat on-ramp) |
|
|
269
|
-
| `listMeters()` | List available meters |
|
|
270
205
|
| `ping()` | Verify API connection |
|
|
206
|
+
| `createCustomer(params)` | Create a customer |
|
|
207
|
+
| `getCustomer(customerId)` | Get customer details |
|
|
208
|
+
| `listCustomers(options)` | List all customers |
|
|
209
|
+
| `trackUsage(params)` | Record metered usage |
|
|
210
|
+
| `recordRun(params)` | Log complete agent run (simplified) |
|
|
211
|
+
| `startRun(params)` | Start execution trace |
|
|
212
|
+
| `emitEvent(params)` | Log event within run |
|
|
213
|
+
| `emitEventsBatch(params)` | Batch log events |
|
|
214
|
+
| `endRun(runId, params)` | Complete execution trace |
|
|
215
|
+
| `getRunTimeline(runId)` | Get execution timeline |
|
|
271
216
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
For LLM token streaming, accumulate usage locally and charge once:
|
|
275
|
-
|
|
276
|
-
```typescript
|
|
277
|
-
const meter = drip.createStreamMeter({
|
|
278
|
-
customerId: 'cus_123',
|
|
279
|
-
meter: 'tokens',
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
for await (const chunk of llmStream) {
|
|
283
|
-
meter.add(chunk.tokens);
|
|
284
|
-
yield chunk;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// Single API call at end
|
|
288
|
-
await meter.flush();
|
|
289
|
-
```
|
|
217
|
+
---
|
|
290
218
|
|
|
291
|
-
##
|
|
219
|
+
## Who This Is For
|
|
292
220
|
|
|
293
|
-
|
|
221
|
+
- AI agents (token metering, tool calls, execution traces)
|
|
222
|
+
- API companies (per-request billing, endpoint attribution)
|
|
223
|
+
- RPC providers (multi-chain call tracking)
|
|
224
|
+
- Cloud/infra (compute seconds, storage, bandwidth)
|
|
294
225
|
|
|
295
|
-
|
|
296
|
-
import { withDrip } from '@drip-sdk/node/next';
|
|
226
|
+
---
|
|
297
227
|
|
|
298
|
-
|
|
299
|
-
meter: 'api_calls',
|
|
300
|
-
quantity: 1,
|
|
301
|
-
}, async (req, { customerId }) => {
|
|
302
|
-
return Response.json({ result: 'success' });
|
|
303
|
-
});
|
|
304
|
-
```
|
|
228
|
+
## Full SDK (Billing, Webhooks, Integrations)
|
|
305
229
|
|
|
306
|
-
|
|
230
|
+
For billing, webhooks, middleware, and advanced features:
|
|
307
231
|
|
|
308
232
|
```typescript
|
|
309
|
-
import {
|
|
310
|
-
|
|
311
|
-
app.use('/api', dripMiddleware({
|
|
312
|
-
meter: 'api_calls',
|
|
313
|
-
quantity: 1,
|
|
314
|
-
}));
|
|
233
|
+
import { Drip } from '@drip-sdk/node';
|
|
315
234
|
```
|
|
316
235
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
```typescript
|
|
320
|
-
import { DripCallbackHandler } from '@drip-sdk/node/langchain';
|
|
321
|
-
|
|
322
|
-
const handler = new DripCallbackHandler({
|
|
323
|
-
drip,
|
|
324
|
-
customerId: 'cus_123',
|
|
325
|
-
});
|
|
236
|
+
See **[FULL_SDK.md](./FULL_SDK.md)** for complete documentation.
|
|
326
237
|
|
|
327
|
-
|
|
328
|
-
await agent.invoke({ input: '...' }, { callbacks: [handler] });
|
|
329
|
-
```
|
|
238
|
+
---
|
|
330
239
|
|
|
331
240
|
## Error Handling
|
|
332
241
|
|
|
@@ -342,50 +251,7 @@ try {
|
|
|
342
251
|
}
|
|
343
252
|
```
|
|
344
253
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
```typescript
|
|
348
|
-
import { Drip } from '@drip-sdk/node';
|
|
349
|
-
|
|
350
|
-
const drip = new Drip({ apiKey: process.env.DRIP_API_KEY! });
|
|
351
|
-
|
|
352
|
-
// Create a billable charge
|
|
353
|
-
const result = await drip.charge({
|
|
354
|
-
customerId: 'cus_123',
|
|
355
|
-
meter: 'api_calls',
|
|
356
|
-
quantity: 1,
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
// Get customer balance
|
|
360
|
-
const balance = await drip.getBalance('cus_123');
|
|
361
|
-
console.log(`Balance: $${balance.balanceUsdc}`);
|
|
362
|
-
|
|
363
|
-
// Query charges
|
|
364
|
-
const charge = await drip.getCharge(chargeId);
|
|
365
|
-
const charges = await drip.listCharges({ customerId: 'cus_123' });
|
|
366
|
-
|
|
367
|
-
// Cost estimation
|
|
368
|
-
await drip.estimateFromUsage({ customerId, startDate, endDate });
|
|
369
|
-
await drip.estimateFromHypothetical({ items: [...] });
|
|
370
|
-
|
|
371
|
-
// Checkout (fiat on-ramp)
|
|
372
|
-
await drip.checkout({ customerId: 'cus_123', amountUsd: 5000 });
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
## TypeScript Support
|
|
376
|
-
|
|
377
|
-
Full TypeScript support with exported types:
|
|
378
|
-
|
|
379
|
-
```typescript
|
|
380
|
-
import type {
|
|
381
|
-
Customer,
|
|
382
|
-
Charge,
|
|
383
|
-
ChargeResult,
|
|
384
|
-
TrackUsageParams,
|
|
385
|
-
RunResult,
|
|
386
|
-
Webhook,
|
|
387
|
-
} from '@drip-sdk/node';
|
|
388
|
-
```
|
|
254
|
+
---
|
|
389
255
|
|
|
390
256
|
## Requirements
|
|
391
257
|
|
|
@@ -393,6 +259,6 @@ import type {
|
|
|
393
259
|
|
|
394
260
|
## Links
|
|
395
261
|
|
|
396
|
-
- [Documentation](
|
|
397
|
-
- [
|
|
262
|
+
- [Full SDK Documentation](./FULL_SDK.md)
|
|
263
|
+
- [API Documentation](https://drippay.dev/api-reference)
|
|
398
264
|
- [npm](https://www.npmjs.com/package/@drip-sdk/node)
|
package/dist/core.cjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
'use strict';Object.defineProperty(exports,'__esModule',{value:true});var u=class
|
|
2
|
-
exports.Drip=
|
|
1
|
+
'use strict';Object.defineProperty(exports,'__esModule',{value:true});var crypto=require('crypto');var I=0;function y(m,...t){let e=++I,n=t.filter(r=>r!==void 0).map(String);n.push(String(e));let s=crypto.createHash("sha256").update(n.join("|")).digest("hex").slice(0,24);return `${m}_${s}`}var u=class m extends Error{constructor(e,n,s){super(e);this.statusCode=n;this.code=s;this.name="DripError",Object.setPrototypeOf(this,m.prototype);}},f=class{apiKey;baseUrl;timeout;keyType;constructor(t={}){let e=t.apiKey??(typeof process<"u"?process.env.DRIP_API_KEY:void 0),n=t.baseUrl??(typeof process<"u"?process.env.DRIP_BASE_URL:void 0);if(!e)throw new Error("Drip API key is required. Either pass { apiKey } to constructor or set DRIP_API_KEY environment variable.");this.apiKey=e,this.baseUrl=n||"https://drip-app-hlunj.ondigitalocean.app/v1",this.timeout=t.timeout||3e4,e.startsWith("sk_")?this.keyType="secret":e.startsWith("pk_")?this.keyType="public":this.keyType="unknown";}async request(t,e={}){let n=new AbortController,s=setTimeout(()=>n.abort(),this.timeout);try{let r=await fetch(`${this.baseUrl}${t}`,{...e,signal:n.signal,headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`,...e.headers}});if(r.status===204)return {success:!0};let o=await r.json();if(!r.ok)throw new u(o.message||o.error||"Request failed",r.status,o.code);return o}catch(r){throw r instanceof u?r:r instanceof Error&&r.name==="AbortError"?new u("Request timed out",408,"TIMEOUT"):new u(r instanceof Error?r.message:"Unknown error",0,"UNKNOWN")}finally{clearTimeout(s);}}async ping(){let t=new AbortController,e=setTimeout(()=>t.abort(),this.timeout),n=this.baseUrl;n.endsWith("/v1/")?n=n.slice(0,-4):n.endsWith("/v1")&&(n=n.slice(0,-3)),n=n.replace(/\/+$/,"");let s=Date.now();try{let r=await fetch(`${n}/health`,{signal:t.signal,headers:{Authorization:`Bearer ${this.apiKey}`}}),o=Date.now()-s,a="unknown",l=Date.now();try{let c=await r.json();typeof c.status=="string"&&(a=c.status),typeof c.timestamp=="number"&&(l=c.timestamp);}catch{a=r.ok?"healthy":`error:${r.status}`;}return !r.ok&&a==="unknown"&&(a=`error:${r.status}`),{ok:r.ok&&a==="healthy",status:a,latencyMs:o,timestamp:l}}catch(r){throw r instanceof Error&&r.name==="AbortError"?new u("Request timed out",408,"TIMEOUT"):new u(r instanceof Error?r.message:"Unknown error",0,"UNKNOWN")}finally{clearTimeout(e);}}async createCustomer(t){return this.request("/customers",{method:"POST",body:JSON.stringify(t)})}async getCustomer(t){return this.request(`/customers/${t}`)}async listCustomers(t){let e=new URLSearchParams;t?.limit&&e.set("limit",t.limit.toString()),t?.status&&e.set("status",t.status);let n=e.toString(),s=n?`/customers?${n}`:"/customers";return this.request(s)}async trackUsage(t){let e=t.idempotencyKey??y("track",t.customerId,t.meter,t.quantity);return this.request("/usage/internal",{method:"POST",body:JSON.stringify({customerId:t.customerId,usageType:t.meter,quantity:t.quantity,idempotencyKey:e,units:t.units,description:t.description,metadata:t.metadata})})}async createWorkflow(t){return this.request("/workflows",{method:"POST",body:JSON.stringify(t)})}async listWorkflows(){return this.request("/workflows")}async startRun(t){return this.request("/runs",{method:"POST",body:JSON.stringify(t)})}async endRun(t,e){return this.request(`/runs/${t}`,{method:"PATCH",body:JSON.stringify(e)})}async getRun(t){return this.request(`/runs/${t}`)}async getRunTimeline(t,e){let n=new URLSearchParams;e?.limit&&n.set("limit",e.limit.toString()),e?.cursor&&n.set("cursor",e.cursor),e?.includeAnomalies!==void 0&&n.set("includeAnomalies",String(e.includeAnomalies)),e?.collapseRetries!==void 0&&n.set("collapseRetries",String(e.collapseRetries));let s=n.toString(),r=s?`/runs/${t}/timeline?${s}`:`/runs/${t}/timeline`;return this.request(r)}async emitEvent(t){let e=t.idempotencyKey??y("evt",t.runId,t.eventType,t.quantity);return this.request("/run-events",{method:"POST",body:JSON.stringify({...t,idempotencyKey:e})})}async emitEventsBatch(t){return this.request("/run-events/batch",{method:"POST",body:JSON.stringify({events:t})})}async recordRun(t){let e=Date.now(),n=t.workflow,s=t.workflow;if(!t.workflow.startsWith("wf_"))try{let d=(await this.listWorkflows()).data.find(i=>i.slug===t.workflow||i.id===t.workflow);if(d)n=d.id,s=d.name;else {let i=await this.createWorkflow({name:t.workflow.replace(/[_-]/g," ").replace(/\b\w/g,g=>g.toUpperCase()),slug:t.workflow,productSurface:"CUSTOM"});n=i.id,s=i.name;}}catch{n=t.workflow;}let r=await this.startRun({customerId:t.customerId,workflowId:n,externalRunId:t.externalRunId,correlationId:t.correlationId,metadata:t.metadata}),o=0,a=0;if(t.events.length>0){let w=t.events.map((i,g)=>({runId:r.id,eventType:i.eventType,quantity:i.quantity,units:i.units,description:i.description,costUnits:i.costUnits,metadata:i.metadata,idempotencyKey:t.externalRunId?`${t.externalRunId}:${i.eventType}:${g}`:y("run",r.id,i.eventType,g)})),d=await this.emitEventsBatch(w);o=d.created,a=d.duplicates;}let l=await this.endRun(r.id,{status:t.status,errorMessage:t.errorMessage,errorCode:t.errorCode}),c=Date.now()-e,R=t.events.length>0?`${o} events recorded`:"no events",h=`${t.status==="COMPLETED"?"\u2713":t.status==="FAILED"?"\u2717":"\u25CB"} ${s}: ${R} (${l.durationMs??c}ms)`;return {run:{id:r.id,workflowId:n,workflowName:s,status:t.status,durationMs:l.durationMs},events:{created:o,duplicates:a},totalCostUnits:l.totalCostUnits,summary:h}}},S=f,p=null;function k(){return p||(p=new f),p}var U=new Proxy({},{get(m,t){let e=k(),n=e[t];return typeof n=="function"?n.bind(e):n}});
|
|
2
|
+
exports.Drip=f;exports.DripError=u;exports.default=S;exports.drip=U;//# sourceMappingURL=core.cjs.map
|
|
3
3
|
//# sourceMappingURL=core.cjs.map
|