StackBE SDK

Official TypeScript SDK for integrating StackBE into your application.

Installation

bash
npm install @stackbe/sdk

The SDK works in Node.js, Edge runtimes, and modern browsers.

Quick Setup

typescript
import { StackBE } from '@stackbe/sdk';

const stackbe = new StackBE({
  apiKey: process.env.STACKBE_API_KEY!,
  appId: process.env.STACKBE_APP_ID!,
  sessionCacheTTL: 120, // Optional: cache sessions for 2 min
  devCallbackUrl: 'http://localhost:3000/auth/callback', // Optional: for local dev
});

Customers

Create, retrieve, and manage customers in your application.

typescript
// Get or create customer (idempotent)
const customer = await stackbe.customers.getOrCreate({
  email: 'user@example.com',
  name: 'John Doe',
});

// Get customer by email
const existing = await stackbe.customers.getByEmail('user@example.com');

// Get customer by ID
const customer = await stackbe.customers.get('cust_123');

// Create a new customer
const newCustomer = await stackbe.customers.create({
  email: 'new@example.com',
  name: 'Jane Doe',
  metadata: { source: 'website' },
});

// Update customer
const updated = await stackbe.customers.update('cust_123', {
  name: 'Updated Name',
  metadata: { tier: 'premium' },
});

Authentication

Passwordless magic link authentication for your customers.

typescript
// Send magic link email
await stackbe.auth.sendMagicLink('user@example.com', {
  redirectUrl: 'https://yourapp.com/dashboard',
});

// Verify magic link token (returns session)
const session = await stackbe.auth.verifyToken(token);

// Get current session
const session = await stackbe.auth.getSession(token);
console.log(session.customer);
console.log(session.entitlements);

Checkout

Create Stripe checkout sessions for subscriptions.

typescript
// Create checkout session
const { url } = await stackbe.checkout.createSession({
  customer: customer.id, // or email
  planId: 'plan_pro',
  successUrl: 'https://yourapp.com/success',
  cancelUrl: 'https://yourapp.com/pricing',
});

// Redirect user to checkout
window.location.href = url;

Entitlements

Check feature access based on subscription plan.

typescript
// Check if customer has access to a feature
const { hasAccess } = await stackbe.entitlements.check(
  customer.id,
  'premium_export'
);

if (hasAccess) {
  // Allow feature
} else {
  // Prompt upgrade
}

// List all entitlements for a customer
const entitlements = await stackbe.entitlements.list(customer.id);

Usage Tracking

Track and check usage for metered billing.

typescript
// Track usage event
await stackbe.usage.track(customer.id, 'api_calls');

// Track with custom amount
await stackbe.usage.track(customer.id, 'storage_gb', { amount: 2.5 });

// Check usage limits
const { allowed, remaining, limit, used } = await stackbe.usage.check(
  customer.id,
  'api_calls'
);

if (!allowed) {
  throw new Error('Usage limit exceeded');
}

Subscriptions

Manage customer subscriptions.

typescript
// Get current subscription
const subscription = await stackbe.subscriptions.get(customer.id);

// Pause subscription
await stackbe.subscriptions.pause('sub_123', {
  resumesAt: new Date('2025-02-01'), // Optional: auto-resume date
});

// Resume subscription
await stackbe.subscriptions.resume('sub_123');

// Cancel subscription
await stackbe.subscriptions.cancel('sub_123', {
  atPeriodEnd: true, // Cancel at end of billing period
});

Plans & Products

List available pricing plans for your pricing page.

typescript
// List all plans sorted by price
const plans = await stackbe.plans.listByPrice();
// [Free ($0), Starter ($9), Pro ($29), Enterprise ($99)]

// Get a specific plan
const plan = await stackbe.plans.get('plan_123');
console.log(plan.name, plan.priceCents, plan.entitlements);

// Dynamic pricing page
{plans.map((plan) => (
  <div key={plan.id}>
    <h3>{plan.name}</h3>
    <p>${plan.priceCents / 100}/{plan.interval}</p>
    <a href={`/checkout?plan=${plan.id}`}>Subscribe</a>
  </div>
))}

Organizations (B2B)

Multi-user support for B2B apps. Organizations share subscriptions across members.

typescript
// Create organization with customer as owner
const org = await stackbe.organizations.create({
  name: 'Acme Corp',
  ownerId: customer.id,
});

// Add member to organization
await stackbe.organizations.addMember(org.id, {
  customerId: 'cust_456',
  role: 'member', // 'admin' | 'member'
});

// Invite by email
await stackbe.organizations.invite(org.id, {
  email: 'newuser@company.com',
  role: 'member',
});

// Update member role
await stackbe.organizations.updateMember(org.id, 'member_456', {
  role: 'admin',
});

Coupons & Promo Codes

Create and validate discount codes for checkout.

typescript
// Create a coupon
const coupon = await stackbe.coupons.create({
  code: 'LAUNCH50',
  discountType: 'percentage', // or 'fixed'
  discountValue: 50,          // 50% off or $50 off
  duration: 'once',           // 'once' | 'repeating' | 'forever'
  maxRedemptions: 100,        // Optional limit
  expiresAt: '2025-12-31',    // Optional expiry
});

// Validate a coupon
const result = await stackbe.coupons.validate('LAUNCH50', {
  planId: 'plan_pro', // Optional: check if valid for plan
});
if (result.valid) {
  console.log('Discount:', result.discountValue);
}

// Apply at checkout
const { url } = await stackbe.checkout.createSession({
  customer: 'cust_123',
  planId: 'plan_pro',
  couponCode: 'LAUNCH50', // Apply discount
  successUrl: 'https://yourapp.com/success',
});

// List all coupons
const coupons = await stackbe.coupons.list();

// Get coupon stats
const coupon = await stackbe.coupons.get('coupon_123');
console.log(coupon.redemptions, coupon.maxRedemptions);

Analytics & Metrics

Access business metrics for dashboards and reporting.

typescript
// Full dashboard summary
const dashboard = await stackbe.analytics.getDashboard();
console.log('MRR:', dashboard.revenue.mrr);
console.log('ARPU:', dashboard.revenue.arpu);
console.log('Active:', dashboard.subscriptions.active);
console.log('Churn Rate:', dashboard.subscriptions.churnRate);
console.log('Trial Conversion:', dashboard.trials.conversionRate);
console.log('Net Growth:', dashboard.growth.netGrowth);

// MRR history for charts
const mrrHistory = await stackbe.analytics.getMRRHistory({ days: 30 });
// [{ date: '2025-01-01', mrr: 1234.56 }, ...]

// Revenue by plan
const byPlan = await stackbe.analytics.getRevenueByPlan();
// [{ planName: 'Pro', mrr: 890, percentage: 65 }, ...]

// Trial metrics (for freemium/trial models)
const trials = await stackbe.analytics.getTrialMetrics({ days: 30 });
console.log(trials.trialStarts, trials.trialConversions);
console.log('Conversion Rate:', trials.trialConversionRate);

// Growth chart (new vs churned)
const growth = await stackbe.analytics.getGrowthChart({ days: 30 });
// [{ date: '2025-01-01', new: 5, churned: 1, net: 4 }, ...]

// Filter by specific app (multi-app tenants)
const appMetrics = await stackbe.analytics.getDashboard({
  appId: 'app_123',
});

Subscription Access & Dunning

Check access with grace period support for failed payments.

typescript
// Check if customer has access (respects grace period)
const hasAccess = await stackbe.subscriptions.checkAccess('cust_123');
// Returns true if active OR within grace period

// Get dunning status for failed payments
const dunning = await stackbe.subscriptions.getDunningStatus('sub_123');
console.log(dunning.isPastDue);          // true if payment failed
console.log(dunning.isInGracePeriod);    // true if still has access
console.log(dunning.daysUntilAccessRevoked); // days remaining
console.log(dunning.failedPaymentCount); // number of failures
console.log(dunning.hasAccess);          // convenience field

// List past due subscriptions
const pastDue = await stackbe.subscriptions.listPastDue();
// Filter: { inGracePeriod: true } or { gracePeriodExpired: true }

// Get subscription with plan details
const sub = await stackbe.subscriptions.get('cust_123', {
  expand: ['plan'],
});
console.log(sub.plan.name, sub.plan.priceCents);

Feature Requests

Collect and prioritize user feedback with voting.

typescript
// Create a feature request
const request = await stackbe.featureRequests.create({
  title: 'Dark mode support',
  description: 'Add a dark theme option',
  customerId: 'cust_123', // Optional: associate with customer
});

// List feature requests (public roadmap)
const requests = await stackbe.featureRequests.list({
  status: 'open', // 'open' | 'planned' | 'in_progress' | 'completed'
  sortBy: 'votes', // 'votes' | 'created' | 'updated'
});

// Vote on a feature
await stackbe.featureRequests.vote(request.id, {
  customerId: 'cust_123',
});

// Add comment
await stackbe.featureRequests.addComment(request.id, {
  customerId: 'cust_123',
  content: 'This would be great for accessibility!',
});

// Update status (admin)
await stackbe.featureRequests.updateStatus(request.id, 'planned');

Affiliate Program

Enable customers to earn commissions for referrals.

typescript
// Enroll customer as affiliate
const affiliate = await stackbe.affiliates.enroll({
  customerId: 'cust_123',
  code: 'JOHN20', // Optional: custom referral code
});
console.log(affiliate.referralLink);
// https://yourapp.com?ref=JOHN20

// Get affiliate stats
const stats = await stackbe.affiliates.getStats('cust_123');
console.log(stats.totalReferrals);
console.log(stats.totalCommissions);
console.log(stats.pendingPayout);

// Track referral (on signup)
await stackbe.affiliates.trackReferral({
  referralCode: 'JOHN20',
  newCustomerId: 'cust_456',
});

// List commissions
const commissions = await stackbe.affiliates.getCommissions('cust_123');

Early Access & Waitlist

Collect signups before launch or for new features.

typescript
// Add to waitlist
const signup = await stackbe.earlyAccess.signup({
  email: 'eager@example.com',
  name: 'Eager User',
  source: 'landing_page',
  metadata: { referrer: 'twitter' },
});

// Check if already signed up
const existing = await stackbe.earlyAccess.getByEmail('eager@example.com');

// List signups (admin)
const signups = await stackbe.earlyAccess.list({
  limit: 50,
  offset: 0,
});

Express Middleware

Built-in middleware for Express/Node.js apps.

typescript
// Auto-track usage on every request
app.use(stackbe.middleware({
  getCustomerId: (req) => req.user?.customerId,
  metric: 'api_calls',
  skip: (req) => req.path === '/health',
}));

// Require feature entitlements
app.get('/api/export',
  stackbe.requireFeature({
    getCustomerId: (req) => req.user?.customerId,
    feature: 'premium_export',
    onDenied: (req, res) => {
      res.status(403).json({ error: 'Upgrade to Pro' });
    },
  }),
  exportHandler
);

// Enforce usage limits
app.use('/api',
  stackbe.enforceLimit({
    getCustomerId: (req) => req.user?.customerId,
    metric: 'api_calls',
    onLimitExceeded: (req, res, { current, limit }) => {
      res.status(429).json({ error: 'Rate limit exceeded' });
    },
  })
);

Next.js Integration

Works with App Router, Server Actions, and API routes.

typescript
// app/api/generate/route.ts
import { StackBE } from '@stackbe/sdk';
import { NextResponse } from 'next/server';

const stackbe = new StackBE({
  apiKey: process.env.STACKBE_API_KEY!,
  appId: process.env.STACKBE_APP_ID!,
});

export async function POST(request: Request) {
  const { customerId } = await request.json();

  // Check limits
  const { allowed, remaining } = await stackbe.usage.check(
    customerId,
    'generations'
  );
  if (!allowed) {
    return NextResponse.json({ error: 'Limit reached' }, { status: 429 });
  }

  // Track usage
  await stackbe.usage.track(customerId, 'generations');

  // Do work...
  return NextResponse.json({ success: true, remaining: remaining! - 1 });
}

Webhooks

Typed webhook payloads for handling StackBE events.

typescript
import type { AnyWebhookEvent } from '@stackbe/sdk';

app.post('/webhooks/stackbe', (req, res) => {
  const event = req.body as AnyWebhookEvent;

  switch (event.type) {
    case 'subscription_created':
      console.log('New subscription:', event.data.planName);
      break;
    case 'subscription_cancelled':
      console.log('Cancelled:', event.data.id);
      break;
    case 'payment_failed':
      console.log('Payment failed:', event.data.failureReason);
      // Send dunning email
      break;
  }

  res.json({ received: true });
});

// Event types: subscription_created, subscription_updated,
// subscription_cancelled, subscription_renewed, trial_started,
// trial_ended, payment_succeeded, payment_failed,
// customer_created, customer_updated

Error Handling

The SDK throws typed errors for easy handling.

typescript
import { StackBEError } from '@stackbe/sdk';

try {
  const customer = await stackbe.customers.get('invalid_id');
} catch (error) {
  if (error instanceof StackBEError) {
    console.log(error.code);      // 'CUSTOMER_NOT_FOUND'
    console.log(error.statusCode); // 404
    console.log(error.message);    // 'Customer not found'
  }
}

// Common error codes:
// - CUSTOMER_NOT_FOUND
// - SUBSCRIPTION_NOT_FOUND
// - PLAN_NOT_FOUND
// - USAGE_LIMIT_EXCEEDED
// - TOKEN_EXPIRED
// - UNAUTHORIZED

Configuration Options

OptionTypeDefaultDescription
apiKeystringrequiredYour StackBE API key
appIdstringrequiredYour StackBE app ID
baseUrlstringapi.stackbe.ioAPI base URL
timeoutnumber30000Request timeout in ms
sessionCacheTTLnumber0Session cache TTL in seconds
devCallbackUrlstring-Callback URL for local development

Resources