@stackbe/sdk 0.6.5 → 0.7.2

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/README.md CHANGED
@@ -549,9 +549,22 @@ stackbe.auth.invalidateSession(token);
549
549
  stackbe.auth.clearCache(); // Clear all
550
550
  ```
551
551
 
552
- ### Environment-Aware Redirects
552
+ ### Magic Link Callback URLs
553
553
 
554
- Auto-detect development environment for magic link redirects:
554
+ **Recommended approach**: Pass the callback URL explicitly for maximum reliability:
555
+
556
+ ```typescript
557
+ // Determine callback URL based on your environment
558
+ const callbackUrl = process.env.NODE_ENV === 'production'
559
+ ? 'https://myapp.com/auth/callback'
560
+ : 'http://localhost:3000/auth/callback';
561
+
562
+ await stackbe.auth.sendMagicLink('user@example.com', {
563
+ redirectUrl: callbackUrl,
564
+ });
565
+ ```
566
+
567
+ **Alternative**: Use `devCallbackUrl` for automatic detection (uses `devCallbackUrl` when `NODE_ENV !== 'production'`):
555
568
 
556
569
  ```typescript
557
570
  const stackbe = new StackBE({
@@ -560,17 +573,18 @@ const stackbe = new StackBE({
560
573
  devCallbackUrl: 'http://localhost:3000/auth/callback',
561
574
  });
562
575
 
563
- // In development (NODE_ENV !== 'production'), uses devCallbackUrl automatically
576
+ // In development, uses devCallbackUrl automatically
564
577
  await stackbe.auth.sendMagicLink('user@example.com');
565
- // Redirects to: http://localhost:3000/auth/callback
566
578
 
567
- // In production, uses your configured auth callback URL
568
- // Or pass explicit redirectUrl to override
569
- await stackbe.auth.sendMagicLink('user@example.com', {
570
- redirectUrl: 'https://myapp.com/custom-callback',
571
- });
579
+ // In production, falls back to app settings callback URL
580
+ // Explicit redirectUrl always takes priority if provided
572
581
  ```
573
582
 
583
+ **Notes:**
584
+ - `devCallbackUrl` only activates when `NODE_ENV !== 'production'`
585
+ - Explicit `redirectUrl` parameter always takes priority
586
+ - For predictable behavior, use the explicit approach above
587
+
574
588
  ## Webhooks
575
589
 
576
590
  Typed webhook payloads for handling StackBE events:
package/dist/index.d.mts CHANGED
@@ -13,7 +13,7 @@ declare class HttpClient {
13
13
  post<T>(path: string, body?: unknown, params?: Record<string, string | number | undefined>): Promise<T>;
14
14
  put<T>(path: string, body?: unknown, params?: Record<string, string | number | undefined>): Promise<T>;
15
15
  patch<T>(path: string, body?: unknown): Promise<T>;
16
- delete<T>(path: string): Promise<T>;
16
+ delete<T>(path: string, body?: unknown): Promise<T>;
17
17
  }
18
18
 
19
19
  interface StackBEConfig {
@@ -340,7 +340,7 @@ interface ListProductsOptions {
340
340
  appId?: string;
341
341
  }
342
342
  interface CreateCheckoutOptions {
343
- /** Customer ID or email */
343
+ /** Customer ID or object with email. Use string for existing customer ID, object for email-based lookup/creation */
344
344
  customer: string | {
345
345
  email: string;
346
346
  name?: string;
@@ -351,6 +351,8 @@ interface CreateCheckoutOptions {
351
351
  successUrl: string;
352
352
  /** URL to redirect if checkout is canceled */
353
353
  cancelUrl?: string;
354
+ /** Organization ID for org-level subscriptions. Customer must be owner/admin of the org. */
355
+ organizationId?: string;
354
356
  /** Allow promotion codes */
355
357
  allowPromotionCodes?: boolean;
356
358
  /** Trial period in days */
@@ -825,7 +827,7 @@ declare class CheckoutClient {
825
827
  *
826
828
  * @example
827
829
  * ```typescript
828
- * // With new customer (will be created)
830
+ * // With customer email (looks up or creates customer)
829
831
  * const { url } = await stackbe.checkout.createSession({
830
832
  * customer: { email: 'user@example.com', name: 'John' },
831
833
  * planId: 'plan_pro_monthly',
@@ -833,6 +835,18 @@ declare class CheckoutClient {
833
835
  * trialDays: 14,
834
836
  * });
835
837
  * ```
838
+ *
839
+ * @example
840
+ * ```typescript
841
+ * // Organization-level subscription (B2B)
842
+ * const { url } = await stackbe.checkout.createSession({
843
+ * customer: 'cust_123',
844
+ * organizationId: 'org_456', // Customer must be owner/admin
845
+ * planId: 'plan_team',
846
+ * successUrl: 'https://myapp.com/success',
847
+ * cancelUrl: 'https://myapp.com/pricing',
848
+ * });
849
+ * ```
836
850
  */
837
851
  createSession(options: CreateCheckoutOptions): Promise<CheckoutSessionResponse>;
838
852
  /**
@@ -1375,6 +1389,240 @@ declare class ProductsClient {
1375
1389
  get(productId: string): Promise<Product>;
1376
1390
  }
1377
1391
 
1392
+ interface FeatureRequest {
1393
+ id: string;
1394
+ title: string;
1395
+ description?: string;
1396
+ status: 'new' | 'under_review' | 'planned' | 'in_progress' | 'completed' | 'declined';
1397
+ category?: string;
1398
+ authorId: string;
1399
+ authorEmail: string;
1400
+ voteCount: number;
1401
+ hasVoted?: boolean;
1402
+ createdAt: string;
1403
+ updatedAt: string;
1404
+ }
1405
+ interface FeatureRequestComment {
1406
+ id: string;
1407
+ customerEmail: string;
1408
+ content: string;
1409
+ isAdminReply: boolean;
1410
+ createdAt: string;
1411
+ }
1412
+ interface CreateFeatureRequestOptions {
1413
+ title: string;
1414
+ description?: string;
1415
+ category?: string;
1416
+ }
1417
+ interface ListFeatureRequestsOptions {
1418
+ status?: 'new' | 'under_review' | 'planned' | 'in_progress' | 'completed' | 'declined';
1419
+ category?: string;
1420
+ sortBy?: 'newest' | 'votes' | 'recent_activity';
1421
+ limit?: number;
1422
+ offset?: number;
1423
+ }
1424
+ interface FeatureRequestListResponse {
1425
+ data: FeatureRequest[];
1426
+ total: number;
1427
+ limit: number;
1428
+ offset: number;
1429
+ }
1430
+ interface InterestedCustomer {
1431
+ customerId: string;
1432
+ email: string;
1433
+ votedAt?: string;
1434
+ submittedAt?: string;
1435
+ }
1436
+ interface InterestedCustomersResponse {
1437
+ featureRequestId: string;
1438
+ title: string;
1439
+ status: string;
1440
+ author: {
1441
+ customerId: string;
1442
+ email: string;
1443
+ submittedAt: string;
1444
+ };
1445
+ voters: InterestedCustomer[];
1446
+ totalInterested: number;
1447
+ }
1448
+ interface CustomerFeatureActivity {
1449
+ customerId: string;
1450
+ submitted: Array<{
1451
+ id: string;
1452
+ title: string;
1453
+ status: string;
1454
+ voteCount: number;
1455
+ submittedAt: string;
1456
+ }>;
1457
+ votedOn: Array<{
1458
+ id: string;
1459
+ title: string;
1460
+ status: string;
1461
+ voteCount: number;
1462
+ votedAt: string;
1463
+ }>;
1464
+ totalSubmitted: number;
1465
+ totalVotes: number;
1466
+ }
1467
+ declare class FeatureRequestsClient {
1468
+ private http;
1469
+ private appId;
1470
+ constructor(http: HttpClient, appId: string);
1471
+ /**
1472
+ * List feature requests for the app.
1473
+ *
1474
+ * @example
1475
+ * ```typescript
1476
+ * // Get all feature requests
1477
+ * const { data, total } = await stackbe.featureRequests.list();
1478
+ *
1479
+ * // Filter by status
1480
+ * const planned = await stackbe.featureRequests.list({ status: 'planned' });
1481
+ *
1482
+ * // Sort by votes
1483
+ * const popular = await stackbe.featureRequests.list({ sortBy: 'votes' });
1484
+ * ```
1485
+ */
1486
+ list(options?: ListFeatureRequestsOptions): Promise<FeatureRequestListResponse>;
1487
+ /**
1488
+ * Submit a new feature request.
1489
+ * Requires customer session token.
1490
+ *
1491
+ * @example
1492
+ * ```typescript
1493
+ * const request = await stackbe.featureRequests.create({
1494
+ * title: 'Dark mode support',
1495
+ * description: 'Would love to have a dark theme option',
1496
+ * category: 'UI',
1497
+ * });
1498
+ * ```
1499
+ */
1500
+ create(options: CreateFeatureRequestOptions): Promise<FeatureRequest>;
1501
+ /**
1502
+ * Get a specific feature request by ID.
1503
+ *
1504
+ * @example
1505
+ * ```typescript
1506
+ * const request = await stackbe.featureRequests.get('req_123');
1507
+ * console.log(`${request.title} has ${request.voteCount} votes`);
1508
+ * ```
1509
+ */
1510
+ get(requestId: string): Promise<FeatureRequest & {
1511
+ comments: FeatureRequestComment[];
1512
+ }>;
1513
+ /**
1514
+ * Upvote a feature request.
1515
+ * Requires customer session token, OR API key with customerId.
1516
+ *
1517
+ * @example
1518
+ * ```typescript
1519
+ * // With customer session token
1520
+ * await stackbe.featureRequests.vote('req_123');
1521
+ *
1522
+ * // With API key (server-side)
1523
+ * await stackbe.featureRequests.vote('req_123', 'cust_456');
1524
+ * ```
1525
+ */
1526
+ vote(requestId: string, customerId?: string): Promise<{
1527
+ success?: boolean;
1528
+ alreadyVoted?: boolean;
1529
+ voteCount: number;
1530
+ }>;
1531
+ /**
1532
+ * Remove your vote from a feature request.
1533
+ * Requires customer session token, OR API key with customerId.
1534
+ *
1535
+ * @example
1536
+ * ```typescript
1537
+ * // With customer session token
1538
+ * await stackbe.featureRequests.removeVote('req_123');
1539
+ *
1540
+ * // With API key (server-side)
1541
+ * await stackbe.featureRequests.removeVote('req_123', 'cust_456');
1542
+ * ```
1543
+ */
1544
+ removeVote(requestId: string, customerId?: string): Promise<{
1545
+ success: boolean;
1546
+ voteCount: number;
1547
+ }>;
1548
+ /**
1549
+ * Add a comment to a feature request.
1550
+ * Requires customer session token.
1551
+ *
1552
+ * @example
1553
+ * ```typescript
1554
+ * await stackbe.featureRequests.addComment('req_123', {
1555
+ * content: 'This would be really helpful for my workflow!',
1556
+ * });
1557
+ * ```
1558
+ */
1559
+ addComment(requestId: string, options: {
1560
+ content: string;
1561
+ }): Promise<FeatureRequestComment>;
1562
+ /**
1563
+ * Update a feature request status or category.
1564
+ * Requires API key (admin).
1565
+ *
1566
+ * @example
1567
+ * ```typescript
1568
+ * await stackbe.featureRequests.update('req_123', {
1569
+ * status: 'planned',
1570
+ * category: 'Core Features',
1571
+ * });
1572
+ * ```
1573
+ */
1574
+ update(requestId: string, options: {
1575
+ status?: string;
1576
+ category?: string;
1577
+ }): Promise<FeatureRequest>;
1578
+ /**
1579
+ * Delete a feature request.
1580
+ * Requires API key (admin).
1581
+ *
1582
+ * @example
1583
+ * ```typescript
1584
+ * await stackbe.featureRequests.delete('req_123');
1585
+ * ```
1586
+ */
1587
+ delete(requestId: string): Promise<{
1588
+ success: boolean;
1589
+ }>;
1590
+ /**
1591
+ * Get all customers interested in a feature request (author + voters).
1592
+ * Useful for user research or notifying users when the feature ships.
1593
+ * Requires API key (admin).
1594
+ *
1595
+ * @example
1596
+ * ```typescript
1597
+ * const { author, voters, totalInterested } = await stackbe.featureRequests.getInterestedCustomers('req_123');
1598
+ *
1599
+ * // Notify all interested users
1600
+ * const emails = [author.email, ...voters.map(v => v.email)];
1601
+ * await sendNotification(emails, 'Your requested feature is now live!');
1602
+ * ```
1603
+ */
1604
+ getInterestedCustomers(requestId: string): Promise<InterestedCustomersResponse>;
1605
+ /**
1606
+ * Get a customer's feature request activity (requests submitted + votes).
1607
+ * Useful for displaying on customer profile.
1608
+ * Requires API key (admin).
1609
+ *
1610
+ * @example
1611
+ * ```typescript
1612
+ * const activity = await stackbe.featureRequests.getCustomerActivity('cust_123');
1613
+ *
1614
+ * console.log(`Submitted ${activity.totalSubmitted} requests`);
1615
+ * console.log(`Voted on ${activity.totalVotes} requests`);
1616
+ *
1617
+ * // Show their most wanted features
1618
+ * activity.votedOn.forEach(r => {
1619
+ * console.log(`- ${r.title} (${r.status})`);
1620
+ * });
1621
+ * ```
1622
+ */
1623
+ getCustomerActivity(customerId: string): Promise<CustomerFeatureActivity>;
1624
+ }
1625
+
1378
1626
  declare class StackBE {
1379
1627
  private http;
1380
1628
  private appId;
@@ -1396,6 +1644,8 @@ declare class StackBE {
1396
1644
  readonly plans: PlansClient;
1397
1645
  /** Products */
1398
1646
  readonly products: ProductsClient;
1647
+ /** Feature requests and voting */
1648
+ readonly featureRequests: FeatureRequestsClient;
1399
1649
  /**
1400
1650
  * Create a new StackBE client.
1401
1651
  *
@@ -1496,4 +1746,4 @@ declare class StackBE {
1496
1746
  }): (req: any, res: any, next: any) => Promise<any>;
1497
1747
  }
1498
1748
 
1499
- export { type AddMemberOptions, type AnyWebhookEvent, AuthClient, type CancelSubscriptionOptions, type CancelSubscriptionResponse, type CheckEntitlementResponse, type CheckUsageResponse, CheckoutClient, type CheckoutSessionResponse, type CreateCheckoutOptions, type CreateCustomerOptions, type CreateOrganizationOptions, type Customer, type CustomerCreatedEvent, type CustomerUpdatedEvent, type CustomerUsageResponse, type CustomerWebhookPayload, type CustomerWithSubscription, CustomersClient, EntitlementsClient, type EntitlementsResponse, type InviteMemberOptions, type ListPlansOptions, type ListProductsOptions, type MagicLinkOptions, type MagicLinkResponse, type Organization, type OrganizationInvite, type OrganizationMember, OrganizationsClient, type PaymentFailedEvent, type PaymentSucceededEvent, type PaymentWebhookPayload, type Plan, PlansClient, type Product, ProductsClient, type SessionResponse, StackBE, type StackBEConfig, StackBEError, type StackBEErrorCode, type StackBEErrorResponse, type Subscription, type SubscriptionCancelledEvent, type SubscriptionCreatedEvent, type SubscriptionRenewedEvent, type SubscriptionUpdatedEvent, type SubscriptionWebhookPayload, type SubscriptionWithPlan, SubscriptionsClient, type TrackUsageOptions, type TrackUsageResponse, type TrialEndedEvent, type TrialStartedEvent, type UpdateCustomerOptions, type UpdateOrganizationOptions, type UpdateSubscriptionOptions, UsageClient, type UsageMetric, type VerifyTokenResponse, type WebhookEvent, type WebhookEventType };
1749
+ export { type AddMemberOptions, type AnyWebhookEvent, AuthClient, type CancelSubscriptionOptions, type CancelSubscriptionResponse, type CheckEntitlementResponse, type CheckUsageResponse, CheckoutClient, type CheckoutSessionResponse, type CreateCheckoutOptions, type CreateCustomerOptions, type CreateFeatureRequestOptions, type CreateOrganizationOptions, type Customer, type CustomerCreatedEvent, type CustomerFeatureActivity, type CustomerUpdatedEvent, type CustomerUsageResponse, type CustomerWebhookPayload, type CustomerWithSubscription, CustomersClient, EntitlementsClient, type EntitlementsResponse, type FeatureRequest, type FeatureRequestComment, type FeatureRequestListResponse, FeatureRequestsClient, type InterestedCustomer, type InterestedCustomersResponse, type InviteMemberOptions, type ListFeatureRequestsOptions, type ListPlansOptions, type ListProductsOptions, type MagicLinkOptions, type MagicLinkResponse, type Organization, type OrganizationInvite, type OrganizationMember, OrganizationsClient, type PaymentFailedEvent, type PaymentSucceededEvent, type PaymentWebhookPayload, type Plan, PlansClient, type Product, ProductsClient, type SessionResponse, StackBE, type StackBEConfig, StackBEError, type StackBEErrorCode, type StackBEErrorResponse, type Subscription, type SubscriptionCancelledEvent, type SubscriptionCreatedEvent, type SubscriptionRenewedEvent, type SubscriptionUpdatedEvent, type SubscriptionWebhookPayload, type SubscriptionWithPlan, SubscriptionsClient, type TrackUsageOptions, type TrackUsageResponse, type TrialEndedEvent, type TrialStartedEvent, type UpdateCustomerOptions, type UpdateOrganizationOptions, type UpdateSubscriptionOptions, UsageClient, type UsageMetric, type VerifyTokenResponse, type WebhookEvent, type WebhookEventType };
package/dist/index.d.ts CHANGED
@@ -13,7 +13,7 @@ declare class HttpClient {
13
13
  post<T>(path: string, body?: unknown, params?: Record<string, string | number | undefined>): Promise<T>;
14
14
  put<T>(path: string, body?: unknown, params?: Record<string, string | number | undefined>): Promise<T>;
15
15
  patch<T>(path: string, body?: unknown): Promise<T>;
16
- delete<T>(path: string): Promise<T>;
16
+ delete<T>(path: string, body?: unknown): Promise<T>;
17
17
  }
18
18
 
19
19
  interface StackBEConfig {
@@ -340,7 +340,7 @@ interface ListProductsOptions {
340
340
  appId?: string;
341
341
  }
342
342
  interface CreateCheckoutOptions {
343
- /** Customer ID or email */
343
+ /** Customer ID or object with email. Use string for existing customer ID, object for email-based lookup/creation */
344
344
  customer: string | {
345
345
  email: string;
346
346
  name?: string;
@@ -351,6 +351,8 @@ interface CreateCheckoutOptions {
351
351
  successUrl: string;
352
352
  /** URL to redirect if checkout is canceled */
353
353
  cancelUrl?: string;
354
+ /** Organization ID for org-level subscriptions. Customer must be owner/admin of the org. */
355
+ organizationId?: string;
354
356
  /** Allow promotion codes */
355
357
  allowPromotionCodes?: boolean;
356
358
  /** Trial period in days */
@@ -825,7 +827,7 @@ declare class CheckoutClient {
825
827
  *
826
828
  * @example
827
829
  * ```typescript
828
- * // With new customer (will be created)
830
+ * // With customer email (looks up or creates customer)
829
831
  * const { url } = await stackbe.checkout.createSession({
830
832
  * customer: { email: 'user@example.com', name: 'John' },
831
833
  * planId: 'plan_pro_monthly',
@@ -833,6 +835,18 @@ declare class CheckoutClient {
833
835
  * trialDays: 14,
834
836
  * });
835
837
  * ```
838
+ *
839
+ * @example
840
+ * ```typescript
841
+ * // Organization-level subscription (B2B)
842
+ * const { url } = await stackbe.checkout.createSession({
843
+ * customer: 'cust_123',
844
+ * organizationId: 'org_456', // Customer must be owner/admin
845
+ * planId: 'plan_team',
846
+ * successUrl: 'https://myapp.com/success',
847
+ * cancelUrl: 'https://myapp.com/pricing',
848
+ * });
849
+ * ```
836
850
  */
837
851
  createSession(options: CreateCheckoutOptions): Promise<CheckoutSessionResponse>;
838
852
  /**
@@ -1375,6 +1389,240 @@ declare class ProductsClient {
1375
1389
  get(productId: string): Promise<Product>;
1376
1390
  }
1377
1391
 
1392
+ interface FeatureRequest {
1393
+ id: string;
1394
+ title: string;
1395
+ description?: string;
1396
+ status: 'new' | 'under_review' | 'planned' | 'in_progress' | 'completed' | 'declined';
1397
+ category?: string;
1398
+ authorId: string;
1399
+ authorEmail: string;
1400
+ voteCount: number;
1401
+ hasVoted?: boolean;
1402
+ createdAt: string;
1403
+ updatedAt: string;
1404
+ }
1405
+ interface FeatureRequestComment {
1406
+ id: string;
1407
+ customerEmail: string;
1408
+ content: string;
1409
+ isAdminReply: boolean;
1410
+ createdAt: string;
1411
+ }
1412
+ interface CreateFeatureRequestOptions {
1413
+ title: string;
1414
+ description?: string;
1415
+ category?: string;
1416
+ }
1417
+ interface ListFeatureRequestsOptions {
1418
+ status?: 'new' | 'under_review' | 'planned' | 'in_progress' | 'completed' | 'declined';
1419
+ category?: string;
1420
+ sortBy?: 'newest' | 'votes' | 'recent_activity';
1421
+ limit?: number;
1422
+ offset?: number;
1423
+ }
1424
+ interface FeatureRequestListResponse {
1425
+ data: FeatureRequest[];
1426
+ total: number;
1427
+ limit: number;
1428
+ offset: number;
1429
+ }
1430
+ interface InterestedCustomer {
1431
+ customerId: string;
1432
+ email: string;
1433
+ votedAt?: string;
1434
+ submittedAt?: string;
1435
+ }
1436
+ interface InterestedCustomersResponse {
1437
+ featureRequestId: string;
1438
+ title: string;
1439
+ status: string;
1440
+ author: {
1441
+ customerId: string;
1442
+ email: string;
1443
+ submittedAt: string;
1444
+ };
1445
+ voters: InterestedCustomer[];
1446
+ totalInterested: number;
1447
+ }
1448
+ interface CustomerFeatureActivity {
1449
+ customerId: string;
1450
+ submitted: Array<{
1451
+ id: string;
1452
+ title: string;
1453
+ status: string;
1454
+ voteCount: number;
1455
+ submittedAt: string;
1456
+ }>;
1457
+ votedOn: Array<{
1458
+ id: string;
1459
+ title: string;
1460
+ status: string;
1461
+ voteCount: number;
1462
+ votedAt: string;
1463
+ }>;
1464
+ totalSubmitted: number;
1465
+ totalVotes: number;
1466
+ }
1467
+ declare class FeatureRequestsClient {
1468
+ private http;
1469
+ private appId;
1470
+ constructor(http: HttpClient, appId: string);
1471
+ /**
1472
+ * List feature requests for the app.
1473
+ *
1474
+ * @example
1475
+ * ```typescript
1476
+ * // Get all feature requests
1477
+ * const { data, total } = await stackbe.featureRequests.list();
1478
+ *
1479
+ * // Filter by status
1480
+ * const planned = await stackbe.featureRequests.list({ status: 'planned' });
1481
+ *
1482
+ * // Sort by votes
1483
+ * const popular = await stackbe.featureRequests.list({ sortBy: 'votes' });
1484
+ * ```
1485
+ */
1486
+ list(options?: ListFeatureRequestsOptions): Promise<FeatureRequestListResponse>;
1487
+ /**
1488
+ * Submit a new feature request.
1489
+ * Requires customer session token.
1490
+ *
1491
+ * @example
1492
+ * ```typescript
1493
+ * const request = await stackbe.featureRequests.create({
1494
+ * title: 'Dark mode support',
1495
+ * description: 'Would love to have a dark theme option',
1496
+ * category: 'UI',
1497
+ * });
1498
+ * ```
1499
+ */
1500
+ create(options: CreateFeatureRequestOptions): Promise<FeatureRequest>;
1501
+ /**
1502
+ * Get a specific feature request by ID.
1503
+ *
1504
+ * @example
1505
+ * ```typescript
1506
+ * const request = await stackbe.featureRequests.get('req_123');
1507
+ * console.log(`${request.title} has ${request.voteCount} votes`);
1508
+ * ```
1509
+ */
1510
+ get(requestId: string): Promise<FeatureRequest & {
1511
+ comments: FeatureRequestComment[];
1512
+ }>;
1513
+ /**
1514
+ * Upvote a feature request.
1515
+ * Requires customer session token, OR API key with customerId.
1516
+ *
1517
+ * @example
1518
+ * ```typescript
1519
+ * // With customer session token
1520
+ * await stackbe.featureRequests.vote('req_123');
1521
+ *
1522
+ * // With API key (server-side)
1523
+ * await stackbe.featureRequests.vote('req_123', 'cust_456');
1524
+ * ```
1525
+ */
1526
+ vote(requestId: string, customerId?: string): Promise<{
1527
+ success?: boolean;
1528
+ alreadyVoted?: boolean;
1529
+ voteCount: number;
1530
+ }>;
1531
+ /**
1532
+ * Remove your vote from a feature request.
1533
+ * Requires customer session token, OR API key with customerId.
1534
+ *
1535
+ * @example
1536
+ * ```typescript
1537
+ * // With customer session token
1538
+ * await stackbe.featureRequests.removeVote('req_123');
1539
+ *
1540
+ * // With API key (server-side)
1541
+ * await stackbe.featureRequests.removeVote('req_123', 'cust_456');
1542
+ * ```
1543
+ */
1544
+ removeVote(requestId: string, customerId?: string): Promise<{
1545
+ success: boolean;
1546
+ voteCount: number;
1547
+ }>;
1548
+ /**
1549
+ * Add a comment to a feature request.
1550
+ * Requires customer session token.
1551
+ *
1552
+ * @example
1553
+ * ```typescript
1554
+ * await stackbe.featureRequests.addComment('req_123', {
1555
+ * content: 'This would be really helpful for my workflow!',
1556
+ * });
1557
+ * ```
1558
+ */
1559
+ addComment(requestId: string, options: {
1560
+ content: string;
1561
+ }): Promise<FeatureRequestComment>;
1562
+ /**
1563
+ * Update a feature request status or category.
1564
+ * Requires API key (admin).
1565
+ *
1566
+ * @example
1567
+ * ```typescript
1568
+ * await stackbe.featureRequests.update('req_123', {
1569
+ * status: 'planned',
1570
+ * category: 'Core Features',
1571
+ * });
1572
+ * ```
1573
+ */
1574
+ update(requestId: string, options: {
1575
+ status?: string;
1576
+ category?: string;
1577
+ }): Promise<FeatureRequest>;
1578
+ /**
1579
+ * Delete a feature request.
1580
+ * Requires API key (admin).
1581
+ *
1582
+ * @example
1583
+ * ```typescript
1584
+ * await stackbe.featureRequests.delete('req_123');
1585
+ * ```
1586
+ */
1587
+ delete(requestId: string): Promise<{
1588
+ success: boolean;
1589
+ }>;
1590
+ /**
1591
+ * Get all customers interested in a feature request (author + voters).
1592
+ * Useful for user research or notifying users when the feature ships.
1593
+ * Requires API key (admin).
1594
+ *
1595
+ * @example
1596
+ * ```typescript
1597
+ * const { author, voters, totalInterested } = await stackbe.featureRequests.getInterestedCustomers('req_123');
1598
+ *
1599
+ * // Notify all interested users
1600
+ * const emails = [author.email, ...voters.map(v => v.email)];
1601
+ * await sendNotification(emails, 'Your requested feature is now live!');
1602
+ * ```
1603
+ */
1604
+ getInterestedCustomers(requestId: string): Promise<InterestedCustomersResponse>;
1605
+ /**
1606
+ * Get a customer's feature request activity (requests submitted + votes).
1607
+ * Useful for displaying on customer profile.
1608
+ * Requires API key (admin).
1609
+ *
1610
+ * @example
1611
+ * ```typescript
1612
+ * const activity = await stackbe.featureRequests.getCustomerActivity('cust_123');
1613
+ *
1614
+ * console.log(`Submitted ${activity.totalSubmitted} requests`);
1615
+ * console.log(`Voted on ${activity.totalVotes} requests`);
1616
+ *
1617
+ * // Show their most wanted features
1618
+ * activity.votedOn.forEach(r => {
1619
+ * console.log(`- ${r.title} (${r.status})`);
1620
+ * });
1621
+ * ```
1622
+ */
1623
+ getCustomerActivity(customerId: string): Promise<CustomerFeatureActivity>;
1624
+ }
1625
+
1378
1626
  declare class StackBE {
1379
1627
  private http;
1380
1628
  private appId;
@@ -1396,6 +1644,8 @@ declare class StackBE {
1396
1644
  readonly plans: PlansClient;
1397
1645
  /** Products */
1398
1646
  readonly products: ProductsClient;
1647
+ /** Feature requests and voting */
1648
+ readonly featureRequests: FeatureRequestsClient;
1399
1649
  /**
1400
1650
  * Create a new StackBE client.
1401
1651
  *
@@ -1496,4 +1746,4 @@ declare class StackBE {
1496
1746
  }): (req: any, res: any, next: any) => Promise<any>;
1497
1747
  }
1498
1748
 
1499
- export { type AddMemberOptions, type AnyWebhookEvent, AuthClient, type CancelSubscriptionOptions, type CancelSubscriptionResponse, type CheckEntitlementResponse, type CheckUsageResponse, CheckoutClient, type CheckoutSessionResponse, type CreateCheckoutOptions, type CreateCustomerOptions, type CreateOrganizationOptions, type Customer, type CustomerCreatedEvent, type CustomerUpdatedEvent, type CustomerUsageResponse, type CustomerWebhookPayload, type CustomerWithSubscription, CustomersClient, EntitlementsClient, type EntitlementsResponse, type InviteMemberOptions, type ListPlansOptions, type ListProductsOptions, type MagicLinkOptions, type MagicLinkResponse, type Organization, type OrganizationInvite, type OrganizationMember, OrganizationsClient, type PaymentFailedEvent, type PaymentSucceededEvent, type PaymentWebhookPayload, type Plan, PlansClient, type Product, ProductsClient, type SessionResponse, StackBE, type StackBEConfig, StackBEError, type StackBEErrorCode, type StackBEErrorResponse, type Subscription, type SubscriptionCancelledEvent, type SubscriptionCreatedEvent, type SubscriptionRenewedEvent, type SubscriptionUpdatedEvent, type SubscriptionWebhookPayload, type SubscriptionWithPlan, SubscriptionsClient, type TrackUsageOptions, type TrackUsageResponse, type TrialEndedEvent, type TrialStartedEvent, type UpdateCustomerOptions, type UpdateOrganizationOptions, type UpdateSubscriptionOptions, UsageClient, type UsageMetric, type VerifyTokenResponse, type WebhookEvent, type WebhookEventType };
1749
+ export { type AddMemberOptions, type AnyWebhookEvent, AuthClient, type CancelSubscriptionOptions, type CancelSubscriptionResponse, type CheckEntitlementResponse, type CheckUsageResponse, CheckoutClient, type CheckoutSessionResponse, type CreateCheckoutOptions, type CreateCustomerOptions, type CreateFeatureRequestOptions, type CreateOrganizationOptions, type Customer, type CustomerCreatedEvent, type CustomerFeatureActivity, type CustomerUpdatedEvent, type CustomerUsageResponse, type CustomerWebhookPayload, type CustomerWithSubscription, CustomersClient, EntitlementsClient, type EntitlementsResponse, type FeatureRequest, type FeatureRequestComment, type FeatureRequestListResponse, FeatureRequestsClient, type InterestedCustomer, type InterestedCustomersResponse, type InviteMemberOptions, type ListFeatureRequestsOptions, type ListPlansOptions, type ListProductsOptions, type MagicLinkOptions, type MagicLinkResponse, type Organization, type OrganizationInvite, type OrganizationMember, OrganizationsClient, type PaymentFailedEvent, type PaymentSucceededEvent, type PaymentWebhookPayload, type Plan, PlansClient, type Product, ProductsClient, type SessionResponse, StackBE, type StackBEConfig, StackBEError, type StackBEErrorCode, type StackBEErrorResponse, type Subscription, type SubscriptionCancelledEvent, type SubscriptionCreatedEvent, type SubscriptionRenewedEvent, type SubscriptionUpdatedEvent, type SubscriptionWebhookPayload, type SubscriptionWithPlan, SubscriptionsClient, type TrackUsageOptions, type TrackUsageResponse, type TrialEndedEvent, type TrialStartedEvent, type UpdateCustomerOptions, type UpdateOrganizationOptions, type UpdateSubscriptionOptions, UsageClient, type UsageMetric, type VerifyTokenResponse, type WebhookEvent, type WebhookEventType };
package/dist/index.js CHANGED
@@ -24,6 +24,7 @@ __export(index_exports, {
24
24
  CheckoutClient: () => CheckoutClient,
25
25
  CustomersClient: () => CustomersClient,
26
26
  EntitlementsClient: () => EntitlementsClient,
27
+ FeatureRequestsClient: () => FeatureRequestsClient,
27
28
  OrganizationsClient: () => OrganizationsClient,
28
29
  PlansClient: () => PlansClient,
29
30
  ProductsClient: () => ProductsClient,
@@ -168,8 +169,8 @@ var HttpClient = class {
168
169
  async patch(path, body) {
169
170
  return this.request("PATCH", path, { body });
170
171
  }
171
- async delete(path) {
172
- return this.request("DELETE", path);
172
+ async delete(path, body) {
173
+ return this.request("DELETE", path, body ? { body } : void 0);
173
174
  }
174
175
  };
175
176
 
@@ -620,7 +621,7 @@ var CheckoutClient = class {
620
621
  *
621
622
  * @example
622
623
  * ```typescript
623
- * // With new customer (will be created)
624
+ * // With customer email (looks up or creates customer)
624
625
  * const { url } = await stackbe.checkout.createSession({
625
626
  * customer: { email: 'user@example.com', name: 'John' },
626
627
  * planId: 'plan_pro_monthly',
@@ -628,6 +629,18 @@ var CheckoutClient = class {
628
629
  * trialDays: 14,
629
630
  * });
630
631
  * ```
632
+ *
633
+ * @example
634
+ * ```typescript
635
+ * // Organization-level subscription (B2B)
636
+ * const { url } = await stackbe.checkout.createSession({
637
+ * customer: 'cust_123',
638
+ * organizationId: 'org_456', // Customer must be owner/admin
639
+ * planId: 'plan_team',
640
+ * successUrl: 'https://myapp.com/success',
641
+ * cancelUrl: 'https://myapp.com/pricing',
642
+ * });
643
+ * ```
631
644
  */
632
645
  async createSession(options) {
633
646
  const body = {
@@ -635,15 +648,25 @@ var CheckoutClient = class {
635
648
  planId: options.planId,
636
649
  successUrl: options.successUrl,
637
650
  cancelUrl: options.cancelUrl,
651
+ organizationId: options.organizationId,
638
652
  allowPromotionCodes: options.allowPromotionCodes,
639
653
  trialDays: options.trialDays,
640
654
  metadata: options.metadata
641
655
  };
656
+ if (!options.customer) {
657
+ throw new Error(
658
+ 'customer is required. Use customer: "cust_xxx" for existing customer ID, or customer: { email: "user@example.com" } for email-based lookup.'
659
+ );
660
+ }
642
661
  if (typeof options.customer === "string") {
643
662
  body.customerId = options.customer;
644
- } else {
663
+ } else if (options.customer.email) {
645
664
  body.customerEmail = options.customer.email;
646
665
  body.customerName = options.customer.name;
666
+ } else {
667
+ throw new Error(
668
+ 'customer.email is required when using object form. Use customer: { email: "user@example.com" }'
669
+ );
647
670
  }
648
671
  return this.http.post("/v1/checkout/session", body);
649
672
  }
@@ -1438,6 +1461,204 @@ var ProductsClient = class {
1438
1461
  }
1439
1462
  };
1440
1463
 
1464
+ // src/feature-requests.ts
1465
+ var FeatureRequestsClient = class {
1466
+ constructor(http, appId) {
1467
+ this.http = http;
1468
+ this.appId = appId;
1469
+ }
1470
+ /**
1471
+ * List feature requests for the app.
1472
+ *
1473
+ * @example
1474
+ * ```typescript
1475
+ * // Get all feature requests
1476
+ * const { data, total } = await stackbe.featureRequests.list();
1477
+ *
1478
+ * // Filter by status
1479
+ * const planned = await stackbe.featureRequests.list({ status: 'planned' });
1480
+ *
1481
+ * // Sort by votes
1482
+ * const popular = await stackbe.featureRequests.list({ sortBy: 'votes' });
1483
+ * ```
1484
+ */
1485
+ async list(options = {}) {
1486
+ const params = {};
1487
+ if (options.status) params.status = options.status;
1488
+ if (options.category) params.category = options.category;
1489
+ if (options.sortBy) params.sortBy = options.sortBy;
1490
+ if (options.limit) params.limit = options.limit;
1491
+ if (options.offset) params.offset = options.offset;
1492
+ return this.http.get(
1493
+ `/v1/apps/${this.appId}/feature-requests`,
1494
+ params
1495
+ );
1496
+ }
1497
+ /**
1498
+ * Submit a new feature request.
1499
+ * Requires customer session token.
1500
+ *
1501
+ * @example
1502
+ * ```typescript
1503
+ * const request = await stackbe.featureRequests.create({
1504
+ * title: 'Dark mode support',
1505
+ * description: 'Would love to have a dark theme option',
1506
+ * category: 'UI',
1507
+ * });
1508
+ * ```
1509
+ */
1510
+ async create(options) {
1511
+ return this.http.post(
1512
+ `/v1/apps/${this.appId}/feature-requests`,
1513
+ options
1514
+ );
1515
+ }
1516
+ /**
1517
+ * Get a specific feature request by ID.
1518
+ *
1519
+ * @example
1520
+ * ```typescript
1521
+ * const request = await stackbe.featureRequests.get('req_123');
1522
+ * console.log(`${request.title} has ${request.voteCount} votes`);
1523
+ * ```
1524
+ */
1525
+ async get(requestId) {
1526
+ return this.http.get(
1527
+ `/v1/apps/${this.appId}/feature-requests/${requestId}`
1528
+ );
1529
+ }
1530
+ /**
1531
+ * Upvote a feature request.
1532
+ * Requires customer session token, OR API key with customerId.
1533
+ *
1534
+ * @example
1535
+ * ```typescript
1536
+ * // With customer session token
1537
+ * await stackbe.featureRequests.vote('req_123');
1538
+ *
1539
+ * // With API key (server-side)
1540
+ * await stackbe.featureRequests.vote('req_123', 'cust_456');
1541
+ * ```
1542
+ */
1543
+ async vote(requestId, customerId) {
1544
+ return this.http.post(
1545
+ `/v1/apps/${this.appId}/feature-requests/${requestId}/vote`,
1546
+ customerId ? { customerId } : void 0
1547
+ );
1548
+ }
1549
+ /**
1550
+ * Remove your vote from a feature request.
1551
+ * Requires customer session token, OR API key with customerId.
1552
+ *
1553
+ * @example
1554
+ * ```typescript
1555
+ * // With customer session token
1556
+ * await stackbe.featureRequests.removeVote('req_123');
1557
+ *
1558
+ * // With API key (server-side)
1559
+ * await stackbe.featureRequests.removeVote('req_123', 'cust_456');
1560
+ * ```
1561
+ */
1562
+ async removeVote(requestId, customerId) {
1563
+ return this.http.delete(
1564
+ `/v1/apps/${this.appId}/feature-requests/${requestId}/vote`,
1565
+ customerId ? { customerId } : void 0
1566
+ );
1567
+ }
1568
+ /**
1569
+ * Add a comment to a feature request.
1570
+ * Requires customer session token.
1571
+ *
1572
+ * @example
1573
+ * ```typescript
1574
+ * await stackbe.featureRequests.addComment('req_123', {
1575
+ * content: 'This would be really helpful for my workflow!',
1576
+ * });
1577
+ * ```
1578
+ */
1579
+ async addComment(requestId, options) {
1580
+ return this.http.post(
1581
+ `/v1/apps/${this.appId}/feature-requests/${requestId}/comments`,
1582
+ options
1583
+ );
1584
+ }
1585
+ // ==================== Admin Methods ====================
1586
+ /**
1587
+ * Update a feature request status or category.
1588
+ * Requires API key (admin).
1589
+ *
1590
+ * @example
1591
+ * ```typescript
1592
+ * await stackbe.featureRequests.update('req_123', {
1593
+ * status: 'planned',
1594
+ * category: 'Core Features',
1595
+ * });
1596
+ * ```
1597
+ */
1598
+ async update(requestId, options) {
1599
+ return this.http.patch(
1600
+ `/v1/apps/${this.appId}/feature-requests/${requestId}`,
1601
+ options
1602
+ );
1603
+ }
1604
+ /**
1605
+ * Delete a feature request.
1606
+ * Requires API key (admin).
1607
+ *
1608
+ * @example
1609
+ * ```typescript
1610
+ * await stackbe.featureRequests.delete('req_123');
1611
+ * ```
1612
+ */
1613
+ async delete(requestId) {
1614
+ return this.http.delete(
1615
+ `/v1/apps/${this.appId}/feature-requests/${requestId}`
1616
+ );
1617
+ }
1618
+ /**
1619
+ * Get all customers interested in a feature request (author + voters).
1620
+ * Useful for user research or notifying users when the feature ships.
1621
+ * Requires API key (admin).
1622
+ *
1623
+ * @example
1624
+ * ```typescript
1625
+ * const { author, voters, totalInterested } = await stackbe.featureRequests.getInterestedCustomers('req_123');
1626
+ *
1627
+ * // Notify all interested users
1628
+ * const emails = [author.email, ...voters.map(v => v.email)];
1629
+ * await sendNotification(emails, 'Your requested feature is now live!');
1630
+ * ```
1631
+ */
1632
+ async getInterestedCustomers(requestId) {
1633
+ return this.http.get(
1634
+ `/v1/apps/${this.appId}/feature-requests/${requestId}/interested`
1635
+ );
1636
+ }
1637
+ /**
1638
+ * Get a customer's feature request activity (requests submitted + votes).
1639
+ * Useful for displaying on customer profile.
1640
+ * Requires API key (admin).
1641
+ *
1642
+ * @example
1643
+ * ```typescript
1644
+ * const activity = await stackbe.featureRequests.getCustomerActivity('cust_123');
1645
+ *
1646
+ * console.log(`Submitted ${activity.totalSubmitted} requests`);
1647
+ * console.log(`Voted on ${activity.totalVotes} requests`);
1648
+ *
1649
+ * // Show their most wanted features
1650
+ * activity.votedOn.forEach(r => {
1651
+ * console.log(`- ${r.title} (${r.status})`);
1652
+ * });
1653
+ * ```
1654
+ */
1655
+ async getCustomerActivity(customerId) {
1656
+ return this.http.get(
1657
+ `/v1/apps/${this.appId}/feature-requests/customer/${customerId}/activity`
1658
+ );
1659
+ }
1660
+ };
1661
+
1441
1662
  // src/client.ts
1442
1663
  var DEFAULT_BASE_URL = "https://api.stackbe.io";
1443
1664
  var DEFAULT_TIMEOUT = 3e4;
@@ -1500,6 +1721,7 @@ var StackBE = class {
1500
1721
  this.organizations = new OrganizationsClient(this.http, config.appId);
1501
1722
  this.plans = new PlansClient(this.http);
1502
1723
  this.products = new ProductsClient(this.http, config.appId);
1724
+ this.featureRequests = new FeatureRequestsClient(this.http, config.appId);
1503
1725
  }
1504
1726
  /**
1505
1727
  * Create a middleware for Express that tracks usage automatically.
@@ -1637,6 +1859,7 @@ var StackBE = class {
1637
1859
  CheckoutClient,
1638
1860
  CustomersClient,
1639
1861
  EntitlementsClient,
1862
+ FeatureRequestsClient,
1640
1863
  OrganizationsClient,
1641
1864
  PlansClient,
1642
1865
  ProductsClient,
package/dist/index.mjs CHANGED
@@ -132,8 +132,8 @@ var HttpClient = class {
132
132
  async patch(path, body) {
133
133
  return this.request("PATCH", path, { body });
134
134
  }
135
- async delete(path) {
136
- return this.request("DELETE", path);
135
+ async delete(path, body) {
136
+ return this.request("DELETE", path, body ? { body } : void 0);
137
137
  }
138
138
  };
139
139
 
@@ -584,7 +584,7 @@ var CheckoutClient = class {
584
584
  *
585
585
  * @example
586
586
  * ```typescript
587
- * // With new customer (will be created)
587
+ * // With customer email (looks up or creates customer)
588
588
  * const { url } = await stackbe.checkout.createSession({
589
589
  * customer: { email: 'user@example.com', name: 'John' },
590
590
  * planId: 'plan_pro_monthly',
@@ -592,6 +592,18 @@ var CheckoutClient = class {
592
592
  * trialDays: 14,
593
593
  * });
594
594
  * ```
595
+ *
596
+ * @example
597
+ * ```typescript
598
+ * // Organization-level subscription (B2B)
599
+ * const { url } = await stackbe.checkout.createSession({
600
+ * customer: 'cust_123',
601
+ * organizationId: 'org_456', // Customer must be owner/admin
602
+ * planId: 'plan_team',
603
+ * successUrl: 'https://myapp.com/success',
604
+ * cancelUrl: 'https://myapp.com/pricing',
605
+ * });
606
+ * ```
595
607
  */
596
608
  async createSession(options) {
597
609
  const body = {
@@ -599,15 +611,25 @@ var CheckoutClient = class {
599
611
  planId: options.planId,
600
612
  successUrl: options.successUrl,
601
613
  cancelUrl: options.cancelUrl,
614
+ organizationId: options.organizationId,
602
615
  allowPromotionCodes: options.allowPromotionCodes,
603
616
  trialDays: options.trialDays,
604
617
  metadata: options.metadata
605
618
  };
619
+ if (!options.customer) {
620
+ throw new Error(
621
+ 'customer is required. Use customer: "cust_xxx" for existing customer ID, or customer: { email: "user@example.com" } for email-based lookup.'
622
+ );
623
+ }
606
624
  if (typeof options.customer === "string") {
607
625
  body.customerId = options.customer;
608
- } else {
626
+ } else if (options.customer.email) {
609
627
  body.customerEmail = options.customer.email;
610
628
  body.customerName = options.customer.name;
629
+ } else {
630
+ throw new Error(
631
+ 'customer.email is required when using object form. Use customer: { email: "user@example.com" }'
632
+ );
611
633
  }
612
634
  return this.http.post("/v1/checkout/session", body);
613
635
  }
@@ -1402,6 +1424,204 @@ var ProductsClient = class {
1402
1424
  }
1403
1425
  };
1404
1426
 
1427
+ // src/feature-requests.ts
1428
+ var FeatureRequestsClient = class {
1429
+ constructor(http, appId) {
1430
+ this.http = http;
1431
+ this.appId = appId;
1432
+ }
1433
+ /**
1434
+ * List feature requests for the app.
1435
+ *
1436
+ * @example
1437
+ * ```typescript
1438
+ * // Get all feature requests
1439
+ * const { data, total } = await stackbe.featureRequests.list();
1440
+ *
1441
+ * // Filter by status
1442
+ * const planned = await stackbe.featureRequests.list({ status: 'planned' });
1443
+ *
1444
+ * // Sort by votes
1445
+ * const popular = await stackbe.featureRequests.list({ sortBy: 'votes' });
1446
+ * ```
1447
+ */
1448
+ async list(options = {}) {
1449
+ const params = {};
1450
+ if (options.status) params.status = options.status;
1451
+ if (options.category) params.category = options.category;
1452
+ if (options.sortBy) params.sortBy = options.sortBy;
1453
+ if (options.limit) params.limit = options.limit;
1454
+ if (options.offset) params.offset = options.offset;
1455
+ return this.http.get(
1456
+ `/v1/apps/${this.appId}/feature-requests`,
1457
+ params
1458
+ );
1459
+ }
1460
+ /**
1461
+ * Submit a new feature request.
1462
+ * Requires customer session token.
1463
+ *
1464
+ * @example
1465
+ * ```typescript
1466
+ * const request = await stackbe.featureRequests.create({
1467
+ * title: 'Dark mode support',
1468
+ * description: 'Would love to have a dark theme option',
1469
+ * category: 'UI',
1470
+ * });
1471
+ * ```
1472
+ */
1473
+ async create(options) {
1474
+ return this.http.post(
1475
+ `/v1/apps/${this.appId}/feature-requests`,
1476
+ options
1477
+ );
1478
+ }
1479
+ /**
1480
+ * Get a specific feature request by ID.
1481
+ *
1482
+ * @example
1483
+ * ```typescript
1484
+ * const request = await stackbe.featureRequests.get('req_123');
1485
+ * console.log(`${request.title} has ${request.voteCount} votes`);
1486
+ * ```
1487
+ */
1488
+ async get(requestId) {
1489
+ return this.http.get(
1490
+ `/v1/apps/${this.appId}/feature-requests/${requestId}`
1491
+ );
1492
+ }
1493
+ /**
1494
+ * Upvote a feature request.
1495
+ * Requires customer session token, OR API key with customerId.
1496
+ *
1497
+ * @example
1498
+ * ```typescript
1499
+ * // With customer session token
1500
+ * await stackbe.featureRequests.vote('req_123');
1501
+ *
1502
+ * // With API key (server-side)
1503
+ * await stackbe.featureRequests.vote('req_123', 'cust_456');
1504
+ * ```
1505
+ */
1506
+ async vote(requestId, customerId) {
1507
+ return this.http.post(
1508
+ `/v1/apps/${this.appId}/feature-requests/${requestId}/vote`,
1509
+ customerId ? { customerId } : void 0
1510
+ );
1511
+ }
1512
+ /**
1513
+ * Remove your vote from a feature request.
1514
+ * Requires customer session token, OR API key with customerId.
1515
+ *
1516
+ * @example
1517
+ * ```typescript
1518
+ * // With customer session token
1519
+ * await stackbe.featureRequests.removeVote('req_123');
1520
+ *
1521
+ * // With API key (server-side)
1522
+ * await stackbe.featureRequests.removeVote('req_123', 'cust_456');
1523
+ * ```
1524
+ */
1525
+ async removeVote(requestId, customerId) {
1526
+ return this.http.delete(
1527
+ `/v1/apps/${this.appId}/feature-requests/${requestId}/vote`,
1528
+ customerId ? { customerId } : void 0
1529
+ );
1530
+ }
1531
+ /**
1532
+ * Add a comment to a feature request.
1533
+ * Requires customer session token.
1534
+ *
1535
+ * @example
1536
+ * ```typescript
1537
+ * await stackbe.featureRequests.addComment('req_123', {
1538
+ * content: 'This would be really helpful for my workflow!',
1539
+ * });
1540
+ * ```
1541
+ */
1542
+ async addComment(requestId, options) {
1543
+ return this.http.post(
1544
+ `/v1/apps/${this.appId}/feature-requests/${requestId}/comments`,
1545
+ options
1546
+ );
1547
+ }
1548
+ // ==================== Admin Methods ====================
1549
+ /**
1550
+ * Update a feature request status or category.
1551
+ * Requires API key (admin).
1552
+ *
1553
+ * @example
1554
+ * ```typescript
1555
+ * await stackbe.featureRequests.update('req_123', {
1556
+ * status: 'planned',
1557
+ * category: 'Core Features',
1558
+ * });
1559
+ * ```
1560
+ */
1561
+ async update(requestId, options) {
1562
+ return this.http.patch(
1563
+ `/v1/apps/${this.appId}/feature-requests/${requestId}`,
1564
+ options
1565
+ );
1566
+ }
1567
+ /**
1568
+ * Delete a feature request.
1569
+ * Requires API key (admin).
1570
+ *
1571
+ * @example
1572
+ * ```typescript
1573
+ * await stackbe.featureRequests.delete('req_123');
1574
+ * ```
1575
+ */
1576
+ async delete(requestId) {
1577
+ return this.http.delete(
1578
+ `/v1/apps/${this.appId}/feature-requests/${requestId}`
1579
+ );
1580
+ }
1581
+ /**
1582
+ * Get all customers interested in a feature request (author + voters).
1583
+ * Useful for user research or notifying users when the feature ships.
1584
+ * Requires API key (admin).
1585
+ *
1586
+ * @example
1587
+ * ```typescript
1588
+ * const { author, voters, totalInterested } = await stackbe.featureRequests.getInterestedCustomers('req_123');
1589
+ *
1590
+ * // Notify all interested users
1591
+ * const emails = [author.email, ...voters.map(v => v.email)];
1592
+ * await sendNotification(emails, 'Your requested feature is now live!');
1593
+ * ```
1594
+ */
1595
+ async getInterestedCustomers(requestId) {
1596
+ return this.http.get(
1597
+ `/v1/apps/${this.appId}/feature-requests/${requestId}/interested`
1598
+ );
1599
+ }
1600
+ /**
1601
+ * Get a customer's feature request activity (requests submitted + votes).
1602
+ * Useful for displaying on customer profile.
1603
+ * Requires API key (admin).
1604
+ *
1605
+ * @example
1606
+ * ```typescript
1607
+ * const activity = await stackbe.featureRequests.getCustomerActivity('cust_123');
1608
+ *
1609
+ * console.log(`Submitted ${activity.totalSubmitted} requests`);
1610
+ * console.log(`Voted on ${activity.totalVotes} requests`);
1611
+ *
1612
+ * // Show their most wanted features
1613
+ * activity.votedOn.forEach(r => {
1614
+ * console.log(`- ${r.title} (${r.status})`);
1615
+ * });
1616
+ * ```
1617
+ */
1618
+ async getCustomerActivity(customerId) {
1619
+ return this.http.get(
1620
+ `/v1/apps/${this.appId}/feature-requests/customer/${customerId}/activity`
1621
+ );
1622
+ }
1623
+ };
1624
+
1405
1625
  // src/client.ts
1406
1626
  var DEFAULT_BASE_URL = "https://api.stackbe.io";
1407
1627
  var DEFAULT_TIMEOUT = 3e4;
@@ -1464,6 +1684,7 @@ var StackBE = class {
1464
1684
  this.organizations = new OrganizationsClient(this.http, config.appId);
1465
1685
  this.plans = new PlansClient(this.http);
1466
1686
  this.products = new ProductsClient(this.http, config.appId);
1687
+ this.featureRequests = new FeatureRequestsClient(this.http, config.appId);
1467
1688
  }
1468
1689
  /**
1469
1690
  * Create a middleware for Express that tracks usage automatically.
@@ -1600,6 +1821,7 @@ export {
1600
1821
  CheckoutClient,
1601
1822
  CustomersClient,
1602
1823
  EntitlementsClient,
1824
+ FeatureRequestsClient,
1603
1825
  OrganizationsClient,
1604
1826
  PlansClient,
1605
1827
  ProductsClient,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackbe/sdk",
3
- "version": "0.6.5",
3
+ "version": "0.7.2",
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",