Use Case: SaaS with free and pro tiers
Set up a SaaS with a free tier, a paid Pro plan, usage-based AI features, entitlement checks, and Stripe billing.
This recipe walks through a complete SaaS billing setup: a free tier with usage limits, a Pro plan with higher limits, usage-based AI features, entitlement gating, and Stripe checkout.
What you're building
| Free | Pro ($49/mo) | |
|---|---|---|
| API calls | 1,000/mo | 50,000/mo |
| AI summaries | 10/mo | Unlimited |
| Support | Community | Priority |
AI summaries are metered. Overages on Pro are billed at $0.02 per summary.
1. Connect Stripe
In the dashboard, go to Settings > Integrations and connect your Stripe account. Tanso imports your existing customers and products automatically.
Or via API:
curl -X POST https://api.tansohq.com/api/v1/tanso/stripe/connect \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"stripeSecretKey": "sk_live_..."}'2. Create features
const tansoHeaders = {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY'
}
// API calls feature
await fetch('https://api.tansohq.com/api/v1/client/features', {
method: 'POST',
headers: tansoHeaders,
body: JSON.stringify({
key: 'api_calls',
name: 'API Calls',
type: 'USAGE_BASED',
})
})
// AI summaries feature
await fetch('https://api.tansohq.com/api/v1/client/features', {
method: 'POST',
headers: tansoHeaders,
body: JSON.stringify({
key: 'ai_summaries',
name: 'AI Summaries',
type: 'USAGE_BASED',
})
})Or use the dashboard -- go to Features and create them there.
3. Create plans
Set up the Free and Pro plans in the dashboard. For each plan, link the features with their limits and pricing:
Free plan ($0/mo):
api_calls-- included, limit 1,000ai_summaries-- included, limit 10
Pro plan ($49/mo, billed in advance):
api_calls-- included, limit 50,000ai_summaries-- usage-based, $0.02 per unit, no hard limit
4. Gate access with entitlement checks
Before serving a request, check if the customer has access:
app.post('/api/summarize', async (req, res) => {
// Check entitlement before doing expensive work
const check = await fetch(
`https://api.tansohq.com/api/v1/client/entitlements/check?customerReferenceId=${req.userId}&featureKey=ai_summaries`,
{ headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
)
const entitlement = await check.json()
if (!entitlement.data.isAllowed) {
return res.status(403).json({
error: 'Usage limit reached',
reason: entitlement.data.reason,
used: entitlement.data.used,
limit: entitlement.data.limit,
})
}
// Do the work
const result = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [{ role: 'user', content: req.body.text }],
})
// Track the usage event
await fetch('https://api.tansohq.com/api/v1/client/events', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY'
},
body: JSON.stringify({
eventName: 'ai_summary',
customerReferenceId: req.userId,
featureKey: 'ai_summaries',
costInput: {
model: 'gpt-4o',
modelProvider: 'openai',
inputTokens: result.usage.prompt_tokens,
outputTokens: result.usage.completion_tokens,
},
usageUnits: 1,
revenueAmount: 0.02,
})
})
res.json({ summary: result.choices[0].message.content })
})5. Handle subscriptions and checkout
When a customer upgrades to Pro, create a subscription and redirect them to Stripe Checkout:
app.post('/api/upgrade', async (req, res) => {
// Create the subscription
const sub = await fetch('https://api.tansohq.com/api/v1/client/subscriptions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY'
},
body: JSON.stringify({
customerReferenceId: req.userId,
planId: 'PRO_PLAN_ID',
})
})
const subscription = await sub.json()
// Get the Stripe Checkout URL for the initial invoice
const checkout = await fetch('https://api.tansohq.com/api/v1/client/billing/checkout-session', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY'
},
body: JSON.stringify({
invoiceId: subscription.data.currentInvoiceId,
successUrl: 'https://yourapp.com/billing?success=true',
cancelUrl: 'https://yourapp.com/billing?canceled=true',
})
})
const session = await checkout.json()
res.json({ checkoutUrl: session.data.url })
})6. Show usage in your UI
Pull current usage to show customers where they stand:
app.get('/api/usage', async (req, res) => {
const entitlements = await fetch(
`https://api.tansohq.com/api/v1/client/entitlements?customerReferenceId=${req.userId}`,
{ headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
)
const data = await entitlements.json()
// Returns used, limit, and remaining for each feature
res.json(data.data)
})The full flow
- Customer signs up -- create a customer, subscribe to Free plan
- Customer uses your API -- check entitlements, track events
- Customer hits their limit -- return 403 with upgrade prompt
- Customer upgrades -- create Pro subscription, redirect to Stripe Checkout
- Payment completes -- subscription activates, limits increase
- Pro usage -- AI summaries are metered, overages appear on next invoice
Tips
- Check before you compute. Always call entitlement check before doing expensive AI work. Don't generate a response and then find out the customer is over their limit.
- Use evaluate for simulations. The evaluate endpoint lets you check "would this request be allowed?" without recording usage. Useful for showing "you have 3 summaries left" in your UI.
- Grace periods. Set a grace period on subscriptions so customers don't lose access the instant a payment fails. 3-7 days is typical.
- Start with Observe. You can send events in Observe mode first to understand your costs, then layer on Platform billing when you're ready. Your event pipeline carries over.
Updated about 1 month ago