Subscription Billing API

Stop building billing infrastructure.

You shouldn't be writing code for trials, retries, dunning, pause logic, or reconciliation scripts. Define a plan, attach a customer, and Flint handles every billing cycle automatically.

No credit cardFree sandbox forever3 API calls to go live
What you typically build
// cron job, retry queue, webhook handler,
// reconciliation script, state machine,
// dunning emails, proration calculator...
//
// 2 months later: more billing code
// than product code.
What Flint handles
3 API calls
const sub = await flint.subscriptions.create({
customerId: "cus_01HZ4X8",
planId: "pln_01HZ8K3",
paymentMethodId: "pm_01HZ8K4",
});
// sub.status → "TRIALING"
// Flint handles everything else.

The problem

You wanted a $29/month plan.

Two months later you have a billing state machine, a cron job, a retry queue, a webhook handler, and a reconciliation script. You've written more billing code than product code. Every edge case you thought was simple turned out to be a week of work.

Trials need timers that fire at exactly the right moment. Failed payments need retries with exponential backoff and a decision about what happens when retries run out.

Pausing needs to freeze the billing cycle without losing the customer's place. Cancellation needs to respect contract terms, handle prorations, and clean up downstream state. Each of these is a mini-system you have to build, test, and maintain forever.

Subscription billing is a solved problem. You shouldn't have to re-solve it.

See the difference

What subscription billing actually looks like.

Same outcome - a customer on a recurring plan. One approach requires a billing engine. The other requires three API calls.

Typical subscription billing setup
// 1. Create a product
const product = await stripe.products.create({
name: "Pro Plan",
});
// 2. Create a price
const price = await stripe.prices.create({
product: product.id,
unit_amount: 4900,
currency: "usd",
recurring: { interval: "month" },
});
// 3. Create or retrieve a customer
const customer = await stripe.customers.create({
email: "user@example.com",
});
// 4. Attach a payment method
await stripe.paymentMethods.attach(pmId, {
customer: customer.id,
});
await stripe.customers.update(customer.id, {
invoice_settings: {
default_payment_method: pmId,
},
});
// 5. Create the subscription
const sub = await stripe.subscriptions.create({
customer: customer.id,
items: [{ price: price.id }],
trial_period_days: 14,
});
// 6. Handle webhook events:
// invoice.created
// invoice.payment_succeeded
// invoice.payment_failed
// customer.subscription.updated
// customer.subscription.deleted
// customer.subscription.trial_will_end
Flint subscription billing
// 1. Create a plan (product + price in one)
const plan = await flint.subscriptionPlans.create({
name: "Pro Plan",
billingInterval: "MONTHLY",
currency: "USD",
lineItems: [{
name: "Pro seat license",
quantity: 1,
unitPriceMoney: { amount: 4900, currency: "USD" },
}],
trialPeriodDays: 14,
});
// 2. Save a payment method
const pm = await flint.paymentMethods.save({
customerId: "cus_01HZ4X8",
sourceToken: "tok_visa",
setAsDefault: true,
});
// 3. Start the subscription
const sub = await flint.subscriptions.create({
customerId: "cus_01HZ4X8",
planId: plan.id,
paymentMethodId: pm.id,
});
// That's it. Flint handles:
// - Trial → Active transition
// - Billing cycle charges
// - Failed payment retries
// - Lifecycle webhooks
// - Order creation per cycle

Code examples

From zero to recurring billing in three calls.

Create a plan, save a payment method, and start a subscription. Flint computes the billing schedule and handles every renewal.

1

Create a subscription plan

create-plan.ts

2

Save a payment method

save-payment-method.ts

3

Create a subscription

create-subscription.ts

create-plan.ts
const plan = await flint.subscriptionPlans.create({
name: "Pro Monthly",
billingInterval: "MONTHLY",
currency: "USD",
lineItems: [
{
name: "Pro seat license",
quantity: 1,
unitPriceMoney: { amount: 4900, currency: "USD" },
},
],
trialPeriodDays: 14,
});
// plan.id → "pln_01HZ8K3"
// plan.status → "ACTIVE"
save-payment-method.ts
const paymentMethod = await flint.paymentMethods.save({
customerId: "cus_01HZ4X8",
sourceToken: "tok_visa",
setAsDefault: true,
});
// paymentMethod.id → "pm_01HZ8K4"
// paymentMethod.card.last4 → "4242"
// paymentMethod.isDefault → true
create-subscription.ts
const subscription = await flint.subscriptions.create({
customerId: "cus_01HZ4X8",
planId: "pln_01HZ8K3",
paymentMethodId: "pm_01HZ8K4",
});
// subscription.id → "sub_01HZ8K5"
// subscription.status → "TRIALING"
// subscription.trialEnd → "2026-04-13T00:00:00Z"
// subscription.nextBillingDate → "2026-04-13T00:00:00Z"
// Each billing cycle creates a Flint Order - same as one-off purchases.

Orders-native billing

Every billing cycle is a real order.

Most billing APIs create invoices that live in a separate system from your orders. Flint creates a real Order for every billing cycle - the same Order object you use for one-off purchases.

Unified order history - one-off and recurring purchases in one timeline
Refunds work identically - same CreateRefund API for subscriptions and one-time orders
Tax and discounts apply automatically - no subscription-specific tax logic to maintain
subscription-generated-order.json
{
"id": "ord_01HZ9A3",
"status": "PAID",
"source": "SUBSCRIPTION",
"subscriptionId": "sub_01HZ8K5",
"lineItems": [
{
"name": "Pro seat license",
"quantity": 1,
"unitPriceMoney": { "amount": 4900, "currency": "USD" }
}
],
"netAmounts": {
"subtotalMoney": { "amount": 4900, "currency": "USD" },
"taxMoney": { "amount": 392, "currency": "USD" },
"totalMoney": { "amount": 5292, "currency": "USD" },
"paidMoney": { "amount": 5292, "currency": "USD" },
"balanceMoney": { "amount": 0, "currency": "USD" }
}
}

What's included

Everything a recurring billing API needs.

No separate dunning service, no pause logic to build, no webhook plumbing. Subscriptions handle it.

Flexible billing cadences

Monthly, yearly, or custom intervals via billingIntervalCount. Bill every 3 months, every 6 weeks, or any cycle your business needs. Multiple line items per plan with independent quantities and prices.

Free trials

Set trialPeriodDays on any plan. The subscription starts in TRIALING status and automatically transitions to ACTIVE when the trial ends. First charge happens on the transition - no manual scheduling.

Dunning & retries

Failed payments trigger automatic retries with configurable backoff. Subscriptions move to PAST_DUE and recover when payment succeeds. Configure the end action: cancel the subscription or pause it.

Pause & resume

Pause a subscription with one API call. Billing stops immediately. Set optional pauseDurationCycles to auto-resume after a set number of cycles, or resume manually when the customer returns.

Contract terms

Set contractTermMonths for minimum commitment periods. Cancellations before the term ends can apply an earlyTerminationFee or be blocked entirely. You control the policy per plan.

Real-time webhooks

Events for every lifecycle change: subscription.created, subscription.renewed, subscription.past_due, subscription.canceled, subscription.paused, subscription.resumed. Build automations without polling.

Lifecycle

Predictable subscription status machine.

Every subscription follows a deterministic lifecycle. Transitions are enforced server-side - no ambiguous states, no manual bookkeeping.

Main flow
TRIALINGACTIVEPAST_DUECANCELED
or
Pause branch
ACTIVEPAUSEDACTIVE
TRIALING
Customer is in a free trial. No charges yet.
subscription.created
ACTIVE
Subscription is active and billing on schedule.
subscription.renewed
PAST_DUE
Payment failed. Automatic retries in progress.
subscription.past_due
PAUSED
Billing paused by request. Resume anytime.
subscription.paused
CANCELED
Subscription ended. No further charges.
subscription.canceled

Ship this week

Ship subscription billing this week.

Three API calls to recurring revenue. Plans, billing cycles, retries, lifecycle webhooks, and real orders - from a single API. Free to start.

No credit card requiredFree sandbox foreverProduction ready in minutes
billing.ts
const plan = await flint.subscriptionPlans
.create({
name: "Pro Monthly",
billingInterval: "MONTHLY",
lineItems: [{
name: "Pro seat",
unitPriceMoney: { amount: 4900 },
}],
});
const sub = await flint.subscriptions
.create({
customerId: "cus_01HZ4X8",
planId: plan.id,
paymentMethodId: "pm_01HZ8K4",
});
// sub.status → "TRIALING"
// Billing starts automatically.