How Do You Design a Subscription Management Data Model and API?
How Do You Design a Subscription Management Data Model and API?
How Do You Design a Subscription Management Data Model and API?
How Do You Design a Subscription Management Data Model and API?
How Do You Design a Subscription Management Data Model and API?

Team Flexprice
Editorial
Subscription management needs four things working together: a data model that survives edge cases, an API to build on, a trial-to-paid path, and dunning logic for failed payments. Flexprice exists because building and maintaining all four yourself has a real cost, and one customer reclaimed 30% of engineering bandwidth by not doing it. Here's the architecture, and what it costs to run.
Key takeaways
Subscription status needs a named enum, not a boolean, because active and past_due form the only reversible transition when a card recovers.
A minimal API needs six resources (customers, plans, subscriptions, invoices, entitlements, webhooks) plus an idempotency key on every write.
Card-required trials convert at a GOOD rate of 25-35%, versus 4-6% without a card, per a 2026 survey of 200 B2B products.
Dunning splits into hard declines, which never retry, and soft declines, which get a staged retry schedule instead of one attempt.
Simplismart spent 1.5 to 2 months building a custom billing engine, then reclaimed 30% of engineering bandwidth after switching to Flexprice.
How do you design a subscription data model that survives real edge cases?
Four tables carry most of the weight: plans, subscriptions, invoices, and invoice line items. Each has a different update frequency, so none should collapse into one flexible table with nullable columns.
Status needs to be a named enum, not a boolean. Trialing, active, past_due, unpaid, and canceled cover the real states. The only reversible pair is active and past_due: a card fails, the subscription drops to past_due, a retry succeeds, and it goes back to active. Model this as is_active instead, and that breaks the first time a card recovers.
Store period_start and period_end explicitly on every invoice and line item, rather than computing them each time. You'll need them for proration, tax filing, and an audit trail when a customer disputes a charge.
This is exactly the state we track per subscription in Flexprice's Billing and Invoicing, so building it from scratch means recreating a state machine that already ships.
What API endpoints does subscription management actually need?
A minimal surface covers six resources: /customers, /plans, /subscriptions, /invoices, /entitlements, and webhooks for events like subscription.updated, invoice.paid, and invoice.failed.
Every write endpoint needs an idempotency key. A billing charge isn't a typical write: the request can time out after your processor has already charged the card, and you won't know which happened until you check. A UUID key lets the server return the original response on a retry instead of charging twice.
Two layers catch two failures. The key carries a short TTL, protecting against a retry within that window. A separate check, querying whether a charge already exists for this subscription and period, catches a retry after the key expires.
How do free trials convert, and what should dunning do when a card fails?
Free trials convert far less often than most teams assume. A 2026 survey of 200 B2B products by ChartMogul and ProductLed found the median free-to-paid rate sits at just 8%, and free-trial-led products specifically land at a GOOD rate of 4-6%, GREAT at 10-15%.
Requiring a credit card upfront changes that substantially, to a GOOD rate of 25-35%, roughly five times higher. That's a real tradeoff: a card requirement also blunts signups, so the right call depends on whether your product earns that friction upfront.
Dunning starts by classifying the decline into one of two types. As one founder building a dunning-recovery tool put it on X: "a failed payment isn't automatically a reason to retry the card again."
Decline type | Retry? | Typical schedule |
|---|---|---|
Hard (stolen card, closed account) | No | Request new payment method immediately |
Soft (insufficient funds, temporary hold) | Yes | 24h, 72h, day 7, day 14 |
A successful retry walks the subscription back from past_due to active. Handled well, the customer never notices.
Subscription management needs four things working together: a data model that survives edge cases, an API to build on, a trial-to-paid path, and dunning logic for failed payments. Flexprice exists because building and maintaining all four yourself has a real cost, and one customer reclaimed 30% of engineering bandwidth by not doing it. Here's the architecture, and what it costs to run.
Key takeaways
Subscription status needs a named enum, not a boolean, because active and past_due form the only reversible transition when a card recovers.
A minimal API needs six resources (customers, plans, subscriptions, invoices, entitlements, webhooks) plus an idempotency key on every write.
Card-required trials convert at a GOOD rate of 25-35%, versus 4-6% without a card, per a 2026 survey of 200 B2B products.
Dunning splits into hard declines, which never retry, and soft declines, which get a staged retry schedule instead of one attempt.
Simplismart spent 1.5 to 2 months building a custom billing engine, then reclaimed 30% of engineering bandwidth after switching to Flexprice.
How do you design a subscription data model that survives real edge cases?
Four tables carry most of the weight: plans, subscriptions, invoices, and invoice line items. Each has a different update frequency, so none should collapse into one flexible table with nullable columns.
Status needs to be a named enum, not a boolean. Trialing, active, past_due, unpaid, and canceled cover the real states. The only reversible pair is active and past_due: a card fails, the subscription drops to past_due, a retry succeeds, and it goes back to active. Model this as is_active instead, and that breaks the first time a card recovers.
Store period_start and period_end explicitly on every invoice and line item, rather than computing them each time. You'll need them for proration, tax filing, and an audit trail when a customer disputes a charge.
This is exactly the state we track per subscription in Flexprice's Billing and Invoicing, so building it from scratch means recreating a state machine that already ships.
What API endpoints does subscription management actually need?
A minimal surface covers six resources: /customers, /plans, /subscriptions, /invoices, /entitlements, and webhooks for events like subscription.updated, invoice.paid, and invoice.failed.
Every write endpoint needs an idempotency key. A billing charge isn't a typical write: the request can time out after your processor has already charged the card, and you won't know which happened until you check. A UUID key lets the server return the original response on a retry instead of charging twice.
Two layers catch two failures. The key carries a short TTL, protecting against a retry within that window. A separate check, querying whether a charge already exists for this subscription and period, catches a retry after the key expires.
How do free trials convert, and what should dunning do when a card fails?
Free trials convert far less often than most teams assume. A 2026 survey of 200 B2B products by ChartMogul and ProductLed found the median free-to-paid rate sits at just 8%, and free-trial-led products specifically land at a GOOD rate of 4-6%, GREAT at 10-15%.
Requiring a credit card upfront changes that substantially, to a GOOD rate of 25-35%, roughly five times higher. That's a real tradeoff: a card requirement also blunts signups, so the right call depends on whether your product earns that friction upfront.
Dunning starts by classifying the decline into one of two types. As one founder building a dunning-recovery tool put it on X: "a failed payment isn't automatically a reason to retry the card again."
Decline type | Retry? | Typical schedule |
|---|---|---|
Hard (stolen card, closed account) | No | Request new payment method immediately |
Soft (insufficient funds, temporary hold) | Yes | 24h, 72h, day 7, day 14 |
A successful retry walks the subscription back from past_due to active. Handled well, the customer never notices.
AI Billing Is Not Easy, But Flexprice Can Make it Easy
AI Billing Is Not Easy, But Flexprice Can Make it Easy
How do you model plans, add-ons, and coupons without breaking the schema?
A plan table with a nullable column for every add-on or coupon variant breaks the first time pricing gets more complex than flat-rate. Every new plan type becomes a migration, and every migration is a deploy your team has to schedule around.
Simplismart ran into exactly this. Building a custom billing engine on Lago took 1.5 to 2 months, then kept costing 20 to 30% of a developer's daily bandwidth to maintain. After moving to Flexprice, the team reclaimed 30% of that bandwidth.
"If billing doesn't work, we don't make money. Flexprice lets us focus on the core business instead of building billing as a second product." - Shubhendu Shishir, Head of Engineering, Simplismart
We built Flexprice's Pricing Models and Billing and Invoicing so a team can add a plan, add-on, or coupon through configuration, not a migration or a deploy. Flexprice is enterprise-grade, open source usage based billing infrastructure for AI and SaaS companies. It can be deployed in your own VPC, on-prem, or on Flexprice's managed cloud.
If you're weighing how much of this to build yourself, the Flexprice docs on Pricing Models walk through how plan and add-on changes work without a migration.
Frequently asked questions
Should subscription status and feature entitlement be the same field?
No. Subscription status tracks billing state, like trialing or past_due, while entitlement tracks what a customer can use right now, since a past_due account often keeps a grace period before losing access.
How long should a dunning retry sequence run before giving up?
A common sequence retries at 24 hours, 72 hours, day 7, and day 14 after a soft decline, then marks the invoice uncollectible and moves the subscription to unpaid.
Does requiring a credit card upfront actually improve trial-to-paid conversion?
Yes. ChartMogul and ProductLed's 2026 survey of 200 B2B products found card-required trials convert at a GOOD rate of 25-35%, against 4-6% without a card.
How do you model plans, add-ons, and coupons without breaking the schema?
A plan table with a nullable column for every add-on or coupon variant breaks the first time pricing gets more complex than flat-rate. Every new plan type becomes a migration, and every migration is a deploy your team has to schedule around.
Simplismart ran into exactly this. Building a custom billing engine on Lago took 1.5 to 2 months, then kept costing 20 to 30% of a developer's daily bandwidth to maintain. After moving to Flexprice, the team reclaimed 30% of that bandwidth.
"If billing doesn't work, we don't make money. Flexprice lets us focus on the core business instead of building billing as a second product." - Shubhendu Shishir, Head of Engineering, Simplismart
We built Flexprice's Pricing Models and Billing and Invoicing so a team can add a plan, add-on, or coupon through configuration, not a migration or a deploy. Flexprice is enterprise-grade, open source usage based billing infrastructure for AI and SaaS companies. It can be deployed in your own VPC, on-prem, or on Flexprice's managed cloud.
If you're weighing how much of this to build yourself, the Flexprice docs on Pricing Models walk through how plan and add-on changes work without a migration.
Frequently asked questions
Should subscription status and feature entitlement be the same field?
No. Subscription status tracks billing state, like trialing or past_due, while entitlement tracks what a customer can use right now, since a past_due account often keeps a grace period before losing access.
How long should a dunning retry sequence run before giving up?
A common sequence retries at 24 hours, 72 hours, day 7, and day 14 after a soft decline, then marks the invoice uncollectible and moves the subscription to unpaid.
Does requiring a credit card upfront actually improve trial-to-paid conversion?
Yes. ChartMogul and ProductLed's 2026 survey of 200 B2B products found card-required trials convert at a GOOD rate of 25-35%, against 4-6% without a card.
Share it on:






















