@vxil/sdk 0.7.0 → 0.9.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/dist/index.d.ts CHANGED
@@ -535,6 +535,57 @@ export interface PaymentsEntitlementView {
535
535
  period_end: string | null;
536
536
  };
537
537
  }
538
+ /** The provider's own state of a charge, as a closed union (2026-09-25). Paddle
539
+ * delivers `paid` (money taken) and then `completed` (fulfilled) for ONE
540
+ * charge; every other provider has a single terminal state and lands
541
+ * `completed` at once. `payments.charge.succeeded` carries the state of the
542
+ * delivery that recorded the charge; `payments.charge.completed` fires at
543
+ * most once per charge, only when a recorded charge later TRANSITIONS to
544
+ * `completed` (Paddle paid → completed). Handle `succeeded` gated on
545
+ * `provider_status === 'completed'` plus `completed` to act once per charge. */
546
+ export type PaymentsProviderChargeStatus = 'paid' | 'completed';
547
+ /** A charge row as listed by GET /v1/payments/charges (payments.md §3). */
548
+ export interface PaymentsCharge {
549
+ /** the vxil charge id (`chg_…`) — what refunds and a charge-linked grant key on */
550
+ charge_id: string;
551
+ end_user_id: string;
552
+ provider: string;
553
+ /** the provider's own transaction / payment id */
554
+ provider_charge_id: string | null;
555
+ /** null on rows recorded before the provider-state column existed */
556
+ provider_status: PaymentsProviderChargeStatus | null;
557
+ /** the one-off product the charge bought (the `ledger.productMap` key), if any */
558
+ product_id: string | null;
559
+ amount_cents: number;
560
+ amount_refunded: number;
561
+ currency: string;
562
+ status: string;
563
+ created_at: string;
564
+ }
565
+ /** A subscription row as listed by GET /v1/payments/subscriptions (payments.md
566
+ * §3) — provider subscriptions, manual grants and purchase passes alike.
567
+ * `current_period_start` (2026-09-25) is the row's own window start: for a
568
+ * purchase pass (`provider_sub_id: 'purchase:<charge_id>'`) the day its access
569
+ * begins, which is in the FUTURE for a pass queued behind a live one; for a
570
+ * manual grant the time the grant was written. A `type` with an index
571
+ * signature (not an interface), so code that typed these rows as
572
+ * `Record<string, unknown>` keeps compiling. */
573
+ export type PaymentsSubscription = {
574
+ subscription_id: string;
575
+ end_user_id: string;
576
+ /** 'manual' for a support grant or a purchase pass; else the provider */
577
+ provider: string;
578
+ provider_sub_id: string;
579
+ tier: string | null;
580
+ /** trialing | active | past_due | cancelled | expired | lapsed */
581
+ status: string;
582
+ current_period_start: string | null;
583
+ current_period_end: string | null;
584
+ /** the vxil charge (`chg_…`) a pass or a charge-linked grant was bought with */
585
+ charge_id: string | null;
586
+ created_at: string;
587
+ [key: string]: unknown;
588
+ };
538
589
  export interface FileObject {
539
590
  object_id: string;
540
591
  filename: string;
@@ -1513,7 +1564,12 @@ export interface CmsReindexPage {
1513
1564
  /** The result of a bounded filtered delete (cms.md §20). `matched` counts the
1514
1565
  * rows this page selected (≤ `limit`); `deleted` counts the ones actually
1515
1566
  * removed (a row that vanished between the match and the delete is skipped).
1516
- * Loop while `deleted > 0` — the filter re-evaluates against live rows. */
1567
+ * Loop while `deleted > 0` — the filter re-evaluates against live rows.
1568
+ * `complete` is false when the call stopped starting rows at its wall-clock
1569
+ * bound (8 s; the row in flight finishes) before every matched row was
1570
+ * processed — such a call always deleted at least one row, so the loop above
1571
+ * stays exact; `next_cursor` resumes that page if you prefer. Rows already
1572
+ * deleted stay deleted. */
1517
1573
  export interface CmsBulkDeleteResult {
1518
1574
  collection: string;
1519
1575
  matched: number;
@@ -1521,6 +1577,8 @@ export interface CmsBulkDeleteResult {
1521
1577
  cascaded: number;
1522
1578
  set_null: number;
1523
1579
  dry_run: boolean;
1580
+ /** every matched row was processed (always true for a dry run). */
1581
+ complete: boolean;
1524
1582
  next_cursor: string | null;
1525
1583
  }
1526
1584
  /** A write guard (cms.md §10): "after this write, at most `max` live items
@@ -1959,6 +2017,24 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
1959
2017
  delete: (id: string, opts?: {
1960
2018
  erase?: boolean;
1961
2019
  }) => Promise<void>;
2020
+ /**
2021
+ * Merge a REGISTRY-ONLY user (a row you created with upsert that never
2022
+ * signed in) INTO another user of the tenant: its attributes fill the
2023
+ * survivor's gaps (the survivor wins on conflicts), the merged id is
2024
+ * soft-deleted, its owner-scoped cms / files / payments rows move to the
2025
+ * survivor, and `auth.user.merged { from, into, method: 'registry_merge' }`
2026
+ * is audited. `rekeyed: false` means a re-key could not finish inside the
2027
+ * request — the event is the backstop (call the feature's re-key route
2028
+ * again; it is idempotent). 409 `has_auth_identity` when the id already
2029
+ * has a sign-in identity: merge that through the auth flows instead.
2030
+ * Server-only (403 in end-user mode). Scope users:write.
2031
+ */
2032
+ merge: (id: string, into: string) => Promise<{
2033
+ from: string;
2034
+ into: string;
2035
+ merged: boolean;
2036
+ rekeyed: boolean;
2037
+ }>;
1962
2038
  list: (q?: {
1963
2039
  email?: string;
1964
2040
  /** substring match on email + display name (server-side, trgm-indexed), 1-100 chars */
@@ -2086,6 +2162,8 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
2086
2162
  * `counts.total / sent` to 4 dp, or null when nothing was sent. `sent`
2087
2163
  * counts deliveries the provider ACCEPTED, not inbox-delivered mail —
2088
2164
  * this is a suppressions-per-accepted-send ratio, not an RFC bounce rate.
2165
+ * `sent` is counted up to 100 000: past that it is the cap and
2166
+ * `sent_capped` is true (the rate is then a lower bound).
2089
2167
  * Operator surface: a verified end-user token gets 403. */
2090
2168
  stats: (q?: {
2091
2169
  days?: number;
@@ -2100,6 +2178,7 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
2100
2178
  total: number;
2101
2179
  };
2102
2180
  sent: number;
2181
+ sent_capped?: boolean;
2103
2182
  rate: number | null;
2104
2183
  }>;
2105
2184
  };
@@ -2434,7 +2513,8 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
2434
2513
  run_id: string;
2435
2514
  state: string;
2436
2515
  }>;
2437
- /** Clone a terminal run into a fresh queued run. */
2516
+ /** Clone a terminal run into a fresh queued run. A generation run answers
2517
+ * `409 not_replayable` — submit the generation again instead. */
2438
2518
  replay: (runId: string) => Promise<{
2439
2519
  run_id: string;
2440
2520
  replayed_from: string;
@@ -2749,8 +2829,11 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
2749
2829
  * refresh token in one process share ONE rotation, and for 30 s after it
2750
2830
  * settles a call still carrying the OLD pair gets the new pair (no second
2751
2831
  * request) while a call with the just-minted pair gets it back unchanged.
2752
- * A refresh that fails (revoked, expired, or already rotated by another
2753
- * process) throws the `VxilError` — treat it as "sign in again".
2832
+ * A refresh that fails throws the `VxilError`. A `401 invalid_refresh`
2833
+ * can be a rotation another process (another server instance) just
2834
+ * made, so do not clear the session on the first one — keep the cookie
2835
+ * and treat only a repeat failure of the same pair after a short grace
2836
+ * as "sign in again"; see guide 09's refresh recipe.
2754
2837
  *
2755
2838
  * Call it on a client whose key carries `auth:signin` — normally the
2756
2839
  * sign-in key, in server mode. An end-user-mode client sends its
@@ -2910,9 +2993,18 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
2910
2993
  * Consume budget. With behavior 'block' an exceeded check throws
2911
2994
  * VxilError(429); 'shape' resolves with allowed=false instead.
2912
2995
  * `override_id` is present when a per-identifier override was applied.
2996
+ * Name the policy by EXACTLY ONE of `policy_id` or `policy` (its name —
2997
+ * unique per project, and it survives a delete-and-recreate). An unknown
2998
+ * name is the same 404 `not_found` as an unknown id; a policy created
2999
+ * before 2026-09-23 is found by name once it has been saved or listed again.
2913
3000
  */
2914
- check: (input: {
3001
+ check: (input: ({
2915
3002
  policy_id: string;
3003
+ policy?: never;
3004
+ } | {
3005
+ policy: string;
3006
+ policy_id?: never;
3007
+ }) & {
2916
3008
  key_values?: Record<string, string>;
2917
3009
  cost?: number;
2918
3010
  }) => Promise<{
@@ -3132,7 +3224,10 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
3132
3224
  * the SAME single-item delete path (restrict refusal, cascade budget,
3133
3225
  * unique-claim freeing, CDC), so this is N deletes in one round trip, not
3134
3226
  * a set-delete. `dryRun` reports `matched` and mutates nothing. Loop
3135
- * while `deleted > 0`. A refusal mid-sweep (409 referenced / 422
3227
+ * while `deleted > 0`. A call stops STARTING rows after 8 s of wall
3228
+ * clock (the row in flight finishes; never before its first delete) and
3229
+ * answers `complete: false` + `next_cursor` (pass it as `cursor` to
3230
+ * resume the page). A refusal mid-sweep (409 referenced / 422
3136
3231
  * cascade_too_large) stops the call — rows already deleted STAY deleted,
3137
3232
  * and the error message names how many. Soft-deleted rows are hard-purged
3138
3233
  * by the platform 30 days later (there is no per-tenant retention knob). */
@@ -3677,6 +3772,21 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
3677
3772
  state: string;
3678
3773
  }>>;
3679
3774
  unsubscribe: (subId: string) => Promise<void>;
3775
+ /**
3776
+ * Change a subscription's event filter in place (2026-09-25). The sub_id
3777
+ * and the delivery cursor are kept — nothing is replayed, nothing is
3778
+ * skipped; the new prefixes apply from the next delivery pass. An empty
3779
+ * array subscribes to every event. `target_url` is the subscription's
3780
+ * identity and is not editable: a new target is a new subscription.
3781
+ */
3782
+ update: (subId: string, input: {
3783
+ event_prefixes: string[];
3784
+ }) => Promise<{
3785
+ sub_id: string;
3786
+ target_url: string;
3787
+ event_prefixes: string[];
3788
+ state: string;
3789
+ }>;
3680
3790
  /**
3681
3791
  * Send a synthetic 'webhooks.test' event to the subscription's endpoint
3682
3792
  * through the REAL signing+delivery pipeline and wait inline (≤ ~8 s).
@@ -4716,11 +4826,14 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
4716
4826
  cancel_at: string | null;
4717
4827
  }>;
4718
4828
  /** List subscriptions (the cancel enabler — discover the subscription_id);
4719
- * optionally scoped to one user / status. */
4829
+ * optionally scoped to one user / status. Each row carries its own
4830
+ * window: `current_period_start` … `current_period_end` (a queued pass
4831
+ * starts in the future). In end-user mode the list is always the signed-in
4832
+ * user's own rows, whatever `user_id` says. */
4720
4833
  listSubscriptions: (q?: {
4721
4834
  user_id?: string;
4722
4835
  status?: string;
4723
- }) => Promise<Array<Record<string, unknown>>>;
4836
+ }) => Promise<PaymentsSubscription[]>;
4724
4837
  /**
4725
4838
  * Create a hosted-checkout session (payments.md §3). Redirect the buyer to
4726
4839
  * the returned `url`; completion lands server-side via the provider webhook
@@ -4794,19 +4907,32 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
4794
4907
  * entitlements + the tier's recurring credits apply; `until` null =
4795
4908
  * open-ended, else access ends at `until`. Idempotent on
4796
4909
  * `idempotency_key` (a replay returns the first grant, `replayed: true`).
4797
- * 422 unknown_tier when the tier is not in ledger.tierMap. */
4910
+ * 422 unknown_tier when the tier is not in ledger.tierMap.
4911
+ * `charge_id` (optional) links the grant to the vxil charge (`chg_…`, from
4912
+ * `listCharges` or the `payments.charge.succeeded` / `.completed` event)
4913
+ * it was bought with: a FULL refund or chargeback of that charge ends the
4914
+ * grant (`payments.subscription.revoked` with `via: 'refund' | 'chargeback'`).
4915
+ * 404 charge_not_found; 422 charge_user_mismatch (another user's charge) /
4916
+ * charge_refunded (the refund beat the grant — nothing to grant against).
4917
+ * 422 reserved_idempotency_key when `idempotency_key` starts with
4918
+ * `purchase:` (the namespace of purchase passes). */
4798
4919
  grantSubscription: (input: {
4799
4920
  user_id: string;
4800
4921
  tier: string;
4801
4922
  until?: string | null;
4802
4923
  reason: string;
4803
4924
  idempotency_key: string;
4925
+ charge_id?: string | null;
4804
4926
  }) => Promise<{
4805
4927
  subscription_id: string | null;
4806
4928
  user_id: string;
4807
4929
  tier: string;
4808
4930
  status: string;
4931
+ /** (2026-09-25) the grant row's start — the time it was WRITTEN (a replay
4932
+ * answers the first grant's), never a window you computed */
4933
+ since?: string | null;
4809
4934
  until: string | null;
4935
+ charge_id: string | null;
4810
4936
  replayed: boolean;
4811
4937
  }>;
4812
4938
  /** SERVER-ONLY. Revoke a MANUAL grant now (409 not_manual for a provider
@@ -4884,22 +5010,18 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
4884
5010
  } | null;
4885
5011
  }>;
4886
5012
  /** List charges newest-first (the refund enabler — discover the charge_id).
4887
- * Filters: user_id, status, limit (clamped 1..100, default 50). */
5013
+ * Filters: user_id, status, limit (clamped 1..100, default 50).
5014
+ * Each row carries the provider's own charge id (`provider_charge_id`),
5015
+ * the provider's charge state (`provider_status`: `'paid'` — Paddle's
5016
+ * money-taken state — or `'completed'`; null on rows recorded before
5017
+ * 2026-09-25) and the one-off `product_id` it bought, so a function can
5018
+ * match a charge to a provider event without reading the delivery log. */
4888
5019
  listCharges: (q?: {
4889
5020
  user_id?: string;
4890
5021
  status?: "succeeded" | "failed" | "refunded" | "partially_refunded" | "pending";
4891
5022
  limit?: number;
4892
5023
  }) => Promise<{
4893
- charges: Array<{
4894
- charge_id: string;
4895
- end_user_id: string;
4896
- provider: string;
4897
- amount_cents: number;
4898
- amount_refunded: number;
4899
- currency: string;
4900
- status: string;
4901
- created_at: string;
4902
- }>;
5024
+ charges: PaymentsCharge[];
4903
5025
  }>;
4904
5026
  /** Provider webhook event log (payments.md §7 "Event log & replay"):
4905
5027
  * operator visibility over every delivery — incl. persisted signature
package/dist/index.js CHANGED
@@ -400,6 +400,19 @@ export class Vxil {
400
400
  delete: async (id, opts) => {
401
401
  await this.call('DELETE', `/v1/users/${encodeURIComponent(id)}${opts?.erase ? '?erase=true' : ''}`);
402
402
  },
403
+ /**
404
+ * Merge a REGISTRY-ONLY user (a row you created with upsert that never
405
+ * signed in) INTO another user of the tenant: its attributes fill the
406
+ * survivor's gaps (the survivor wins on conflicts), the merged id is
407
+ * soft-deleted, its owner-scoped cms / files / payments rows move to the
408
+ * survivor, and `auth.user.merged { from, into, method: 'registry_merge' }`
409
+ * is audited. `rekeyed: false` means a re-key could not finish inside the
410
+ * request — the event is the backstop (call the feature's re-key route
411
+ * again; it is idempotent). 409 `has_auth_identity` when the id already
412
+ * has a sign-in identity: merge that through the auth flows instead.
413
+ * Server-only (403 in end-user mode). Scope users:write.
414
+ */
415
+ merge: async (id, into) => (await this.call('POST', `/v1/users/${encodeURIComponent(id)}/merge`, { into })).data,
403
416
  list: async (q) => {
404
417
  const s = qs({
405
418
  email: q?.email || undefined,
@@ -460,6 +473,8 @@ export class Vxil {
460
473
  * `counts.total / sent` to 4 dp, or null when nothing was sent. `sent`
461
474
  * counts deliveries the provider ACCEPTED, not inbox-delivered mail —
462
475
  * this is a suppressions-per-accepted-send ratio, not an RFC bounce rate.
476
+ * `sent` is counted up to 100 000: past that it is the cap and
477
+ * `sent_capped` is true (the rate is then a lower bound).
463
478
  * Operator surface: a verified end-user token gets 403. */
464
479
  stats: async (q) => {
465
480
  const s = qs({ days: q?.days || undefined });
@@ -684,7 +699,8 @@ export class Vxil {
684
699
  * number to alarm on), in-flight runs per lane, dead letters in 24 h. */
685
700
  queue: async () => (await this.call('GET', '/v1/jobs/queue')).data,
686
701
  cancel: async (runId) => (await this.call('POST', `/v1/jobs/runs/${encodeURIComponent(runId)}/cancel`)).data,
687
- /** Clone a terminal run into a fresh queued run. */
702
+ /** Clone a terminal run into a fresh queued run. A generation run answers
703
+ * `409 not_replayable` — submit the generation again instead. */
688
704
  replay: async (runId) => (await this.call('POST', `/v1/jobs/runs/${encodeURIComponent(runId)}/replay`)).data,
689
705
  /**
690
706
  * Suspend the RUNNING run until an event (call from the executing
@@ -864,8 +880,11 @@ export class Vxil {
864
880
  * refresh token in one process share ONE rotation, and for 30 s after it
865
881
  * settles a call still carrying the OLD pair gets the new pair (no second
866
882
  * request) while a call with the just-minted pair gets it back unchanged.
867
- * A refresh that fails (revoked, expired, or already rotated by another
868
- * process) throws the `VxilError` — treat it as "sign in again".
883
+ * A refresh that fails throws the `VxilError`. A `401 invalid_refresh`
884
+ * can be a rotation another process (another server instance) just
885
+ * made, so do not clear the session on the first one — keep the cookie
886
+ * and treat only a repeat failure of the same pair after a short grace
887
+ * as "sign in again"; see guide 09's refresh recipe.
869
888
  *
870
889
  * Call it on a client whose key carries `auth:signin` — normally the
871
890
  * sign-in key, in server mode. An end-user-mode client sends its
@@ -1002,6 +1021,10 @@ export class Vxil {
1002
1021
  * Consume budget. With behavior 'block' an exceeded check throws
1003
1022
  * VxilError(429); 'shape' resolves with allowed=false instead.
1004
1023
  * `override_id` is present when a per-identifier override was applied.
1024
+ * Name the policy by EXACTLY ONE of `policy_id` or `policy` (its name —
1025
+ * unique per project, and it survives a delete-and-recreate). An unknown
1026
+ * name is the same 404 `not_found` as an unknown id; a policy created
1027
+ * before 2026-09-23 is found by name once it has been saved or listed again.
1005
1028
  */
1006
1029
  check: async (input) => (await this.call('POST', '/v1/rate-limits/check', input)).data,
1007
1030
  /** Per-identifier overrides layered over a policy: `pattern` matches the
@@ -1170,7 +1193,10 @@ export class Vxil {
1170
1193
  * the SAME single-item delete path (restrict refusal, cascade budget,
1171
1194
  * unique-claim freeing, CDC), so this is N deletes in one round trip, not
1172
1195
  * a set-delete. `dryRun` reports `matched` and mutates nothing. Loop
1173
- * while `deleted > 0`. A refusal mid-sweep (409 referenced / 422
1196
+ * while `deleted > 0`. A call stops STARTING rows after 8 s of wall
1197
+ * clock (the row in flight finishes; never before its first delete) and
1198
+ * answers `complete: false` + `next_cursor` (pass it as `cursor` to
1199
+ * resume the page). A refusal mid-sweep (409 referenced / 422
1174
1200
  * cascade_too_large) stops the call — rows already deleted STAY deleted,
1175
1201
  * and the error message names how many. Soft-deleted rows are hard-purged
1176
1202
  * by the platform 30 days later (there is no per-tenant retention knob). */
@@ -1439,6 +1465,14 @@ export class Vxil {
1439
1465
  unsubscribe: async (subId) => {
1440
1466
  await this.call('DELETE', `/v1/webhooks/subscriptions/${encodeURIComponent(subId)}`);
1441
1467
  },
1468
+ /**
1469
+ * Change a subscription's event filter in place (2026-09-25). The sub_id
1470
+ * and the delivery cursor are kept — nothing is replayed, nothing is
1471
+ * skipped; the new prefixes apply from the next delivery pass. An empty
1472
+ * array subscribes to every event. `target_url` is the subscription's
1473
+ * identity and is not editable: a new target is a new subscription.
1474
+ */
1475
+ update: async (subId, input) => (await this.call('PATCH', `/v1/webhooks/subscriptions/${encodeURIComponent(subId)}`, input)).data,
1442
1476
  /**
1443
1477
  * Send a synthetic 'webhooks.test' event to the subscription's endpoint
1444
1478
  * through the REAL signing+delivery pipeline and wait inline (≤ ~8 s).
@@ -1941,7 +1975,10 @@ export class Vxil {
1941
1975
  * period closes; false revokes immediately and re-folds entitlements. */
1942
1976
  cancel: async (subscriptionId, opts) => (await this.call('DELETE', `/v1/payments/subscriptions/${encodeURIComponent(subscriptionId)}${opts?.atPeriodEnd ? '?at_period_end=true' : ''}`)).data,
1943
1977
  /** List subscriptions (the cancel enabler — discover the subscription_id);
1944
- * optionally scoped to one user / status. */
1978
+ * optionally scoped to one user / status. Each row carries its own
1979
+ * window: `current_period_start` … `current_period_end` (a queued pass
1980
+ * starts in the future). In end-user mode the list is always the signed-in
1981
+ * user's own rows, whatever `user_id` says. */
1945
1982
  listSubscriptions: async (q) => {
1946
1983
  const s = qs({ user_id: q?.user_id || undefined, status: q?.status || undefined });
1947
1984
  return (await this.call('GET', `/v1/payments/subscriptions${s}`)).data.subscriptions;
@@ -1988,7 +2025,15 @@ export class Vxil {
1988
2025
  * entitlements + the tier's recurring credits apply; `until` null =
1989
2026
  * open-ended, else access ends at `until`. Idempotent on
1990
2027
  * `idempotency_key` (a replay returns the first grant, `replayed: true`).
1991
- * 422 unknown_tier when the tier is not in ledger.tierMap. */
2028
+ * 422 unknown_tier when the tier is not in ledger.tierMap.
2029
+ * `charge_id` (optional) links the grant to the vxil charge (`chg_…`, from
2030
+ * `listCharges` or the `payments.charge.succeeded` / `.completed` event)
2031
+ * it was bought with: a FULL refund or chargeback of that charge ends the
2032
+ * grant (`payments.subscription.revoked` with `via: 'refund' | 'chargeback'`).
2033
+ * 404 charge_not_found; 422 charge_user_mismatch (another user's charge) /
2034
+ * charge_refunded (the refund beat the grant — nothing to grant against).
2035
+ * 422 reserved_idempotency_key when `idempotency_key` starts with
2036
+ * `purchase:` (the namespace of purchase passes). */
1992
2037
  grantSubscription: async (input) => (await this.call('POST', '/v1/payments/subscriptions/grant', input)).data,
1993
2038
  /** SERVER-ONLY. Revoke a MANUAL grant now (409 not_manual for a provider
1994
2039
  * subscription — use `cancel`). Never touches a provider. */
@@ -2022,7 +2067,12 @@ export class Vxil {
2022
2067
  * 403 `server_only`; the sweep is a tenant-wide operator surface. */
2023
2068
  reconcile: async () => (await this.call('GET', '/v1/payments/reconcile')).data,
2024
2069
  /** List charges newest-first (the refund enabler — discover the charge_id).
2025
- * Filters: user_id, status, limit (clamped 1..100, default 50). */
2070
+ * Filters: user_id, status, limit (clamped 1..100, default 50).
2071
+ * Each row carries the provider's own charge id (`provider_charge_id`),
2072
+ * the provider's charge state (`provider_status`: `'paid'` — Paddle's
2073
+ * money-taken state — or `'completed'`; null on rows recorded before
2074
+ * 2026-09-25) and the one-off `product_id` it bought, so a function can
2075
+ * match a charge to a provider event without reading the delivery log. */
2026
2076
  listCharges: async (q) => {
2027
2077
  const suffix = qs({
2028
2078
  user_id: q?.user_id || undefined,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vxil/sdk",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Typed client for the Vxil REST API (notifications, auth, jobs, files, cms, comments, webhooks, realtime, orgs, rate-limits).",