@clocknext/sdk 0.11.0 → 0.12.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/dist/index.d.cts CHANGED
@@ -139,6 +139,11 @@ interface OutcomeSignal extends SignalBase {
139
139
  * Set `true` on the LAST step signal to declare the run finished — completion
140
140
  * is never inferred from step coverage. Sending it twice is a safe no-op and
141
141
  * can't bill again.
142
+ *
143
+ * This closes the RUN only. If the signal also carries a `composite`, that
144
+ * occurrence stays open and unbilled until you call
145
+ * `signals.completeComposite()` — the two completions are separate, and
146
+ * neither implies the other.
142
147
  */
143
148
  complete?: boolean;
144
149
  /** Optional composite this signal belongs to. See {@link CompositeTag}. */
@@ -228,9 +233,46 @@ interface SerializedUsageLog {
228
233
  * When the server has no queue configured it prices inline instead and returns
229
234
  * `queued: false` with a populated `usageLog`, validating synchronously.
230
235
  */
236
+ /**
237
+ * How far a metering call actually got. See {@link TrackResult.status} — the
238
+ * three states differ in whether the signal can still be lost, and in whether
239
+ * it has been billed.
240
+ */
241
+ type SignalDelivery = "buffered" | "accepted" | "recorded" | "dropped";
231
242
  interface TrackResult {
232
- /** True when the signal has not been priced yet — either buffered locally
233
- * (async mode) or accepted for asynchronous processing by the server. */
243
+ /**
244
+ * How far this signal has actually got. Branch on this, not on `queued`.
245
+ *
246
+ * `"buffered"` — held in THIS process's memory and not yet sent. Lost if
247
+ * the process exits first, so `flush()`/`close()` before it
248
+ * can. No `rawId` exists yet.
249
+ * `"accepted"` — the server has written it to its durable inbox (`202`)
250
+ * and will price it shortly. It can no longer be lost, but
251
+ * it is not billed yet and `usageLog` is null. A rejection
252
+ * discovered during pricing will NOT come back to you here,
253
+ * nor on `onError`. `signals.list()` shows what got priced;
254
+ * the dashboard's signal log — owner-only, by design — is
255
+ * where an individual rejection says why.
256
+ * `"recorded"` — the server priced it inline and `usageLog` is populated.
257
+ * `"dropped"` — the SDK threw it away without sending: the client is
258
+ * closed, or the local queue is full. `onDrop` fired with
259
+ * the reason. This state exists because the alternative was
260
+ * reporting `"buffered"` for a signal that was discarded —
261
+ * a success-shaped answer to a lost billable event.
262
+ *
263
+ * Note what none of these mean: "waited for a price". Even `{ wait: true }`
264
+ * waits only for the server to ACCEPT the signal, never for it to finish
265
+ * pricing — holding a request thread open for a billing round-trip is
266
+ * exactly the cost this design exists to avoid.
267
+ */
268
+ status: SignalDelivery;
269
+ /**
270
+ * True for both `"buffered"` and `"accepted"` — i.e. "not priced yet".
271
+ *
272
+ * Kept for compatibility, but it collapses two states with opposite failure
273
+ * behaviour (one is lost when the process dies, the other cannot be) into
274
+ * one boolean, which is why {@link TrackResult.status} exists. Prefer that.
275
+ */
234
276
  queued: boolean;
235
277
  /** The computed usage log. Non-null ONLY when the server priced the signal
236
278
  * inline (`queued: false`); null on a `202` hand-off and absent while the
@@ -416,7 +458,15 @@ type BillingCycle = "MONTHLY" | "QUARTERLY" | "SEMI_ANNUAL" | "YEARLY" | "EVERY_
416
458
  type PlanType = "WALLET" | "BUNDLE";
417
459
  type PlanKind = "FLAT_PRICE" | "PAY_AS_YOU_GO" | "PAY_PER_SEAT";
418
460
  /** A plan component's meter type. */
419
- type ComponentType = "WALLET" | "CREDIT" | "OUTCOME" | "UNIT" | "FLAT";
461
+ /**
462
+ * A plan component's kind.
463
+ *
464
+ * `PRICING_METRIC` is a COMPOSITE component — the server's wire name for it,
465
+ * kept verbatim because that is the string you will actually receive and must
466
+ * send back. It was missing here, which made a `switch` over `PlanComponent.type`
467
+ * look exhaustive to TypeScript while silently skipping every composite.
468
+ */
469
+ type ComponentType = "WALLET" | "CREDIT" | "OUTCOME" | "UNIT" | "FLAT" | "PRICING_METRIC";
420
470
  /** ADVANCE bills up-front for the cycle; ARREAR bills for what was consumed. */
421
471
  type BillingMode = "ADVANCE" | "ARREAR";
422
472
  /** Prepaid USD wallet. `amount` is the funded balance (required). Max one
@@ -536,6 +586,11 @@ interface PlanComponent {
536
586
  creditId: string | null;
537
587
  outcomeId: string | null;
538
588
  unitId: string | null;
589
+ /** The composite this component sells, on a `PRICING_METRIC` component.
590
+ * Without it a plan read back could not be written back: the write shape
591
+ * ({@link CompositeComponentInput}) requires this id, so a read-modify-write
592
+ * silently dropped every composite component. */
593
+ pricingMetricId: string | null;
539
594
  targetName: string | null;
540
595
  quantity: number | null;
541
596
  amount: number | null;
@@ -550,6 +605,9 @@ interface Plan {
550
605
  cost: number;
551
606
  /** Signed adjustment applied to the plan's headline price (default 0). */
552
607
  priceAdjustment: number;
608
+ /** When true, ARREAR consumption is funded from the customer's wallet as it
609
+ * happens instead of being invoiced at cycle end. All-or-nothing per plan. */
610
+ walletFundedArrear: boolean;
553
611
  currencyCode: string;
554
612
  isActive: boolean;
555
613
  components: PlanComponent[];
@@ -591,6 +649,8 @@ interface Purchase {
591
649
  hasCredit: boolean;
592
650
  hasOutcome: boolean;
593
651
  hasUnit: boolean;
652
+ /** True when the purchased plan sells at least one composite. */
653
+ hasComposite: boolean;
594
654
  billingCycle: BillingCycle;
595
655
  cost: number;
596
656
  currencyCode: string;
@@ -798,9 +858,16 @@ interface UpdateMemberInput {
798
858
  email?: string;
799
859
  }
800
860
  type WalletTransactionType = "CREDIT" | "DEBIT";
861
+ /** What produced a wallet entry. `null` on rows written before the column
862
+ * existed. */
863
+ type WalletTransactionSource = "TOPUP" | "USAGE" | "SETTLE" | "ADJUSTMENT";
864
+ /** The entitlement kinds a wallet-funded metering debit can name. */
865
+ type WalletEntitlementKind = "CREDIT" | "OUTCOME" | "UNIT";
801
866
  interface WalletTransaction {
802
867
  id: string;
803
868
  type: WalletTransactionType;
869
+ /** What produced the entry. Null on rows older than the column. */
870
+ source: WalletTransactionSource | null;
804
871
  amount: number;
805
872
  amountUsd: number;
806
873
  currencyCode: string;
@@ -809,6 +876,19 @@ interface WalletTransaction {
809
876
  batchId: string | null;
810
877
  createdAt: string;
811
878
  createdByName: string | null;
879
+ /** Set only on a wallet-funded metering debit: which kind of entitlement was
880
+ * consumed. Null on top-ups and manual adjustments. */
881
+ entitlementType: WalletEntitlementKind | null;
882
+ /** Id of the credit / outcome / unit consumed. Null unless
883
+ * `entitlementType` is set. */
884
+ targetId: string | null;
885
+ /** Its name at the time of the debit. */
886
+ targetName: string | null;
887
+ /** The signal this debit paid for (credit / outcome). In wallet-funded mode
888
+ * no invoice exists, so this is the only trail from a charge to its cause. */
889
+ usageLogId: string | null;
890
+ /** The unit event this debit paid for. */
891
+ unitUsageLogId: string | null;
812
892
  }
813
893
  interface WalletList {
814
894
  balanceUsd: number;
@@ -959,14 +1039,14 @@ interface Outcome {
959
1039
  isActive: boolean;
960
1040
  marginPercent: number;
961
1041
  pricePerOutcome: number;
962
- createdAt: string;
963
- steps?: {
964
- name: string;
965
- agentKey: string;
966
- }[];
967
1042
  }
968
1043
  /** `outcomes.get()` — adds steps plus in-flight/completed stats. */
969
1044
  interface OutcomeDetail extends Outcome {
1045
+ /** Only the DETAIL row carries these — `outcomes.list()` returns neither.
1046
+ * They were previously declared on {@link Outcome} itself, where
1047
+ * `createdAt` was typed as a required `string` that is always `undefined`
1048
+ * in practice. */
1049
+ createdAt: string;
970
1050
  createdByName: string | null;
971
1051
  steps: {
972
1052
  name: string;
@@ -1130,7 +1210,11 @@ interface UnitInput {
1130
1210
  * a bare `{ unitUsage }`. Branch on `queued`; never assume `unitUsage`.
1131
1211
  */
1132
1212
  interface UnitTrackResult {
1133
- /** True when the report was accepted but not yet priced (`202`). */
1213
+ /** How far the report got. Never `"buffered"` — unit reports are always sent
1214
+ * immediately, not queued client-side. See {@link TrackResult.status}. */
1215
+ status: Extract<SignalDelivery, "accepted" | "recorded">;
1216
+ /** True when the report was accepted but not yet priced (`202`). Prefer
1217
+ * {@link UnitTrackResult.status}. */
1134
1218
  queued: boolean;
1135
1219
  /** The priced unit event. Non-null ONLY when the server priced inline. */
1136
1220
  unitUsage?: SerializedUnitUsage | null;
@@ -1227,10 +1311,16 @@ interface CreateCompositeInput {
1227
1311
  * lands. That case answers with no `status`, only a `rawId` to follow.
1228
1312
  */
1229
1313
  interface CompositeCompletionResult {
1230
- /** True when the ping was accepted but not yet applied (`202`). */
1314
+ /** How far the ping got. Never `"buffered"` — completions are always sent
1315
+ * immediately. `"recorded"` means the occurrence was closed inline. See
1316
+ * {@link TrackResult.status}. */
1317
+ delivery: Extract<SignalDelivery, "accepted" | "recorded">;
1318
+ /** True when the ping was accepted but not yet applied (`202`). Prefer
1319
+ * {@link CompositeCompletionResult.delivery}. */
1231
1320
  queued: boolean;
1232
1321
  /** `COMPLETED` on the first close, `ALREADY_COMPLETED` on a replay. Absent
1233
- * when the ping was queued or parked. */
1322
+ * when the ping was queued or parked. Distinct from `delivery`: this is the
1323
+ * server's verdict on the OCCURRENCE, that is how far the REQUEST got. */
1234
1324
  status?: "COMPLETED" | "ALREADY_COMPLETED";
1235
1325
  /** The composite that was closed. Null on the replay of a historical row
1236
1326
  * whose occurrence can no longer be read back. */
@@ -1281,10 +1371,14 @@ interface UnitUsageList {
1281
1371
  }
1282
1372
  type ThresholdKind = "CREDIT" | "OUTCOME" | "WALLET";
1283
1373
  interface Webhook {
1374
+ id: string;
1284
1375
  kind: ThresholdKind;
1285
1376
  threshold: number;
1286
1377
  url: string;
1287
1378
  isActive: boolean;
1379
+ createdAt: string;
1380
+ updatedAt: string;
1381
+ createdByName: string | null;
1288
1382
  }
1289
1383
  interface UpsertWebhookInput {
1290
1384
  kind: ThresholdKind;
@@ -1300,7 +1394,16 @@ interface Logger {
1300
1394
  warn?: (...args: unknown[]) => void;
1301
1395
  error?: (...args: unknown[]) => void;
1302
1396
  }
1303
- type DropReason = "queue_full" | "send_failed";
1397
+ /**
1398
+ * Why a signal was dropped without being billed.
1399
+ *
1400
+ * `queue_full` — the local buffer hit `batch.maxQueueSize`; never sent.
1401
+ * `closed` — tracked after `close()`; never sent.
1402
+ * `send_failed` — sent, and permanently rejected or unreachable. This is the
1403
+ * only reason that also fires `onError`, which carries the
1404
+ * error (and its `idempotencyKey`, for a safe replay).
1405
+ */
1406
+ type DropReason = "queue_full" | "closed" | "send_failed";
1304
1407
  interface ClockNextConfig {
1305
1408
  /** The org API key (`cnk_…`). Required. */
1306
1409
  apiKey: string;
@@ -1391,6 +1494,15 @@ interface RequestOptions {
1391
1494
  * opt in explicitly.
1392
1495
  */
1393
1496
  retry?: boolean;
1497
+ /**
1498
+ * The idempotency key carried in this request's body, for metering calls.
1499
+ *
1500
+ * Copied onto any error thrown, so a caller handling a permanent failure can
1501
+ * replay the signal with the SAME key instead of minting a fresh one — which
1502
+ * would defeat server-side dedup and bill twice. See
1503
+ * `ClockNextError.idempotencyKey`.
1504
+ */
1505
+ idempotencyKey?: string;
1394
1506
  }
1395
1507
  /**
1396
1508
  * The single HTTP seam. Builds the authenticated request, enforces a timeout,
@@ -1428,7 +1540,9 @@ declare class Composites {
1428
1540
  constructor(transport: Transport);
1429
1541
  /** List composites. Pass `{ active: true }` for only the ones a signal can
1430
1542
  * still be tagged with — the default list INCLUDES archived ones, which
1431
- * keep every row they already own. */
1543
+ * keep every row they already own.
1544
+ * `{ active: false }` returns only archived ones; omitting it returns
1545
+ * both. See `activeQuery` — the API itself only supports `true`. */
1432
1546
  list(params?: {
1433
1547
  active?: boolean;
1434
1548
  }): Promise<Composite[]>;
@@ -1444,7 +1558,9 @@ declare class Composites {
1444
1558
  declare class Credits {
1445
1559
  private readonly transport;
1446
1560
  constructor(transport: Transport);
1447
- /** List credit types. Pass `{ active: true }` to only return active ones. */
1561
+ /** List credit types. Pass `{ active: true }` to only return active ones.
1562
+ * `{ active: false }` returns only archived ones; omitting it returns
1563
+ * both. See `activeQuery` — the API itself only supports `true`. */
1448
1564
  list(params?: {
1449
1565
  active?: boolean;
1450
1566
  }): Promise<Credit[]>;
@@ -1593,7 +1709,9 @@ declare class Invoices {
1593
1709
  declare class Outcomes {
1594
1710
  private readonly transport;
1595
1711
  constructor(transport: Transport);
1596
- /** List outcome types. Pass `{ active: true }` to only return active ones. */
1712
+ /** List outcome types. Pass `{ active: true }` to only return active ones.
1713
+ * `{ active: false }` returns only archived ones; omitting it returns
1714
+ * both. See `activeQuery` — the API itself only supports `true`. */
1597
1715
  list(params?: {
1598
1716
  active?: boolean;
1599
1717
  }): Promise<Outcome[]>;
@@ -1633,7 +1751,9 @@ declare class Payments {
1633
1751
  declare class Plans {
1634
1752
  private readonly transport;
1635
1753
  constructor(transport: Transport);
1636
- /** List plans, oldest-first. Pass `{ active: true }` to only return active plans. */
1754
+ /** List plans, oldest-first. Pass `{ active: true }` to only return active plans.
1755
+ * `{ active: false }` returns only archived ones; omitting it returns
1756
+ * both. See `activeQuery` — the API itself only supports `true`. */
1637
1757
  list(params?: {
1638
1758
  active?: boolean;
1639
1759
  }): Promise<Plan[]>;
@@ -1725,13 +1845,30 @@ declare class Flusher {
1725
1845
  private draining;
1726
1846
  private closed;
1727
1847
  constructor(transport: Transport, cfg: ResolvedConfig);
1728
- /** Buffer one signal. Drops (with onDrop) when closed or the queue is full. */
1729
- enqueue(item: QueuedSignal): void;
1848
+ /**
1849
+ * Buffer one signal. Drops (with onDrop) when closed or the queue is full.
1850
+ * Returns whether it was actually buffered, so `track()` can report a drop
1851
+ * honestly instead of answering "buffered" for a signal it threw away.
1852
+ */
1853
+ enqueue(item: QueuedSignal): boolean;
1730
1854
  /** Arm the interval timer if it isn't already running. */
1731
1855
  private arm;
1856
+ /** Cancel the interval timer if one is armed. */
1857
+ private clearTimer;
1732
1858
  /**
1733
- * Send everything currently buffered. Overlapping calls share the single
1734
- * in-progress drain, so total in-flight sends never exceed `maxConcurrency`.
1859
+ * Send everything buffered at the moment this resolves.
1860
+ *
1861
+ * Overlapping calls CHAIN onto the in-flight drain rather than sharing it.
1862
+ * Sharing looks equivalent and is not: a drain that has already passed its
1863
+ * final `size() > 0` check cannot see a signal enqueued a moment later, so
1864
+ * handing that same promise back would resolve `flush()` — and therefore
1865
+ * `close()` — with the buffer non-empty. Since `close()` has already set
1866
+ * `closed`, `arm()` will not re-arm, and the timer it did arm is `unref`'d,
1867
+ * so the process is free to exit and that signal is simply lost. Chaining
1868
+ * guarantees at least one full drain begins after this call.
1869
+ *
1870
+ * Concurrency is unaffected: drains run one after another, and each one
1871
+ * sends at most `maxConcurrency` signals at a time.
1735
1872
  */
1736
1873
  flush(): Promise<void>;
1737
1874
  /** Drain the buffer in concurrency-bounded waves until it's empty. */
@@ -1765,18 +1902,31 @@ declare function signalToWire(signal: Signal): Record<string, unknown>;
1765
1902
  * The `signals` resource: record usage signals and read them back.
1766
1903
  *
1767
1904
  * In async mode (default), `track()` buffers the signal locally and returns
1768
- * immediately with `{ queued: true }`. Pass `{ wait: true }` (or construct the
1769
- * client in `sync` mode) to await the request.
1905
+ * immediately with `{ status: "buffered" }`. Pass `{ wait: true }` (or
1906
+ * construct the client in `sync` mode) to await the request instead.
1907
+ *
1908
+ * Awaiting the request does NOT mean waiting for a price, and deliberately so.
1909
+ * The server writes every signal to a durable inbox and hands it to a worker,
1910
+ * answering `202` — so `track()` resolves `{ status: "accepted", messageId,
1911
+ * rawId, usageLog: null }`: impossible to lose, not yet billed. Only when the
1912
+ * server prices inline do you get `{ status: "recorded", usageLog }`. Branch on
1913
+ * `status`; never assume `usageLog`.
1914
+ *
1915
+ * Nothing here ever blocks a request thread on billing. That is the point: a
1916
+ * meter that makes the caller wait for pricing has moved your billing system
1917
+ * onto your product's critical path.
1770
1918
  *
1771
- * Awaiting the request does NOT guarantee a priced result. The server writes
1772
- * every signal to a durable inbox and then hands it to a worker, so it answers
1773
- * `202` and `track()` resolves `{ queued: true, messageId, rawId, usageLog:
1774
- * null }` — accepted and impossible to lose, but not yet billed. Only when the
1775
- * server prices inline do you get `{ queued: false, usageLog }`. Branch on
1776
- * `queued`; never assume `usageLog`.
1919
+ * Because of that, a signal the server later REJECTS (unknown `agentKey`, a
1920
+ * model the org has not enabled, no active plan) cannot come back through this
1921
+ * promise — and does not reach `onError` either, since from here that request
1922
+ * succeeded. Such a rejection has no programmatic channel at all: it is
1923
+ * recorded on the workspace's signal log in the ClockNext dashboard, which is
1924
+ * owner-only by design.
1777
1925
  *
1778
- * `list()` is how you confirm one actually landed — and, ingestion being
1779
- * asynchronous, the only way to.
1926
+ * `list()` is the partial check available from code. It returns priced usage
1927
+ * logs with a `status`, so it confirms a signal landed and whether pricing
1928
+ * succeeded — but a signal refused BEFORE pricing never becomes a usage log
1929
+ * and does not appear there.
1780
1930
  */
1781
1931
  declare class Signals {
1782
1932
  private readonly transport;
@@ -1843,9 +1993,13 @@ declare class Signals {
1843
1993
  * arrive in any order and the occurrence stays open and unbilled until you
1844
1994
  * say otherwise. Put it on the line before your own `return`.
1845
1995
  *
1846
- * An OUTCOME never needs this — its own `complete: true` closes its run and
1847
- * the matching occurrence together. Credits and units have no "done" signal
1848
- * of their own, so this is theirs.
1996
+ * EVERY composite needs this, outcome-backed ones included. An outcome's own
1997
+ * `complete: true` closes its RUN and nothing else — it does not close the
1998
+ * composite occurrence the run was tagged into, whatever mix of credits,
1999
+ * outcomes and units that composite wraps. (Earlier versions of this SDK
2000
+ * documented the opposite. Believing it means the occurrence stays ONGOING
2001
+ * forever and is never billed, so if you tag outcome signals with a
2002
+ * `composite`, you must still call this.)
1849
2003
  *
1850
2004
  * Closing twice is safe: the second call answers `ALREADY_COMPLETED` and
1851
2005
  * cannot bill again. Calling it EARLY is also safe — before every entitlement
@@ -1874,7 +2028,9 @@ declare class Signals {
1874
2028
  declare class Units {
1875
2029
  private readonly transport;
1876
2030
  constructor(transport: Transport);
1877
- /** List unit types. Pass `{ active: true }` to only return active ones. */
2031
+ /** List unit types. Pass `{ active: true }` to only return active ones.
2032
+ * `{ active: false }` returns only archived ones; omitting it returns
2033
+ * both. See `activeQuery` — the API itself only supports `true`. */
1878
2034
  list(params?: {
1879
2035
  active?: boolean;
1880
2036
  }): Promise<Unit[]>;
@@ -1916,7 +2072,9 @@ declare class Workspace {
1916
2072
  /** Identify the org + key behind the request, incl. `sandbox` | `live`. */
1917
2073
  me(): Promise<WorkspaceIdentity>;
1918
2074
  /** The org's enabled models with live prices. `{ active: true }` returns only
1919
- * the models that can be metered right now. */
2075
+ * the models that can be metered right now.
2076
+ * `{ active: false }` returns only archived ones; omitting it returns
2077
+ * both. See `activeQuery` — the API itself only supports `true`. */
1920
2078
  models(params?: {
1921
2079
  active?: boolean;
1922
2080
  }): Promise<Model[]>;
@@ -1978,24 +2136,45 @@ declare class ClockNext {
1978
2136
  }
1979
2137
 
1980
2138
  /**
1981
- * Typed error hierarchy. The server returns `{ ok: false, error }` with a
1982
- * meaningful HTTP status; we map that status to a class so callers can branch
1983
- * on the *kind* of failure (e.g. allowance exhausted ⇒ upsell; network ⇒ ignore,
1984
- * it's queued) rather than string-matching messages.
2139
+ * Typed error hierarchy. The server returns `{ statusCode, statusDetail, result }`
2140
+ * with a meaningful HTTP status; we map that status to a class so callers can
2141
+ * branch on the *kind* of failure (e.g. allowance exhausted ⇒ upsell; network ⇒
2142
+ * ignore, it's queued) rather than string-matching messages.
1985
2143
  */
2144
+ /** Machine-readable failure code, when the server sends one
2145
+ * (`statusDetail.code`). Preferred over any message heuristic. */
2146
+ type ClockNextErrorCode = "ALLOWANCE_EXHAUSTED" | "PLAN_REJECTED" | "IDEMPOTENCY_UNRESOLVED" | (string & {});
1986
2147
  declare class ClockNextError extends Error {
1987
2148
  /** HTTP status, when the failure came from a server response. */
1988
2149
  readonly status?: number;
1989
2150
  /** Whether the SDK considers this failure worth retrying. */
1990
2151
  readonly retryable: boolean;
2152
+ /**
2153
+ * The server's machine-readable code, when it sent one. Branch on this in
2154
+ * preference to `.message`, which is human copy and may be reworded.
2155
+ */
2156
+ readonly code?: ClockNextErrorCode;
2157
+ /**
2158
+ * The idempotency key of the request that failed, for metering calls.
2159
+ *
2160
+ * This is what makes a dead-letter retry safe: re-send the failed signal with
2161
+ * this SAME key and the server deduplicates it instead of billing twice. A
2162
+ * fresh key would double-bill, so never generate one when replaying.
2163
+ */
2164
+ readonly idempotencyKey?: string;
1991
2165
  constructor(message: string, opts?: {
1992
2166
  status?: number;
1993
2167
  retryable?: boolean;
2168
+ code?: ClockNextErrorCode;
2169
+ idempotencyKey?: string;
1994
2170
  });
1995
2171
  }
1996
2172
  /** 401 — missing / invalid / expired API key. */
1997
2173
  declare class AuthError extends ClockNextError {
1998
2174
  }
2175
+ /** 403 — the key is valid but cannot perform this operation. */
2176
+ declare class ForbiddenError extends ClockNextError {
2177
+ }
1999
2178
  /** 400 — malformed request / schema validation failure. */
2000
2179
  declare class ValidationError extends ClockNextError {
2001
2180
  }
@@ -2005,10 +2184,13 @@ declare class NotFoundError extends ClockNextError {
2005
2184
  /** 422 — no active plan, meter not in plan, model not enabled, member missing. */
2006
2185
  declare class PlanError extends ClockNextError {
2007
2186
  }
2008
- /** 422 — insufficient credits / units / outcomes for the call. */
2187
+ /** 422 — the entitlement ran out: no credits / units / outcomes / composite
2188
+ * slots left. Distinct from {@link PlanError} because the fix is different —
2189
+ * this one is a top-up or an upsell, not a configuration change. */
2009
2190
  declare class AllowanceError extends ClockNextError {
2010
2191
  }
2011
- /** 409 — conflict. */
2192
+ /** 409 — conflict. Almost always permanent (an archived customer); only the
2193
+ * idempotency-resolution conflict is transient, and only that one retries. */
2012
2194
  declare class ConflictError extends ClockNextError {
2013
2195
  }
2014
2196
  /** 429 — rate limited. */
@@ -2016,6 +2198,7 @@ declare class RateLimitError extends ClockNextError {
2016
2198
  readonly retryAfterMs?: number;
2017
2199
  constructor(message: string, opts?: {
2018
2200
  retryAfterMs?: number;
2201
+ idempotencyKey?: string;
2019
2202
  });
2020
2203
  }
2021
2204
  /** 5xx — server-side error. */
@@ -2023,7 +2206,9 @@ declare class ServerError extends ClockNextError {
2023
2206
  }
2024
2207
  /** fetch threw / request timed out — request never completed. */
2025
2208
  declare class NetworkError extends ClockNextError {
2026
- constructor(message: string);
2209
+ constructor(message: string, opts?: {
2210
+ idempotencyKey?: string;
2211
+ });
2027
2212
  }
2028
2213
 
2029
- export { type AddMemberInput, AllowanceError, type ApiCustomer, AuthError, type BillingCycle, type BillingMode, ClockNext, type ClockNextConfig, ClockNextError, type ComponentType, type Composite, type CompositeCatalogueRef, type CompositeCompletionInput, type CompositeCompletionResult, type CompositeComponentInput, type CompositeTag, ConflictError, type CostPoint, type CostStats, type CreateCompositeInput, type CreateCreditInput, type CreateCustomerInput, type CreateOutcomeInput, type CreatePlanInput, type CreatePurchaseInput, type CreateUnitInput, type Credit, type CreditAdjustInput, type CreditAnalytics, type CreditBalance, type CreditComponentInput, type CreditDetail, type CreditInput, type CreditSignal, type CustomerBalances, type CustomerList, type CustomerPlan, type CustomerPlanDetails, type CustomerProfileInput, type DropReason, type FetchLike, type FlatComponentInput, type FlatUnitInput, type Invoice, type InvoiceDetail, type InvoiceLineItem, type InvoiceListRow, type InvoiceParty, type InvoicePlanSnapshot, type InvoiceStatus, type Logger, type Member, type MemberProfile, type Model, type ModelBundleEntry, NetworkError, NotFoundError, type Outcome, type OutcomeAdjustInput, type OutcomeAnalytics, type OutcomeBalance, type OutcomeComponentInput, type OutcomeDetail, type OutcomeInput, type OutcomeSignal, type OutcomeStepInput, type PayLink, type PaymentDetail, type PaymentRow, type Plan, type PlanComponent, type PlanComponentInput, type PlanDetail, PlanError, type PlanKind, type PlanRef, type PlanType, type PortalToken, type PortalTokenInput, type Purchase, type PurchaseDetail, type PurchaseStatus, RateLimitError, type RemovedMember, type SerializedOutcomeAttribution, type SerializedRuleApplication, type SerializedUnitUsage, type SerializedUsageLog, ServerError, type Signal, type SignalStatus, type SignalType, type ThresholdKind, type TieredUnitInput, type Tokens, type TrackResult, type Unit, type UnitAdjustInput, type UnitBalance, type UnitComponentInput, type UnitCustomerRow, type UnitDetail, type UnitInput, type UnitPricingType, type UnitTier, type UnitTierInput, type UnitTrackResult, type UnitUsageList, type UnitUsageRow, type UpdateCreditInput, type UpdateMemberInput, type UpdateOutcomeInput, type UpdatePlanInput, type UpdateUnitInput, type UpsertWebhookInput, type UsageBalance, type UsageList, ValidationError, type WalletComponentInput, type WalletEntryInput, type WalletInput, type WalletList, type WalletSignal, type WalletTransaction, type WalletTransactionType, type Webhook, type WorkspaceIdentity, signalToWire };
2214
+ export { type AddMemberInput, AllowanceError, type ApiCustomer, AuthError, type BillingCycle, type BillingMode, ClockNext, type ClockNextConfig, ClockNextError, type ClockNextErrorCode, type ComponentType, type Composite, type CompositeCatalogueRef, type CompositeCompletionInput, type CompositeCompletionResult, type CompositeComponentInput, type CompositeTag, ConflictError, type CostPoint, type CostStats, type CreateCompositeInput, type CreateCreditInput, type CreateCustomerInput, type CreateOutcomeInput, type CreatePlanInput, type CreatePurchaseInput, type CreateUnitInput, type Credit, type CreditAdjustInput, type CreditAnalytics, type CreditBalance, type CreditComponentInput, type CreditDetail, type CreditInput, type CreditSignal, type CustomerBalances, type CustomerList, type CustomerPlan, type CustomerPlanDetails, type CustomerProfileInput, type DropReason, type FetchLike, type FlatComponentInput, type FlatUnitInput, ForbiddenError, type Invoice, type InvoiceDetail, type InvoiceLineItem, type InvoiceListRow, type InvoiceParty, type InvoicePlanSnapshot, type InvoiceStatus, type Logger, type Member, type MemberProfile, type Model, type ModelBundleEntry, NetworkError, NotFoundError, type Outcome, type OutcomeAdjustInput, type OutcomeAnalytics, type OutcomeBalance, type OutcomeComponentInput, type OutcomeDetail, type OutcomeInput, type OutcomeSignal, type OutcomeStepInput, type PayLink, type PaymentDetail, type PaymentRow, type Plan, type PlanComponent, type PlanComponentInput, type PlanDetail, PlanError, type PlanKind, type PlanRef, type PlanType, type PortalToken, type PortalTokenInput, type Purchase, type PurchaseDetail, type PurchaseStatus, RateLimitError, type RemovedMember, type SerializedOutcomeAttribution, type SerializedRuleApplication, type SerializedUnitUsage, type SerializedUsageLog, ServerError, type Signal, type SignalDelivery, type SignalStatus, type SignalType, type ThresholdKind, type TieredUnitInput, type Tokens, type TrackResult, type Unit, type UnitAdjustInput, type UnitBalance, type UnitComponentInput, type UnitCustomerRow, type UnitDetail, type UnitInput, type UnitPricingType, type UnitTier, type UnitTierInput, type UnitTrackResult, type UnitUsageList, type UnitUsageRow, type UpdateCreditInput, type UpdateMemberInput, type UpdateOutcomeInput, type UpdatePlanInput, type UpdateUnitInput, type UpsertWebhookInput, type UsageBalance, type UsageList, ValidationError, type WalletComponentInput, type WalletEntitlementKind, type WalletEntryInput, type WalletInput, type WalletList, type WalletSignal, type WalletTransaction, type WalletTransactionSource, type WalletTransactionType, type Webhook, type WorkspaceIdentity, signalToWire };