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.
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 IDconst 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
Create a route handler to handle Flowglad API requests from your frontend. The Server SDK provides route handler constructors that enable your client to communicate with your server, allowing you to load billing and feature access data via the useBilling hook on your frontend. It should handle requests at “/api/flowglad/…“
Next.js App Router
Next.js Pages Router
Express
Other Frameworks
app/api/flowglad/[...path]/route.ts
Copy
Ask AI
import { nextRouteHandler } from '@flowglad/nextjs/server'import { flowglad } from '@/lib/flowglad'import { customerIdFromRequest } from '@/lib/auth'export const { GET, POST } = nextRouteHandler({ flowglad, getCustomerExternalId: async (req) => { const externalId = await customerIdFromRequest(req) if (!externalId) { throw new Error('Unable to determine customer external ID') } return externalId },})
pages/api/flowglad/[...path].ts
Copy
Ask AI
import { pagesRouteHandler } from '@flowglad/nextjs/server'import { flowglad } from '@/lib/flowglad'import { customerIdFromRequest } from '@/lib/auth'export default pagesRouteHandler({ flowglad, getCustomerExternalId: async (req) => { const externalId = await customerIdFromRequest(req) if (!externalId) { throw new Error('Unable to determine customer external ID') } return externalId },})
server.ts
Copy
Ask AI
import express from 'express'import { expressRouter } from '@flowglad/server/express'import { flowglad } from './lib/flowglad'import { customerIdFromRequest } from './lib/auth'const app = express()app.use( '/api/flowglad', expressRouter({ flowglad, getCustomerExternalId: async (req) => { const externalId = await customerIdFromRequest(req) if (!externalId) { throw new Error('Unable to determine customer external ID') } return externalId }, }))
server.ts
Copy
Ask AI
import { requestHandler } from '@flowglad/server'import { flowglad } from './lib/flowglad'import { customerIdFromRequest } from './lib/auth'import type { HTTPMethod } from '@flowglad/shared'const flowgladHandler = requestHandler({ flowglad, getCustomerExternalId: async (req) => { const externalId = await customerIdFromRequest(req) if (!externalId) { throw new Error('Unable to determine customer external ID') } return externalId },})// Example: Hono, Cloudflare Workers, Elysia, etc.// Adapt this pattern to your frameworkasync function handleFlowgladRequest(request: Request): Promise<Response> { const url = new URL(request.url) const path = url.pathname .replace('/api/flowglad/', '') .split('/') .filter((segment) => segment !== '') const result = await flowgladHandler( { path, method: request.method as HTTPMethod, query: request.method === 'GET' ? Object.fromEntries(url.searchParams) : undefined, body: request.method !== 'GET' ? await request.json().catch(() => ({})) : undefined, }, request ) return Response.json( { error: result.error, data: result.data, }, { status: result.status, } )}
type FlowgladServerOptions = ScopedFlowgladServerParamsinterface 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
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.
Copy
Ask AI
// Pass YOUR app's user/organization ID, not Flowglad's customer IDconst customer = await flowglad(userId).findOrCreateCustomer()
Get comprehensive billing information for the customer, including helper functions like checkFeatureAccess, checkUsageBalance, getProduct, and getPrice.
Copy
Ask AI
// Pass YOUR app's user/organization ID, not Flowglad's customer IDconst billing = await flowglad(userId).getBilling()const hasPremium = billing.checkFeatureAccess('premium_feature')const invoiceCount = billing.invoices.length
Get the default pricing model with products, prices, and features.
Copy
Ask AI
// Pass YOUR app's user/organization ID, not Flowglad's customer IDconst pricingModel = await flowglad(userId).getPricingModel()// Returns products, prices, and features available for purchase
type CreateSubscriptionParams = { customerId: string /** * Must specify either priceSlug or priceId * Slug is encouraged over Id */ priceSlug?: string priceId?: string /** * Quantity of the subscription items. * @default 1 */ quantity?: number startDate?: string /** * Epoch time in milliseconds of when the trial ends. If not provided, defaults to startDate + the associated price's trialPeriodDays */ trialEnd?: number metadata?: Record<string, unknown> name?: string backupPaymentMethodId?: string defaultPaymentMethodId?: string interval?: 'day' | 'week' | 'month' | 'year' intervalCount?: number}
// Pass YOUR app's user/organization ID, not Flowglad's customer IDconst checkoutSession = await flowglad(userId).createCheckoutSession({ priceSlug: 'pro_plan', successUrl: 'https://example.com/success', cancelUrl: 'https://example.com/cancel', outputMetadata: { campaign: 'summer_sale', },})// Redirect user to checkoutSession.url
Parameters:
Copy
Ask AI
type CreateProductCheckoutSessionParams = { /* * Must specify either priceSlug or priceId * Slug is encouraged over Id */ priceSlug?: string priceId?: string successUrl: string cancelUrl: string quantity?: number outputMetadata?: Record<string, string> outputName?: string}
Record a usage event for metered billing. You can create usage events with either a price (for tracking usage with billing) or a usage meter (for tracking usage without billing).
Copy
Ask AI
// Pass YOUR app's user/organization ID, not Flowglad's customer ID// Example: With price (for tracking usage with billing)const usageEvent = await flowglad(userId).createUsageEvent({ amount: 125, priceSlug: 'price_usage_meter', subscriptionId: 'sub_123', transactionId: 'txn_unique', usageDate: Date.now(), properties: { endpoint: '/api/data', },})// Example: With usage meter (for tracking usage without billing)const usageEvent = await flowglad(userId).createUsageEvent({ amount: 125, usageMeterSlug: 'api_calls', subscriptionId: 'sub_123', transactionId: 'txn_unique', usageDate: Date.now(), properties: { endpoint: '/api/data', },})
Parameters:
Copy
Ask AI
type CreateUsageEventParams = { amount: number /** * Must specify exactly one of: * - priceSlug or priceId (for billing) * - usageMeterSlug or usageMeterId (for tracking usage without billing) * Slug is encouraged over Id */ priceSlug?: string priceId?: string usageMeterSlug?: string usageMeterId?: string subscriptionId: string transactionId: string usageDate?: number properties?: Record<string, unknown> }
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, }}
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 IDconst 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