@monigo/sdk 0.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 +712 -0
- package/dist/index.d.ts +992 -0
- package/dist/monigo.cjs +806 -0
- package/dist/monigo.cjs.map +1 -0
- package/dist/monigo.js +806 -0
- package/dist/monigo.js.map +1 -0
- package/package.json +60 -0
package/Readme.md
ADDED
|
@@ -0,0 +1,712 @@
|
|
|
1
|
+
# @monigo/sdk
|
|
2
|
+
|
|
3
|
+
The official JavaScript/TypeScript client library for the [Monigo](https://monigo.co) API. Zero external dependencies — works natively in Node.js (≥ 18), browsers, Cloudflare Workers, Bun, and Deno.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @monigo/sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or with other package managers:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
yarn add @monigo/sdk
|
|
15
|
+
pnpm add @monigo/sdk
|
|
16
|
+
bun add @monigo/sdk
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Requires Node.js 18 or later (for native `fetch`). For Node.js 16, see [Polyfilling fetch](#polyfilling-fetch).
|
|
20
|
+
|
|
21
|
+
## Quick start
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { MonigoClient, Aggregation, BillingPeriod, PricingModel } from '@monigo/sdk'
|
|
25
|
+
|
|
26
|
+
const client = new MonigoClient({ apiKey: process.env.MONIGO_API_KEY! })
|
|
27
|
+
|
|
28
|
+
// Create a customer
|
|
29
|
+
const customer = await client.customers.create({
|
|
30
|
+
external_id: 'user_abc123',
|
|
31
|
+
name: 'Acme Corp',
|
|
32
|
+
email: 'billing@acme.com',
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
// Ingest a usage event
|
|
36
|
+
await client.events.ingest({
|
|
37
|
+
events: [
|
|
38
|
+
{
|
|
39
|
+
event_name: 'api_call',
|
|
40
|
+
customer_id: customer.id,
|
|
41
|
+
idempotency_key: 'evt_20260201_001',
|
|
42
|
+
timestamp: new Date(),
|
|
43
|
+
properties: { endpoint: '/v1/data', region: 'us-east-1' },
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
console.log(`Customer ${customer.id} created and event ingested`)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
> **Tip:** Use a test-mode API key (`mk_test_...`) during development. Test events are isolated from live data and won't trigger real charges.
|
|
52
|
+
|
|
53
|
+
## Client configuration
|
|
54
|
+
|
|
55
|
+
### Constructor options
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
const client = new MonigoClient({
|
|
59
|
+
apiKey: 'mk_live_...', // required — your Monigo API key
|
|
60
|
+
baseURL: 'https://api.monigo.co', // optional — defaults to production
|
|
61
|
+
timeout: 30_000, // optional — request timeout in ms (default: none)
|
|
62
|
+
fetch: customFetch, // optional — custom fetch implementation
|
|
63
|
+
})
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Custom base URL
|
|
67
|
+
|
|
68
|
+
For self-hosted deployments or a local development server:
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
const client = new MonigoClient({
|
|
72
|
+
apiKey: process.env.MONIGO_API_KEY!,
|
|
73
|
+
baseURL: 'http://localhost:8000',
|
|
74
|
+
})
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Polyfilling fetch
|
|
78
|
+
|
|
79
|
+
Node.js 16 does not include a built-in `fetch`. Pass a polyfill via the `fetch` option:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import fetch from 'node-fetch'
|
|
83
|
+
|
|
84
|
+
const client = new MonigoClient({
|
|
85
|
+
apiKey: process.env.MONIGO_API_KEY!,
|
|
86
|
+
fetch: fetch as unknown as typeof globalThis.fetch,
|
|
87
|
+
})
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Using in tests
|
|
91
|
+
|
|
92
|
+
The `fetch` option makes the client fully testable without a network:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
const client = new MonigoClient({
|
|
96
|
+
apiKey: 'test_key',
|
|
97
|
+
fetch: (_url, _init) =>
|
|
98
|
+
Promise.resolve(new Response(JSON.stringify({ customer: mockCustomer }))),
|
|
99
|
+
})
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Authentication
|
|
103
|
+
|
|
104
|
+
Every request is authenticated with a Bearer token derived from your API key:
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
Authorization: Bearer mk_live_...
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
API keys are scoped to an organisation and carry one of two prefixes:
|
|
111
|
+
|
|
112
|
+
| Prefix | Mode | Usage |
|
|
113
|
+
|--------|------|-------|
|
|
114
|
+
| `mk_live_` | Live | Production traffic — charges and payouts are real |
|
|
115
|
+
| `mk_test_` | Test | Development / CI — events, customers, and invoices are sandboxed |
|
|
116
|
+
|
|
117
|
+
## Error handling
|
|
118
|
+
|
|
119
|
+
All methods return a `Promise`. On a 4xx or 5xx response the SDK throws a `MonigoAPIError` with a typed status code, message, and optional field-level details map.
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
import { MonigoAPIError } from '@monigo/sdk'
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const customer = await client.customers.get('cust_missing')
|
|
126
|
+
} catch (err) {
|
|
127
|
+
if (err instanceof MonigoAPIError) {
|
|
128
|
+
console.error(`API error ${err.statusCode}: ${err.message}`)
|
|
129
|
+
// { statusCode: 404, message: 'customer not found' }
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Static type-narrowing guards
|
|
135
|
+
|
|
136
|
+
Instead of catching and casting, use the static guards on `MonigoAPIError`:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
try {
|
|
140
|
+
await client.subscriptions.create({ customer_id: 'c', plan_id: 'p' })
|
|
141
|
+
} catch (err) {
|
|
142
|
+
if (MonigoAPIError.isConflict(err)) {
|
|
143
|
+
// customer already has an active subscription of this plan type
|
|
144
|
+
} else if (MonigoAPIError.isNotFound(err)) {
|
|
145
|
+
// customer or plan not found
|
|
146
|
+
} else if (MonigoAPIError.isRateLimited(err)) {
|
|
147
|
+
// back off and retry
|
|
148
|
+
} else if (err) {
|
|
149
|
+
throw err
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
| Guard | HTTP Status | When it fires |
|
|
155
|
+
|-------|-------------|---------------|
|
|
156
|
+
| `MonigoAPIError.isNotFound(err)` | 404 | Resource does not exist |
|
|
157
|
+
| `MonigoAPIError.isUnauthorized(err)` | 401 | Invalid or missing API key |
|
|
158
|
+
| `MonigoAPIError.isForbidden(err)` | 403 | API key lacks required scope |
|
|
159
|
+
| `MonigoAPIError.isConflict(err)` | 409 | Duplicate resource (e.g. second active subscription of same plan type) |
|
|
160
|
+
| `MonigoAPIError.isRateLimited(err)` | 429 | Request rate limit exceeded |
|
|
161
|
+
| `MonigoAPIError.isQuotaExceeded(err)` | 402 | Organisation event quota exhausted |
|
|
162
|
+
| `MonigoAPIError.isServerError(err)` | 5xx | Unexpected server-side error |
|
|
163
|
+
|
|
164
|
+
### Instance flags
|
|
165
|
+
|
|
166
|
+
`MonigoAPIError` also exposes boolean instance properties:
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
catch (err) {
|
|
170
|
+
if (err instanceof MonigoAPIError && err.isNotFound) {
|
|
171
|
+
// same as MonigoAPIError.isNotFound(err)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Events
|
|
177
|
+
|
|
178
|
+
The `client.events` service handles usage event ingestion and event replay.
|
|
179
|
+
|
|
180
|
+
### Ingest events
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
const response = await client.events.ingest({
|
|
184
|
+
events: [
|
|
185
|
+
{
|
|
186
|
+
event_name: 'api_call',
|
|
187
|
+
customer_id: 'cust_abc',
|
|
188
|
+
idempotency_key: 'evt_20260201_001',
|
|
189
|
+
timestamp: new Date(),
|
|
190
|
+
properties: {
|
|
191
|
+
endpoint: '/v1/predict',
|
|
192
|
+
region: 'us-east-1',
|
|
193
|
+
tokens: 1500,
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
console.log('ingested:', response.ingested)
|
|
200
|
+
console.log('duplicates:', response.duplicates)
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
The server deduplicates events by `idempotency_key`. Sending the same key twice is safe — the second call appears in `response.duplicates` and won't be counted again. A single `ingest` call can contain up to **1,000 events**.
|
|
204
|
+
|
|
205
|
+
> **Tip:** Always supply a stable `idempotency_key` (e.g. a UUID or `${customer_id}_${event_type}_${timestamp}`) so retries after a network error don't double-count events.
|
|
206
|
+
|
|
207
|
+
### Replay events
|
|
208
|
+
|
|
209
|
+
Replay reprocesses all events in a time window through the metering pipeline. Use this to correct rollups after an outage or metric definition change.
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
let job = await client.events.startReplay({
|
|
213
|
+
from: new Date('2026-01-01'),
|
|
214
|
+
to: new Date('2026-01-31'),
|
|
215
|
+
event_name: 'api_call', // omit to replay all event names
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
// Poll until complete
|
|
219
|
+
while (job.status === 'pending' || job.status === 'processing') {
|
|
220
|
+
await new Promise(r => setTimeout(r, 5_000))
|
|
221
|
+
job = await client.events.getReplay(job.id)
|
|
222
|
+
console.log(`replayed ${job.events_replayed} / ${job.events_total}`)
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Customers
|
|
227
|
+
|
|
228
|
+
```ts
|
|
229
|
+
// Create
|
|
230
|
+
const customer = await client.customers.create({
|
|
231
|
+
external_id: 'user_123',
|
|
232
|
+
name: 'Acme Corp',
|
|
233
|
+
email: 'billing@acme.com',
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
// List
|
|
237
|
+
const { customers, count } = await client.customers.list()
|
|
238
|
+
console.log(`${count} customers`)
|
|
239
|
+
|
|
240
|
+
// Get by Monigo ID
|
|
241
|
+
const customer = await client.customers.get('cust_abc')
|
|
242
|
+
|
|
243
|
+
// Update
|
|
244
|
+
const updated = await client.customers.update('cust_abc', {
|
|
245
|
+
name: 'Acme Corp Ltd',
|
|
246
|
+
email: 'newbilling@acme.com',
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
// Delete
|
|
250
|
+
await client.customers.delete('cust_abc')
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
> **Tip:** Set `external_id` to your own system's user ID. This lets you look up a customer by your existing identifier without storing Monigo's UUID separately.
|
|
254
|
+
|
|
255
|
+
## Metrics
|
|
256
|
+
|
|
257
|
+
A metric defines what gets counted (e.g. "API calls", "GB stored") and how raw event values are aggregated.
|
|
258
|
+
|
|
259
|
+
```ts
|
|
260
|
+
import { Aggregation } from '@monigo/sdk'
|
|
261
|
+
|
|
262
|
+
// Count events
|
|
263
|
+
const metric = await client.metrics.create({
|
|
264
|
+
name: 'API Calls',
|
|
265
|
+
event_name: 'api_call',
|
|
266
|
+
aggregation: Aggregation.Count,
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
// Sum a numeric property (e.g. tokens used)
|
|
270
|
+
const metric = await client.metrics.create({
|
|
271
|
+
name: 'Tokens Used',
|
|
272
|
+
event_name: 'completion',
|
|
273
|
+
aggregation: Aggregation.Sum,
|
|
274
|
+
aggregation_property: 'tokens',
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
// List / get / update / delete
|
|
278
|
+
const { metrics } = await client.metrics.list()
|
|
279
|
+
const metric = await client.metrics.get('metric_abc')
|
|
280
|
+
const updated = await client.metrics.update('metric_abc', { name: 'Renamed' })
|
|
281
|
+
await client.metrics.delete('metric_abc')
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Aggregation constants
|
|
285
|
+
|
|
286
|
+
| Constant | Value | Description |
|
|
287
|
+
|----------|-------|-------------|
|
|
288
|
+
| `Aggregation.Count` | `count` | Count the number of matching events |
|
|
289
|
+
| `Aggregation.Sum` | `sum` | Sum a numeric property across events |
|
|
290
|
+
| `Aggregation.Max` | `max` | Maximum value of a property |
|
|
291
|
+
| `Aggregation.Min` | `minimum` | Minimum value of a property |
|
|
292
|
+
| `Aggregation.Average` | `average` | Average value of a property |
|
|
293
|
+
| `Aggregation.Unique` | `unique` | Count distinct values of a property |
|
|
294
|
+
|
|
295
|
+
## Plans
|
|
296
|
+
|
|
297
|
+
A plan combines billing period, currency, and one or more prices. Each price links a metric to a pricing model.
|
|
298
|
+
|
|
299
|
+
```ts
|
|
300
|
+
import { BillingPeriod, PricingModel, PlanType } from '@monigo/sdk'
|
|
301
|
+
|
|
302
|
+
// Flat rate: ₦50 per API call
|
|
303
|
+
const plan = await client.plans.create({
|
|
304
|
+
name: 'Starter',
|
|
305
|
+
currency: 'NGN',
|
|
306
|
+
billing_period: BillingPeriod.Monthly,
|
|
307
|
+
prices: [
|
|
308
|
+
{
|
|
309
|
+
metric_id: 'metric_api_calls',
|
|
310
|
+
model: PricingModel.Flat,
|
|
311
|
+
unit_price: '50.000000',
|
|
312
|
+
},
|
|
313
|
+
],
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
// Tiered pricing
|
|
317
|
+
const plan = await client.plans.create({
|
|
318
|
+
name: 'Growth',
|
|
319
|
+
currency: 'NGN',
|
|
320
|
+
billing_period: BillingPeriod.Monthly,
|
|
321
|
+
prices: [
|
|
322
|
+
{
|
|
323
|
+
metric_id: 'metric_api_calls',
|
|
324
|
+
model: PricingModel.Tiered,
|
|
325
|
+
tiers: [
|
|
326
|
+
{ up_to: 10_000, unit_amount: '5.00' },
|
|
327
|
+
{ up_to: 100_000, unit_amount: '3.00' },
|
|
328
|
+
{ up_to: null, unit_amount: '1.50' }, // null = infinity
|
|
329
|
+
],
|
|
330
|
+
},
|
|
331
|
+
],
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
// List / get / update / delete
|
|
335
|
+
const { plans } = await client.plans.list()
|
|
336
|
+
const plan = await client.plans.get('plan_abc')
|
|
337
|
+
const updated = await client.plans.update('plan_abc', { name: 'Renamed' })
|
|
338
|
+
await client.plans.delete('plan_abc')
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### Pricing model constants
|
|
342
|
+
|
|
343
|
+
| Constant | Value | Description |
|
|
344
|
+
|----------|-------|-------------|
|
|
345
|
+
| `PricingModel.Flat` | `flat` | Fixed price per unit |
|
|
346
|
+
| `PricingModel.Tiered` | `tiered` | Graduated tiers — each unit priced at its tier |
|
|
347
|
+
| `PricingModel.Volume` | `volume` | Whole volume priced at one tier |
|
|
348
|
+
| `PricingModel.Package` | `package` | Price per block of N units |
|
|
349
|
+
| `PricingModel.Overage` | `overage` | Base allowance + per-unit above threshold |
|
|
350
|
+
| `PricingModel.WeightedTiered` | `weighted_tiered` | Average price across graduated tiers |
|
|
351
|
+
|
|
352
|
+
### Plan type and billing period constants
|
|
353
|
+
|
|
354
|
+
| Constant | Value | Meaning |
|
|
355
|
+
|----------|-------|---------|
|
|
356
|
+
| `PlanType.Collection` | `collection` | Charge customers (default) |
|
|
357
|
+
| `PlanType.Payout` | `payout` | Pay out to vendors |
|
|
358
|
+
| `BillingPeriod.Daily` | `daily` | Billed every day |
|
|
359
|
+
| `BillingPeriod.Weekly` | `weekly` | Billed every week |
|
|
360
|
+
| `BillingPeriod.Monthly` | `monthly` | Billed every month |
|
|
361
|
+
| `BillingPeriod.Quarterly` | `quarterly` | Billed every quarter |
|
|
362
|
+
| `BillingPeriod.Annually` | `annually` | Billed every year |
|
|
363
|
+
|
|
364
|
+
## Subscriptions
|
|
365
|
+
|
|
366
|
+
A subscription links a customer to a plan and tracks the current billing period.
|
|
367
|
+
|
|
368
|
+
```ts
|
|
369
|
+
import { SubscriptionStatus } from '@monigo/sdk'
|
|
370
|
+
|
|
371
|
+
// Create
|
|
372
|
+
const sub = await client.subscriptions.create({
|
|
373
|
+
customer_id: 'cust_abc',
|
|
374
|
+
plan_id: 'plan_starter',
|
|
375
|
+
})
|
|
376
|
+
|
|
377
|
+
// List with filters
|
|
378
|
+
const { subscriptions } = await client.subscriptions.list({
|
|
379
|
+
customer_id: 'cust_abc',
|
|
380
|
+
status: SubscriptionStatus.Active,
|
|
381
|
+
})
|
|
382
|
+
|
|
383
|
+
// Get
|
|
384
|
+
const sub = await client.subscriptions.get('sub_abc')
|
|
385
|
+
|
|
386
|
+
// Pause / resume
|
|
387
|
+
await client.subscriptions.updateStatus('sub_abc', SubscriptionStatus.Paused)
|
|
388
|
+
await client.subscriptions.updateStatus('sub_abc', SubscriptionStatus.Active)
|
|
389
|
+
|
|
390
|
+
// Cancel (soft-delete)
|
|
391
|
+
await client.subscriptions.delete('sub_abc')
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
| Status constant | Value | Meaning |
|
|
395
|
+
|-----------------|-------|---------|
|
|
396
|
+
| `SubscriptionStatus.Active` | `active` | Billing is running |
|
|
397
|
+
| `SubscriptionStatus.Paused` | `paused` | Billing paused; events still accepted |
|
|
398
|
+
| `SubscriptionStatus.Canceled` | `canceled` | Subscription ended |
|
|
399
|
+
|
|
400
|
+
> **Warning:** A customer can hold at most one active subscription per plan type. A second active **collection** or **payout** subscription returns a 409 — catch it with `MonigoAPIError.isConflict(err)`.
|
|
401
|
+
|
|
402
|
+
## Payout accounts
|
|
403
|
+
|
|
404
|
+
Payout accounts are bank or mobile-money accounts that a customer can receive payouts to. All methods require a `customerId` as the first argument.
|
|
405
|
+
|
|
406
|
+
```ts
|
|
407
|
+
import { PayoutMethod } from '@monigo/sdk'
|
|
408
|
+
|
|
409
|
+
// Bank transfer
|
|
410
|
+
const account = await client.payoutAccounts.create('cust_abc', {
|
|
411
|
+
account_name: 'John Driver',
|
|
412
|
+
payout_method: PayoutMethod.BankTransfer,
|
|
413
|
+
bank_name: 'First Bank Nigeria',
|
|
414
|
+
bank_code: '011',
|
|
415
|
+
account_number: '3001234567',
|
|
416
|
+
currency: 'NGN',
|
|
417
|
+
is_default: true,
|
|
418
|
+
})
|
|
419
|
+
|
|
420
|
+
// Mobile money
|
|
421
|
+
const account = await client.payoutAccounts.create('cust_abc', {
|
|
422
|
+
account_name: 'John Driver',
|
|
423
|
+
payout_method: PayoutMethod.MobileMoney,
|
|
424
|
+
mobile_money_number: '+2348012345678',
|
|
425
|
+
currency: 'NGN',
|
|
426
|
+
})
|
|
427
|
+
|
|
428
|
+
// List all accounts for a customer
|
|
429
|
+
const { payout_accounts } = await client.payoutAccounts.list('cust_abc')
|
|
430
|
+
|
|
431
|
+
// Get / update / delete
|
|
432
|
+
const acct = await client.payoutAccounts.get('cust_abc', 'acct_xyz')
|
|
433
|
+
await client.payoutAccounts.update('cust_abc', 'acct_xyz', { account_name: 'Jane Driver' })
|
|
434
|
+
await client.payoutAccounts.delete('cust_abc', 'acct_xyz')
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
| Method constant | Value |
|
|
438
|
+
|-----------------|-------|
|
|
439
|
+
| `PayoutMethod.BankTransfer` | `bank_transfer` |
|
|
440
|
+
| `PayoutMethod.MobileMoney` | `mobile_money` |
|
|
441
|
+
|
|
442
|
+
## Invoices
|
|
443
|
+
|
|
444
|
+
Invoices are generated from subscriptions and contain line items derived from the customer's usage in the billing period.
|
|
445
|
+
|
|
446
|
+
```ts
|
|
447
|
+
import { InvoiceStatus } from '@monigo/sdk'
|
|
448
|
+
|
|
449
|
+
// Generate a draft invoice
|
|
450
|
+
const invoice = await client.invoices.generate('sub_abc')
|
|
451
|
+
console.log(`Draft invoice ${invoice.id} — total: ${invoice.total} ${invoice.currency}`)
|
|
452
|
+
|
|
453
|
+
for (const item of invoice.line_items ?? []) {
|
|
454
|
+
console.log(` ${item.description}: qty ${item.quantity} × ${item.unit_price} = ${item.amount}`)
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// List with filters
|
|
458
|
+
const { invoices } = await client.invoices.list({
|
|
459
|
+
customer_id: 'cust_abc',
|
|
460
|
+
status: InvoiceStatus.Draft,
|
|
461
|
+
})
|
|
462
|
+
|
|
463
|
+
// Get / finalize / void
|
|
464
|
+
const invoice = await client.invoices.get('inv_abc')
|
|
465
|
+
const finalized = await client.invoices.finalize('inv_abc')
|
|
466
|
+
const voided = await client.invoices.void('inv_abc')
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
| Status constant | Value | Meaning |
|
|
470
|
+
|-----------------|-------|---------|
|
|
471
|
+
| `InvoiceStatus.Draft` | `draft` | Not yet finalized; amounts may change |
|
|
472
|
+
| `InvoiceStatus.Finalized` | `finalized` | Locked and ready for payment |
|
|
473
|
+
| `InvoiceStatus.Paid` | `paid` | Payment collected |
|
|
474
|
+
| `InvoiceStatus.Void` | `void` | Cancelled |
|
|
475
|
+
|
|
476
|
+
> **Note:** All monetary amounts (`subtotal`, `total`, `unit_price`) are returned as decimal strings (e.g. `"1500.00"`) to preserve precision across currencies.
|
|
477
|
+
|
|
478
|
+
## Usage
|
|
479
|
+
|
|
480
|
+
Query aggregated usage rollups to see how much a customer has consumed in a period.
|
|
481
|
+
|
|
482
|
+
```ts
|
|
483
|
+
// All usage for the current period
|
|
484
|
+
const result = await client.usage.query()
|
|
485
|
+
|
|
486
|
+
// Filter by customer and metric
|
|
487
|
+
const result = await client.usage.query({
|
|
488
|
+
customer_id: 'cust_abc',
|
|
489
|
+
metric_id: 'metric_api_calls',
|
|
490
|
+
})
|
|
491
|
+
|
|
492
|
+
// Custom time window — accepts Date objects or ISO strings
|
|
493
|
+
const result = await client.usage.query({
|
|
494
|
+
customer_id: 'cust_abc',
|
|
495
|
+
from: new Date('2026-01-01'),
|
|
496
|
+
to: new Date('2026-01-31'),
|
|
497
|
+
})
|
|
498
|
+
|
|
499
|
+
for (const rollup of result.rollups) {
|
|
500
|
+
console.log(
|
|
501
|
+
`customer ${rollup.customer_id} — ${rollup.aggregation}: ${rollup.value}`,
|
|
502
|
+
`(period: ${rollup.period_start} → ${rollup.period_end})`
|
|
503
|
+
)
|
|
504
|
+
}
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
## Example programs
|
|
508
|
+
|
|
509
|
+
The SDK ships five runnable example programs under `examples/`:
|
|
510
|
+
|
|
511
|
+
| Program | What it demonstrates |
|
|
512
|
+
|---------|----------------------|
|
|
513
|
+
| `examples/quickstart` | End-to-end: customer → metric → plan → subscription → ingest events |
|
|
514
|
+
| `examples/metering` | High-volume batch ingest with idempotency and rate-limit handling |
|
|
515
|
+
| `examples/billing` | Generate, inspect, and finalize an invoice |
|
|
516
|
+
| `examples/payouts` | Register payout accounts and trigger event replay |
|
|
517
|
+
| `examples/usage-report` | Query rollups and print a terminal usage table |
|
|
518
|
+
|
|
519
|
+
Run any example by setting `MONIGO_API_KEY` and executing from the `examples/` directory:
|
|
520
|
+
|
|
521
|
+
```bash
|
|
522
|
+
cd js-sdk/examples
|
|
523
|
+
npm install
|
|
524
|
+
MONIGO_API_KEY=mk_test_... npm run quickstart
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
## TypeScript exports
|
|
528
|
+
|
|
529
|
+
All types and constants are exported from the root:
|
|
530
|
+
|
|
531
|
+
```ts
|
|
532
|
+
import {
|
|
533
|
+
MonigoClient,
|
|
534
|
+
MonigoAPIError,
|
|
535
|
+
|
|
536
|
+
// Constants
|
|
537
|
+
Aggregation,
|
|
538
|
+
PricingModel,
|
|
539
|
+
PlanType,
|
|
540
|
+
BillingPeriod,
|
|
541
|
+
SubscriptionStatus,
|
|
542
|
+
InvoiceStatus,
|
|
543
|
+
PayoutMethod,
|
|
544
|
+
|
|
545
|
+
// Types
|
|
546
|
+
type Customer,
|
|
547
|
+
type CreateCustomerRequest,
|
|
548
|
+
type UpdateCustomerRequest,
|
|
549
|
+
type ListCustomersResponse,
|
|
550
|
+
type Metric,
|
|
551
|
+
type CreateMetricRequest,
|
|
552
|
+
type Plan,
|
|
553
|
+
type CreatePlanRequest,
|
|
554
|
+
type CreatePriceRequest,
|
|
555
|
+
type PriceTier,
|
|
556
|
+
type Subscription,
|
|
557
|
+
type CreateSubscriptionRequest,
|
|
558
|
+
type ListSubscriptionsParams,
|
|
559
|
+
type PayoutAccount,
|
|
560
|
+
type CreatePayoutAccountRequest,
|
|
561
|
+
type Invoice,
|
|
562
|
+
type InvoiceLineItem,
|
|
563
|
+
type ListInvoicesParams,
|
|
564
|
+
type IngestEvent,
|
|
565
|
+
type IngestRequest,
|
|
566
|
+
type IngestResponse,
|
|
567
|
+
type EventReplayJob,
|
|
568
|
+
type UsageRollup,
|
|
569
|
+
type UsageQueryParams,
|
|
570
|
+
type UsageQueryResult,
|
|
571
|
+
} from '@monigo/sdk'
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
## Module format
|
|
575
|
+
|
|
576
|
+
The package ships dual-format bundles to work everywhere:
|
|
577
|
+
|
|
578
|
+
| Format | File | Used by |
|
|
579
|
+
|--------|------|---------|
|
|
580
|
+
| ESM | `dist/monigo.js` | Node.js ≥ 12 (with `"type":"module"`), bundlers, browsers |
|
|
581
|
+
| CJS | `dist/monigo.cjs` | Node.js CommonJS (`require()`) |
|
|
582
|
+
| Types | `dist/index.d.ts` | TypeScript consumers (both formats) |
|
|
583
|
+
|
|
584
|
+
The `exports` map in `package.json` routes each environment automatically — no manual import paths needed.
|
|
585
|
+
|
|
586
|
+
## Browser / CDN usage
|
|
587
|
+
|
|
588
|
+
```html
|
|
589
|
+
<script type="module">
|
|
590
|
+
import { MonigoClient } from 'https://esm.sh/@monigo/sdk'
|
|
591
|
+
|
|
592
|
+
const client = new MonigoClient({ apiKey: 'mk_test_...' })
|
|
593
|
+
const { customers } = await client.customers.list()
|
|
594
|
+
console.log(customers)
|
|
595
|
+
</script>
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
> **Warning:** Never embed a live API key in frontend code. For browser usage, proxy requests through your own backend or use a restricted publishable key if supported.
|
|
599
|
+
|
|
600
|
+
## Development
|
|
601
|
+
|
|
602
|
+
### Prerequisites
|
|
603
|
+
|
|
604
|
+
- Node.js ≥ 18
|
|
605
|
+
- npm
|
|
606
|
+
|
|
607
|
+
### Setup
|
|
608
|
+
|
|
609
|
+
```bash
|
|
610
|
+
cd js-sdk
|
|
611
|
+
npm install
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
### Build
|
|
615
|
+
|
|
616
|
+
```bash
|
|
617
|
+
npm run build
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
Produces `dist/monigo.js` (ESM), `dist/monigo.cjs` (CJS), and `dist/index.d.ts`.
|
|
621
|
+
|
|
622
|
+
### Run tests
|
|
623
|
+
|
|
624
|
+
```bash
|
|
625
|
+
npm run test:run # run all 90 tests once
|
|
626
|
+
npm run test # watch mode
|
|
627
|
+
npm run test:coverage # with V8 coverage report
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
### Type check
|
|
631
|
+
|
|
632
|
+
```bash
|
|
633
|
+
npm run typecheck
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
## Publishing
|
|
637
|
+
|
|
638
|
+
The release workflow builds, tests, bumps the version, tags the commit, and publishes to npm in one command:
|
|
639
|
+
|
|
640
|
+
```bash
|
|
641
|
+
# Patch release (1.0.0 → 1.0.1) — bug fixes
|
|
642
|
+
npm run release:patch
|
|
643
|
+
|
|
644
|
+
# Minor release (1.0.0 → 1.1.0) — new features, backwards-compatible
|
|
645
|
+
npm run release:minor
|
|
646
|
+
|
|
647
|
+
# Major release (1.0.0 → 2.0.0) — breaking changes
|
|
648
|
+
npm run release:major
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
Each command runs:
|
|
652
|
+
1. `npm run build` — compile TypeScript and bundle
|
|
653
|
+
2. `npm run test:run` — run the full test suite
|
|
654
|
+
3. `npm version <patch|minor|major>` — bump `package.json` and create a git tag
|
|
655
|
+
4. `git push --follow-tags` — push the commit and tag to the remote
|
|
656
|
+
5. `npm publish --access public` — publish to the npm registry
|
|
657
|
+
|
|
658
|
+
### CI/CD
|
|
659
|
+
|
|
660
|
+
Add this GitHub Actions workflow to publish automatically on a tag push:
|
|
661
|
+
|
|
662
|
+
```yaml
|
|
663
|
+
# .github/workflows/publish.yml
|
|
664
|
+
name: Publish to npm
|
|
665
|
+
|
|
666
|
+
on:
|
|
667
|
+
push:
|
|
668
|
+
tags:
|
|
669
|
+
- 'js-sdk/v*'
|
|
670
|
+
|
|
671
|
+
jobs:
|
|
672
|
+
publish:
|
|
673
|
+
runs-on: ubuntu-latest
|
|
674
|
+
steps:
|
|
675
|
+
- uses: actions/checkout@v4
|
|
676
|
+
- uses: actions/setup-node@v4
|
|
677
|
+
with:
|
|
678
|
+
node-version: '22'
|
|
679
|
+
registry-url: 'https://registry.npmjs.org'
|
|
680
|
+
- run: npm ci
|
|
681
|
+
working-directory: js-sdk
|
|
682
|
+
- run: npm run build
|
|
683
|
+
working-directory: js-sdk
|
|
684
|
+
- run: npm run test:run
|
|
685
|
+
working-directory: js-sdk
|
|
686
|
+
- run: npm publish --access public
|
|
687
|
+
working-directory: js-sdk
|
|
688
|
+
env:
|
|
689
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
## Versioning
|
|
693
|
+
|
|
694
|
+
This SDK follows [Semantic Versioning](https://semver.org/):
|
|
695
|
+
|
|
696
|
+
- **Patch** (`x.y.Z`) — bug fixes, internal improvements, no API changes
|
|
697
|
+
- **Minor** (`x.Y.0`) — new methods or options, fully backwards-compatible
|
|
698
|
+
- **Major** (`X.0.0`) — breaking changes to the public API surface
|
|
699
|
+
|
|
700
|
+
## Changelog
|
|
701
|
+
|
|
702
|
+
### 1.0.0 (2026-02-22)
|
|
703
|
+
|
|
704
|
+
- Initial release
|
|
705
|
+
- Full coverage of all 9 Monigo API resource groups: Events, Customers, Metrics, Plans, Subscriptions, Payout Accounts, Invoices, Usage
|
|
706
|
+
- Dual ESM + CJS build via Vite library mode
|
|
707
|
+
- 90 unit tests with zero external dependencies
|
|
708
|
+
- TypeScript types and constant objects for all enum values
|
|
709
|
+
|
|
710
|
+
## License
|
|
711
|
+
|
|
712
|
+
MIT
|