@clocknext/sdk 0.18.0 → 0.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -1
- package/dist/index.cjs +66 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +230 -12
- package/dist/index.d.ts +230 -12
- package/dist/index.js +66 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +7 -0
- package/src/resources/customers.ts +54 -0
- package/src/resources/signals.ts +26 -0
- package/src/types.ts +182 -11
package/README.md
CHANGED
|
@@ -618,6 +618,71 @@ 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
|
+
|
|
658
|
+
### Look up usage rows by id
|
|
659
|
+
|
|
660
|
+
`signals.lookupUsageLogs(ids)` returns up to 100 usage-log rows in one call —
|
|
661
|
+
for example the row behind each wallet debit's `usageLogId`. It is scoped to
|
|
662
|
+
your org, not one customer: every row has its own `customerId`. Rows come back
|
|
663
|
+
in the order of `ids`; ids with no row in your org are listed in `notFound`.
|
|
664
|
+
|
|
665
|
+
Each row has `entitlement: { type, id, name }` — `type` is `credit`, `outcome`,
|
|
666
|
+
`outcome_completion`, `unit`, `composite_completion` or `wallet` (a wallet row
|
|
667
|
+
is named `"Wallet"`) — and only the matching group (`credit`, `outcome`,
|
|
668
|
+
`outcomeCompletion`, `unit`, `compositeCompletion`, `wallet`) is set.
|
|
669
|
+
`composite: { id, refId, runId }` names the composite that pays for a row.
|
|
670
|
+
`cost.customerCost` is what the row charged, so it matches the wallet debit.
|
|
671
|
+
|
|
672
|
+
```ts
|
|
673
|
+
const ledger = await cnk.customers.wallet(customerId);
|
|
674
|
+
const ids = ledger.transactions
|
|
675
|
+
.filter((t) => t.source === "USAGE_LOG" && t.usageLogId)
|
|
676
|
+
.map((t) => t.usageLogId!);
|
|
677
|
+
|
|
678
|
+
const { usageLogs, notFound } = await cnk.signals.lookupUsageLogs(ids);
|
|
679
|
+
for (const row of usageLogs) {
|
|
680
|
+
row.entitlement.type; // what was charged
|
|
681
|
+
row.cost.customerCost; // what it charged
|
|
682
|
+
row.composite; // { id, refId, runId } | null
|
|
683
|
+
}
|
|
684
|
+
```
|
|
685
|
+
|
|
621
686
|
To change how much a customer is granted **every billing cycle** (not just
|
|
622
687
|
their balance right now), use `updateRecurringGrant`. Any increase or decrease
|
|
623
688
|
applies to their balance immediately. `quantity` is USD for the wallet and a
|
|
@@ -656,9 +721,10 @@ cnk.customers.get / update / delete / iterate
|
|
|
656
721
|
cnk.customers.getByExternalId / updateByExternalId / updateRecurringGrantByExternalId
|
|
657
722
|
cnk.customers.adjustCreditByExternalId / adjustOutcomeByExternalId / adjustUnitByExternalId
|
|
658
723
|
cnk.customers.adjustCompositeByExternalId / adjustWalletByExternalId
|
|
659
|
-
cnk.customers.deleteByExternalId / balancesByExternalId / planByExternalId
|
|
724
|
+
cnk.customers.deleteByExternalId / balancesByExternalId / planByExternalId / walletByExternalId
|
|
660
725
|
cnk.customers.listMembers / addMember / updateMember / removeMember
|
|
661
726
|
cnk.customers.balances / creditBalance / outcomeBalance / unitBalance / plan
|
|
727
|
+
cnk.customers.wallet / iterateWallet
|
|
662
728
|
cnk.customers.adjustCredit / adjustOutcome / adjustUnit / adjustComposite / adjustWallet
|
|
663
729
|
cnk.customers.updateRecurringGrant
|
|
664
730
|
|
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.
|
|
336
|
+
var SDK_VERSION = "0.19.1" ;
|
|
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) {
|
|
@@ -1187,6 +1228,7 @@ var UNIT_PATH = "/api/v1/signal/unit";
|
|
|
1187
1228
|
var COMPOSITE_COMPLETE_PATH = "/api/v1/signal/composite/complete";
|
|
1188
1229
|
var OUTCOME_COMPLETE_PATH = "/api/v1/signal/outcome/complete";
|
|
1189
1230
|
var USAGE_LOG_PATH = "/api/v1/signal/usage-log";
|
|
1231
|
+
var USAGE_LOG_LOOKUP_PATH = "/api/v1/signal/usage-log/lookup";
|
|
1190
1232
|
function signalToWire(signal) {
|
|
1191
1233
|
const t = signal.tokens;
|
|
1192
1234
|
const body = {
|
|
@@ -1306,6 +1348,7 @@ var Signals = class {
|
|
|
1306
1348
|
...customerRefToWire(input),
|
|
1307
1349
|
runId: input.runId,
|
|
1308
1350
|
...input.member ? { member: input.member } : {},
|
|
1351
|
+
...input.custom ? { custom: input.custom } : {},
|
|
1309
1352
|
idempotencyKey
|
|
1310
1353
|
},
|
|
1311
1354
|
retry: true
|
|
@@ -1343,6 +1386,7 @@ var Signals = class {
|
|
|
1343
1386
|
...input.quantity != null ? { quantity: input.quantity } : {},
|
|
1344
1387
|
...input.member ? { member: input.member } : {},
|
|
1345
1388
|
...input.composite ? { composite: { [input.composite.ref]: input.composite.value } } : {},
|
|
1389
|
+
...input.custom ? { custom: input.custom } : {},
|
|
1346
1390
|
// Assigned HERE, before the send, so the SAME key rides every
|
|
1347
1391
|
// transport-level retry below. Without it a single 500 followed by a
|
|
1348
1392
|
// success is TWO un-keyed reports, and the server dedups on the key —
|
|
@@ -1397,6 +1441,7 @@ var Signals = class {
|
|
|
1397
1441
|
body: {
|
|
1398
1442
|
...customerRefToWire(input),
|
|
1399
1443
|
composite: { [input.composite.ref]: input.composite.value },
|
|
1444
|
+
...input.custom ? { custom: input.custom } : {},
|
|
1400
1445
|
// Auto-assigned like the others. A retry here could not double-BILL
|
|
1401
1446
|
// even without it — closing a closed occurrence is a server-side
|
|
1402
1447
|
// no-op — but that safety comes from occurrence-level dedup, not from
|
|
@@ -1446,6 +1491,26 @@ var Signals = class {
|
|
|
1446
1491
|
}
|
|
1447
1492
|
});
|
|
1448
1493
|
}
|
|
1494
|
+
/**
|
|
1495
|
+
* Fetches up to 100 usage-log rows by id, in one call.
|
|
1496
|
+
* `GET /api/v1/signal/usage-log/lookup`.
|
|
1497
|
+
*
|
|
1498
|
+
* Built for linking a wallet debit to its usage: pass the `usageLogId`s
|
|
1499
|
+
* from `customers.wallet()` instead of paging through
|
|
1500
|
+
* {@link usageLog}. Scoped to your org, not to one customer — every row
|
|
1501
|
+
* carries its own `customerId`.
|
|
1502
|
+
*
|
|
1503
|
+
* Rows come back in the order of `ids`; duplicates are removed. Ids that
|
|
1504
|
+
* match nothing in your org are listed in `notFound`. More than 100
|
|
1505
|
+
* distinct ids is a 400.
|
|
1506
|
+
*/
|
|
1507
|
+
lookupUsageLogs(ids) {
|
|
1508
|
+
return this.transport.request({
|
|
1509
|
+
method: "GET",
|
|
1510
|
+
path: USAGE_LOG_LOOKUP_PATH,
|
|
1511
|
+
query: { ids: ids.join(",") }
|
|
1512
|
+
});
|
|
1513
|
+
}
|
|
1449
1514
|
};
|
|
1450
1515
|
|
|
1451
1516
|
// src/resources/units.ts
|