@drip-sdk/node 1.1.0 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +191 -282
- package/dist/core.cjs +2 -2
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +182 -46
- package/dist/core.d.ts +182 -46
- 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.d.cts +2 -2
- package/dist/express.d.ts +2 -2
- 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 +215 -66
- package/dist/index.d.ts +215 -66
- 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.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 +2 -2
- package/dist/next.d.ts +2 -2
- package/dist/next.js +2 -2
- package/dist/next.js.map +1 -1
- package/dist/{types-B2qwDadD.d.ts → types-BQzxIAqF.d.ts} +1 -1
- package/dist/{types-Bo8SiUdl.d.cts → types-DX3iqXqe.d.cts} +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,332 +1,284 @@
|
|
|
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
|
-
|
|
22
|
+
### 2. Set your API key
|
|
17
23
|
|
|
18
|
-
|
|
24
|
+
```bash
|
|
25
|
+
# Secret key — full API access (server-side only, never expose publicly)
|
|
26
|
+
export DRIP_API_KEY=sk_test_...
|
|
19
27
|
|
|
20
|
-
|
|
21
|
-
|
|
28
|
+
# Or public key — read/write access for usage, customers, billing (safe for client-side)
|
|
29
|
+
export DRIP_API_KEY=pk_test_...
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### 3. Create a customer and track usage
|
|
22
33
|
|
|
23
34
|
```typescript
|
|
24
|
-
import {
|
|
35
|
+
import { drip } from '@drip-sdk/node';
|
|
25
36
|
|
|
26
|
-
|
|
37
|
+
// Create a customer first
|
|
38
|
+
const customer = await drip.createCustomer({ externalCustomerId: 'user_123' });
|
|
27
39
|
|
|
28
|
-
//
|
|
29
|
-
await drip.
|
|
40
|
+
// Track usage — that's it
|
|
41
|
+
await drip.trackUsage({ customerId: customer.id, meter: 'api_calls', quantity: 1 });
|
|
30
42
|
```
|
|
31
43
|
|
|
32
|
-
|
|
44
|
+
The `drip` singleton reads `DRIP_API_KEY` from your environment automatically.
|
|
45
|
+
|
|
46
|
+
### Alternative: Explicit Configuration
|
|
33
47
|
|
|
34
48
|
```typescript
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
});
|
|
49
|
+
import { Drip } from '@drip-sdk/node';
|
|
50
|
+
|
|
51
|
+
// Auto-reads DRIP_API_KEY from environment
|
|
52
|
+
const drip = new Drip();
|
|
53
|
+
|
|
54
|
+
// Or pass config explicitly with a secret key (full access)
|
|
55
|
+
const drip = new Drip({ apiKey: 'sk_test_...' });
|
|
56
|
+
|
|
57
|
+
// Or with a public key (safe for client-side, limited scope)
|
|
58
|
+
const drip = new Drip({ apiKey: 'pk_test_...' });
|
|
42
59
|
```
|
|
43
60
|
|
|
44
|
-
###
|
|
61
|
+
### Full Example
|
|
45
62
|
|
|
46
63
|
```typescript
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
64
|
+
import { drip } from '@drip-sdk/node';
|
|
65
|
+
|
|
66
|
+
async function main() {
|
|
67
|
+
// Verify connectivity
|
|
68
|
+
await drip.ping();
|
|
69
|
+
|
|
70
|
+
// Create a customer (at least one of externalCustomerId or onchainAddress required)
|
|
71
|
+
const customer = await drip.createCustomer({ externalCustomerId: 'user_123' });
|
|
72
|
+
|
|
73
|
+
// Record usage
|
|
74
|
+
await drip.trackUsage({
|
|
75
|
+
customerId: customer.id,
|
|
76
|
+
meter: 'llm_tokens',
|
|
77
|
+
quantity: 842,
|
|
78
|
+
metadata: { model: 'gpt-4o-mini' },
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// Record an execution lifecycle
|
|
82
|
+
await drip.recordRun({
|
|
83
|
+
customerId: customer.id,
|
|
84
|
+
workflow: 'research-agent',
|
|
85
|
+
events: [
|
|
86
|
+
{ eventType: 'llm.call', quantity: 1700, units: 'tokens' },
|
|
87
|
+
{ eventType: 'tool.call', quantity: 1 },
|
|
88
|
+
],
|
|
89
|
+
status: 'COMPLETED',
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
console.log(`Customer ${customer.id}: usage + run recorded`);
|
|
93
|
+
}
|
|
58
94
|
|
|
59
|
-
|
|
60
|
-
// Output: "Research Agent: 3 events recorded (2.5s)"
|
|
95
|
+
main();
|
|
61
96
|
```
|
|
62
97
|
|
|
63
|
-
|
|
64
|
-
|
|
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 |
|
|
98
|
+
**Expected result:**
|
|
99
|
+
- No errors
|
|
100
|
+
- Events appear in your Drip dashboard within seconds
|
|
78
101
|
|
|
79
102
|
---
|
|
80
103
|
|
|
81
|
-
##
|
|
104
|
+
## Core Concepts (2-minute mental model)
|
|
82
105
|
|
|
83
|
-
|
|
106
|
+
| Concept | Description |
|
|
107
|
+
|---------|-------------|
|
|
108
|
+
| `customerId` | The end user, API key, or account you're attributing usage to |
|
|
109
|
+
| `meter` | What you're measuring (tokens, requests, seconds, rows, etc.) |
|
|
110
|
+
| `quantity` | Numeric usage for that meter |
|
|
111
|
+
| `run` | A single execution or request lifecycle (success / failure / duration) |
|
|
112
|
+
| `correlationId` | Optional. Your trace/request ID for linking Drip data with your APM (OpenTelemetry, Datadog, etc.) |
|
|
84
113
|
|
|
85
|
-
|
|
86
|
-
import { Drip } from '@drip-sdk/node';
|
|
114
|
+
**Status values:** `PENDING` | `RUNNING` | `COMPLETED` | `FAILED`
|
|
87
115
|
|
|
88
|
-
|
|
116
|
+
**Event schema:** Payloads are schema-flexible. Drip stores events as structured JSON and does not enforce a fixed event taxonomy.
|
|
89
117
|
|
|
90
|
-
|
|
91
|
-
// - charge(), getBalance(), getCharge(), listCharges()
|
|
92
|
-
// - createWebhook(), listWebhooks(), deleteWebhook()
|
|
93
|
-
// - estimateFromUsage(), estimateFromHypothetical()
|
|
94
|
-
// - checkout(), and more
|
|
95
|
-
```
|
|
118
|
+
Drip is append-only and idempotent-friendly. You can safely retry events.
|
|
96
119
|
|
|
97
|
-
|
|
120
|
+
> **Distributed tracing:** Pass `correlationId` to `startRun()`, `recordRun()`, or `emitEvent()` to cross-reference Drip billing with your observability stack. See [FULL_SDK.md](./FULL_SDK.md#distributed-tracing-correlationid) for details.
|
|
98
121
|
|
|
99
|
-
|
|
122
|
+
---
|
|
100
123
|
|
|
101
|
-
|
|
102
|
-
import { Drip } from '@drip-sdk/node';
|
|
124
|
+
## Idempotency Keys
|
|
103
125
|
|
|
104
|
-
|
|
126
|
+
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.
|
|
105
127
|
|
|
106
|
-
|
|
107
|
-
await drip.trackUsage({
|
|
108
|
-
customerId: 'cus_123',
|
|
109
|
-
meter: 'api_calls',
|
|
110
|
-
quantity: 1,
|
|
111
|
-
metadata: { endpoint: '/v1/generate', method: 'POST' },
|
|
112
|
-
});
|
|
128
|
+
`recordRun` generates idempotency keys internally for its batch events (using `externalRunId` when provided, otherwise deterministic keys).
|
|
113
129
|
|
|
114
|
-
|
|
115
|
-
const balance = await drip.getBalance('cus_123');
|
|
116
|
-
console.log(`Total usage: $${balance.totalUsageUsd}`);
|
|
117
|
-
```
|
|
130
|
+
### Auto-generated keys (default)
|
|
118
131
|
|
|
119
|
-
|
|
132
|
+
When you omit `idempotencyKey`, the SDK generates one automatically. The auto key is:
|
|
120
133
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
});
|
|
134
|
+
- **Unique per call** — two separate calls with identical parameters produce different keys (a monotonic counter ensures this).
|
|
135
|
+
- **Stable across retries** — the key is generated once and reused for all retry attempts of that call, so network retries are safely deduplicated.
|
|
136
|
+
- **Deterministic** — no randomness; keys are reproducible for debugging.
|
|
133
137
|
|
|
134
|
-
|
|
135
|
-
// Output: "Research Agent: 3 events recorded (2.5s)"
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
### View Execution Traces
|
|
139
|
-
|
|
140
|
-
```typescript
|
|
141
|
-
// Get detailed timeline of an agent run
|
|
142
|
-
const timeline = await drip.getRunTimeline(runId);
|
|
138
|
+
This means you get **free retry safety** with zero configuration.
|
|
143
139
|
|
|
144
|
-
|
|
145
|
-
console.log(`${event.eventType}: ${event.duration}ms`);
|
|
146
|
-
}
|
|
147
|
-
```
|
|
140
|
+
> **Note:** `wrapApiCall` generates a time-based key when no explicit `idempotencyKey` is provided. Pass your own key if you need deterministic deduplication with `wrapApiCall`.
|
|
148
141
|
|
|
149
|
-
|
|
142
|
+
### When to pass explicit keys
|
|
150
143
|
|
|
151
|
-
|
|
144
|
+
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:
|
|
152
145
|
|
|
153
146
|
```typescript
|
|
154
|
-
|
|
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
|
-
},
|
|
165
|
-
});
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
### API Companies
|
|
147
|
+
const customer = await drip.createCustomer({ externalCustomerId: 'user_123' });
|
|
169
148
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
await drip.trackUsage({
|
|
173
|
-
customerId: 'cus_123',
|
|
149
|
+
await drip.charge({
|
|
150
|
+
customerId: customer.id,
|
|
174
151
|
meter: 'api_calls',
|
|
175
152
|
quantity: 1,
|
|
176
|
-
|
|
177
|
-
endpoint: '/v1/embeddings',
|
|
178
|
-
tokens: 1500,
|
|
179
|
-
model: 'text-embedding-3-small',
|
|
180
|
-
},
|
|
153
|
+
idempotencyKey: `order_${orderId}_charge`, // your business-level key
|
|
181
154
|
});
|
|
182
155
|
```
|
|
183
156
|
|
|
184
|
-
|
|
157
|
+
Common patterns:
|
|
158
|
+
- `order_${orderId}` — one charge per order
|
|
159
|
+
- `run_${runId}_step_${stepIndex}` — one charge per pipeline step
|
|
160
|
+
- `invoice_${invoiceId}` — one charge per invoice
|
|
185
161
|
|
|
186
|
-
|
|
187
|
-
// Track agent execution with detailed events
|
|
188
|
-
const run = await drip.startRun({
|
|
189
|
-
customerId: 'cus_123',
|
|
190
|
-
workflowSlug: 'document-processor',
|
|
191
|
-
});
|
|
162
|
+
### StreamMeter
|
|
192
163
|
|
|
193
|
-
|
|
194
|
-
runId: run.id,
|
|
195
|
-
eventType: 'ocr.process',
|
|
196
|
-
quantity: 5,
|
|
197
|
-
units: 'pages',
|
|
198
|
-
});
|
|
164
|
+
`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
165
|
|
|
200
|
-
|
|
201
|
-
runId: run.id,
|
|
202
|
-
eventType: 'llm.summarize',
|
|
203
|
-
model: 'gpt-4',
|
|
204
|
-
inputTokens: 10000,
|
|
205
|
-
outputTokens: 500,
|
|
206
|
-
});
|
|
166
|
+
---
|
|
207
167
|
|
|
208
|
-
|
|
209
|
-
```
|
|
168
|
+
## API Key Types
|
|
210
169
|
|
|
211
|
-
|
|
170
|
+
Drip issues two key types per API key pair. Each has different access scopes:
|
|
212
171
|
|
|
213
|
-
|
|
172
|
+
| Key Type | Prefix | Access | Use In |
|
|
173
|
+
|----------|--------|--------|--------|
|
|
174
|
+
| **Secret Key** | `sk_live_` / `sk_test_` | Full API access (all endpoints) | Server-side only |
|
|
175
|
+
| **Public Key** | `pk_live_` / `pk_test_` | Usage tracking, customers, billing, analytics, sessions | Client-side safe |
|
|
214
176
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
| `getCharge(chargeId)` | Get charge details |
|
|
221
|
-
| `listCharges(options)` | List all charges |
|
|
222
|
-
| `getChargeStatus(chargeId)` | Get charge status |
|
|
177
|
+
### What public keys **can** access
|
|
178
|
+
- Usage tracking (`trackUsage`, `recordRun`, `startRun`, `emitEvent`, etc.)
|
|
179
|
+
- Customer management (`createCustomer`, `getCustomer`, `listCustomers`)
|
|
180
|
+
- Billing & charges (`charge`, `getBalance`, `listCharges`, etc.)
|
|
181
|
+
- Pricing plans, sessions, analytics, usage caps, refunds
|
|
223
182
|
|
|
224
|
-
###
|
|
183
|
+
### What public keys **cannot** access (secret key required)
|
|
184
|
+
- Webhook management (`createWebhook`, `listWebhooks`, `deleteWebhook`, etc.)
|
|
185
|
+
- API key management (create, rotate, revoke keys)
|
|
186
|
+
- Feature flag management
|
|
225
187
|
|
|
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 |
|
|
188
|
+
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
189
|
|
|
237
|
-
|
|
190
|
+
```typescript
|
|
191
|
+
const drip = new Drip({ apiKey: 'pk_test_...' });
|
|
192
|
+
console.log(drip.keyType); // 'public'
|
|
238
193
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
| `getCustomer(customerId)` | Get customer details |
|
|
243
|
-
| `listCustomers(options)` | List all customers |
|
|
194
|
+
// Create a customer first, then track usage
|
|
195
|
+
const customer = await drip.createCustomer({ externalCustomerId: 'user_123' });
|
|
196
|
+
await drip.trackUsage({ customerId: customer.id, meter: 'api_calls', quantity: 1 });
|
|
244
197
|
|
|
245
|
-
|
|
198
|
+
// This throws DripError(403, 'PUBLIC_KEY_NOT_ALLOWED')
|
|
199
|
+
await drip.createWebhook({ url: '...', events: ['charge.succeeded'] });
|
|
200
|
+
```
|
|
246
201
|
|
|
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 |
|
|
202
|
+
---
|
|
256
203
|
|
|
257
|
-
|
|
204
|
+
## SDK Variants
|
|
258
205
|
|
|
259
|
-
|
|
|
260
|
-
|
|
261
|
-
|
|
|
262
|
-
|
|
|
206
|
+
| Variant | Description |
|
|
207
|
+
|---------|-------------|
|
|
208
|
+
| **Core SDK** (recommended for pilots) | Usage tracking + execution logging only |
|
|
209
|
+
| **Full SDK** | Includes billing, balances, and workflows (for later stages) |
|
|
210
|
+
|
|
211
|
+
---
|
|
263
212
|
|
|
264
|
-
|
|
213
|
+
## Core SDK Methods
|
|
265
214
|
|
|
266
215
|
| Method | Description |
|
|
267
216
|
|--------|-------------|
|
|
268
|
-
| `checkout(params)` | Create checkout session (fiat on-ramp) |
|
|
269
|
-
| `listMeters()` | List available meters |
|
|
270
217
|
| `ping()` | Verify API connection |
|
|
218
|
+
| `createCustomer(params)` | Create a customer |
|
|
219
|
+
| `getCustomer(customerId)` | Get customer details |
|
|
220
|
+
| `listCustomers(options)` | List all customers |
|
|
221
|
+
| `trackUsage(params)` | Record metered usage |
|
|
222
|
+
| `recordRun(params)` | Log complete agent run (simplified) |
|
|
223
|
+
| `startRun(params)` | Start execution trace |
|
|
224
|
+
| `emitEvent(params)` | Log event within run |
|
|
225
|
+
| `emitEventsBatch(params)` | Batch log events |
|
|
226
|
+
| `endRun(runId, params)` | Complete execution trace |
|
|
227
|
+
| `getRun(runId)` | Get run details and summary |
|
|
228
|
+
| `getRunTimeline(runId)` | Get execution timeline |
|
|
271
229
|
|
|
272
|
-
|
|
230
|
+
### Creating Customers
|
|
273
231
|
|
|
274
|
-
|
|
232
|
+
All parameters are optional, but at least one of `externalCustomerId` or `onchainAddress` must be provided:
|
|
275
233
|
|
|
276
234
|
```typescript
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
meter: 'tokens',
|
|
280
|
-
});
|
|
235
|
+
// Simplest — just your internal user ID
|
|
236
|
+
const customer = await drip.createCustomer({ externalCustomerId: 'user_123' });
|
|
281
237
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
238
|
+
// With an on-chain address (for on-chain billing)
|
|
239
|
+
const customer = await drip.createCustomer({
|
|
240
|
+
onchainAddress: '0x1234...',
|
|
241
|
+
externalCustomerId: 'user_123',
|
|
242
|
+
});
|
|
286
243
|
|
|
287
|
-
//
|
|
288
|
-
await
|
|
244
|
+
// Internal/non-billing customer (for tracking only)
|
|
245
|
+
const customer = await drip.createCustomer({
|
|
246
|
+
externalCustomerId: 'internal-team',
|
|
247
|
+
isInternal: true,
|
|
248
|
+
});
|
|
289
249
|
```
|
|
290
250
|
|
|
291
|
-
|
|
251
|
+
| Parameter | Type | Required | Description |
|
|
252
|
+
|-----------|------|----------|-------------|
|
|
253
|
+
| `externalCustomerId` | `string` | No* | Your internal user/account ID |
|
|
254
|
+
| `onchainAddress` | `string` | No* | Customer's Ethereum address |
|
|
255
|
+
| `isInternal` | `boolean` | No | Mark as internal (non-billing). Default: `false` |
|
|
256
|
+
| `metadata` | `object` | No | Arbitrary key-value metadata |
|
|
292
257
|
|
|
293
|
-
|
|
258
|
+
\*At least one of `externalCustomerId` or `onchainAddress` is required.
|
|
294
259
|
|
|
295
|
-
|
|
296
|
-
import { withDrip } from '@drip-sdk/node/next';
|
|
260
|
+
---
|
|
297
261
|
|
|
298
|
-
|
|
299
|
-
meter: 'api_calls',
|
|
300
|
-
quantity: 1,
|
|
301
|
-
}, async (req, { customerId }) => {
|
|
302
|
-
return Response.json({ result: 'success' });
|
|
303
|
-
});
|
|
304
|
-
```
|
|
262
|
+
## Who This Is For
|
|
305
263
|
|
|
306
|
-
|
|
264
|
+
- AI agents (token metering, tool calls, execution traces)
|
|
265
|
+
- API companies (per-request billing, endpoint attribution)
|
|
266
|
+
- RPC providers (multi-chain call tracking)
|
|
267
|
+
- Cloud/infra (compute seconds, storage, bandwidth)
|
|
307
268
|
|
|
308
|
-
|
|
309
|
-
import { dripMiddleware } from '@drip-sdk/node/express';
|
|
269
|
+
---
|
|
310
270
|
|
|
311
|
-
|
|
312
|
-
meter: 'api_calls',
|
|
313
|
-
quantity: 1,
|
|
314
|
-
}));
|
|
315
|
-
```
|
|
271
|
+
## Full SDK (Billing, Webhooks, Integrations)
|
|
316
272
|
|
|
317
|
-
|
|
273
|
+
For billing, webhooks, middleware, and advanced features:
|
|
318
274
|
|
|
319
275
|
```typescript
|
|
320
|
-
import {
|
|
276
|
+
import { Drip } from '@drip-sdk/node';
|
|
277
|
+
```
|
|
321
278
|
|
|
322
|
-
|
|
323
|
-
drip,
|
|
324
|
-
customerId: 'cus_123',
|
|
325
|
-
});
|
|
279
|
+
See **[FULL_SDK.md](./FULL_SDK.md)** for complete documentation.
|
|
326
280
|
|
|
327
|
-
|
|
328
|
-
await agent.invoke({ input: '...' }, { callbacks: [handler] });
|
|
329
|
-
```
|
|
281
|
+
---
|
|
330
282
|
|
|
331
283
|
## Error Handling
|
|
332
284
|
|
|
@@ -342,50 +294,7 @@ try {
|
|
|
342
294
|
}
|
|
343
295
|
```
|
|
344
296
|
|
|
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
|
-
```
|
|
297
|
+
---
|
|
389
298
|
|
|
390
299
|
## Requirements
|
|
391
300
|
|
|
@@ -393,6 +302,6 @@ import type {
|
|
|
393
302
|
|
|
394
303
|
## Links
|
|
395
304
|
|
|
396
|
-
- [Documentation](
|
|
397
|
-
- [
|
|
305
|
+
- [Full SDK Documentation](./FULL_SDK.md)
|
|
306
|
+
- [API Documentation](https://drippay.dev/api-reference)
|
|
398
307
|
- [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
|
|
2
|
-
exports.Drip=
|
|
1
|
+
'use strict';Object.defineProperty(exports,'__esModule',{value:true});var crypto=require('crypto');var k=0;function p(m,...t){let e=++k,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 a=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 i=await r.json();if(!r.ok)throw new a(i.message||i.error||"Request failed",r.status,i.code);return i}catch(r){throw r instanceof a?r:r instanceof Error&&r.name==="AbortError"?new a("Request timed out",408,"TIMEOUT"):new a(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}`}}),i=Date.now()-s,o="unknown",d=Date.now();try{let u=await r.json();typeof u.status=="string"&&(o=u.status),typeof u.timestamp=="number"&&(d=u.timestamp);}catch{o=r.ok?"healthy":`error:${r.status}`;}return !r.ok&&o==="unknown"&&(o=`error:${r.status}`),{ok:r.ok&&o==="healthy",status:o,latencyMs:i,timestamp:d}}catch(r){throw r instanceof Error&&r.name==="AbortError"?new a("Request timed out",408,"TIMEOUT"):new a(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??p("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??p("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){try{return await this.request("/runs/record",{method:"POST",body:JSON.stringify({customerId:t.customerId,workflow:t.workflow,events:t.events,status:t.status,errorMessage:t.errorMessage,errorCode:t.errorCode,externalRunId:t.externalRunId,correlationId:t.correlationId,metadata:t.metadata})})}catch(e){if(e instanceof a&&e.statusCode===404)return this._recordRunFallback(t);throw e}}async _recordRunFallback(t){let e=Date.now(),n=t.workflow,s=t.workflow,{data:r}=await this.listWorkflows(),i=r.find(l=>l.slug===t.workflow||l.id===t.workflow);if(i)n=i.id,s=i.name;else {let l=await this.request("/workflows",{method:"POST",body:JSON.stringify({name:t.workflow.replace(/[_-]/g," ").replace(/\b\w/g,y=>y.toUpperCase()),slug:t.workflow,productSurface:"CUSTOM"})});n=l.id,s=l.name;}let o=await this.startRun({customerId:t.customerId,workflowId:n,correlationId:t.correlationId,externalRunId:t.externalRunId,metadata:t.metadata}),d=0,u=0;if(t.events.length>0){let l=t.events.map((c,I)=>({runId:o.id,eventType:c.eventType,quantity:c.quantity??1,units:c.units,description:c.description,costUnits:c.costUnits,metadata:c.metadata,idempotencyKey:t.externalRunId?`${t.externalRunId}:${c.eventType}:${I}`:void 0})),y=await this.emitEventsBatch(l);d=y.created,u=y.duplicates;}let g=await this.endRun(o.id,{status:t.status,errorMessage:t.errorMessage,errorCode:t.errorCode}),w=Date.now()-e,h=t.status==="COMPLETED"?"\u2713":t.status==="FAILED"?"\u2717":"\u25CB";return {run:{id:o.id,workflowId:n,workflowName:s,status:g.status,durationMs:g.durationMs??w},events:{created:d,duplicates:u},totalCostUnits:g.totalCostUnits??null,summary:`${h} ${s}: ${d} events recorded (${g.durationMs??w}ms)`}}},S=f,R=null;function C(){return R||(R=new f),R}var O=new Proxy({},{get(m,t){let e=C(),n=e[t];return typeof n=="function"?n.bind(e):n}});
|
|
2
|
+
exports.Drip=f;exports.DripError=a;exports.default=S;exports.drip=O;//# sourceMappingURL=core.cjs.map
|
|
3
3
|
//# sourceMappingURL=core.cjs.map
|