@stackbe/sdk 0.4.0 → 0.6.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.mts CHANGED
@@ -11,6 +11,7 @@ declare class HttpClient {
11
11
  private request;
12
12
  get<T>(path: string, params?: Record<string, string | number | undefined>): Promise<T>;
13
13
  post<T>(path: string, body?: unknown, params?: Record<string, string | number | undefined>): Promise<T>;
14
+ put<T>(path: string, body?: unknown, params?: Record<string, string | number | undefined>): Promise<T>;
14
15
  patch<T>(path: string, body?: unknown): Promise<T>;
15
16
  delete<T>(path: string): Promise<T>;
16
17
  }
@@ -89,6 +90,69 @@ interface CustomerUsageResponse {
89
90
  billingPeriod: string;
90
91
  metrics: UsageMetric[];
91
92
  }
93
+ interface SyncUsageOptions {
94
+ /** Idempotency key to prevent duplicate syncs */
95
+ idempotencyKey?: string;
96
+ }
97
+ interface SyncUsageResponse {
98
+ success: boolean;
99
+ currentUsage: number;
100
+ billingPeriod: string;
101
+ limit: number | null;
102
+ remaining: number | null;
103
+ }
104
+ interface CheckUsageWithAddonsResponse extends CheckUsageResponse {
105
+ /** Limit from plan (before addons) */
106
+ planLimit: number | null;
107
+ /** Total addon credits available */
108
+ addonCredits: number;
109
+ /** Total effective limit (plan + addons) */
110
+ effectiveLimit: number | null;
111
+ }
112
+ interface TrackUsageWithAddonsResponse extends TrackUsageResponse {
113
+ /** Usage deducted from plan allocation */
114
+ planUsage: number;
115
+ /** Usage deducted from addon credits */
116
+ addonUsage: number;
117
+ }
118
+ interface UsageAddon {
119
+ id: string;
120
+ appId: string;
121
+ metricId: string;
122
+ metricName: string;
123
+ name: string;
124
+ description?: string;
125
+ quantity: number;
126
+ priceCents: number;
127
+ currency: string;
128
+ status: 'active' | 'archived';
129
+ stripePriceId?: string;
130
+ createdAt: string;
131
+ }
132
+ interface UsageAddonPurchase {
133
+ id: string;
134
+ addonId: string;
135
+ addonName: string;
136
+ customerId: string;
137
+ metricId: string;
138
+ metricName: string;
139
+ quantity: number;
140
+ usedQuantity: number;
141
+ remainingQuantity: number;
142
+ priceCents: number;
143
+ currency: string;
144
+ billingPeriod: string;
145
+ expiresAt?: string;
146
+ createdAt: string;
147
+ }
148
+ interface CustomerCreditsResponse {
149
+ customerId: string;
150
+ metricName: string;
151
+ totalPurchased: number;
152
+ totalUsed: number;
153
+ remaining: number;
154
+ purchases: UsageAddonPurchase[];
155
+ }
92
156
  interface CheckEntitlementResponse {
93
157
  /** Whether the customer has access to this feature */
94
158
  hasAccess: boolean;
@@ -120,6 +184,10 @@ interface Subscription {
120
184
  currentPeriodStart: string;
121
185
  currentPeriodEnd: string;
122
186
  cancelAtPeriodEnd: boolean;
187
+ /** When the subscription was paused (null if not paused) */
188
+ pausedAt?: string | null;
189
+ /** When the subscription will auto-resume (null = indefinite pause) */
190
+ resumesAt?: string | null;
123
191
  createdAt: string;
124
192
  }
125
193
  interface CustomerWithSubscription extends Customer {
@@ -314,6 +382,10 @@ interface SubscriptionWebhookPayload {
314
382
  currentPeriodEnd: string;
315
383
  cancelAtPeriodEnd: boolean;
316
384
  trialEnd?: string;
385
+ /** When the subscription was paused (null if not paused) */
386
+ pausedAt?: string | null;
387
+ /** When the subscription will auto-resume (null = indefinite pause) */
388
+ resumesAt?: string | null;
317
389
  }
318
390
  /** Customer webhook payload */
319
391
  interface CustomerWebhookPayload {
@@ -409,6 +481,89 @@ declare class UsageClient {
409
481
  trackAndCheck(customerId: string, metric: string, options?: TrackUsageOptions): Promise<TrackUsageResponse & {
410
482
  allowed: boolean;
411
483
  }>;
484
+ /**
485
+ * Sync usage to an absolute value (instead of incrementing).
486
+ * Use this when your app tracks usage internally and reports the final count periodically.
487
+ *
488
+ * @example
489
+ * ```typescript
490
+ * // Your app tracks 847 billable tickets internally
491
+ * await stackbe.usage.sync('cust_123', 'tickets', 847);
492
+ * ```
493
+ */
494
+ sync(customerId: string, metric: string, value: number, options?: SyncUsageOptions): Promise<SyncUsageResponse>;
495
+ /**
496
+ * Check usage limits including purchased add-on credits.
497
+ *
498
+ * @example
499
+ * ```typescript
500
+ * const result = await stackbe.usage.checkWithAddons('cust_123', 'tickets');
501
+ *
502
+ * console.log(`Plan limit: ${result.planLimit}`);
503
+ * console.log(`Addon credits: ${result.addonCredits}`);
504
+ * console.log(`Effective limit: ${result.effectiveLimit}`);
505
+ *
506
+ * if (!result.allowed) {
507
+ * // Prompt to purchase more credits
508
+ * }
509
+ * ```
510
+ */
511
+ checkWithAddons(customerId: string, metric: string): Promise<CheckUsageWithAddonsResponse>;
512
+ /**
513
+ * Track usage with automatic add-on credit deduction.
514
+ * When plan limit is exceeded, usage is deducted from purchased add-on credits.
515
+ *
516
+ * @example
517
+ * ```typescript
518
+ * const result = await stackbe.usage.trackWithAddons('cust_123', 'tickets');
519
+ *
520
+ * if (result.addonUsage > 0) {
521
+ * console.log(`Used ${result.addonUsage} from addon credits`);
522
+ * }
523
+ * ```
524
+ */
525
+ trackWithAddons(customerId: string, metric: string, options?: TrackUsageOptions): Promise<TrackUsageWithAddonsResponse>;
526
+ /**
527
+ * List available add-on packs for purchase.
528
+ *
529
+ * @example
530
+ * ```typescript
531
+ * const addons = await stackbe.usage.listAddons();
532
+ *
533
+ * for (const addon of addons) {
534
+ * console.log(`${addon.name}: ${addon.quantity} ${addon.metricName} for $${addon.priceCents / 100}`);
535
+ * }
536
+ * ```
537
+ */
538
+ listAddons(): Promise<UsageAddon[]>;
539
+ /**
540
+ * Purchase an add-on pack for a customer.
541
+ * Credits are immediately available after purchase.
542
+ *
543
+ * @example
544
+ * ```typescript
545
+ * // Purchase a 10k ticket pack
546
+ * const purchase = await stackbe.usage.purchaseAddon('cust_123', 'addon_xyz');
547
+ * console.log(`Purchased ${purchase.quantity} credits`);
548
+ * ```
549
+ */
550
+ purchaseAddon(customerId: string, addonId: string): Promise<UsageAddonPurchase>;
551
+ /**
552
+ * Get customer's purchased add-on credits for a metric.
553
+ *
554
+ * @example
555
+ * ```typescript
556
+ * const credits = await stackbe.usage.getCredits('cust_123', 'tickets');
557
+ *
558
+ * console.log(`Total purchased: ${credits.totalPurchased}`);
559
+ * console.log(`Remaining: ${credits.remaining}`);
560
+ *
561
+ * for (const purchase of credits.purchases) {
562
+ * console.log(`${purchase.addonName}: ${purchase.remainingQuantity} left`);
563
+ * }
564
+ * ```
565
+ */
566
+ getCredits(customerId: string, metric: string): Promise<CustomerCreditsResponse>;
412
567
  }
413
568
 
414
569
  declare class EntitlementsClient {
@@ -713,6 +868,50 @@ declare class SubscriptionsClient {
713
868
  * ```
714
869
  */
715
870
  isActive(customerId: string): Promise<boolean>;
871
+ /**
872
+ * Pause a subscription.
873
+ *
874
+ * Pauses billing collection for a subscription. The subscription remains
875
+ * active but no invoices are generated while paused. Use cases include:
876
+ * - Customer requests temporary pause (vacation, budget constraints)
877
+ * - Seasonal businesses with predictable downtime
878
+ * - Retention strategy: offer pause instead of cancel
879
+ *
880
+ * @example
881
+ * ```typescript
882
+ * // Pause indefinitely (until manually resumed)
883
+ * await stackbe.subscriptions.pause('sub_123');
884
+ *
885
+ * // Pause for 30 days (auto-resume)
886
+ * const resumeDate = new Date();
887
+ * resumeDate.setDate(resumeDate.getDate() + 30);
888
+ * await stackbe.subscriptions.pause('sub_123', { resumesAt: resumeDate });
889
+ *
890
+ * // Check if paused
891
+ * const sub = await stackbe.subscriptions.get('cust_123');
892
+ * if (sub?.pausedAt) {
893
+ * console.log('Subscription is paused');
894
+ * if (sub.resumesAt) {
895
+ * console.log(`Will resume on ${sub.resumesAt}`);
896
+ * }
897
+ * }
898
+ * ```
899
+ */
900
+ pause(subscriptionId: string, options?: {
901
+ resumesAt?: Date;
902
+ }): Promise<Subscription>;
903
+ /**
904
+ * Resume a paused subscription.
905
+ *
906
+ * Restarts billing collection for a paused subscription.
907
+ *
908
+ * @example
909
+ * ```typescript
910
+ * // Resume a paused subscription
911
+ * await stackbe.subscriptions.resume('sub_123');
912
+ * ```
913
+ */
914
+ resume(subscriptionId: string): Promise<Subscription>;
716
915
  }
717
916
 
718
917
  interface AuthClientOptions {
package/dist/index.d.ts CHANGED
@@ -11,6 +11,7 @@ declare class HttpClient {
11
11
  private request;
12
12
  get<T>(path: string, params?: Record<string, string | number | undefined>): Promise<T>;
13
13
  post<T>(path: string, body?: unknown, params?: Record<string, string | number | undefined>): Promise<T>;
14
+ put<T>(path: string, body?: unknown, params?: Record<string, string | number | undefined>): Promise<T>;
14
15
  patch<T>(path: string, body?: unknown): Promise<T>;
15
16
  delete<T>(path: string): Promise<T>;
16
17
  }
@@ -89,6 +90,69 @@ interface CustomerUsageResponse {
89
90
  billingPeriod: string;
90
91
  metrics: UsageMetric[];
91
92
  }
93
+ interface SyncUsageOptions {
94
+ /** Idempotency key to prevent duplicate syncs */
95
+ idempotencyKey?: string;
96
+ }
97
+ interface SyncUsageResponse {
98
+ success: boolean;
99
+ currentUsage: number;
100
+ billingPeriod: string;
101
+ limit: number | null;
102
+ remaining: number | null;
103
+ }
104
+ interface CheckUsageWithAddonsResponse extends CheckUsageResponse {
105
+ /** Limit from plan (before addons) */
106
+ planLimit: number | null;
107
+ /** Total addon credits available */
108
+ addonCredits: number;
109
+ /** Total effective limit (plan + addons) */
110
+ effectiveLimit: number | null;
111
+ }
112
+ interface TrackUsageWithAddonsResponse extends TrackUsageResponse {
113
+ /** Usage deducted from plan allocation */
114
+ planUsage: number;
115
+ /** Usage deducted from addon credits */
116
+ addonUsage: number;
117
+ }
118
+ interface UsageAddon {
119
+ id: string;
120
+ appId: string;
121
+ metricId: string;
122
+ metricName: string;
123
+ name: string;
124
+ description?: string;
125
+ quantity: number;
126
+ priceCents: number;
127
+ currency: string;
128
+ status: 'active' | 'archived';
129
+ stripePriceId?: string;
130
+ createdAt: string;
131
+ }
132
+ interface UsageAddonPurchase {
133
+ id: string;
134
+ addonId: string;
135
+ addonName: string;
136
+ customerId: string;
137
+ metricId: string;
138
+ metricName: string;
139
+ quantity: number;
140
+ usedQuantity: number;
141
+ remainingQuantity: number;
142
+ priceCents: number;
143
+ currency: string;
144
+ billingPeriod: string;
145
+ expiresAt?: string;
146
+ createdAt: string;
147
+ }
148
+ interface CustomerCreditsResponse {
149
+ customerId: string;
150
+ metricName: string;
151
+ totalPurchased: number;
152
+ totalUsed: number;
153
+ remaining: number;
154
+ purchases: UsageAddonPurchase[];
155
+ }
92
156
  interface CheckEntitlementResponse {
93
157
  /** Whether the customer has access to this feature */
94
158
  hasAccess: boolean;
@@ -120,6 +184,10 @@ interface Subscription {
120
184
  currentPeriodStart: string;
121
185
  currentPeriodEnd: string;
122
186
  cancelAtPeriodEnd: boolean;
187
+ /** When the subscription was paused (null if not paused) */
188
+ pausedAt?: string | null;
189
+ /** When the subscription will auto-resume (null = indefinite pause) */
190
+ resumesAt?: string | null;
123
191
  createdAt: string;
124
192
  }
125
193
  interface CustomerWithSubscription extends Customer {
@@ -314,6 +382,10 @@ interface SubscriptionWebhookPayload {
314
382
  currentPeriodEnd: string;
315
383
  cancelAtPeriodEnd: boolean;
316
384
  trialEnd?: string;
385
+ /** When the subscription was paused (null if not paused) */
386
+ pausedAt?: string | null;
387
+ /** When the subscription will auto-resume (null = indefinite pause) */
388
+ resumesAt?: string | null;
317
389
  }
318
390
  /** Customer webhook payload */
319
391
  interface CustomerWebhookPayload {
@@ -409,6 +481,89 @@ declare class UsageClient {
409
481
  trackAndCheck(customerId: string, metric: string, options?: TrackUsageOptions): Promise<TrackUsageResponse & {
410
482
  allowed: boolean;
411
483
  }>;
484
+ /**
485
+ * Sync usage to an absolute value (instead of incrementing).
486
+ * Use this when your app tracks usage internally and reports the final count periodically.
487
+ *
488
+ * @example
489
+ * ```typescript
490
+ * // Your app tracks 847 billable tickets internally
491
+ * await stackbe.usage.sync('cust_123', 'tickets', 847);
492
+ * ```
493
+ */
494
+ sync(customerId: string, metric: string, value: number, options?: SyncUsageOptions): Promise<SyncUsageResponse>;
495
+ /**
496
+ * Check usage limits including purchased add-on credits.
497
+ *
498
+ * @example
499
+ * ```typescript
500
+ * const result = await stackbe.usage.checkWithAddons('cust_123', 'tickets');
501
+ *
502
+ * console.log(`Plan limit: ${result.planLimit}`);
503
+ * console.log(`Addon credits: ${result.addonCredits}`);
504
+ * console.log(`Effective limit: ${result.effectiveLimit}`);
505
+ *
506
+ * if (!result.allowed) {
507
+ * // Prompt to purchase more credits
508
+ * }
509
+ * ```
510
+ */
511
+ checkWithAddons(customerId: string, metric: string): Promise<CheckUsageWithAddonsResponse>;
512
+ /**
513
+ * Track usage with automatic add-on credit deduction.
514
+ * When plan limit is exceeded, usage is deducted from purchased add-on credits.
515
+ *
516
+ * @example
517
+ * ```typescript
518
+ * const result = await stackbe.usage.trackWithAddons('cust_123', 'tickets');
519
+ *
520
+ * if (result.addonUsage > 0) {
521
+ * console.log(`Used ${result.addonUsage} from addon credits`);
522
+ * }
523
+ * ```
524
+ */
525
+ trackWithAddons(customerId: string, metric: string, options?: TrackUsageOptions): Promise<TrackUsageWithAddonsResponse>;
526
+ /**
527
+ * List available add-on packs for purchase.
528
+ *
529
+ * @example
530
+ * ```typescript
531
+ * const addons = await stackbe.usage.listAddons();
532
+ *
533
+ * for (const addon of addons) {
534
+ * console.log(`${addon.name}: ${addon.quantity} ${addon.metricName} for $${addon.priceCents / 100}`);
535
+ * }
536
+ * ```
537
+ */
538
+ listAddons(): Promise<UsageAddon[]>;
539
+ /**
540
+ * Purchase an add-on pack for a customer.
541
+ * Credits are immediately available after purchase.
542
+ *
543
+ * @example
544
+ * ```typescript
545
+ * // Purchase a 10k ticket pack
546
+ * const purchase = await stackbe.usage.purchaseAddon('cust_123', 'addon_xyz');
547
+ * console.log(`Purchased ${purchase.quantity} credits`);
548
+ * ```
549
+ */
550
+ purchaseAddon(customerId: string, addonId: string): Promise<UsageAddonPurchase>;
551
+ /**
552
+ * Get customer's purchased add-on credits for a metric.
553
+ *
554
+ * @example
555
+ * ```typescript
556
+ * const credits = await stackbe.usage.getCredits('cust_123', 'tickets');
557
+ *
558
+ * console.log(`Total purchased: ${credits.totalPurchased}`);
559
+ * console.log(`Remaining: ${credits.remaining}`);
560
+ *
561
+ * for (const purchase of credits.purchases) {
562
+ * console.log(`${purchase.addonName}: ${purchase.remainingQuantity} left`);
563
+ * }
564
+ * ```
565
+ */
566
+ getCredits(customerId: string, metric: string): Promise<CustomerCreditsResponse>;
412
567
  }
413
568
 
414
569
  declare class EntitlementsClient {
@@ -713,6 +868,50 @@ declare class SubscriptionsClient {
713
868
  * ```
714
869
  */
715
870
  isActive(customerId: string): Promise<boolean>;
871
+ /**
872
+ * Pause a subscription.
873
+ *
874
+ * Pauses billing collection for a subscription. The subscription remains
875
+ * active but no invoices are generated while paused. Use cases include:
876
+ * - Customer requests temporary pause (vacation, budget constraints)
877
+ * - Seasonal businesses with predictable downtime
878
+ * - Retention strategy: offer pause instead of cancel
879
+ *
880
+ * @example
881
+ * ```typescript
882
+ * // Pause indefinitely (until manually resumed)
883
+ * await stackbe.subscriptions.pause('sub_123');
884
+ *
885
+ * // Pause for 30 days (auto-resume)
886
+ * const resumeDate = new Date();
887
+ * resumeDate.setDate(resumeDate.getDate() + 30);
888
+ * await stackbe.subscriptions.pause('sub_123', { resumesAt: resumeDate });
889
+ *
890
+ * // Check if paused
891
+ * const sub = await stackbe.subscriptions.get('cust_123');
892
+ * if (sub?.pausedAt) {
893
+ * console.log('Subscription is paused');
894
+ * if (sub.resumesAt) {
895
+ * console.log(`Will resume on ${sub.resumesAt}`);
896
+ * }
897
+ * }
898
+ * ```
899
+ */
900
+ pause(subscriptionId: string, options?: {
901
+ resumesAt?: Date;
902
+ }): Promise<Subscription>;
903
+ /**
904
+ * Resume a paused subscription.
905
+ *
906
+ * Restarts billing collection for a paused subscription.
907
+ *
908
+ * @example
909
+ * ```typescript
910
+ * // Resume a paused subscription
911
+ * await stackbe.subscriptions.resume('sub_123');
912
+ * ```
913
+ */
914
+ resume(subscriptionId: string): Promise<Subscription>;
716
915
  }
717
916
 
718
917
  interface AuthClientOptions {
package/dist/index.js CHANGED
@@ -157,6 +157,9 @@ var HttpClient = class {
157
157
  async post(path, body, params) {
158
158
  return this.request("POST", path, { body, params });
159
159
  }
160
+ async put(path, body, params) {
161
+ return this.request("PUT", path, { body, params });
162
+ }
160
163
  async patch(path, body) {
161
164
  return this.request("PATCH", path, { body });
162
165
  }
@@ -256,6 +259,122 @@ var UsageClient = class {
256
259
  allowed
257
260
  };
258
261
  }
262
+ /**
263
+ * Sync usage to an absolute value (instead of incrementing).
264
+ * Use this when your app tracks usage internally and reports the final count periodically.
265
+ *
266
+ * @example
267
+ * ```typescript
268
+ * // Your app tracks 847 billable tickets internally
269
+ * await stackbe.usage.sync('cust_123', 'tickets', 847);
270
+ * ```
271
+ */
272
+ async sync(customerId, metric, value, options = {}) {
273
+ const headers = {};
274
+ if (options.idempotencyKey) {
275
+ headers["Idempotency-Key"] = options.idempotencyKey;
276
+ }
277
+ return this.http.put("/v1/usage/sync", {
278
+ customerId,
279
+ metric,
280
+ value
281
+ });
282
+ }
283
+ /**
284
+ * Check usage limits including purchased add-on credits.
285
+ *
286
+ * @example
287
+ * ```typescript
288
+ * const result = await stackbe.usage.checkWithAddons('cust_123', 'tickets');
289
+ *
290
+ * console.log(`Plan limit: ${result.planLimit}`);
291
+ * console.log(`Addon credits: ${result.addonCredits}`);
292
+ * console.log(`Effective limit: ${result.effectiveLimit}`);
293
+ *
294
+ * if (!result.allowed) {
295
+ * // Prompt to purchase more credits
296
+ * }
297
+ * ```
298
+ */
299
+ async checkWithAddons(customerId, metric) {
300
+ return this.http.get(
301
+ `/v1/customers/${customerId}/usage/check-with-addons`,
302
+ { metric }
303
+ );
304
+ }
305
+ /**
306
+ * Track usage with automatic add-on credit deduction.
307
+ * When plan limit is exceeded, usage is deducted from purchased add-on credits.
308
+ *
309
+ * @example
310
+ * ```typescript
311
+ * const result = await stackbe.usage.trackWithAddons('cust_123', 'tickets');
312
+ *
313
+ * if (result.addonUsage > 0) {
314
+ * console.log(`Used ${result.addonUsage} from addon credits`);
315
+ * }
316
+ * ```
317
+ */
318
+ async trackWithAddons(customerId, metric, options = {}) {
319
+ return this.http.post("/v1/usage/addons/track", {
320
+ customerId,
321
+ metric,
322
+ quantity: options.quantity ?? 1
323
+ });
324
+ }
325
+ /**
326
+ * List available add-on packs for purchase.
327
+ *
328
+ * @example
329
+ * ```typescript
330
+ * const addons = await stackbe.usage.listAddons();
331
+ *
332
+ * for (const addon of addons) {
333
+ * console.log(`${addon.name}: ${addon.quantity} ${addon.metricName} for $${addon.priceCents / 100}`);
334
+ * }
335
+ * ```
336
+ */
337
+ async listAddons() {
338
+ return this.http.get("/v1/usage/addons");
339
+ }
340
+ /**
341
+ * Purchase an add-on pack for a customer.
342
+ * Credits are immediately available after purchase.
343
+ *
344
+ * @example
345
+ * ```typescript
346
+ * // Purchase a 10k ticket pack
347
+ * const purchase = await stackbe.usage.purchaseAddon('cust_123', 'addon_xyz');
348
+ * console.log(`Purchased ${purchase.quantity} credits`);
349
+ * ```
350
+ */
351
+ async purchaseAddon(customerId, addonId) {
352
+ return this.http.post("/v1/usage/addons/purchase", {
353
+ customerId,
354
+ addonId
355
+ });
356
+ }
357
+ /**
358
+ * Get customer's purchased add-on credits for a metric.
359
+ *
360
+ * @example
361
+ * ```typescript
362
+ * const credits = await stackbe.usage.getCredits('cust_123', 'tickets');
363
+ *
364
+ * console.log(`Total purchased: ${credits.totalPurchased}`);
365
+ * console.log(`Remaining: ${credits.remaining}`);
366
+ *
367
+ * for (const purchase of credits.purchases) {
368
+ * console.log(`${purchase.addonName}: ${purchase.remainingQuantity} left`);
369
+ * }
370
+ * ```
371
+ */
372
+ async getCredits(customerId, metric) {
373
+ return this.http.get(
374
+ `/v1/customers/${customerId}/usage/credits`,
375
+ { metric }
376
+ );
377
+ }
259
378
  };
260
379
 
261
380
  // src/entitlements.ts
@@ -683,6 +802,59 @@ var SubscriptionsClient = class {
683
802
  const subscription = await this.get(customerId);
684
803
  return subscription !== null && (subscription.status === "active" || subscription.status === "trialing");
685
804
  }
805
+ /**
806
+ * Pause a subscription.
807
+ *
808
+ * Pauses billing collection for a subscription. The subscription remains
809
+ * active but no invoices are generated while paused. Use cases include:
810
+ * - Customer requests temporary pause (vacation, budget constraints)
811
+ * - Seasonal businesses with predictable downtime
812
+ * - Retention strategy: offer pause instead of cancel
813
+ *
814
+ * @example
815
+ * ```typescript
816
+ * // Pause indefinitely (until manually resumed)
817
+ * await stackbe.subscriptions.pause('sub_123');
818
+ *
819
+ * // Pause for 30 days (auto-resume)
820
+ * const resumeDate = new Date();
821
+ * resumeDate.setDate(resumeDate.getDate() + 30);
822
+ * await stackbe.subscriptions.pause('sub_123', { resumesAt: resumeDate });
823
+ *
824
+ * // Check if paused
825
+ * const sub = await stackbe.subscriptions.get('cust_123');
826
+ * if (sub?.pausedAt) {
827
+ * console.log('Subscription is paused');
828
+ * if (sub.resumesAt) {
829
+ * console.log(`Will resume on ${sub.resumesAt}`);
830
+ * }
831
+ * }
832
+ * ```
833
+ */
834
+ async pause(subscriptionId, options = {}) {
835
+ return this.http.post(
836
+ `/v1/subscriptions/${subscriptionId}/pause`,
837
+ {
838
+ resumesAt: options.resumesAt?.toISOString()
839
+ }
840
+ );
841
+ }
842
+ /**
843
+ * Resume a paused subscription.
844
+ *
845
+ * Restarts billing collection for a paused subscription.
846
+ *
847
+ * @example
848
+ * ```typescript
849
+ * // Resume a paused subscription
850
+ * await stackbe.subscriptions.resume('sub_123');
851
+ * ```
852
+ */
853
+ async resume(subscriptionId) {
854
+ return this.http.post(
855
+ `/v1/subscriptions/${subscriptionId}/resume`
856
+ );
857
+ }
686
858
  };
687
859
 
688
860
  // src/auth.ts
package/dist/index.mjs CHANGED
@@ -124,6 +124,9 @@ var HttpClient = class {
124
124
  async post(path, body, params) {
125
125
  return this.request("POST", path, { body, params });
126
126
  }
127
+ async put(path, body, params) {
128
+ return this.request("PUT", path, { body, params });
129
+ }
127
130
  async patch(path, body) {
128
131
  return this.request("PATCH", path, { body });
129
132
  }
@@ -223,6 +226,122 @@ var UsageClient = class {
223
226
  allowed
224
227
  };
225
228
  }
229
+ /**
230
+ * Sync usage to an absolute value (instead of incrementing).
231
+ * Use this when your app tracks usage internally and reports the final count periodically.
232
+ *
233
+ * @example
234
+ * ```typescript
235
+ * // Your app tracks 847 billable tickets internally
236
+ * await stackbe.usage.sync('cust_123', 'tickets', 847);
237
+ * ```
238
+ */
239
+ async sync(customerId, metric, value, options = {}) {
240
+ const headers = {};
241
+ if (options.idempotencyKey) {
242
+ headers["Idempotency-Key"] = options.idempotencyKey;
243
+ }
244
+ return this.http.put("/v1/usage/sync", {
245
+ customerId,
246
+ metric,
247
+ value
248
+ });
249
+ }
250
+ /**
251
+ * Check usage limits including purchased add-on credits.
252
+ *
253
+ * @example
254
+ * ```typescript
255
+ * const result = await stackbe.usage.checkWithAddons('cust_123', 'tickets');
256
+ *
257
+ * console.log(`Plan limit: ${result.planLimit}`);
258
+ * console.log(`Addon credits: ${result.addonCredits}`);
259
+ * console.log(`Effective limit: ${result.effectiveLimit}`);
260
+ *
261
+ * if (!result.allowed) {
262
+ * // Prompt to purchase more credits
263
+ * }
264
+ * ```
265
+ */
266
+ async checkWithAddons(customerId, metric) {
267
+ return this.http.get(
268
+ `/v1/customers/${customerId}/usage/check-with-addons`,
269
+ { metric }
270
+ );
271
+ }
272
+ /**
273
+ * Track usage with automatic add-on credit deduction.
274
+ * When plan limit is exceeded, usage is deducted from purchased add-on credits.
275
+ *
276
+ * @example
277
+ * ```typescript
278
+ * const result = await stackbe.usage.trackWithAddons('cust_123', 'tickets');
279
+ *
280
+ * if (result.addonUsage > 0) {
281
+ * console.log(`Used ${result.addonUsage} from addon credits`);
282
+ * }
283
+ * ```
284
+ */
285
+ async trackWithAddons(customerId, metric, options = {}) {
286
+ return this.http.post("/v1/usage/addons/track", {
287
+ customerId,
288
+ metric,
289
+ quantity: options.quantity ?? 1
290
+ });
291
+ }
292
+ /**
293
+ * List available add-on packs for purchase.
294
+ *
295
+ * @example
296
+ * ```typescript
297
+ * const addons = await stackbe.usage.listAddons();
298
+ *
299
+ * for (const addon of addons) {
300
+ * console.log(`${addon.name}: ${addon.quantity} ${addon.metricName} for $${addon.priceCents / 100}`);
301
+ * }
302
+ * ```
303
+ */
304
+ async listAddons() {
305
+ return this.http.get("/v1/usage/addons");
306
+ }
307
+ /**
308
+ * Purchase an add-on pack for a customer.
309
+ * Credits are immediately available after purchase.
310
+ *
311
+ * @example
312
+ * ```typescript
313
+ * // Purchase a 10k ticket pack
314
+ * const purchase = await stackbe.usage.purchaseAddon('cust_123', 'addon_xyz');
315
+ * console.log(`Purchased ${purchase.quantity} credits`);
316
+ * ```
317
+ */
318
+ async purchaseAddon(customerId, addonId) {
319
+ return this.http.post("/v1/usage/addons/purchase", {
320
+ customerId,
321
+ addonId
322
+ });
323
+ }
324
+ /**
325
+ * Get customer's purchased add-on credits for a metric.
326
+ *
327
+ * @example
328
+ * ```typescript
329
+ * const credits = await stackbe.usage.getCredits('cust_123', 'tickets');
330
+ *
331
+ * console.log(`Total purchased: ${credits.totalPurchased}`);
332
+ * console.log(`Remaining: ${credits.remaining}`);
333
+ *
334
+ * for (const purchase of credits.purchases) {
335
+ * console.log(`${purchase.addonName}: ${purchase.remainingQuantity} left`);
336
+ * }
337
+ * ```
338
+ */
339
+ async getCredits(customerId, metric) {
340
+ return this.http.get(
341
+ `/v1/customers/${customerId}/usage/credits`,
342
+ { metric }
343
+ );
344
+ }
226
345
  };
227
346
 
228
347
  // src/entitlements.ts
@@ -650,6 +769,59 @@ var SubscriptionsClient = class {
650
769
  const subscription = await this.get(customerId);
651
770
  return subscription !== null && (subscription.status === "active" || subscription.status === "trialing");
652
771
  }
772
+ /**
773
+ * Pause a subscription.
774
+ *
775
+ * Pauses billing collection for a subscription. The subscription remains
776
+ * active but no invoices are generated while paused. Use cases include:
777
+ * - Customer requests temporary pause (vacation, budget constraints)
778
+ * - Seasonal businesses with predictable downtime
779
+ * - Retention strategy: offer pause instead of cancel
780
+ *
781
+ * @example
782
+ * ```typescript
783
+ * // Pause indefinitely (until manually resumed)
784
+ * await stackbe.subscriptions.pause('sub_123');
785
+ *
786
+ * // Pause for 30 days (auto-resume)
787
+ * const resumeDate = new Date();
788
+ * resumeDate.setDate(resumeDate.getDate() + 30);
789
+ * await stackbe.subscriptions.pause('sub_123', { resumesAt: resumeDate });
790
+ *
791
+ * // Check if paused
792
+ * const sub = await stackbe.subscriptions.get('cust_123');
793
+ * if (sub?.pausedAt) {
794
+ * console.log('Subscription is paused');
795
+ * if (sub.resumesAt) {
796
+ * console.log(`Will resume on ${sub.resumesAt}`);
797
+ * }
798
+ * }
799
+ * ```
800
+ */
801
+ async pause(subscriptionId, options = {}) {
802
+ return this.http.post(
803
+ `/v1/subscriptions/${subscriptionId}/pause`,
804
+ {
805
+ resumesAt: options.resumesAt?.toISOString()
806
+ }
807
+ );
808
+ }
809
+ /**
810
+ * Resume a paused subscription.
811
+ *
812
+ * Restarts billing collection for a paused subscription.
813
+ *
814
+ * @example
815
+ * ```typescript
816
+ * // Resume a paused subscription
817
+ * await stackbe.subscriptions.resume('sub_123');
818
+ * ```
819
+ */
820
+ async resume(subscriptionId) {
821
+ return this.http.post(
822
+ `/v1/subscriptions/${subscriptionId}/resume`
823
+ );
824
+ }
653
825
  };
654
826
 
655
827
  // src/auth.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackbe/sdk",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Official JavaScript/TypeScript SDK for StackBE - the billing backend for your side project",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",