@clocknext/sdk 0.14.0 → 0.15.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/src/types.ts CHANGED
@@ -65,42 +65,40 @@ export interface CompositeTag {
65
65
  }
66
66
 
67
67
  // ---------------------------------------------------------------------------
68
- // Workspace (mirrors GET /api/v1/me and GET /api/v1/models)
68
+ // Naming a customer
69
69
  // ---------------------------------------------------------------------------
70
70
 
71
- /** The organisation + key behind a request — `cnk.workspace.me()`. */
72
- export interface WorkspaceIdentity {
73
- organization: {
74
- id: string;
75
- name: string;
76
- slug: string | null;
77
- /** `sandbox` is a disposable staging twin; `live` bills for real. Check
78
- * this before sending production traffic. */
79
- environment: "sandbox" | "live";
80
- };
81
- apiKey: {
82
- prefix: string;
83
- lastFour: string;
84
- expiresAt: string | null;
85
- } | null;
86
- }
71
+ /**
72
+ * How a request names its customer: by OUR id (`customerId`) or by YOUR id for
73
+ * them (`externalId` — the value set on the customer via
74
+ * `customers.create()` / `customers.update()`). At least one is required.
75
+ *
76
+ * When both are sent, `customerId` wins and `externalId` is ignored — the
77
+ * server does not check that they agree. An `externalId` matching no active
78
+ * customer is a `NotFoundError` (or, on an async signal, leaves it Pending
79
+ * with that reason — retry once the customer exists).
80
+ */
81
+ export type CustomerRef =
82
+ | {
83
+ /** Our customer id. Wins over `externalId` when both are sent. */
84
+ customerId: string;
85
+ externalId?: string;
86
+ }
87
+ | {
88
+ customerId?: undefined;
89
+ /** Your id for the customer: 1–32 letters, numbers or "-", matched
90
+ * exactly (case-sensitive). */
91
+ externalId: string;
92
+ };
87
93
 
88
- /** One model the org has enabled — `cnk.workspace.models()`. The `modelId` is
89
- * what you put in a signal's `model`. Prices are USD per 1,000,000 tokens. */
90
- export interface Model {
91
- modelId: string;
92
- modelName: string;
93
- provider: string;
94
- providerName: string;
95
- isActive: boolean;
96
- inputPrice: number;
97
- outputPrice: number;
98
- cachePrice: number;
99
- }
94
+ /** `Omit` that keeps a union a union (plain `Omit` flattens it to the
95
+ * members' shared keys, losing the {@link CustomerRef} either/or). */
96
+ type DistributiveOmit<T, K extends PropertyKey> = T extends unknown
97
+ ? Omit<T, K>
98
+ : never;
100
99
 
101
- interface SignalBase {
102
- /** The customer this usage belongs to (required). */
103
- customerId: string;
100
+ /** Every signal names its customer — see {@link CustomerRef}. */
101
+ interface SignalFields {
104
102
  /**
105
103
  * The model's catalog id — must be enabled in your ClockNext org
106
104
  * (Settings → Models). Matched case-insensitively against `OrgModel.modelId`.
@@ -123,19 +121,21 @@ interface SignalBase {
123
121
  idempotencyKey?: string;
124
122
  }
125
123
 
124
+ type SignalBase = CustomerRef & SignalFields;
125
+
126
126
  /** Meters a credit, identified by the credit's stable `agentKey`. */
127
- export interface CreditSignal extends SignalBase {
127
+ export type CreditSignal = SignalBase & {
128
128
  type: "credit";
129
129
  /** The Credit's stable agent key (assigned when the credit was created). */
130
130
  agentKey: string;
131
131
  /** Optional composite this signal belongs to. See {@link CompositeTag}. */
132
132
  composite?: CompositeTag;
133
- }
133
+ };
134
134
 
135
135
  /** Debits the customer's wallet in USD at the model's cost. */
136
- export interface WalletSignal extends SignalBase {
136
+ export type WalletSignal = SignalBase & {
137
137
  type: "wallet";
138
- }
138
+ };
139
139
 
140
140
  /**
141
141
  * Records one step of a multi-call outcome run. The `agentKey` identifies the
@@ -149,7 +149,7 @@ export interface WalletSignal extends SignalBase {
149
149
  * carries `complete` — see {@link OutcomeCompletionInput} /
150
150
  * `signals.completeOutcome()`, its own dedicated route.
151
151
  */
152
- export interface OutcomeSignal extends SignalBase {
152
+ export type OutcomeSignal = SignalBase & {
153
153
  type: "outcome";
154
154
  /** The OutcomeStep's stable agent key — identifies the outcome AND the step. */
155
155
  agentKey: string;
@@ -161,14 +161,14 @@ export interface OutcomeSignal extends SignalBase {
161
161
  runId: string;
162
162
  /** Optional composite this signal belongs to. See {@link CompositeTag}. */
163
163
  composite?: CompositeTag;
164
- }
164
+ };
165
165
 
166
166
  export type Signal = CreditSignal | WalletSignal | OutcomeSignal;
167
167
 
168
168
  /** Helper input shapes for the per-type convenience methods. */
169
- export type CreditInput = Omit<CreditSignal, "type">;
170
- export type WalletInput = Omit<WalletSignal, "type">;
171
- export type OutcomeInput = Omit<OutcomeSignal, "type">;
169
+ export type CreditInput = DistributiveOmit<CreditSignal, "type">;
170
+ export type WalletInput = DistributiveOmit<WalletSignal, "type">;
171
+ export type OutcomeInput = DistributiveOmit<OutcomeSignal, "type">;
172
172
 
173
173
  /**
174
174
  * Input for `signals.completeOutcome()` — closes ONE outcome run, the outcome
@@ -181,17 +181,17 @@ export type OutcomeInput = Omit<OutcomeSignal, "type">;
181
181
  * `signals.completeComposite()` — the two completions are independent, and
182
182
  * neither implies the other.
183
183
  */
184
- export interface OutcomeCompletionInput {
185
- customerId: string;
184
+ export type OutcomeCompletionInput = CustomerRef & {
186
185
  /** The same `runId` the run's step signals were sent with. */
187
186
  runId: string;
188
187
  /** Optional member (email) this completion is attributed to. */
189
188
  member?: string;
190
- /** Idempotency key, auto-generated if omitted. Sending `complete` twice for
191
- * the same run is already a safe no-op server-side, so this only collapses
192
- * a retry into one attempt rather than protecting against a double bill. */
189
+ /** Idempotency key, auto-generated if omitted. Sending this completion twice
190
+ * for the same run is already a safe no-op server-side, so this only
191
+ * collapses a retry into one attempt rather than protecting against a
192
+ * double bill. */
193
193
  idempotencyKey?: string;
194
- }
194
+ };
195
195
 
196
196
  /**
197
197
  * The result of `signals.completeOutcome()`.
@@ -304,7 +304,7 @@ export interface SerializedUsageLog {
304
304
  * exhausted allowance is detected by the worker LATER and never surfaces here —
305
305
  * the signal is simply dropped. A `202` therefore means "accepted", never
306
306
  * "recorded". To confirm a signal actually landed, read it back with
307
- * `signals.list({ customerId })`.
307
+ * `signals.usageLog({ customerId })`.
308
308
  *
309
309
  * When the server has no queue configured it prices inline instead and returns
310
310
  * `queued: false` with a populated `usageLog`, validating synchronously.
@@ -327,7 +327,7 @@ export interface TrackResult {
327
327
  * and will price it shortly. It can no longer be lost, but
328
328
  * it is not billed yet and `usageLog` is null. A rejection
329
329
  * discovered during pricing will NOT come back to you here,
330
- * nor on `onError`. `signals.list()` shows what got priced;
330
+ * nor on `onError`. `signals.usageLog()` shows what got priced;
331
331
  * the dashboard's signal log — owner-only, by design — is
332
332
  * where an individual rejection says why.
333
333
  * `"recorded"` — the server priced it inline and `usageLog` is populated.
@@ -373,18 +373,6 @@ export interface TrackResult {
373
373
  rawId?: string;
374
374
  }
375
375
 
376
- export interface UsageList {
377
- customer: { id: string; name: string; creditAllowance: number };
378
- totals: {
379
- count: number;
380
- cost: number;
381
- revenue: number;
382
- profit: number;
383
- creditsUsed: number;
384
- };
385
- logs: SerializedUsageLog[];
386
- }
387
-
388
376
  // ---------------------------------------------------------------------------
389
377
  // Usage log (mirrors GET /api/v1/signal/usage-log)
390
378
  // ---------------------------------------------------------------------------
@@ -471,6 +459,9 @@ export interface UsageLogPage {
471
459
 
472
460
  export interface ApiCustomer {
473
461
  id: string;
462
+ /** Your own id for this customer, or `null` when unset. See
463
+ * {@link CustomerProfileInput.externalId}. */
464
+ externalId: string | null;
474
465
  slug: string;
475
466
  name: string;
476
467
  description: string | null;
@@ -486,12 +477,24 @@ export interface ApiCustomer {
486
477
  taxId: string | null;
487
478
  currencyCode: string;
488
479
  creditAllowance: number;
480
+ recurringTransactionEmail: boolean;
481
+ autoRecurring: boolean;
489
482
  createdAt: string;
490
483
  updatedAt: string;
491
484
  }
492
485
 
493
486
  /** Writable customer profile fields. `null` clears a column; omit to leave it. */
494
487
  export interface CustomerProfileInput {
488
+ /**
489
+ * The id YOUR product uses for this customer: 1–32 letters, numbers or "-",
490
+ * case-sensitive. Unique among the org's active customers (a clash is a
491
+ * `ConflictError`; an archived customer's id can be reused). Once set,
492
+ * signals and `purchases.create()` can take `externalId` instead of
493
+ * `customerId`, and `customers.getByExternalId()` / `updateByExternalId()` /
494
+ * `updateRecurringGrantByExternalId()` address the customer by it. `null`
495
+ * clears it on update.
496
+ */
497
+ externalId?: string | null;
495
498
  name?: string;
496
499
  description?: string | null;
497
500
  email?: string | null;
@@ -506,6 +509,10 @@ export interface CustomerProfileInput {
506
509
  taxId?: string | null;
507
510
  /** ISO 4217 (3 letters); must be enabled for the org. */
508
511
  currencyCode?: string;
512
+ /** Send the invoice, payment receipt and card-payment-failed emails for recurring invoices. Defaults to `true` on create. */
513
+ recurringTransactionEmail?: boolean;
514
+ /** Issue every recurring invoice as `AUTO_CLOSED` (applied as if paid, never payable). Defaults to `false` on create. */
515
+ autoRecurring?: boolean;
509
516
  }
510
517
 
511
518
  /** `name` and `email` are required on create; every other field is optional. */
@@ -541,7 +548,7 @@ export interface OutcomeBalance {
541
548
  remaining: number;
542
549
  /** Each step's `agentKey` is what `signals.outcome()` and the outcome adjust
543
550
  * API use to address this outcome at runtime. The outcome also has its own
544
- * separate key ({@link Outcome.agentKey}), but neither of those APIs accepts
551
+ * separate key (the outcome's own `agentKey`), but neither of those APIs accepts
545
552
  * it — it identifies the outcome in the catalogue, not in a signal. */
546
553
  steps: { id: string; name: string; agentKey: string }[];
547
554
  }
@@ -563,7 +570,11 @@ export interface CustomerBalances {
563
570
  customerId: string;
564
571
  currency: { code: string; rateUsd: number };
565
572
  hasPlan: boolean;
573
+ /** `null` when the plan has no Wallet component. */
566
574
  wallet: { balanceUsd: number } | null;
575
+ /** The Reserved Wallet; `null` when the org hasn't enabled it. Independent
576
+ * of `wallet` — present whether or not the plan has a Wallet. */
577
+ reservedWallet: { balanceUsd: number } | null;
567
578
  credits: CreditBalance[];
568
579
  outcomes: OutcomeBalance[];
569
580
  units: UnitBalance[];
@@ -625,7 +636,6 @@ export type BillingCycle =
625
636
  | "FREE";
626
637
  export type PlanType = "WALLET" | "BUNDLE";
627
638
  export type PlanKind = "FLAT_PRICE" | "PAY_AS_YOU_GO" | "PAY_PER_SEAT";
628
- /** A plan component's meter type. */
629
639
  /**
630
640
  * A plan component's kind.
631
641
  *
@@ -644,109 +654,6 @@ export type ComponentType =
644
654
  /** ADVANCE bills up-front for the cycle; ARREAR bills for what was consumed. */
645
655
  export type BillingMode = "ADVANCE" | "ARREAR";
646
656
 
647
- /** Prepaid USD wallet. `amount` is the funded balance (required). Max one
648
- * wallet component per plan. */
649
- export interface WalletComponentInput {
650
- type: "WALLET";
651
- billingMode: BillingMode;
652
- amount: number;
653
- }
654
-
655
- /** A one-off flat fee — `amount` is required. Max one flat component per plan;
656
- * not allowed on FREE plans. */
657
- export interface FlatComponentInput {
658
- type: "FLAT";
659
- billingMode: BillingMode;
660
- amount: number;
661
- }
662
-
663
- /** A credit entitlement, referenced by `creditId`. Billed ADVANCE it needs a
664
- * granted `quantity`; billed ARREAR it meters usage with no upfront grant. */
665
- export type CreditComponentInput =
666
- | { type: "CREDIT"; billingMode: "ADVANCE"; creditId: string; quantity: number }
667
- | { type: "CREDIT"; billingMode: "ARREAR"; creditId: string; quantity?: number | null };
668
-
669
- /** An outcome entitlement, referenced by `outcomeId`. ADVANCE needs a granted
670
- * `quantity`; ARREAR meters usage. */
671
- export type OutcomeComponentInput =
672
- | { type: "OUTCOME"; billingMode: "ADVANCE"; outcomeId: string; quantity: number }
673
- | { type: "OUTCOME"; billingMode: "ARREAR"; outcomeId: string; quantity?: number | null };
674
-
675
- /** A unit entitlement, referenced by `unitId`. ADVANCE needs a granted
676
- * `quantity`; ARREAR meters usage. */
677
- export type UnitComponentInput =
678
- | { type: "UNIT"; billingMode: "ADVANCE"; unitId: string; quantity: number }
679
- | { type: "UNIT"; billingMode: "ARREAR"; unitId: string; quantity?: number | null };
680
-
681
- /**
682
- * A COMPOSITE sold on the plan, referenced by `pricingMetricId` (the id from
683
- * `composites.list()`). This is the line that makes a composite bill: the
684
- * catalogue entry on its own charges nobody, and this component is what turns
685
- * it into money — `quantity × the composite's price`, frozen into the purchase
686
- * snapshot at buy time.
687
- *
688
- * The two billing modes differ in what `quantity` means:
689
- * - **ADVANCE** — a PREPAID POOL of `quantity` flat-rate slots, shared
690
- * across whichever credits / outcomes / units the composite wraps. Each
691
- * distinct tag value claims one slot on completion; repeat signals under a
692
- * value reuse the slot they already claimed, at no extra cost. Once every
693
- * slot is claimed, further occurrences are refused rather than billed.
694
- * - **ARREAR** — nothing prepaid; completed occurrences are counted on the
695
- * cycle-end line.
696
- *
697
- * `type` is the wire value `"PRICING_METRIC"`, not `"COMPOSITE"`: the product
698
- * word changed but the stored enum did not, and the API still speaks the old
699
- * one. That mismatch is the server's, and it is surfaced rather than hidden so
700
- * a response and a request read the same.
701
- */
702
- export type CompositeComponentInput =
703
- | {
704
- type: "PRICING_METRIC";
705
- billingMode: "ADVANCE";
706
- pricingMetricId: string;
707
- quantity: number;
708
- }
709
- | {
710
- type: "PRICING_METRIC";
711
- billingMode: "ARREAR";
712
- pricingMetricId: string;
713
- quantity?: number | null;
714
- };
715
-
716
- /**
717
- * One entitlement line in a plan — a discriminated union on `type` (and, where
718
- * it matters, `billingMode`) so the per-type fields the server requires are
719
- * enforced at compile time instead of surfacing as runtime 422s:
720
- * - CREDIT / OUTCOME / UNIT require their matching id;
721
- * - PRICING_METRIC (a composite) requires `pricingMetricId`;
722
- * - CREDIT / OUTCOME / UNIT / PRICING_METRIC require `quantity` when billed
723
- * ADVANCE;
724
- * - WALLET / FLAT require `amount`.
725
- */
726
- export type PlanComponentInput =
727
- | WalletComponentInput
728
- | FlatComponentInput
729
- | CreditComponentInput
730
- | OutcomeComponentInput
731
- | UnitComponentInput
732
- | CompositeComponentInput;
733
-
734
- export interface CreatePlanInput {
735
- name: string;
736
- description?: string | null;
737
- billingCycle: BillingCycle;
738
- /** Carry an unused balance into the next cycle. Default true. */
739
- carryForward?: boolean;
740
- /** ISO 4217 (3 letters). Default "USD". */
741
- currencyCode?: string;
742
- isActive?: boolean;
743
- /** At least one component. FREE plans must be ADVANCE-only with no FLAT component. */
744
- components: PlanComponentInput[];
745
- }
746
-
747
- /** A full-rewrite update carries the complete plan (same shape as create). */
748
- export type UpdatePlanInput = CreatePlanInput;
749
-
750
657
  export interface PlanComponent {
751
658
  id: string;
752
659
  type: ComponentType;
@@ -754,11 +661,9 @@ export interface PlanComponent {
754
661
  creditId: string | null;
755
662
  outcomeId: string | null;
756
663
  unitId: string | null;
757
- /** The composite this component sells, on a `PRICING_METRIC` component.
758
- * Without it a plan read back could not be written back: the write shape
759
- * ({@link CompositeComponentInput}) requires this id, so a read-modify-write
760
- * silently dropped every composite component. */
761
- pricingMetricId: string | null;
664
+ /** The id of the composite this component sells, on a `PRICING_METRIC`
665
+ * component. */
666
+ compositeId: string | null;
762
667
  targetName: string | null;
763
668
  quantity: number | null;
764
669
  amount: number | null;
@@ -782,24 +687,13 @@ export interface Plan {
782
687
  components: PlanComponent[];
783
688
  }
784
689
 
785
- /** The richer plan returned by `plans.get()` (adds aggregate stats). */
786
- export interface PlanDetail extends Plan {
787
- createdAt: string;
788
- createdByName: string | null;
789
- customerCount: number;
790
- invoiceCount: number;
791
- openInvoiceCount: number;
792
- stats: { totalInvoiced: number; totalCollected: number; openBalance: number };
793
- }
794
-
795
690
  // ---------------------------------------------------------------------------
796
691
  // Purchases (subscriptions)
797
692
  // ---------------------------------------------------------------------------
798
693
 
799
694
  export type PurchaseStatus = "SCHEDULED" | "ACTIVE" | "CANCELLED";
800
695
 
801
- export interface CreatePurchaseInput {
802
- customerId: string;
696
+ export type CreatePurchaseInput = CustomerRef & {
803
697
  planId: string;
804
698
  /** `YYYY-MM-DD`. Defaults to today; must be a valid non-past date. */
805
699
  billingDate?: string;
@@ -807,7 +701,7 @@ export interface CreatePurchaseInput {
807
701
  autoPayment?: boolean;
808
702
  /** Auto-void the purchase if unpaid after N minutes. */
809
703
  voidAfterMinutes?: number;
810
- }
704
+ };
811
705
 
812
706
  export interface Purchase {
813
707
  id: string;
@@ -863,7 +757,9 @@ export interface PurchaseDetail {
863
757
  // Invoices & Payments
864
758
  // ---------------------------------------------------------------------------
865
759
 
866
- export type InvoiceStatus = "OPEN" | "PAID" | "VOID";
760
+ /** `AUTO_CLOSED` — a recurring invoice for a customer with `autoRecurring` on:
761
+ * applied as if paid, but never payable. */
762
+ export type InvoiceStatus = "OPEN" | "PAID" | "VOID" | "AUTO_CLOSED";
867
763
 
868
764
  /** Trimmed row from `invoices.list()`. */
869
765
  export interface InvoiceListRow {
@@ -1048,78 +944,82 @@ export interface UpdateMemberInput {
1048
944
  }
1049
945
 
1050
946
  // ---------------------------------------------------------------------------
1051
- // Wallet & balance adjustments (customer-scoped)
947
+ // Balance adjustments (customer-scoped)
1052
948
  // ---------------------------------------------------------------------------
1053
949
 
1054
- export type WalletTransactionType = "CREDIT" | "DEBIT";
1055
-
1056
- /** What produced a wallet entry. `null` on rows written before the column
1057
- * existed. */
1058
- export type WalletTransactionSource =
1059
- | "TOPUP"
1060
- | "USAGE"
1061
- | "SETTLE"
1062
- | "ADJUSTMENT";
1063
-
1064
- /** The entitlement kinds a wallet-funded metering debit can name. */
1065
- export type WalletEntitlementKind = "CREDIT" | "OUTCOME" | "UNIT";
1066
-
1067
- export interface WalletTransaction {
1068
- id: string;
1069
- type: WalletTransactionType;
1070
- /** What produced the entry. Null on rows older than the column. */
1071
- source: WalletTransactionSource | null;
1072
- amount: number;
1073
- amountUsd: number;
1074
- currencyCode: string;
1075
- currencyRateUsd: number;
1076
- notes: string | null;
1077
- batchId: string | null;
1078
- createdAt: string;
1079
- createdByName: string | null;
1080
- /** Set only on a wallet-funded metering debit: which kind of entitlement was
1081
- * consumed. Null on top-ups and manual adjustments. */
1082
- entitlementType: WalletEntitlementKind | null;
1083
- /** Id of the credit / outcome / unit consumed. Null unless
1084
- * `entitlementType` is set. */
1085
- targetId: string | null;
1086
- /** Its name at the time of the debit. */
1087
- targetName: string | null;
1088
- /** The signal this debit paid for (credit / outcome). In wallet-funded mode
1089
- * no invoice exists, so this is the only trail from a charge to its cause. */
1090
- usageLogId: string | null;
1091
- /** The unit event this debit paid for. */
1092
- unitUsageLogId: string | null;
1093
- }
1094
-
1095
- export interface WalletList {
1096
- balanceUsd: number;
1097
- transactions: WalletTransaction[];
1098
- }
1099
-
1100
- export interface WalletEntryInput {
1101
- /** DEBIT spends, CREDIT tops up. */
1102
- type: WalletTransactionType;
1103
- /** Always positive; the sign comes from `type`. */
1104
- amount: number;
950
+ /**
951
+ * Which of a customer's two wallet balances an entry moves: `MAIN` (the
952
+ * ordinary wallet) or `RESERVED` (the Reserved Wallet — an org-gated second
953
+ * balance behind it; see {@link WalletAdjustInput.pool}).
954
+ */
955
+ export type WalletPool = "MAIN" | "RESERVED";
956
+
957
+ /** Top up (positive) or draw down (negative) a customer's wallet. `delta` is
958
+ * a non-zero amount in USD (cents allowed); absolute value ≤ 10,000,000. No
959
+ * `agentKey` — a customer has exactly one wallet. The wallet can always be
960
+ * adjusted, whatever the customer's plan (even with no plan). A draw-down is
961
+ * never rejected or floored for going below 0 — the wallet may read negative. */
962
+ export interface WalletAdjustInput {
963
+ /** Signed change in USD: positive tops up, negative draws down. */
964
+ delta: number;
1105
965
  notes?: string;
966
+ /**
967
+ * Which balance to move. Default `"MAIN"`, the ordinary wallet.
968
+ *
969
+ * `"RESERVED"` moves the Reserved Wallet instead — a second balance behind
970
+ * the wallet that a wallet-funded Arrear charge draws on once the wallet
971
+ * runs out (Wallet → Reserved down to 0 → Wallet goes negative). It must be
972
+ * switched on for the org (Settings → Organisation → "Enable Reserved
973
+ * balance in wallet"), else this is a `PlanError`. It never goes negative:
974
+ * a reduce below zero is refused (an `AllowanceError`), not capped — drawing
975
+ * down to exactly 0 is fine. `remaining` is then the Reserved balance. A
976
+ * `"MAIN"` adjustment never touches it.
977
+ */
978
+ pool?: WalletPool;
979
+ /**
980
+ * Optional idempotency key (1–255 characters) that makes this adjustment
981
+ * safe to retry. The first call with a key adjusts; any repeat with the
982
+ * same key and the same request adjusts nothing and comes back with
983
+ * `replayed: true` (and when the original happened, `adjustedAt`). The same
984
+ * key with a different request is a `ConflictError`. Keys are unique per
985
+ * workspace and per adjust endpoint. **Auto-generated (UUID v4) if
986
+ * omitted** and reused across every retry of this call, so a network blip
987
+ * never adjusts twice — same as signals. Supply your own (e.g. your order
988
+ * id) to also dedupe your OWN retries, across restarts or redelivery.
989
+ */
990
+ idempotencyKey?: string;
1106
991
  }
1107
992
 
1108
993
  /** Grant (positive) or claw back (negative) a credit balance. `delta` must be a
1109
- * non-zero whole number. The credit is addressed by its stable `agentKey`. A
1110
- * reduce is never rejected or floored for going below 0 — the resulting
1111
- * balance may read negative, same as a real-usage overdraft already can. */
994
+ * non-zero whole number. The credit is addressed by its stable `agentKey`,
995
+ * and must be a prepaid (ADVANCE) part of the customer's current plan — else
996
+ * a `PlanError` ("… is not part of this customer's plan"). A reduce is never
997
+ * rejected or floored for going below 0 — the resulting balance may read
998
+ * negative, same as a real-usage overdraft already can. */
1112
999
  export interface CreditAdjustInput {
1113
1000
  /** The credit's stable agent key (the same key used to record usage). */
1114
1001
  agentKey: string;
1115
1002
  delta: number;
1116
1003
  notes?: string;
1004
+ /**
1005
+ * Optional idempotency key (1–255 characters) that makes this adjustment
1006
+ * safe to retry. The first call with a key adjusts; any repeat with the
1007
+ * same key and the same request adjusts nothing and comes back with
1008
+ * `replayed: true` (and when the original happened, `adjustedAt`). The same
1009
+ * key with a different request is a `ConflictError`. Keys are unique per
1010
+ * workspace and per adjust endpoint. **Auto-generated (UUID v4) if
1011
+ * omitted** and reused across every retry of this call, so a network blip
1012
+ * never adjusts twice — same as signals. Supply your own (e.g. your order
1013
+ * id) to also dedupe your OWN retries, across restarts or redelivery.
1014
+ */
1015
+ idempotencyKey?: string;
1117
1016
  }
1118
1017
 
1119
1018
  /** Grant/claw an outcome balance. `delta` is a non-zero whole number. The
1120
1019
  * outcome is addressed here by a STEP's `agentKey`, which resolves to its
1121
- * parent outcome — NOT by the outcome's own `agentKey` ({@link Outcome.agentKey}),
1122
- * which this endpoint does not accept. A reduce is never rejected or floored
1020
+ * parent outcome — NOT by the outcome's own `agentKey`,
1021
+ * which this endpoint does not accept. The outcome must be a prepaid
1022
+ * (ADVANCE) part of the customer's current plan — else a `PlanError`. A reduce is never rejected or floored
1123
1023
  * for going below 0 — the resulting balance may read negative, same as a
1124
1024
  * real-usage overdraft already can. */
1125
1025
  export interface OutcomeAdjustInput {
@@ -1128,17 +1028,42 @@ export interface OutcomeAdjustInput {
1128
1028
  agentKey: string;
1129
1029
  delta: number;
1130
1030
  notes?: string;
1031
+ /**
1032
+ * Optional idempotency key (1–255 characters) that makes this adjustment
1033
+ * safe to retry. The first call with a key adjusts; any repeat with the
1034
+ * same key and the same request adjusts nothing and comes back with
1035
+ * `replayed: true` (and when the original happened, `adjustedAt`). The same
1036
+ * key with a different request is a `ConflictError`. Keys are unique per
1037
+ * workspace and per adjust endpoint. **Auto-generated (UUID v4) if
1038
+ * omitted** and reused across every retry of this call, so a network blip
1039
+ * never adjusts twice — same as signals. Supply your own (e.g. your order
1040
+ * id) to also dedupe your OWN retries, across restarts or redelivery.
1041
+ */
1042
+ idempotencyKey?: string;
1131
1043
  }
1132
1044
 
1133
1045
  /** Grant/claw a unit balance. `delta` is non-zero (fractional allowed). The
1134
- * unit is addressed by its stable `agentKey`. A reduce is never rejected or
1135
- * floored for going below 0 — the resulting balance may read negative, same
1136
- * as a real-usage overdraft already can. */
1046
+ * unit is addressed by its stable `agentKey`, and must be a prepaid
1047
+ * (ADVANCE) part of the customer's current plan — else a `PlanError`. A
1048
+ * reduce is never rejected or floored for going below 0 — the resulting
1049
+ * balance may read negative, same as a real-usage overdraft already can. */
1137
1050
  export interface UnitAdjustInput {
1138
1051
  /** The unit's stable agent key (the same key used to record usage). */
1139
1052
  agentKey: string;
1140
1053
  delta: number;
1141
1054
  notes?: string;
1055
+ /**
1056
+ * Optional idempotency key (1–255 characters) that makes this adjustment
1057
+ * safe to retry. The first call with a key adjusts; any repeat with the
1058
+ * same key and the same request adjusts nothing and comes back with
1059
+ * `replayed: true` (and when the original happened, `adjustedAt`). The same
1060
+ * key with a different request is a `ConflictError`. Keys are unique per
1061
+ * workspace and per adjust endpoint. **Auto-generated (UUID v4) if
1062
+ * omitted** and reused across every retry of this call, so a network blip
1063
+ * never adjusts twice — same as signals. Supply your own (e.g. your order
1064
+ * id) to also dedupe your OWN retries, across restarts or redelivery.
1065
+ */
1066
+ idempotencyKey?: string;
1142
1067
  }
1143
1068
 
1144
1069
  /**
@@ -1151,7 +1076,7 @@ export interface UnitAdjustInput {
1151
1076
  */
1152
1077
  export interface CompositeAdjustInput {
1153
1078
  /**
1154
- * The composite's stable identifier — its `refId` (see {@link Composite.refId}),
1079
+ * The composite's stable identifier — its `refId`,
1155
1080
  * the same value signals tag it with, NOT `agentKey` (a composite has no
1156
1081
  * `agentKey` column of its own). Named `agentKey` on the wire only so the
1157
1082
  * four adjust endpoints share one request shape.
@@ -1159,266 +1084,64 @@ export interface CompositeAdjustInput {
1159
1084
  agentKey: string;
1160
1085
  delta: number;
1161
1086
  notes?: string;
1162
- }
1163
-
1164
- // ---------------------------------------------------------------------------
1165
- // Entitlement catalogs: Credits, Outcomes, Units
1166
- // ---------------------------------------------------------------------------
1167
-
1168
- /** A per-model token bundle attached to a credit or outcome step. */
1169
- export interface ModelBundleEntry {
1170
- orgModelId: string;
1171
- /** Display label; the server defaults it from the model when omitted. */
1172
- modelName?: string;
1173
- tokens: number;
1174
- input: number;
1175
- output: number;
1176
- cache: number;
1177
- }
1178
-
1179
- /** One point on a cost-over-time analytics series. */
1180
- export interface CostPoint {
1181
- /** UTC day, `YYYY-MM-DD`. */
1182
- dateIso: string;
1183
- /** Short display label, e.g. "Jun 24". */
1184
- label: string;
1185
- /** Average cost that day, or `null` on days with no usage. */
1186
- cost: number | null;
1187
- reqs: number;
1188
- }
1189
-
1190
- /** Summary stats across a cost-over-time series. */
1191
- export interface CostStats {
1192
- activeDays: number;
1193
- avgCost: number | null;
1194
- peakCost: number | null;
1195
- troughCost: number | null;
1196
- deviationPct: number | null;
1197
- }
1198
-
1199
- /** Cost-over-time analytics for a credit (`credits.get().analytics`). */
1200
- export interface CreditAnalytics {
1201
- basePrice: number;
1202
- pricePerCredit: number;
1203
- marginPercent: number;
1204
- points: CostPoint[];
1205
- stats: CostStats;
1206
- }
1207
-
1208
- /** Cost-over-time analytics for an outcome (`outcomes.get().analytics`).
1209
- * Each point adds `completions` (workflows finished that day). */
1210
- export interface OutcomeAnalytics {
1211
- basePrice: number;
1212
- pricePerOutcome: number;
1213
- marginPercent: number;
1214
- points: (CostPoint & { completions: number })[];
1215
- stats: CostStats;
1216
- }
1217
-
1218
- export interface Credit {
1219
- id: string;
1220
- name: string;
1221
- agentKey: string;
1222
- description: string | null;
1223
- tokensPerCredit: number;
1224
- basePrice: number;
1225
- marginPercent: number;
1226
- pricePerCredit: number;
1227
- isActive: boolean;
1228
- createdAt: string;
1229
- }
1230
-
1231
- /** `credits.get()` — adds the model bundle plus usage stats/analytics. */
1232
- export interface CreditDetail extends Credit {
1233
- modelBundle: ModelBundleEntry[];
1234
- createdByName: string;
1235
- stats: {
1236
- customerCount: number;
1237
- planCount: number;
1238
- totalGranted: number;
1239
- totalUsed: number;
1240
- revenue: number;
1241
- usageLogCount: number;
1242
- };
1243
- analytics: CreditAnalytics;
1244
- }
1245
-
1246
- export interface CreateCreditInput {
1247
- name: string;
1248
- /** Lowercased stable key used by `signals.credit({ agentKey })`. */
1249
- agentKey: string;
1250
- basePrice: number;
1251
- marginPercent: number;
1252
- pricePerCredit: number;
1253
- description?: string;
1254
- tokensPerCredit?: number;
1255
- modelBundle?: ModelBundleEntry[];
1256
- }
1257
-
1258
- export type UpdateCreditInput = CreateCreditInput;
1259
-
1260
- export interface OutcomeStepInput {
1261
- id?: string;
1262
- name: string;
1263
- /** Lowercased stable key used by `signals.outcome({ agentKey })`. */
1264
- agentKey: string;
1265
- basePrice: number;
1266
- modelBundle?: ModelBundleEntry[];
1267
- }
1268
-
1269
- export interface Outcome {
1270
- id: string;
1271
- name: string;
1272
- /**
1273
- * The OUTCOME's own stable agent key — its durable identity, mirroring
1274
- * {@link Credit.agentKey}. Lowercased `[a-z0-9._-]`, max 80 chars, unique per
1275
- * organisation. A rename never changes it.
1276
- *
1277
- * Distinct from each step's `agentKey` in `steps`, and in a SEPARATE
1278
- * namespace: usage is always reported against a STEP's key
1279
- * (`signals.outcome({ agentKey })`), never this one. This key identifies the
1280
- * outcome itself when creating or updating it.
1281
- */
1282
- agentKey: string;
1283
- description: string | null;
1284
- isActive: boolean;
1285
- marginPercent: number;
1286
- pricePerOutcome: number;
1287
- }
1288
-
1289
- /** `outcomes.get()` — adds steps plus in-flight/completed stats. */
1290
- export interface OutcomeDetail extends Outcome {
1291
- /** Only the DETAIL row carries these — `outcomes.list()` returns neither.
1292
- * They were previously declared on {@link Outcome} itself, where
1293
- * `createdAt` was typed as a required `string` that is always `undefined`
1294
- * in practice. */
1295
- createdAt: string;
1296
- createdByName: string | null;
1297
- steps: { name: string; agentKey: string }[];
1298
- stats: {
1299
- stepCount: number;
1300
- customerCount: number;
1301
- inFlight: number;
1302
- completed: number;
1303
- totalRemaining: number;
1304
- };
1305
- analytics: OutcomeAnalytics;
1306
- }
1307
-
1308
- export interface CreateOutcomeInput {
1309
- name: string;
1310
1087
  /**
1311
- * The OUTCOME's own stable agent key — REQUIRED. Lowercased `[a-z0-9._-]`,
1312
- * max 80 chars, unique per organisation (the server lowercases and trims it).
1313
- *
1314
- * Separate namespace from `steps[].agentKey`: this names the outcome, a step
1315
- * key names a step and is what `signals.outcome()` reports against. Omitting
1316
- * it is rejected with a `ValidationError` (400).
1088
+ * Optional idempotency key (1–255 characters) that makes this adjustment
1089
+ * safe to retry. The first call with a key adjusts; any repeat with the
1090
+ * same key and the same request adjusts nothing and comes back with
1091
+ * `replayed: true` (and when the original happened, `adjustedAt`). The same
1092
+ * key with a different request is a `ConflictError`. Keys are unique per
1093
+ * workspace and per adjust endpoint. **Auto-generated (UUID v4) if
1094
+ * omitted** and reused across every retry of this call, so a network blip
1095
+ * never adjusts twice — same as signals. Supply your own (e.g. your order
1096
+ * id) to also dedupe your OWN retries, across restarts or redelivery.
1317
1097
  */
1318
- agentKey: string;
1319
- basePrice: number;
1320
- marginPercent: number;
1321
- pricePerOutcome: number;
1322
- description?: string | null;
1323
- isActive?: boolean;
1324
- /** 1–50 steps; step names and agent keys must be unique. */
1325
- steps: OutcomeStepInput[];
1326
- }
1327
-
1328
- export type UpdateOutcomeInput = CreateOutcomeInput;
1329
-
1330
- export type UnitPricingType = "FLAT" | "SLAB" | "VOLUME";
1331
-
1332
- /** A tier for SLAB/VOLUME pricing; only the last tier may have `upTo: null`. */
1333
- export interface UnitTierInput {
1334
- id?: string;
1335
- upTo: number | null;
1336
- price: number;
1337
- }
1338
-
1339
- export interface UnitTier {
1340
- id: string;
1341
- upTo: number | null;
1342
- price: number;
1343
- order: number;
1344
- }
1345
-
1346
- export interface Unit {
1347
- id: string;
1348
- /** Stable per-org report key — what `signals.unit({ agentKey })` sends.
1349
- * The ONLY identifier accepted when reporting unit consumption. */
1350
- agentKey: string;
1351
- name: string;
1352
- description: string | null;
1353
- pricingType: UnitPricingType;
1354
- flatPrice: number;
1355
- tiers: UnitTier[];
1356
- isActive: boolean;
1357
- createdAt: string;
1358
- createdByName: string | null;
1098
+ idempotencyKey?: string;
1359
1099
  }
1360
1100
 
1361
- /** One row in a unit's linked-customer sample (`units.get().customers`). */
1362
- export interface UnitCustomerRow {
1363
- customerId: string;
1364
- name: string;
1365
- legalName: string | null;
1366
- slug: string;
1367
- logoUrl: string | null;
1368
- granted: number;
1369
- used: number;
1101
+ /**
1102
+ * What every adjust call returns.
1103
+ *
1104
+ * `replayed: true` means this call adjusted NOTHING: an earlier call with the
1105
+ * same `idempotencyKey` already did, at `adjustedAt`. `remaining` is always
1106
+ * the balance as it is now. `customerArchived` is true when the customer is
1107
+ * archived — archived customers follow the same rules (archiving cancels
1108
+ * their plan, so usually only the wallet can still move).
1109
+ */
1110
+ export interface AdjustResult {
1370
1111
  remaining: number;
1371
- currentlyMetered: boolean;
1372
- }
1373
-
1374
- /** `units.get()` — adds linked-customer sample plus stats.
1375
- * `createdByName` is inherited from `Unit`. */
1376
- export interface UnitDetail extends Unit {
1377
- stats: {
1378
- planCount: number;
1379
- customerCount: number;
1380
- totalRemaining: number;
1381
- totalUsed: number;
1382
- };
1383
- /** First page (≤10) of customers currently metered on this unit. */
1384
- customers: UnitCustomerRow[];
1385
- }
1386
-
1387
- /** Flat per-event pricing — a single `flatPrice` (defaults to 0), no tiers. */
1388
- export interface FlatUnitInput {
1389
- name: string;
1390
- /** Stable per-org report key (`signals.unit({ agentKey })`); `[a-z0-9._-]`. */
1391
- agentKey: string;
1392
- pricingType: "FLAT";
1393
- /** Price per event; defaults to 0. */
1394
- flatPrice?: number;
1395
- description?: string | null;
1396
- isActive?: boolean;
1397
- }
1398
-
1399
- /** Tiered pricing (SLAB or VOLUME) — at least one tier is required. */
1400
- export interface TieredUnitInput {
1401
- name: string;
1402
- /** Stable per-org report key (`signals.unit({ agentKey })`); `[a-z0-9._-]`. */
1403
- agentKey: string;
1404
- pricingType: "SLAB" | "VOLUME";
1405
- /** At least one tier (≤50). Only the last tier may have `upTo: null`. */
1406
- tiers: UnitTierInput[];
1407
- description?: string | null;
1408
- isActive?: boolean;
1112
+ replayed: boolean;
1113
+ /** Set only on a replay: when the original adjustment happened (ISO 8601). */
1114
+ adjustedAt?: string;
1115
+ customerArchived: boolean;
1409
1116
  }
1410
1117
 
1411
1118
  /**
1412
- * Create a unit type — a discriminated union on `pricingType`: `FLAT` takes an
1413
- * optional `flatPrice` (defaults to 0); `SLAB`/`VOLUME` require `tiers`, so the
1414
- * server's "add at least one tier" rule is enforced at compile time.
1119
+ * Change how much a customer is granted every billing cycle. The difference is
1120
+ * applied to the current balance immediately (a shrink is never floored — the
1121
+ * balance may read negative). `quantity` is USD for the wallet and a whole
1122
+ * number for everything else. `agentKey` resolves the same way as the adjust
1123
+ * endpoints: a credit's or unit's `agentKey`, an outcome STEP's `agentKey`, or
1124
+ * a composite's `refId`. The wallet needs none — a customer has at most one.
1415
1125
  */
1416
- export type CreateUnitInput = FlatUnitInput | TieredUnitInput;
1126
+ export type RecurringGrantInput =
1127
+ | { kind: "wallet"; quantity: number }
1128
+ | {
1129
+ kind: "credit" | "outcome" | "unit" | "composite";
1130
+ agentKey: string;
1131
+ quantity: number;
1132
+ };
1417
1133
 
1418
- export type UpdateUnitInput = CreateUnitInput;
1134
+ /** Result of `customers.updateRecurringGrant()`. */
1135
+ export interface RecurringGrantResult {
1136
+ kind: RecurringGrantInput["kind"];
1137
+ /** The new per-cycle amount. */
1138
+ quantity: number;
1139
+ /** The wallet balance after the change — only present for `kind: "wallet"`. */
1140
+ balanceUsd?: number;
1141
+ }
1419
1142
 
1420
1143
  // ---------------------------------------------------------------------------
1421
- // Unit consumption (recorded via signals; distinct from catalog `Unit`)
1144
+ // Unit consumption (recorded via signals)
1422
1145
  // ---------------------------------------------------------------------------
1423
1146
 
1424
1147
  /**
@@ -1427,8 +1150,7 @@ export type UpdateUnitInput = CreateUnitInput;
1427
1150
  * the display name is deliberately not accepted, so a rename can never redirect
1428
1151
  * a live integration. Keys are matched exactly (they are stored lowercased).
1429
1152
  */
1430
- export interface UnitInput {
1431
- customerId: string;
1153
+ export type UnitInput = CustomerRef & {
1432
1154
  agentKey: string;
1433
1155
  member?: string;
1434
1156
  /**
@@ -1459,7 +1181,7 @@ export interface UnitInput {
1459
1181
  * auto key cannot survive.
1460
1182
  */
1461
1183
  idempotencyKey?: string;
1462
- }
1184
+ };
1463
1185
 
1464
1186
  /**
1465
1187
  * The result of `signals.unit()`.
@@ -1489,8 +1211,7 @@ export interface UnitTrackResult {
1489
1211
  * Input for `signals.completeComposite()` — closes ONE occurrence of a
1490
1212
  * composite, which is the moment it is billed.
1491
1213
  */
1492
- export interface CompositeCompletionInput {
1493
- customerId: string;
1214
+ export type CompositeCompletionInput = CustomerRef & {
1494
1215
  /** Which occurrence to close: the composite's `refId` and the correlation
1495
1216
  * value the signals were tagged with. */
1496
1217
  composite: CompositeTag;
@@ -1498,79 +1219,12 @@ export interface CompositeCompletionInput {
1498
1219
  * safe no-op, so this only collapses retries into one recorded attempt
1499
1220
  * rather than protecting against a double charge. */
1500
1221
  idempotencyKey?: string;
1501
- }
1222
+ };
1502
1223
 
1503
1224
  // ---------------------------------------------------------------------------
1504
- // Composites catalog (mirrors /api/v1/composites)
1225
+ // Composite completion
1505
1226
  // ---------------------------------------------------------------------------
1506
1227
 
1507
- /** One catalogue item a composite is restricted to. `agentKey` is what a
1508
- * signal names, so it is the field you actually integrate against. */
1509
- export interface CompositeCatalogueRef {
1510
- id: string;
1511
- name: string;
1512
- agentKey: string;
1513
- }
1514
-
1515
- /** One composite in the org's catalogue — `cnk.composites.list()`. */
1516
- export interface Composite {
1517
- id: string;
1518
- name: string;
1519
- /** The stable identifier you put in a signal's `composite` block as `ref`. */
1520
- refId: string;
1521
- description: string | null;
1522
- /** USD charged per occurrence WHEN A PLAN SELLS THIS as a composite
1523
- * component (`quantity × price`, frozen into the purchase at buy time). The
1524
- * catalogue row alone bills nothing. */
1525
- price: number;
1526
- /** False once archived. An archived composite stops NEW signals resolving to
1527
- * it but keeps every row it already owns. */
1528
- isActive: boolean;
1529
- createdAt: string;
1530
- /**
1531
- * The credits / outcomes / units this composite is restricted to — only
1532
- * signals naming one of these may carry its tag.
1533
- *
1534
- * All three lists empty means UNRESTRICTED (it accepts any of the org's
1535
- * traffic under this tag). Nothing created through the API can be
1536
- * unrestricted, but rows predating that rule exist — so don't assume
1537
- * non-empty.
1538
- */
1539
- entitlements: {
1540
- credits: CompositeCatalogueRef[];
1541
- outcomes: CompositeCatalogueRef[];
1542
- units: CompositeCatalogueRef[];
1543
- };
1544
- }
1545
-
1546
- /** Input for `cnk.composites.create()`. */
1547
- export interface CreateCompositeInput {
1548
- name: string;
1549
- /**
1550
- * The tag identifier. Lowercased, and restricted to the same characters as
1551
- * an `agentKey` — but validated harder than one, because it may not collide
1552
- * with a field the ingest schemas already own (`customerId`, `usage`,
1553
- * `agentKey`, `runId`, `member`, `custom`, `composite`, …). A colliding
1554
- * `refId` is refused at create time rather than silently ignored at ingest.
1555
- */
1556
- refId: string;
1557
- /** USD per occurrence once a plan sells it. Required — an unpriced composite
1558
- * put on a plan would bill 0, which is a money bug that is hard to see. */
1559
- price: number;
1560
- description?: string | null;
1561
- /**
1562
- * What the composite wraps. At least one id across the three lists is
1563
- * REQUIRED: a composite restricted to nothing would accept every signal in
1564
- * the org. Get the ids from `credits.list()`, `outcomes.list()` and
1565
- * `units.list()`.
1566
- */
1567
- entitlements: {
1568
- creditIds?: string[];
1569
- outcomeIds?: string[];
1570
- unitIds?: string[];
1571
- };
1572
- }
1573
-
1574
1228
  /**
1575
1229
  * The result of `signals.completeComposite()`.
1576
1230
  *
@@ -1603,8 +1257,7 @@ export interface CompositeCompletionResult {
1603
1257
  rawId?: string;
1604
1258
  }
1605
1259
 
1606
- /** One serialized unit-consumption event — the result of `signals.unit()` and
1607
- * each element of `UnitUsageList.events`. */
1260
+ /** One serialized unit-consumption event — the result of `signals.unit()`. */
1608
1261
  export interface SerializedUnitUsage {
1609
1262
  id: string;
1610
1263
  customerId: string;
@@ -1620,48 +1273,6 @@ export interface SerializedUnitUsage {
1620
1273
  createdAt: string;
1621
1274
  }
1622
1275
 
1623
- export interface UnitUsageRow {
1624
- unit: string;
1625
- /** The unit's stable agent key — what to send to report against it. */
1626
- agentKey: string | null;
1627
- billingMode: BillingMode;
1628
- granted?: number;
1629
- used: number;
1630
- remaining?: number;
1631
- }
1632
-
1633
- export interface UnitUsageList {
1634
- customer: { id: string; name: string };
1635
- units: UnitUsageRow[];
1636
- events: SerializedUnitUsage[];
1637
- }
1638
-
1639
- // ---------------------------------------------------------------------------
1640
- // Webhooks (threshold alerts)
1641
- // ---------------------------------------------------------------------------
1642
-
1643
- export type ThresholdKind = "CREDIT" | "OUTCOME" | "WALLET";
1644
-
1645
- export interface Webhook {
1646
- id: string;
1647
- kind: ThresholdKind;
1648
- threshold: number;
1649
- url: string;
1650
- isActive: boolean;
1651
- createdAt: string;
1652
- updatedAt: string;
1653
- createdByName: string | null;
1654
- }
1655
-
1656
- export interface UpsertWebhookInput {
1657
- kind: ThresholdKind;
1658
- /** Fire when the balance crosses this value. */
1659
- threshold: number;
1660
- /** http(s) URL to POST the alert to. */
1661
- url: string;
1662
- isActive?: boolean;
1663
- }
1664
-
1665
1276
  // ---------------------------------------------------------------------------
1666
1277
  // Config & hooks
1667
1278
  // ---------------------------------------------------------------------------