meterflow 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -23,6 +23,7 @@ Track what your customers use, enforce credit balances, and manage subscriptions
23
23
  - [Usage events](#usage-events)
24
24
  - [Subscriptions](#subscriptions)
25
25
  - [Plans](#plans)
26
+ - [Webhook receipts](#webhook-receipts)
26
27
  - [Entitlements — ask before you act](#entitlements--ask-before-you-act)
27
28
  - [Idempotency — safe retries for writes](#idempotency--safe-retries-for-writes)
28
29
  - [Automatic retries](#automatic-retries)
@@ -177,15 +178,26 @@ bal.total_granted; // "5250.000000"
177
178
  bal.total_consumed; // "1705.000000"
178
179
  ```
179
180
 
180
- **Transactions** — the customer's full history, newest first, in one response:
181
+ **Transactions** — the customer's history, newest first, **a page at a time**:
181
182
 
182
183
  ```typescript
183
- const txns = await client.credits.transactions("customer_123");
184
- for (const t of txns) {
184
+ const page = await client.credits.transactions("customer_123"); // 50 rows by default
185
+ for (const t of page.data) {
185
186
  console.log(t.transaction_type, t.amount, t.balance_after, t.description, t.created_at);
186
187
  }
188
+
189
+ // The whole history, page by page. `next_cursor` is null once there is nothing after it.
190
+ let cursor: string | undefined;
191
+ do {
192
+ const page = await client.credits.transactions("customer_123", { limit: 200, cursor });
193
+ handle(page.data);
194
+ cursor = page.next_cursor ?? undefined;
195
+ } while (cursor);
187
196
  ```
188
197
 
198
+ > **The cursor is opaque.** Pass back exactly what `next_cursor` gave you; do not build one, and do
199
+ > not read anything out of it. `limit` is 1–200 and defaults to 50.
200
+
189
201
  > **Amounts are decimal strings.** Balances and amounts come back as strings (`"3545.000000"`) to avoid floating-point drift on money-like values. Parse deliberately (`Number(...)` or a decimal library) when you need arithmetic.
190
202
 
191
203
  ### Usage events
@@ -198,7 +210,7 @@ Record an event every time a customer does the thing you charge for. Events are
198
210
  await client.usage.record({
199
211
  event_name: "images.generated",
200
212
  customer_external_id: "customer_123",
201
- value: 1, // what the meter aggregates: 1 for counts, seconds/MB/etc. for sums
213
+ value: 1, // optional — what the meter aggregates: 1 for counts, seconds/MB/etc. for sums
202
214
  properties: {}, // free-form context; {} when unused
203
215
  timestamp: new Date().toISOString(), // optional — defaults to arrival time on the server
204
216
  });
@@ -254,7 +266,8 @@ This is usually the difference between fitting a plan and outgrowing it: 1,000 u
254
266
 
255
267
  Two things to keep in mind:
256
268
 
257
- - **It only helps a `sum` meter.** A `count` meter ignores `value` and counts occurrences, so there each occurrence has to be its own event. `max`, `min` and `unique_count` read `value` as a measurement, not a quantity to add.
269
+ - **It only helps a `sum` meter.** A `count` meter ignores `value` and counts occurrences, so there each occurrence has to be its own event. `max` and `min` read `value` as a measurement, not a quantity to add, and `unique_count` does not read it at all — it counts distinct values of the property its meter names.
270
+ - **`value` is optional.** Leave it out and MeterFlow fills it in: for a `sum`, `max` or `min` meter that names an `aggregation_field`, the numeric value of that property on the event; otherwise `1`. So the event above can be written `properties: { tokens: 1300 }` with no `value`, as long as the meter's `aggregation_field` is `tokens`. An explicit `value` always wins, a property that is absent or is not a number falls through to `1`, and a negative one is refused with a `422`.
258
271
  - **Billing is unchanged.** A metered plan limit charges per unit — the event's `value` — so 100 units cost the same whether they arrived as 100 events or one. You are not rounding anyone's bill by batching.
259
272
 
260
273
  **Summary** — a customer's usage, broken down by meter:
@@ -295,11 +308,16 @@ console.log(sub.status); // "trialing" if the plan has trial days, else "active"
295
308
  **List / get:**
296
309
 
297
310
  ```typescript
298
- const all = await client.subscriptions.list(); // whole project (in your key's environment)
311
+ const page = await client.subscriptions.list(); // whole project (in your key's environment)
299
312
  const theirs = await client.subscriptions.list({ customer_id: "customer_123" }); // one customer
313
+ const more = await client.subscriptions.list({ limit: 200, cursor: page.next_cursor ?? undefined });
300
314
  const one = await client.subscriptions.get(sub.id);
315
+
316
+ page.data.forEach((subscription) => console.log(subscription.customer_external_id, subscription.status));
301
317
  ```
302
318
 
319
+ > **`list()` answers a page**, `{ data, next_cursor }` — `get()` still answers one subscription.
320
+
303
321
  > Like all reads, these are scoped to the key's environment: a live key lists live subscriptions only, a test key test ones only.
304
322
 
305
323
  **Update** — change status or metadata. Statuses: `active`, `trialing`, `past_due`, `paused`, `canceled`, `expired`:
@@ -332,6 +350,27 @@ plan.meter_limits; // included units + overage rate per meter
332
350
 
333
351
  Typical use: render your pricing page or signup flow from `plans.list()` so it can never drift from what billing actually enforces.
334
352
 
353
+ ### Webhook receipts
354
+
355
+ Webhooks are **read-only** through the SDK too: endpoints are registered in the dashboard, where the signing secret is shown once and never again. What the SDK gives you is the other half — which endpoints exist, and what MeterFlow actually sent them.
356
+
357
+ ```typescript
358
+ const endpoints = await client.webhooks.list(); // this project's endpoints
359
+ const receipts = await client.webhooks.deliveries(endpoint_id); // newest first
360
+ receipts[0].event_type; // "usage.recorded"
361
+ receipts[0].status; // "pending" | "retrying" | "success" | "failed"
362
+ receipts[0].response_code; // what your server answered — 200, 500, or null if it never answered
363
+ receipts[0].attempts; // how many times we have tried
364
+ receipts[0].next_retry_at; // when we will try again, or null
365
+ receipts[0].payload; // exactly what we sent
366
+ ```
367
+
368
+ Typical use: answering "did that event reach my endpoint?" from your own tooling instead of opening the dashboard — and reading `response_body` when the answer is no.
369
+
370
+ Two things to know. The list is capped at the **most recent 100** attempts per endpoint; it is a debugging surface, not an export. And it follows your key: a `mf_test_*` key reads test receipts, a `mf_live_*` key live ones, while the endpoint list itself is shared by both worlds, because an endpoint is configuration and receives events from each.
371
+
372
+ > This is not the same as [verifying webhooks](#verifying-webhooks) below. That proves a request your server just received really came from us; this reads what we sent.
373
+
335
374
  ### Entitlements — ask before you act
336
375
 
337
376
  The one call to make **before** doing work for a customer: "may they use this feature — and how much is left?" A **hard** limit says no here; recording usage afterwards never blocks, so an app that skips this call gets an honest ledger but no enforcement.
@@ -340,7 +379,7 @@ The one call to make **before** doing work for a customer: "may they use this fe
340
379
  const gate = await client.entitlements.check("customer_123", "images.generated");
341
380
 
342
381
  if (!gate.allowed) {
343
- // gate.reason: "hard_limit_reached" | "no_subscription" | "not_in_plan" | "insufficient_credits"
382
+ // gate.reason: "hard_limit_reached" | "no_subscription" | "not_in_plan" | "insufficient_credits" | "spend_cap_reached"
344
383
  return showUpgradePrompt(gate); // gate.used / gate.included / gate.period_end are there for the copy
345
384
  }
346
385
 
@@ -364,6 +403,30 @@ const { entitlements, period_end } = await client.entitlements.get("customer_123
364
403
 
365
404
  Prefer to be told rather than to ask? Subscribe a webhook to `limit.reached` (see below).
366
405
 
406
+ ### The balance portal — a page you don't have to build
407
+
408
+ Mint a short-lived link to a MeterFlow-hosted page showing **one** customer's balance and ledger.
409
+ Call it from your server; send the `url` to that customer.
410
+
411
+ ```typescript
412
+ const link = await client.portal.createSession({
413
+ customer_external_id: "customer_123",
414
+ topup_url: "https://acme.test/billing", // your checkout — "buy more" goes here
415
+ return_url: "https://acme.test/app", // where "done" goes
416
+ expires_in_minutes: 60, // default 60, maximum 1440
417
+ });
418
+
419
+ sendEmail(customer.email, `Your balance: ${link.url}`);
420
+ ```
421
+
422
+ The token in that link reads **that one customer's** balance and ledger, in the environment of the
423
+ key you minted it with, and nothing else: it cannot write, cannot name another customer, and is
424
+ refused everywhere else in the API. It is never your API key — never send that to anyone.
425
+
426
+ > **MeterFlow never takes the payment.** `topup_url` is a link we carry and hand back to the page;
427
+ > what happens there is between you and your own payment provider. Grant the credits with
428
+ > `credits.grant()` when your checkout tells you it was paid.
429
+
367
430
  ## Idempotency — safe retries for writes
368
431
 
369
432
  Connections drop. When your app can't tell whether a write arrived, the correct move is to send it again **with the same idempotency key** — MeterFlow recognises the key and acts only once:
@@ -484,10 +547,12 @@ Every delivery is a flat JSON object (`Content-Type: application/json`) with two
484
547
  | `subscription.created` | a subscription is created | `customer_id`, `plan_id`, `subscription_id`, `status` |
485
548
  | `subscription.updated` | a subscription is updated | `customer_id`, `subscription_id`, `status` |
486
549
  | `limit.reached` | a customer first reaches a **hard** or **soft** limit's included units this billing period (once per meter per period; metered limits report through `credit.deducted` instead) | `customer_id`, `subscription_id`, `feature` (the meter's `event_name`), `meter_id`, `limit_type`, `included`, `used`, `period_start`, `period_end` |
487
- | `balance.low` | a deduction takes a customer's balance **down through** this endpoint's `low_balance_threshold` — a crossing, so it fires once and not again while they stay below. Needs the threshold set on the webhook; without one it never fires | `customer_id`, `balance`, `threshold`, `transaction_id` |
550
+ | `balance.low` | a spend takes a customer's balance **down through** this endpoint's `low_balance_threshold` — a crossing, so it fires once and not again while they stay below. Raised by both doors that spend: `credits.deduct` and a metered usage charge. **Both crossing events follow from the endpoint's `low_balance_threshold`: set one and the API adds them, clear it and the API removes them — so a line is the only thing to configure.** | `customer_id`, `balance`, `threshold`, `suggested_topup` (the subscription's `topup_amount`, `null` when unset), `transaction_id` |
488
551
  | `balance.exhausted` | a balance reaches zero, **or** a request is refused for want of credits (a deduction returning 402, or `entitlements.check` answering `insufficient_credits`). Unlike `balance.low` this repeats: every refused request is its own blocked request | `customer_id`, `balance`, `reason` (`balance_zero` / `insufficient_credits`), `requested_amount` and `feature` where they apply, `transaction_id` on the zero case |
489
- | `subscription.renewed` | a billing period rolls forward (the renewer; no `subscription.updated` is emitted for a renewal) | `customer_id`, `subscription_id`, `plan_id`, `current_period_start`, `current_period_end`, `credits_granted` |
552
+ | `subscription.renewed` | a billing period opens — either a paid period rolling forward or a **trial converting** to its first paid period, told apart by `reason` (the renewer emits no `subscription.updated` for either) | `customer_id`, `subscription_id`, `plan_id`, `status`, `reason` (`period_renewed` / `trial_converted`), `current_period_start`, `current_period_end`, `credits_granted` |
490
553
  | `subscription.canceled` | a subscription's status becomes canceled — fired **alongside** `subscription.updated`, once, on the call that actually cancels | `customer_id`, `subscription_id`, `plan_id`, `canceled_at` |
554
+ | `balance.topped_up` | a grant takes a customer's balance **up through** this endpoint's `low_balance_threshold` — the upward mirror of `balance.low`, on the same per-endpoint line, so the endpoint told "low" is the one told "recovered". Subscribed together with `balance.low` by setting the threshold, never on its own | `customer_id`, `balance`, `threshold`, `amount`, `transaction_id` |
555
+ | `cap.reached` | a customer's credit spending reaches their subscription's `spend_cap` for the period — once per subscription per period, whether they landed on the cap or were refused by it. A refusal that still leaves headroom is not a reach and does not fire | `customer_id`, `subscription_id`, `cap`, `spent`, `period_start`, `period_end` |
491
556
 
492
557
  For example, a `credit.granted` delivery:
493
558
 
@@ -557,6 +622,6 @@ await client.usage.record({
557
622
 
558
623
  // Support asks: "what's Ana's situation?"
559
624
  const balance = await client.credits.balance("ana");
560
- const history = await client.credits.transactions("ana");
625
+ const { data: history } = await client.credits.transactions("ana");
561
626
  const usage = await client.usage.summary("ana");
562
627
  ```
package/dist/client.d.ts CHANGED
@@ -2,7 +2,9 @@ import { CreditsResource } from "./resources/credits";
2
2
  import { UsageResource } from "./resources/usage";
3
3
  import { SubscriptionsResource } from "./resources/subscriptions";
4
4
  import { PlansResource } from "./resources/plans";
5
+ import { PortalResource } from "./resources/portal";
5
6
  import { EntitlementsResource } from "./resources/entitlements";
7
+ import { WebhooksResource } from "./resources/webhooks";
6
8
  export interface MeterFlowOptions {
7
9
  apiKey: string;
8
10
  baseUrl?: string;
@@ -22,13 +24,18 @@ export declare class MeterFlow {
22
24
  private _usage?;
23
25
  private _subscriptions?;
24
26
  private _plans?;
27
+ private _portal?;
25
28
  private _entitlements?;
29
+ private _webhooks?;
26
30
  constructor(options: MeterFlowOptions);
31
+ get portal(): PortalResource;
27
32
  get credits(): CreditsResource;
28
33
  get usage(): UsageResource;
29
34
  get subscriptions(): SubscriptionsResource;
30
35
  get plans(): PlansResource;
31
36
  get entitlements(): EntitlementsResource;
37
+ /** Read-only: the endpoints registered for this project, and what we sent them. */
38
+ get webhooks(): WebhooksResource;
32
39
  /** @internal — used by resource classes only; not part of the public API */
33
40
  request<T>(method: string, path: string, opts?: RequestOptions): Promise<T>;
34
41
  }
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAEhE,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACjC;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IAClD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,SAAS;IACpB,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IAE7C,OAAO,CAAC,QAAQ,CAAC,CAAkB;IACnC,OAAO,CAAC,MAAM,CAAC,CAAgB;IAC/B,OAAO,CAAC,cAAc,CAAC,CAAwB;IAC/C,OAAO,CAAC,MAAM,CAAC,CAAgB;IAC/B,OAAO,CAAC,aAAa,CAAC,CAAuB;gBAEjC,OAAO,EAAE,gBAAgB;IAerC,IAAI,OAAO,IAAI,eAAe,CAE7B;IAED,IAAI,KAAK,IAAI,aAAa,CAEzB;IAED,IAAI,aAAa,IAAI,qBAAqB,CAEzC;IAED,IAAI,KAAK,IAAI,aAAa,CAEzB;IAED,IAAI,YAAY,IAAI,oBAAoB,CAEvC;IAED,4EAA4E;IAC5E,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;CAG5E"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACjC;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IAClD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,SAAS;IACpB,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IAE7C,OAAO,CAAC,QAAQ,CAAC,CAAkB;IACnC,OAAO,CAAC,MAAM,CAAC,CAAgB;IAC/B,OAAO,CAAC,cAAc,CAAC,CAAwB;IAC/C,OAAO,CAAC,MAAM,CAAC,CAAgB;IAC/B,OAAO,CAAC,OAAO,CAAC,CAAiB;IACjC,OAAO,CAAC,aAAa,CAAC,CAAuB;IAC7C,OAAO,CAAC,SAAS,CAAC,CAAmB;gBAEzB,OAAO,EAAE,gBAAgB;IAerC,IAAI,MAAM,IAAI,cAAc,CAE3B;IAED,IAAI,OAAO,IAAI,eAAe,CAE7B;IAED,IAAI,KAAK,IAAI,aAAa,CAEzB;IAED,IAAI,aAAa,IAAI,qBAAqB,CAEzC;IAED,IAAI,KAAK,IAAI,aAAa,CAEzB;IAED,IAAI,YAAY,IAAI,oBAAoB,CAEvC;IAED,mFAAmF;IACnF,IAAI,QAAQ,IAAI,gBAAgB,CAE/B;IAED,4EAA4E;IAC5E,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;CAG5E"}
package/dist/index.cjs CHANGED
@@ -35,7 +35,7 @@ __export(index_exports, {
35
35
  module.exports = __toCommonJS(index_exports);
36
36
 
37
37
  // package.json
38
- var version = "0.6.0";
38
+ var version = "0.7.0";
39
39
 
40
40
  // src/errors.ts
41
41
  var MeterFlowError = class extends Error {
@@ -245,8 +245,17 @@ var CreditsResource = class {
245
245
  balance(customerId) {
246
246
  return this.client.request("GET", `credits/${encodeURIComponent(customerId)}/balance`);
247
247
  }
248
- transactions(customerId) {
249
- return this.client.request("GET", `credits/${encodeURIComponent(customerId)}/transactions`);
248
+ /**
249
+ * One page of the customer's ledger, newest first.
250
+ *
251
+ * The ledger grows with every charge, so it is read a page at a time: `data` holds the rows and
252
+ * `next_cursor` the mark for the page after this one (`null` on the last). Pass it back as
253
+ * `cursor` unchanged — it is opaque, and nothing but this API reads it.
254
+ */
255
+ transactions(customerId, query) {
256
+ return this.client.request("GET", `credits/${encodeURIComponent(customerId)}/transactions`, {
257
+ ...query !== void 0 && { query }
258
+ });
250
259
  }
251
260
  };
252
261
 
@@ -301,6 +310,12 @@ var SubscriptionsResource = class {
301
310
  ...opts?.idempotencyKey !== void 0 && { idempotencyKey: opts.idempotencyKey }
302
311
  });
303
312
  }
313
+ /**
314
+ * One page of the project's subscriptions in this key's environment, newest first.
315
+ *
316
+ * `data` holds the rows and `next_cursor` the mark for the page after this one (`null` on the
317
+ * last). Pass it back as `cursor` unchanged — it is opaque, and nothing but this API reads it.
318
+ */
304
319
  list(query) {
305
320
  return this.client.request("GET", "subscriptions", {
306
321
  ...query !== void 0 && { query }
@@ -337,6 +352,29 @@ var PlansResource = class {
337
352
  }
338
353
  };
339
354
 
355
+ // src/resources/portal.ts
356
+ var PortalResource = class {
357
+ constructor(client) {
358
+ this.client = client;
359
+ }
360
+ /**
361
+ * Mint a link to the hosted balance page for one of your customers.
362
+ *
363
+ * Call this from **your** server and send the `url` to that customer — it carries a short-lived
364
+ * token that can read their balance and their ledger and nothing else, in the environment of the
365
+ * key you called with. It is never your API key, and it can write nothing.
366
+ *
367
+ * The page shows the balance and sends "buy more" to your own checkout (`topup_url`): MeterFlow
368
+ * never takes the payment. `expires_in_minutes` defaults to 60 and may not exceed 1440.
369
+ *
370
+ * The two reads behind the page are **not** on this client by design: they belong to the person
371
+ * holding the link, and this client holds a secret key.
372
+ */
373
+ createSession(body) {
374
+ return this.client.request("POST", "portal/sessions", { body });
375
+ }
376
+ };
377
+
340
378
  // src/resources/entitlements.ts
341
379
  var EntitlementsResource = class {
342
380
  constructor(client) {
@@ -354,6 +392,28 @@ var EntitlementsResource = class {
354
392
  }
355
393
  };
356
394
 
395
+ // src/resources/webhooks.ts
396
+ var WebhooksResource = class {
397
+ constructor(client) {
398
+ this.client = client;
399
+ }
400
+ /** Every endpoint registered for the key's project. Never includes a secret. */
401
+ list() {
402
+ return this.client.request("GET", "webhooks");
403
+ }
404
+ /**
405
+ * One endpoint's delivery attempts, newest first: the payload sent, the HTTP status that came
406
+ * back, how many attempts and when the next retry is due.
407
+ *
408
+ * Capped at the most recent 100, and scoped to the key's own environment — a `mf_test_*` key
409
+ * reads test receipts, a `mf_live_*` key live ones. A webhook belonging to another project
410
+ * answers 404.
411
+ */
412
+ deliveries(webhookId) {
413
+ return this.client.request("GET", `webhooks/${encodeURIComponent(webhookId)}/deliveries`);
414
+ }
415
+ };
416
+
357
417
  // src/client.ts
358
418
  var MeterFlow = class {
359
419
  options;
@@ -361,7 +421,9 @@ var MeterFlow = class {
361
421
  _usage;
362
422
  _subscriptions;
363
423
  _plans;
424
+ _portal;
364
425
  _entitlements;
426
+ _webhooks;
365
427
  constructor(options) {
366
428
  if (!options.apiKey.startsWith("mf_live_") && !options.apiKey.startsWith("mf_test_")) {
367
429
  throw new Error("apiKey must start with mf_live_ or mf_test_");
@@ -376,6 +438,9 @@ var MeterFlow = class {
376
438
  fetch: options.fetch ?? globalThis.fetch.bind(globalThis)
377
439
  };
378
440
  }
441
+ get portal() {
442
+ return this._portal ??= new PortalResource(this);
443
+ }
379
444
  get credits() {
380
445
  return this._credits ??= new CreditsResource(this);
381
446
  }
@@ -391,6 +456,10 @@ var MeterFlow = class {
391
456
  get entitlements() {
392
457
  return this._entitlements ??= new EntitlementsResource(this);
393
458
  }
459
+ /** Read-only: the endpoints registered for this project, and what we sent them. */
460
+ get webhooks() {
461
+ return this._webhooks ??= new WebhooksResource(this);
462
+ }
394
463
  /** @internal — used by resource classes only; not part of the public API */
395
464
  request(method, path, opts) {
396
465
  return httpRequest(this.options, method, path, opts);
package/dist/index.js CHANGED
@@ -35,7 +35,7 @@ __export(index_exports, {
35
35
  module.exports = __toCommonJS(index_exports);
36
36
 
37
37
  // package.json
38
- var version = "0.6.0";
38
+ var version = "0.7.0";
39
39
 
40
40
  // src/errors.ts
41
41
  var MeterFlowError = class extends Error {
@@ -245,8 +245,17 @@ var CreditsResource = class {
245
245
  balance(customerId) {
246
246
  return this.client.request("GET", `credits/${encodeURIComponent(customerId)}/balance`);
247
247
  }
248
- transactions(customerId) {
249
- return this.client.request("GET", `credits/${encodeURIComponent(customerId)}/transactions`);
248
+ /**
249
+ * One page of the customer's ledger, newest first.
250
+ *
251
+ * The ledger grows with every charge, so it is read a page at a time: `data` holds the rows and
252
+ * `next_cursor` the mark for the page after this one (`null` on the last). Pass it back as
253
+ * `cursor` unchanged — it is opaque, and nothing but this API reads it.
254
+ */
255
+ transactions(customerId, query) {
256
+ return this.client.request("GET", `credits/${encodeURIComponent(customerId)}/transactions`, {
257
+ ...query !== void 0 && { query }
258
+ });
250
259
  }
251
260
  };
252
261
 
@@ -301,6 +310,12 @@ var SubscriptionsResource = class {
301
310
  ...opts?.idempotencyKey !== void 0 && { idempotencyKey: opts.idempotencyKey }
302
311
  });
303
312
  }
313
+ /**
314
+ * One page of the project's subscriptions in this key's environment, newest first.
315
+ *
316
+ * `data` holds the rows and `next_cursor` the mark for the page after this one (`null` on the
317
+ * last). Pass it back as `cursor` unchanged — it is opaque, and nothing but this API reads it.
318
+ */
304
319
  list(query) {
305
320
  return this.client.request("GET", "subscriptions", {
306
321
  ...query !== void 0 && { query }
@@ -337,6 +352,29 @@ var PlansResource = class {
337
352
  }
338
353
  };
339
354
 
355
+ // src/resources/portal.ts
356
+ var PortalResource = class {
357
+ constructor(client) {
358
+ this.client = client;
359
+ }
360
+ /**
361
+ * Mint a link to the hosted balance page for one of your customers.
362
+ *
363
+ * Call this from **your** server and send the `url` to that customer — it carries a short-lived
364
+ * token that can read their balance and their ledger and nothing else, in the environment of the
365
+ * key you called with. It is never your API key, and it can write nothing.
366
+ *
367
+ * The page shows the balance and sends "buy more" to your own checkout (`topup_url`): MeterFlow
368
+ * never takes the payment. `expires_in_minutes` defaults to 60 and may not exceed 1440.
369
+ *
370
+ * The two reads behind the page are **not** on this client by design: they belong to the person
371
+ * holding the link, and this client holds a secret key.
372
+ */
373
+ createSession(body) {
374
+ return this.client.request("POST", "portal/sessions", { body });
375
+ }
376
+ };
377
+
340
378
  // src/resources/entitlements.ts
341
379
  var EntitlementsResource = class {
342
380
  constructor(client) {
@@ -354,6 +392,28 @@ var EntitlementsResource = class {
354
392
  }
355
393
  };
356
394
 
395
+ // src/resources/webhooks.ts
396
+ var WebhooksResource = class {
397
+ constructor(client) {
398
+ this.client = client;
399
+ }
400
+ /** Every endpoint registered for the key's project. Never includes a secret. */
401
+ list() {
402
+ return this.client.request("GET", "webhooks");
403
+ }
404
+ /**
405
+ * One endpoint's delivery attempts, newest first: the payload sent, the HTTP status that came
406
+ * back, how many attempts and when the next retry is due.
407
+ *
408
+ * Capped at the most recent 100, and scoped to the key's own environment — a `mf_test_*` key
409
+ * reads test receipts, a `mf_live_*` key live ones. A webhook belonging to another project
410
+ * answers 404.
411
+ */
412
+ deliveries(webhookId) {
413
+ return this.client.request("GET", `webhooks/${encodeURIComponent(webhookId)}/deliveries`);
414
+ }
415
+ };
416
+
357
417
  // src/client.ts
358
418
  var MeterFlow = class {
359
419
  options;
@@ -361,7 +421,9 @@ var MeterFlow = class {
361
421
  _usage;
362
422
  _subscriptions;
363
423
  _plans;
424
+ _portal;
364
425
  _entitlements;
426
+ _webhooks;
365
427
  constructor(options) {
366
428
  if (!options.apiKey.startsWith("mf_live_") && !options.apiKey.startsWith("mf_test_")) {
367
429
  throw new Error("apiKey must start with mf_live_ or mf_test_");
@@ -376,6 +438,9 @@ var MeterFlow = class {
376
438
  fetch: options.fetch ?? globalThis.fetch.bind(globalThis)
377
439
  };
378
440
  }
441
+ get portal() {
442
+ return this._portal ??= new PortalResource(this);
443
+ }
379
444
  get credits() {
380
445
  return this._credits ??= new CreditsResource(this);
381
446
  }
@@ -391,6 +456,10 @@ var MeterFlow = class {
391
456
  get entitlements() {
392
457
  return this._entitlements ??= new EntitlementsResource(this);
393
458
  }
459
+ /** Read-only: the endpoints registered for this project, and what we sent them. */
460
+ get webhooks() {
461
+ return this._webhooks ??= new WebhooksResource(this);
462
+ }
394
463
  /** @internal — used by resource classes only; not part of the public API */
395
464
  request(method, path, opts) {
396
465
  return httpRequest(this.options, method, path, opts);
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // package.json
2
- var version = "0.6.0";
2
+ var version = "0.7.0";
3
3
 
4
4
  // src/errors.ts
5
5
  var MeterFlowError = class extends Error {
@@ -209,8 +209,17 @@ var CreditsResource = class {
209
209
  balance(customerId) {
210
210
  return this.client.request("GET", `credits/${encodeURIComponent(customerId)}/balance`);
211
211
  }
212
- transactions(customerId) {
213
- return this.client.request("GET", `credits/${encodeURIComponent(customerId)}/transactions`);
212
+ /**
213
+ * One page of the customer's ledger, newest first.
214
+ *
215
+ * The ledger grows with every charge, so it is read a page at a time: `data` holds the rows and
216
+ * `next_cursor` the mark for the page after this one (`null` on the last). Pass it back as
217
+ * `cursor` unchanged — it is opaque, and nothing but this API reads it.
218
+ */
219
+ transactions(customerId, query) {
220
+ return this.client.request("GET", `credits/${encodeURIComponent(customerId)}/transactions`, {
221
+ ...query !== void 0 && { query }
222
+ });
214
223
  }
215
224
  };
216
225
 
@@ -265,6 +274,12 @@ var SubscriptionsResource = class {
265
274
  ...opts?.idempotencyKey !== void 0 && { idempotencyKey: opts.idempotencyKey }
266
275
  });
267
276
  }
277
+ /**
278
+ * One page of the project's subscriptions in this key's environment, newest first.
279
+ *
280
+ * `data` holds the rows and `next_cursor` the mark for the page after this one (`null` on the
281
+ * last). Pass it back as `cursor` unchanged — it is opaque, and nothing but this API reads it.
282
+ */
268
283
  list(query) {
269
284
  return this.client.request("GET", "subscriptions", {
270
285
  ...query !== void 0 && { query }
@@ -301,6 +316,29 @@ var PlansResource = class {
301
316
  }
302
317
  };
303
318
 
319
+ // src/resources/portal.ts
320
+ var PortalResource = class {
321
+ constructor(client) {
322
+ this.client = client;
323
+ }
324
+ /**
325
+ * Mint a link to the hosted balance page for one of your customers.
326
+ *
327
+ * Call this from **your** server and send the `url` to that customer — it carries a short-lived
328
+ * token that can read their balance and their ledger and nothing else, in the environment of the
329
+ * key you called with. It is never your API key, and it can write nothing.
330
+ *
331
+ * The page shows the balance and sends "buy more" to your own checkout (`topup_url`): MeterFlow
332
+ * never takes the payment. `expires_in_minutes` defaults to 60 and may not exceed 1440.
333
+ *
334
+ * The two reads behind the page are **not** on this client by design: they belong to the person
335
+ * holding the link, and this client holds a secret key.
336
+ */
337
+ createSession(body) {
338
+ return this.client.request("POST", "portal/sessions", { body });
339
+ }
340
+ };
341
+
304
342
  // src/resources/entitlements.ts
305
343
  var EntitlementsResource = class {
306
344
  constructor(client) {
@@ -318,6 +356,28 @@ var EntitlementsResource = class {
318
356
  }
319
357
  };
320
358
 
359
+ // src/resources/webhooks.ts
360
+ var WebhooksResource = class {
361
+ constructor(client) {
362
+ this.client = client;
363
+ }
364
+ /** Every endpoint registered for the key's project. Never includes a secret. */
365
+ list() {
366
+ return this.client.request("GET", "webhooks");
367
+ }
368
+ /**
369
+ * One endpoint's delivery attempts, newest first: the payload sent, the HTTP status that came
370
+ * back, how many attempts and when the next retry is due.
371
+ *
372
+ * Capped at the most recent 100, and scoped to the key's own environment — a `mf_test_*` key
373
+ * reads test receipts, a `mf_live_*` key live ones. A webhook belonging to another project
374
+ * answers 404.
375
+ */
376
+ deliveries(webhookId) {
377
+ return this.client.request("GET", `webhooks/${encodeURIComponent(webhookId)}/deliveries`);
378
+ }
379
+ };
380
+
321
381
  // src/client.ts
322
382
  var MeterFlow = class {
323
383
  options;
@@ -325,7 +385,9 @@ var MeterFlow = class {
325
385
  _usage;
326
386
  _subscriptions;
327
387
  _plans;
388
+ _portal;
328
389
  _entitlements;
390
+ _webhooks;
329
391
  constructor(options) {
330
392
  if (!options.apiKey.startsWith("mf_live_") && !options.apiKey.startsWith("mf_test_")) {
331
393
  throw new Error("apiKey must start with mf_live_ or mf_test_");
@@ -340,6 +402,9 @@ var MeterFlow = class {
340
402
  fetch: options.fetch ?? globalThis.fetch.bind(globalThis)
341
403
  };
342
404
  }
405
+ get portal() {
406
+ return this._portal ??= new PortalResource(this);
407
+ }
343
408
  get credits() {
344
409
  return this._credits ??= new CreditsResource(this);
345
410
  }
@@ -355,6 +420,10 @@ var MeterFlow = class {
355
420
  get entitlements() {
356
421
  return this._entitlements ??= new EntitlementsResource(this);
357
422
  }
423
+ /** Read-only: the endpoints registered for this project, and what we sent them. */
424
+ get webhooks() {
425
+ return this._webhooks ??= new WebhooksResource(this);
426
+ }
358
427
  /** @internal — used by resource classes only; not part of the public API */
359
428
  request(method, path, opts) {
360
429
  return httpRequest(this.options, method, path, opts);
@@ -3,6 +3,7 @@ import type { components } from "../types/openapi";
3
3
  type CreditGrantRequest = components["schemas"]["CreditGrantRequest"];
4
4
  type CreditDeductRequest = components["schemas"]["CreditDeductRequest"];
5
5
  type CreditTransactionResponse = components["schemas"]["CreditTransactionResponse"];
6
+ type CreditTransactionPage = components["schemas"]["CreditTransactionPage"];
6
7
  type CreditBalanceResponse = components["schemas"]["CreditBalanceResponse"];
7
8
  export interface GrantOptions {
8
9
  idempotencyKey?: string;
@@ -10,13 +11,25 @@ export interface GrantOptions {
10
11
  export interface DeductOptions {
11
12
  idempotencyKey?: string;
12
13
  }
14
+ /** One page of a list: how many rows, and where to carry on from. */
15
+ export interface PageQuery {
16
+ limit?: number;
17
+ cursor?: string;
18
+ }
13
19
  export declare class CreditsResource {
14
20
  private readonly client;
15
21
  constructor(client: MeterFlow);
16
22
  grant(body: CreditGrantRequest, opts?: GrantOptions): Promise<CreditTransactionResponse>;
17
23
  deduct(body: CreditDeductRequest, opts?: DeductOptions): Promise<CreditTransactionResponse>;
18
24
  balance(customerId: string): Promise<CreditBalanceResponse>;
19
- transactions(customerId: string): Promise<CreditTransactionResponse[]>;
25
+ /**
26
+ * One page of the customer's ledger, newest first.
27
+ *
28
+ * The ledger grows with every charge, so it is read a page at a time: `data` holds the rows and
29
+ * `next_cursor` the mark for the page after this one (`null` on the last). Pass it back as
30
+ * `cursor` unchanged — it is opaque, and nothing but this API reads it.
31
+ */
32
+ transactions(customerId: string, query?: PageQuery): Promise<CreditTransactionPage>;
20
33
  }
21
34
  export {};
22
35
  //# sourceMappingURL=credits.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"credits.d.ts","sourceRoot":"","sources":["../../../src/resources/credits.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,KAAK,kBAAkB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,oBAAoB,CAAC,CAAC;AACtE,KAAK,mBAAmB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,qBAAqB,CAAC,CAAC;AACxE,KAAK,yBAAyB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,2BAA2B,CAAC,CAAC;AACpF,KAAK,qBAAqB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,uBAAuB,CAAC,CAAC;AAE5E,MAAM,WAAW,YAAY;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,SAAS;IAE9C,KAAK,CAAC,IAAI,EAAE,kBAAkB,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,yBAAyB,CAAC;IAOxF,MAAM,CAAC,IAAI,EAAE,mBAAmB,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,yBAAyB,CAAC;IAO3F,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAI3D,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,EAAE,CAAC;CAGvE"}
1
+ {"version":3,"file":"credits.d.ts","sourceRoot":"","sources":["../../../src/resources/credits.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,KAAK,kBAAkB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,oBAAoB,CAAC,CAAC;AACtE,KAAK,mBAAmB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,qBAAqB,CAAC,CAAC;AACxE,KAAK,yBAAyB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,2BAA2B,CAAC,CAAC;AACpF,KAAK,qBAAqB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,uBAAuB,CAAC,CAAC;AAC5E,KAAK,qBAAqB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,uBAAuB,CAAC,CAAC;AAE5E,MAAM,WAAW,YAAY;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,qEAAqE;AACrE,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,SAAS;IAE9C,KAAK,CAAC,IAAI,EAAE,kBAAkB,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,yBAAyB,CAAC;IAOxF,MAAM,CAAC,IAAI,EAAE,mBAAmB,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,yBAAyB,CAAC;IAO3F,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAI3D;;;;;;OAMG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAKpF"}
@@ -0,0 +1,24 @@
1
+ import type { MeterFlow } from "../client";
2
+ import type { components } from "../types/openapi";
3
+ type PortalLinkRequest = components["schemas"]["PortalLinkRequest"];
4
+ type PortalLinkResponse = components["schemas"]["PortalLinkResponse"];
5
+ export declare class PortalResource {
6
+ private readonly client;
7
+ constructor(client: MeterFlow);
8
+ /**
9
+ * Mint a link to the hosted balance page for one of your customers.
10
+ *
11
+ * Call this from **your** server and send the `url` to that customer — it carries a short-lived
12
+ * token that can read their balance and their ledger and nothing else, in the environment of the
13
+ * key you called with. It is never your API key, and it can write nothing.
14
+ *
15
+ * The page shows the balance and sends "buy more" to your own checkout (`topup_url`): MeterFlow
16
+ * never takes the payment. `expires_in_minutes` defaults to 60 and may not exceed 1440.
17
+ *
18
+ * The two reads behind the page are **not** on this client by design: they belong to the person
19
+ * holding the link, and this client holds a secret key.
20
+ */
21
+ createSession(body: PortalLinkRequest): Promise<PortalLinkResponse>;
22
+ }
23
+ export {};
24
+ //# sourceMappingURL=portal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portal.d.ts","sourceRoot":"","sources":["../../../src/resources/portal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,KAAK,iBAAiB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,mBAAmB,CAAC,CAAC;AACpE,KAAK,kBAAkB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,oBAAoB,CAAC,CAAC;AAEtE,qBAAa,cAAc;IACb,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,SAAS;IAE9C;;;;;;;;;;;;OAYG;IACH,aAAa,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAGpE"}
@@ -3,17 +3,28 @@ import type { components } from "../types/openapi";
3
3
  type SubscriptionCreateRequest = components["schemas"]["SubscriptionCreateRequest"];
4
4
  type SubscriptionUpdateRequest = components["schemas"]["SubscriptionUpdateRequest"];
5
5
  type SubscriptionResponse = components["schemas"]["SubscriptionResponse"];
6
+ type SubscriptionPage = components["schemas"]["SubscriptionPage"];
6
7
  export interface CreateOptions {
7
8
  idempotencyKey?: string;
8
9
  }
9
10
  export interface ListQuery {
10
11
  customer_id?: string;
12
+ /** Rows per page, 1–200 (50 by default). */
13
+ limit?: number;
14
+ /** `next_cursor` from the previous page; omit for the first. */
15
+ cursor?: string;
11
16
  }
12
17
  export declare class SubscriptionsResource {
13
18
  private readonly client;
14
19
  constructor(client: MeterFlow);
15
20
  create(body: SubscriptionCreateRequest, opts?: CreateOptions): Promise<SubscriptionResponse>;
16
- list(query?: ListQuery): Promise<SubscriptionResponse[]>;
21
+ /**
22
+ * One page of the project's subscriptions in this key's environment, newest first.
23
+ *
24
+ * `data` holds the rows and `next_cursor` the mark for the page after this one (`null` on the
25
+ * last). Pass it back as `cursor` unchanged — it is opaque, and nothing but this API reads it.
26
+ */
27
+ list(query?: ListQuery): Promise<SubscriptionPage>;
17
28
  get(subscriptionId: string): Promise<SubscriptionResponse>;
18
29
  update(subscriptionId: string, body: SubscriptionUpdateRequest, opts?: {
19
30
  idempotencyKey?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"subscriptions.d.ts","sourceRoot":"","sources":["../../../src/resources/subscriptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,KAAK,yBAAyB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,2BAA2B,CAAC,CAAC;AACpF,KAAK,yBAAyB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,2BAA2B,CAAC,CAAC;AACpF,KAAK,oBAAoB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,sBAAsB,CAAC,CAAC;AAE1E,MAAM,WAAW,aAAa;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,SAAS;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,qBAAqB;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,SAAS;IAE9C,MAAM,CAAC,IAAI,EAAE,yBAAyB,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAO5F,IAAI,CAAC,KAAK,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAMxD,GAAG,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAI1D,MAAM,CAAC,cAAc,EAAE,MAAM,EAAE,IAAI,EAAE,yBAAyB,EAAE,IAAI,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAOlI,MAAM,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG9C"}
1
+ {"version":3,"file":"subscriptions.d.ts","sourceRoot":"","sources":["../../../src/resources/subscriptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,KAAK,yBAAyB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,2BAA2B,CAAC,CAAC;AACpF,KAAK,yBAAyB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,2BAA2B,CAAC,CAAC;AACpF,KAAK,oBAAoB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,sBAAsB,CAAC,CAAC;AAC1E,KAAK,gBAAgB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,kBAAkB,CAAC,CAAC;AAElE,MAAM,WAAW,aAAa;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,SAAS;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,qBAAqB;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,SAAS;IAE9C,MAAM,CAAC,IAAI,EAAE,yBAAyB,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAO5F;;;;;OAKG;IACH,IAAI,CAAC,KAAK,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAMlD,GAAG,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAI1D,MAAM,CAAC,cAAc,EAAE,MAAM,EAAE,IAAI,EAAE,yBAAyB,EAAE,IAAI,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAOlI,MAAM,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG9C"}
@@ -0,0 +1,31 @@
1
+ import type { MeterFlow } from "../client";
2
+ import type { components } from "../types/openapi";
3
+ type WebhookResponse = components["schemas"]["WebhookResponse"];
4
+ type WebhookDeliveryResponse = components["schemas"]["WebhookDeliveryResponse"];
5
+ /**
6
+ * Webhook **receipts** — read-only on purpose.
7
+ *
8
+ * An endpoint is registered, edited and deleted in the dashboard, where the signing secret is
9
+ * shown once and never again. What an SDK consumer needs at 3am is the other half: which
10
+ * endpoints exist, and what MeterFlow actually sent them. That is this.
11
+ *
12
+ * Not to be confused with `meterflow/webhook`, which verifies the signature on a webhook your
13
+ * server has just received. This reads what we sent; that proves we sent it.
14
+ */
15
+ export declare class WebhooksResource {
16
+ private readonly client;
17
+ constructor(client: MeterFlow);
18
+ /** Every endpoint registered for the key's project. Never includes a secret. */
19
+ list(): Promise<WebhookResponse[]>;
20
+ /**
21
+ * One endpoint's delivery attempts, newest first: the payload sent, the HTTP status that came
22
+ * back, how many attempts and when the next retry is due.
23
+ *
24
+ * Capped at the most recent 100, and scoped to the key's own environment — a `mf_test_*` key
25
+ * reads test receipts, a `mf_live_*` key live ones. A webhook belonging to another project
26
+ * answers 404.
27
+ */
28
+ deliveries(webhookId: string): Promise<WebhookDeliveryResponse[]>;
29
+ }
30
+ export {};
31
+ //# sourceMappingURL=webhooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../../src/resources/webhooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,KAAK,eAAe,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,iBAAiB,CAAC,CAAC;AAChE,KAAK,uBAAuB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,yBAAyB,CAAC,CAAC;AAEhF;;;;;;;;;GASG;AACH,qBAAa,gBAAgB;IACf,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,SAAS;IAE9C,gFAAgF;IAChF,IAAI,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAIlC;;;;;;;OAOG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,uBAAuB,EAAE,CAAC;CAGlE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "meterflow",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Official Node.js SDK for MeterFlow — usage-based billing, credit management, and metering.",
5
5
  "license": "MIT",
6
6
  "author": "MeterFlow",