@clocknext/sdk 0.17.0 → 0.18.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clocknext/sdk",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "Official ClockNext SDK — record usage signals and manage plans, purchases, invoices, customers, and billing from your backend.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/client.ts CHANGED
@@ -12,6 +12,7 @@ import { Portal } from "./resources/portal.js";
12
12
  import { Purchases } from "./resources/purchases.js";
13
13
  import { Signals } from "./resources/signals.js";
14
14
  import { Units } from "./resources/units.js";
15
+ import { Taxes } from "./resources/taxes.js";
15
16
  import type { ClockNextConfig, ResolvedConfig } from "./types.js";
16
17
 
17
18
  /**
@@ -46,6 +47,8 @@ export class ClockNext {
46
47
  readonly units: Units;
47
48
  /** Composites: list, get, create, update, archive, unarchive (no delete). */
48
49
  readonly composites: Composites;
50
+ /** Tax rates by country: list, get, create, update (archive in the dashboard). */
51
+ readonly taxes: Taxes;
49
52
  /** Subscribe customers to plans (purchases). */
50
53
  readonly purchases: Purchases;
51
54
  /** Read invoices and mint hosted pay links. */
@@ -76,6 +79,7 @@ export class ClockNext {
76
79
  this.outcomes = new Outcomes(transport);
77
80
  this.units = new Units(transport);
78
81
  this.composites = new Composites(transport);
82
+ this.taxes = new Taxes(transport);
79
83
  this.purchases = new Purchases(transport);
80
84
  this.invoices = new Invoices(transport);
81
85
  this.payments = new Payments(transport);
package/src/index.ts CHANGED
@@ -56,7 +56,6 @@ export type {
56
56
  CompositeCompletionInput,
57
57
  CompositeCompletionResult,
58
58
  SerializedUnitUsage,
59
- UsageLogPeriod,
60
59
  UsageLogListParams,
61
60
  UsageLogAppliedRule,
62
61
  UsageLogOutcomeAttribution,
@@ -114,6 +113,11 @@ export type {
114
113
  Unit,
115
114
  CreateUnitInput,
116
115
  UpdateUnitInput,
116
+ // taxes
117
+ TaxBehavior,
118
+ Tax,
119
+ CreateTaxInput,
120
+ UpdateTaxInput,
117
121
  CompositeItem,
118
122
  Composite,
119
123
  CreateCompositeInput,
@@ -436,7 +436,6 @@ export class Signals {
436
436
  customerId: params.customerId,
437
437
  limit: params.limit,
438
438
  cursor: params.cursor,
439
- period: params.period,
440
439
  from: params.from,
441
440
  to: params.to,
442
441
  },
@@ -0,0 +1,59 @@
1
+ import { activeQuery, filterByActive } from "../filters.js";
2
+ import type { Transport } from "../http.js";
3
+ import type { CreateTaxInput, Tax, UpdateTaxInput } from "../types.js";
4
+
5
+ /**
6
+ * The `taxes` resource — the tax rates charged to customers by billing
7
+ * country (the same rates as Settings → Taxes). `/api/v1/taxes`.
8
+ *
9
+ * Every active tax for a customer's country is applied to their invoices and
10
+ * portal purchases automatically. There is no `delete()`, `archive()` or
11
+ * `unarchive()` — archiving and restoring a tax is done in the dashboard.
12
+ */
13
+ export class Taxes {
14
+ constructor(private readonly transport: Transport) {}
15
+
16
+ /** List them, oldest first. `{ active: true }` returns only active ones,
17
+ * `{ active: false }` only archived ones; omit it for both. */
18
+ async list(params: { active?: boolean } = {}): Promise<Tax[]> {
19
+ const res = await this.transport.request<{ taxes: Tax[] }>({
20
+ method: "GET",
21
+ path: "/api/v1/taxes",
22
+ query: { active: activeQuery(params.active) },
23
+ });
24
+ return filterByActive(res.taxes, params.active);
25
+ }
26
+
27
+ /** One tax. */
28
+ async get(id: string): Promise<Tax> {
29
+ const res = await this.transport.request<{ tax: Tax }>({
30
+ method: "GET",
31
+ path: `/api/v1/taxes/${encodeURIComponent(id)}`,
32
+ });
33
+ return res.tax;
34
+ }
35
+
36
+ /** Create one. It starts active. Two active taxes can't share a name
37
+ * (ignoring case) in the same country — that's a `ConflictError`. Not
38
+ * retried on failure — a retry could create a second row. */
39
+ async create(input: CreateTaxInput): Promise<Tax> {
40
+ const res = await this.transport.request<{ created: true; tax: Tax }>({
41
+ method: "POST",
42
+ path: "/api/v1/taxes",
43
+ body: input,
44
+ });
45
+ return res.tax;
46
+ }
47
+
48
+ /** PARTIAL update: only the fields you send change. Archived taxes can be
49
+ * edited and stay archived. Only invoices issued afterwards use the new
50
+ * values. */
51
+ async update(id: string, input: UpdateTaxInput): Promise<Tax> {
52
+ const res = await this.transport.request<{ updated: true; tax: Tax }>({
53
+ method: "PATCH",
54
+ path: `/api/v1/taxes/${encodeURIComponent(id)}`,
55
+ body: input,
56
+ });
57
+ return res.tax;
58
+ }
59
+ }
package/src/types.ts CHANGED
@@ -383,9 +383,6 @@ export interface TrackResult {
383
383
  // Usage log (mirrors GET /api/v1/signal/usage-log)
384
384
  // ---------------------------------------------------------------------------
385
385
 
386
- /** A preset time window, or `"custom"` for an explicit `from`/`to` range. */
387
- export type UsageLogPeriod = "all" | "today" | "7d" | "30d" | "90d" | "custom";
388
-
389
386
  export interface UsageLogListParams {
390
387
  customerId: string;
391
388
  /** Default 50, max 200. */
@@ -393,11 +390,11 @@ export interface UsageLogListParams {
393
390
  /** Pass back a previous page's `nextCursor` to fetch the next page. Omit for
394
391
  * the first page. */
395
392
  cursor?: string;
396
- /** Default `"all"`. Pair `"custom"` with `from`/`to`. */
397
- period?: UsageLogPeriod;
398
- /** `YYYY-MM-DD`, only used when `period` is `"custom"`. */
393
+ /** First day to include, `YYYY-MM-DD` (UTC). The whole day is included.
394
+ * Keep sending the same value while paging with `cursor`. */
399
395
  from?: string;
400
- /** `YYYY-MM-DD`, only used when `period` is `"custom"`. */
396
+ /** Last day to include, `YYYY-MM-DD` (UTC). The whole day is included.
397
+ * Must not be before `from`. */
401
398
  to?: string;
402
399
  }
403
400
 
@@ -426,13 +423,14 @@ export interface UsageLogOutcomeAttribution {
426
423
  * One row of a customer's usage history, merged from every meter kind
427
424
  * (`UsageLog`, `UnitUsageLog`, completed composite occurrences) into one
428
425
  * feed and sorted by time — the same rows the customer portal's own "Usage
429
- * logs" table shows. `model`, `credit`, `outcome`, `unit` and `composite` are
430
- * each `null` unless `kind` matches that group.
426
+ * logs" table shows. `credit`, `outcome`, `unit` and `composite` are each
427
+ * `null` unless `type` matches that group.
431
428
  */
432
429
  export interface UsageLogEntry {
433
430
  id: string;
434
431
  createdAt: string;
435
- kind: "credit" | "outcome" | "unit" | "wallet" | "composite";
432
+ /** Which meter this row is. */
433
+ type: "credit" | "outcome" | "unit" | "wallet" | "composite";
436
434
  status: SignalStatus;
437
435
  typeName: string | null;
438
436
  member: string | null;
@@ -447,16 +445,19 @@ export interface UsageLogEntry {
447
445
  };
448
446
  /** Formatted USD strings (e.g. `"$0.0064"`). */
449
447
  cost: { providedCost: string; customerCost: string };
450
- /** Populated only when `kind === "credit"`. `amount` is a formatted credit
448
+ /** Populated only when `type === "credit"`. `amount` is a formatted credit
451
449
  * value, honouring the org's credit-decimals display setting. */
452
450
  credit: { amount: string; appliedRules: UsageLogAppliedRule[] } | null;
453
- /** Populated only when `kind === "outcome"`. */
451
+ /** Populated only when `type === "outcome"`. */
454
452
  outcome: UsageLogOutcomeAttribution | null;
455
- /** Populated only when `kind === "unit"`. `quantity` is formatted. */
453
+ /** Populated only when `type === "unit"`. `quantity` is formatted. */
456
454
  unit: { quantity: string } | null;
457
- /** Populated only when `kind === "composite"`. `amount` is a formatted USD
455
+ /** Populated only when `type === "composite"`. `amount` is a formatted USD
458
456
  * price — the composite's flat price, read live, not stored per row. */
459
457
  composite: { amount: string } | null;
458
+ /** The `custom` object sent on the signal, as sent. `null` when none was
459
+ * sent, and always `null` for unit and composite rows. */
460
+ custom: Record<string, unknown> | null;
460
461
  }
461
462
 
462
463
  export interface UsageLogPage {
@@ -963,6 +964,46 @@ export interface CreateUnitInput {
963
964
  /** `units.update()` body — PARTIAL. `tiers`, when sent, is the complete list. */
964
965
  export type UpdateUnitInput = Partial<CreateUnitInput>;
965
966
 
967
+ /** How a tax applies: `"inclusive"` — already inside the price;
968
+ * `"exclusive"` — added on top. Lower case only. */
969
+ export type TaxBehavior = "inclusive" | "exclusive";
970
+
971
+ /** A tax rate, charged to customers whose billing country matches. */
972
+ export interface Tax {
973
+ id: string;
974
+ /** Shown on invoices. Unique among active taxes in the same country,
975
+ * ignoring case. */
976
+ name: string;
977
+ /** A note for your team; not shown to customers. */
978
+ description: string | null;
979
+ /** 0–100. */
980
+ percentage: number;
981
+ /** ISO 3166-1 alpha-2, upper case (e.g. `"IN"`). */
982
+ countryCode: string;
983
+ behavior: TaxBehavior;
984
+ /** `false` once archived in the dashboard (Settings → Taxes): it no longer
985
+ * applies to new invoices. */
986
+ isActive: boolean;
987
+ createdAt: string;
988
+ updatedAt: string;
989
+ }
990
+
991
+ /** `taxes.create()` body. */
992
+ export interface CreateTaxInput {
993
+ /** 1–100 characters. */
994
+ name: string;
995
+ /** 0–100. */
996
+ percentage: number;
997
+ /** ISO 3166-1 alpha-2 (lower case is accepted). A country name is refused. */
998
+ countryCode: string;
999
+ behavior: TaxBehavior;
1000
+ /** Up to 500 characters. */
1001
+ description?: string | null;
1002
+ }
1003
+
1004
+ /** `taxes.update()` body — PARTIAL: only the fields you send change. */
1005
+ export type UpdateTaxInput = Partial<CreateTaxInput>;
1006
+
966
1007
  /** A catalogue item a composite wraps. */
967
1008
  export interface CompositeItem {
968
1009
  id: string;