@clocknext/sdk 0.18.0 → 0.19.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
@@ -618,6 +618,43 @@ wallet.reservedBalance; // number | undefined
618
618
 
619
619
  When the feature is off, `wallet.reservedBalance` is left out of `balances()`.
620
620
 
621
+ ### Wallet ledger
622
+
623
+ `customers.wallet()` lists every movement of wallet money, newest first, with
624
+ both balances. Each row's `source` says what caused it:
625
+
626
+ | `source` | Meaning |
627
+ |---|---|
628
+ | `SUBSCRIPTION` | Wallet money from a new plan purchase |
629
+ | `RENEWAL` | Wallet money from a plan renewal, or unused money removed at renewal |
630
+ | `MANUAL` | Changed in the ClockNext dashboard |
631
+ | `API` | Changed with `adjustWallet()` |
632
+ | `CUSTOMER_PORTAL` | Top-up paid by the customer in the customer portal |
633
+ | `USAGE_LOG` | Charged by a signal; `usageLogId` links the signal |
634
+ | `INVOICE_SETTLEMENT` | Usage cleared by a paid pay-as-you-go invoice |
635
+ | `INVOICE_VOID` | Settlement undone by a voided invoice |
636
+
637
+ On a `USAGE_LOG` row, `usageLogId` is the id of the matching row in
638
+ `signals.usageLog()`, so each charge links to the exact signal that caused it.
639
+ `notes` is only text a person wrote — your own note on an `API` row, the
640
+ admin's note on a `MANUAL` row — and `null` on every row the system writes.
641
+
642
+ ```ts
643
+ const page = await cnk.customers.wallet(customerId, { from: "2026-10-01", to: "2026-10-07" });
644
+ page.balanceUsd; // ordinary wallet, USD
645
+ page.reservedBalanceUsd; // Reserved Wallet, or null when the feature is off
646
+
647
+ for (const t of page.transactions) {
648
+ // t.type: "CREDIT" | "DEBIT"; t.amount is always positive
649
+ // t.entitlementType: "CREDIT" | "OUTCOME" | "UNIT" | "COMPOSITE" | null
650
+ }
651
+
652
+ // Every row, across all pages — pick out the signal charges:
653
+ for await (const t of cnk.customers.iterateWallet(customerId)) {
654
+ if (t.source === "USAGE_LOG") console.log(t.usageLogId, t.amountUsd);
655
+ }
656
+ ```
657
+
621
658
  To change how much a customer is granted **every billing cycle** (not just
622
659
  their balance right now), use `updateRecurringGrant`. Any increase or decrease
623
660
  applies to their balance immediately. `quantity` is USD for the wallet and a
@@ -656,9 +693,10 @@ cnk.customers.get / update / delete / iterate
656
693
  cnk.customers.getByExternalId / updateByExternalId / updateRecurringGrantByExternalId
657
694
  cnk.customers.adjustCreditByExternalId / adjustOutcomeByExternalId / adjustUnitByExternalId
658
695
  cnk.customers.adjustCompositeByExternalId / adjustWalletByExternalId
659
- cnk.customers.deleteByExternalId / balancesByExternalId / planByExternalId
696
+ cnk.customers.deleteByExternalId / balancesByExternalId / planByExternalId / walletByExternalId
660
697
  cnk.customers.listMembers / addMember / updateMember / removeMember
661
698
  cnk.customers.balances / creditBalance / outcomeBalance / unitBalance / plan
699
+ cnk.customers.wallet / iterateWallet
662
700
  cnk.customers.adjustCredit / adjustOutcome / adjustUnit / adjustComposite / adjustWallet
663
701
  cnk.customers.updateRecurringGrant
664
702
 
package/dist/index.cjs CHANGED
@@ -333,7 +333,7 @@ function sleep(ms) {
333
333
  }
334
334
 
335
335
  // src/http.ts
336
- var SDK_VERSION = "0.18.0" ;
336
+ var SDK_VERSION = "0.19.0" ;
337
337
  var Transport = class {
338
338
  constructor(cfg) {
339
339
  this.cfg = cfg;
@@ -682,6 +682,47 @@ var Customers = class {
682
682
  path: `/api/v1/customers/${encodeURIComponent(id)}/balances`
683
683
  });
684
684
  }
685
+ /**
686
+ * One page of the customer's wallet ledger — every movement of wallet
687
+ * money, newest first — plus both wallet balances. Cursor-paginated: pass
688
+ * back `nextCursor` as `cursor`. `GET /api/v1/customers/:id/wallet`.
689
+ */
690
+ wallet(id, params = {}) {
691
+ return this.walletPage(`/api/v1/customers/${encodeURIComponent(id)}/wallet`, params);
692
+ }
693
+ /** {@link wallet}, addressing the customer by its `externalId`.
694
+ * `GET /api/v1/customers/ext/:externalId/wallet`. */
695
+ walletByExternalId(externalId, params = {}) {
696
+ return this.walletPage(
697
+ `/api/v1/customers/ext/${encodeURIComponent(externalId)}/wallet`,
698
+ params
699
+ );
700
+ }
701
+ /**
702
+ * Async iterator over the customer's WHOLE wallet ledger, newest first,
703
+ * following the cursor. `params.cursor` is ignored.
704
+ * `for await (const t of cnk.customers.iterateWallet(id)) { … }`
705
+ */
706
+ async *iterateWallet(id, params = {}) {
707
+ let cursor;
708
+ do {
709
+ const page = await this.wallet(id, { ...params, cursor });
710
+ for (const t of page.transactions) yield t;
711
+ cursor = page.nextCursor ?? void 0;
712
+ } while (cursor);
713
+ }
714
+ walletPage(path, params) {
715
+ return this.transport.request({
716
+ method: "GET",
717
+ path,
718
+ query: {
719
+ limit: params.limit,
720
+ cursor: params.cursor,
721
+ from: params.from,
722
+ to: params.to
723
+ }
724
+ });
725
+ }
685
726
  /** {@link balances}, addressing the customer by its `externalId`.
686
727
  * `GET /api/v1/customers/ext/:externalId/balances`. */
687
728
  balancesByExternalId(externalId) {
@@ -1306,6 +1347,7 @@ var Signals = class {
1306
1347
  ...customerRefToWire(input),
1307
1348
  runId: input.runId,
1308
1349
  ...input.member ? { member: input.member } : {},
1350
+ ...input.custom ? { custom: input.custom } : {},
1309
1351
  idempotencyKey
1310
1352
  },
1311
1353
  retry: true
@@ -1343,6 +1385,7 @@ var Signals = class {
1343
1385
  ...input.quantity != null ? { quantity: input.quantity } : {},
1344
1386
  ...input.member ? { member: input.member } : {},
1345
1387
  ...input.composite ? { composite: { [input.composite.ref]: input.composite.value } } : {},
1388
+ ...input.custom ? { custom: input.custom } : {},
1346
1389
  // Assigned HERE, before the send, so the SAME key rides every
1347
1390
  // transport-level retry below. Without it a single 500 followed by a
1348
1391
  // success is TWO un-keyed reports, and the server dedups on the key —
@@ -1397,6 +1440,7 @@ var Signals = class {
1397
1440
  body: {
1398
1441
  ...customerRefToWire(input),
1399
1442
  composite: { [input.composite.ref]: input.composite.value },
1443
+ ...input.custom ? { custom: input.custom } : {},
1400
1444
  // Auto-assigned like the others. A retry here could not double-BILL
1401
1445
  // even without it — closing a closed occurrence is a server-side
1402
1446
  // no-op — but that safety comes from occurrence-level dedup, not from