Skip to main content
NPM Package: @flowglad/server

Overview

The Server SDK is the core package for server-side Flowglad integration. It provides the FlowgladServer class that handles all billing operations, customer management, and API communication with Flowglad.

Installation

npm install @flowglad/server

Installation

npm install @flowglad/server

Quick Start

1. Set Up Environment Variables

.env
FLOWGLAD_SECRET_KEY="sk_test_..."

2. Create a FlowgladServer Factory Function

import { FlowgladServer } from '@flowglad/server'

export const flowglad = (customerExternalId: string) => {
  // customerExternalId is the ID from YOUR app's database, NOT Flowglad's customer ID
  return new FlowgladServer({
    customerExternalId,
    getCustomerDetails: async (customerExternalId) => {
      // Fetch customer details from YOUR database using YOUR app's ID
      const user = await db.users.findOne({ id: customerExternalId })
      if (!user) {
        throw new Error('Customer not found')
      }
      return {
        email: user.email,
        name: user.name,
      }
    },
  })
}

// Usage:
// Pass YOUR app's user/organization ID, not Flowglad's customer ID
const billing = await flowglad(userId).getBilling()
Important: customerExternalId is the ID from your app’s database (e.g., user.id or organization.id), not Flowglad’s customer ID.B2C apps: Pass user.id as customerExternalId
B2B apps: Pass organization.id or team.id as customerExternalId

3. Call Server Methods

// Fetch billing details (customers, subscriptions, invoices, etc.)
// Pass YOUR app's user/organization ID, not Flowglad's customer ID
const billing = await flowglad(userId).getBilling()

// Ensure the Flowglad customer exists
const customer = await flowglad(userId).findOrCreateCustomer()

// Create a hosted checkout session
const checkoutSession = await flowglad(userId).createCheckoutSession({
  priceId: 'price_123',
  successUrl: 'https://example.com/success',
  cancelUrl: 'https://example.com/cancel',
})

// Check feature access for gating premium functionality
const hasPremium = billing.checkFeatureAccess('premium_feature')

// Record metered usage
await flowglad(userId).createUsageEvent({
  priceId: 'usage_price_id',
  amount: 1,
  transactionId: 'idempotency-key',
})

Key Features

  • Customer Management: Find or create customers automatically
  • Subscriptions: Create, update, and cancel subscriptions
  • Checkout Sessions: Generate checkout URLs for payments
  • Feature Access: Check customer access to features
  • Usage Tracking: Record usage events and track usage credit grants in real time
  • Invoices: Retrieve and manage invoices
  • Payment Methods: Handle customer payment methods
  • Pricing Model: Access your pricing model with products, prices, and features

API Reference

Constructor

new FlowgladServer(options: FlowgladServerOptions)

Options

See types.ts for more details.
type FlowgladServerOptions = ScopedFlowgladServerParams

interface ScopedFlowgladServerParams {
  /**
   * The customer ID from YOUR app's database (e.g., user.id or organization.id).
   * This is NOT Flowglad's customer ID—Flowglad uses this external ID to identify
   * and create customers in its system.
   * 
   * For B2C apps: Use your user.id
   * For B2B apps: Use your organization.id or team.id
   */
  customerExternalId: string
  baseURL?: string
  apiKey?: string
  /**
   * Handler to retrieve customer details from your database.
   *
   * This function is called when attempting to create a customer record in Flowglad
   * for a customer that doesn't yet exist. Implement this to fetch the customer's
   * name and email from your database based on their customer ID.
   *
   * @param customerExternalId - The external customer ID from YOUR system (not Flowglad's)
   * @returns Promise resolving to an object containing the customer's name and email
   */
  getCustomerDetails: (customerExternalId: string) => Promise<{
    name: string
    email: string
  }>
}
Important: customerExternalId is the ID from your app’s database (e.g., user.id or organization.id), not Flowglad’s customer ID.B2C apps: Pass user.id as customerExternalId
B2B apps: Pass organization.id or team.id as customerExternalId

Customer Methods

findOrCreateCustomer()

Find an existing Flowglad customer associated with the current requesting customer (per your app’s authentication) or create a new one if none is found.
// Pass YOUR app's user/organization ID, not Flowglad's customer ID
const customer = await flowglad(userId).findOrCreateCustomer()

Billing Methods

getBilling()

Get comprehensive billing information for the customer, including helper functions like checkFeatureAccess, checkUsageBalance, getProduct, and getPrice.
// Pass YOUR app's user/organization ID, not Flowglad's customer ID
const billing = await flowglad(userId).getBilling()
const hasPremium = billing.checkFeatureAccess('premium_feature')
const invoiceCount = billing.invoices.length

getPricingModel()

Get the default pricing model with products, prices, and features.
// Pass YOUR app's user/organization ID, not Flowglad's customer ID
const pricingModel = await flowglad(userId).getPricingModel()
// Returns products, prices, and features available for purchase

Subscription Methods

createSubscription

Create a new subscription for the customer.
// Pass YOUR app's user/organization ID, not Flowglad's customer ID
const subscription = await flowglad(userId).createSubscription({
  priceId: 'price_123',
  metadata: {
    source: 'upgrade_flow',
  },
})
Parameters:
interface CreateSubscriptionParams {
  priceId: string
  /**
   * Quantity of the subscription items.
   * @default 1
   */
  quantity?: number
  startDate?: string
  trialEnd?: number
  metadata?: Record<string, unknown>
  name?: string
  backupPaymentMethodId?: string
  defaultPaymentMethodId?: string
  interval?: 'day' | 'week' | 'month' | 'year'
  intervalCount?: number
}

cancelSubscription

Cancel an existing subscription.
// Pass YOUR app's user/organization ID, not Flowglad's customer ID
const canceledSubscription = await flowglad(userId).cancelSubscription({
  id: 'sub_123',
  cancellation: {
    timing: 'at_end_of_current_billing_period',
  },
})
Parameters:
interface CancelSubscriptionParams {
  id: string
  cancellation:
    | { timing: 'at_end_of_current_billing_period' }
    | { timing: 'at_future_date'; endDate: Date }
    | { timing: 'immediately' }
}

Checkout Methods

createCheckoutSession

Create a checkout session for payments.
// Pass YOUR app's user/organization ID, not Flowglad's customer ID
const checkoutSession = await flowglad(userId).createCheckoutSession({
  priceSlug: 'price_123',
  successUrl: 'https://example.com/success',
  cancelUrl: 'https://example.com/cancel',
  quantity: 1,
  outputMetadata: {
    campaign: 'summer_sale',
  },
})

// Redirect user to checkoutSession.url
Parameters:
interface CreateCheckoutSessionParams {
  /*
   * Must specify either priceId or priceSlug
   * Slug is encouraged over Id
   */
  priceId?: string
  priceSlug?: string
  successUrl: string
  cancelUrl: string
  quantity?: number
  outputMetadata?: Record<string, string>
  outputName?: string
}

createAddPaymentMethodCheckoutSession

Create a checkout session that collects a payment method for the current customer.
// Pass YOUR app's user/organization ID, not Flowglad's customer ID
const session = await flowglad(userId).createAddPaymentMethodCheckoutSession({
  successUrl: 'https://example.com/payment-methods/success',
  cancelUrl: 'https://example.com/payment-methods/cancel',
  targetSubscriptionId: 'sub_123', // optional
})

createActivateSubscriptionCheckoutSession

Create a checkout session that activates a provisioning or imported subscription.
// Pass YOUR app's user/organization ID, not Flowglad's customer ID
const activationSession =
  await flowglad(userId).createActivateSubscriptionCheckoutSession({
    targetSubscriptionId: 'sub_123', // optional, attaches the paymentMethod to the subscription for future payments
    priceId: 'price_123',
    successUrl: 'https://example.com/subscriptions/success',
    cancelUrl: 'https://example.com/subscriptions/cancel',
  })

Customer Updates

updateCustomer

Update the stored customer record in Flowglad.
// Pass YOUR app's user/organization ID, not Flowglad's customer ID
await flowglad(userId).updateCustomer({
  customer: {
    id: 'cust_abc',
    name: 'New Name',
    email: 'new-email@example.com',
  },
})

Feature Access Methods

checkFeatureAccess

Check if the customer has access to a specific feature.
// Pass YOUR app's user/organization ID, not Flowglad's customer ID
const billing = await flowglad(userId).getBilling()
const hasAccess = billing.checkFeatureAccess('advanced_analytics')

if (hasAccess) {
  // Grant access to feature
}

Usage Tracking Methods

createUsageEvent

Record a usage event for metered billing.
// Pass YOUR app's user/organization ID, not Flowglad's customer ID
const usageEvent = await flowglad(userId).createUsageEvent({
  amount: 125,
  priceId: 'price_usage_meter',
  subscriptionId: 'sub_123',
  usageMeterId: 'usage_meter_api_calls',
  transactionId: 'txn_unique',
  usageDate: Date.now(),
  properties: {
    endpoint: '/api/data',
  },
})
Parameters:
interface CreateUsageEventParams {
  amount: number
  priceId: string
  subscriptionId: string
  usageMeterId: string
  transactionId: string
  usageDate?: number
  properties?: Record<string, unknown>
}

Common Patterns

Feature Gating Middleware

import { FlowgladServer } from '@flowglad/server'
import { db } from './db'

// Factory function to create scoped instance
function getFlowgladServer(customerExternalId: string) {
  return new FlowgladServer({
    customerExternalId,
    getCustomerDetails: async (id) => {
      const user = await db.users.findOne({ id })
      return { name: user.name, email: user.email }
    },
  })
}

export function requireFeature(featureSlug: string) {
  return async (req, res, next) => {
    try {
      const userId = req.user?.id
      if (!userId) {
        return res.status(401).json({ error: 'Unauthorized' })
      }

      const billing = await flowglad(userId).getBilling()
      const hasAccess = billing.checkFeatureAccess(featureSlug)

      if (!hasAccess) {
        return res.status(403).json({
          error: 'Feature not available',
          upgradeUrl: '/pricing',
        })
      }

      next()
    } catch (error) {
      next(error)
    }
  }
}

// Usage
app.get('/api/premium-endpoint', requireFeature('premium_feature'), (req, res) => {
  res.json({ data: 'premium data' })
})

Usage Metering

export async function trackAPIUsage(endpoint: string, customerExternalId: string) {
  // customerExternalId is the ID from YOUR app's database, NOT Flowglad's customer ID
  await flowglad(customerExternalId).createUsageEvent({
    priceId: 'price_usage_api_calls',
    usageMeterId: 'usage_meter_api_calls',
    subscriptionId: 'sub_current',
    amount: 1,
    properties: {
      endpoint,
    },
  })
}

// Usage
app.get('/api/data', async (req, res) => {
  await trackAPIUsage('/api/data')
  res.json({ data: 'response' })
})

Customer Portal Generation

export async function getCustomerPortalData(customerExternalId: string) {
  // customerExternalId is the ID from YOUR app's database, NOT Flowglad's customer ID
  const billing = await flowglad(customerExternalId).getBilling()

  return {
    customer: billing.customer,
    activeSubscriptions: billing.subscriptions.filter(
      (s) => s.status === 'active'
    ),
    paymentMethods: billing.paymentMethods,
    recentInvoices: billing.invoices.slice(0, 10),
    features: billing.features,
  }
}

Integration Examples

Basic Setup

import { FlowgladServer } from '@flowglad/server'

export const flowglad = (customerExternalId: string) => {
  // customerExternalId is the ID from YOUR app's database, NOT Flowglad's customer ID
  return new FlowgladServer({
    customerExternalId,
    getCustomerDetails: async (externalId) => {
      // Fetch customer details from YOUR database using YOUR app's ID
      const user = await db.users.findOne({ id: externalId })
      if (!user) {
        throw new Error('Customer not found')
      }
      return {
        email: user.email,
        name: user.name,
      }
    },
  })
}

// Usage:
// Pass YOUR app's user/organization ID, not Flowglad's customer ID
const billing = await flowglad(userId).getBilling()
Important: customerExternalId is the ID from your app’s database (e.g., user.id or organization.id), not Flowglad’s customer ID.B2C apps: Pass user.id as customerExternalId
B2B apps: Pass organization.id or team.id as customerExternalId

Next Steps