Core Concepts
The Data Flow
In a standard Flowglad integration, data flows from Flowglad’s API, to your server, and then from your server to your React frontend. The result is that you no longer need to maintain glue code that syncs the data in your application with the data in Flowglad’s servers. Instead of this webhook approach, Flowglad is designed to be reactive.The Read Flow
The Write Flow
+Similarly, mutations flow in reverse: propagating from your frontend, to your server, to our server. This allows us to deliver the data door-to-door to your frontend code, while ensuring that all the data is scoped to the specific customer.The Lifecycle
Every Flowglad workflow in your app should start with an attempt to find or create the customer, and then either take an action or check the state.Authentication Coupling
On your backend, Flowglad couples closely with your authentication. Workflows that involve Flowglad should first consider who the customer is that you are making requests for. All customers in your database should have corresponding customer records in Flowglad, regardless of whether they are paying. To achieve this, start by adding a call to /create-customer in your app’s account creation flow. Flowglad manages the billing state for all your customers, including customers on your free plans. That management includes managing which features your customers can access and what balances they have on your product’s usage meters.Account Creation Flow
When you refer to your customers with Flowglad, you do so using your customer id, rather than ours. If your customers are individuals, use theiruser.id from your database. If your customers are businesses, teams, or other organizations, you can use their organization.id.
Integrating Flowglad does not require you to make any schema changes to your database. You do not need to store a
flowglad_customer_id or flowglad_price_id.Implementation Plan
Implementing Flowglad on your backend will consist of 3 steps (feel free to feed this to AI):- Add Flowglad customer creation to your product’s account creation flow
- Call POST /customers to create the customer
-
Create a server route at
/api/flowglad/:subroutethat is publicly accessible and authenticated using your existing server-side authentication logic. Your frontend will send requests to this route via the @flowglad/react SDK. -
Implement a set of helper functions in your backend to handle common actions:
findOrCreateCustomerBilling({ name: string, externalId: string, email: string }): call GET /customers/:externalId/billing. If you receive a 404, call POST /customers to create the customercheckFeatureAccess(slug: string, customerBilling: <GET /customers/:externalId response>):- gets
experimental.featureItemspayload in the firstcurrentSubscriptionin the customer billing response - returns true if a feature is present where
feature.type=="toggle" && feature.slug == slug - returns false otherwise
- gets
- if you plan to track usage in real time:
checkUsageBalance(slug: string, customerBilling: <GET /customers/:externalId response>):- gets
experimental.usageMeterBalancesin the firstcurrentSubscriptionin the customer billing response - finds the
usageMeterBalancewhereusageMeterBalance.slug == slug - returns
{ availableBalance: number }if the usageMeterBalance is found, usingusageMeterBalance.availableBalance - returns
nullif not found
- gets
Authenticate and Derive the Requesting Customer
Every server-originated Flowglad call needs the customer scoped using your ids. TheFlowgladServer class (packages/server/src/FlowgladServer.ts) shows the contract we enforce in the SDKs:
- derive a
{ externalId, name, email }triple either from your auth provider (NextAuth, Supabase, Clerk) or a customgetRequestingCustomer. - only one auth provider can populate a request. If you support multiple providers, branch before instantiating your handler.
- validate the triple:
externalIdmust be a non-empty string andemailmust be present, otherwise the request should fail fast with a helpful error because the React SDK expects authenticated users.
- Run your normal authentication, error out with
401if no session. - Map the session object to Flowglad’s customer shape.
- Pass that shape into every helper that talks to Flowglad’s API.
Implement the Sub-routes
Expose a single authenticated route such as/api/flowglad/:subroute. Each subroute matches a FlowgladActionKey (see packages/shared/src/types.ts) and must accept a POST, even for reads—the React SDK always sends POST requests and expects { data, error? } in the response body.
POST /customers/billing
- use your authentication logic to derive the customer making the request from your frontend
{ name: string, externalId: string, email: string }, where theemailis the email address associated with the owner of the account andexternalIdis the id of the customer in your system - use the
findOrCreateCustomerBillinghelper to get the customer billing data, return it inside{ data: billing }, and optionally include the computed helpers (checkFeatureAccess,checkUsageBalance,getProduct,getPrice) before sending the JSON back to the client.
- accept
{ successUrl, cancelUrl, outputMetadata?, outputName?, quantity?, priceId? | priceSlug? }. - if the frontend sent a
priceSlug, look it up from the catalog you received in/customers/billingand substitute the resolvedpriceIdbefore calling POST /checkout-sessions. - always include
customerExternalId(your id) in the request body so Flowglad scopes the checkout correctly.
- payload mirrors the create endpoint but without price fields, plus an optional
targetSubscriptionId. - send
type: "add_payment_method"to POST /checkout-sessions. - respond with the
{ checkoutSession: { id, url } }object from Flowglad. The React SDK will optionally redirect using theurl.
- requires
{ targetSubscriptionId, priceId, successUrl, cancelUrl }. - send
type: "activate_subscription"to POST /checkout-sessions so the Flowglad dashboard knows to attach the subscription after payment.
- expect
{ id, cancellation }wherecancellationis one of:{ timing: "at_end_of_current_billing_period" }{ timing: "at_future_date", endDate }{ timing: "immediately" }
- fetch the subscription first and confirm that
subscription.customerIdmatches the requesting customer’s Flowglad id before calling POST /subscriptions/:id/cancel. If they do not match, return403.