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

FreePro ($49/mo)
API calls1,000/mo50,000/mo
AI summaries10/moUnlimited
SupportCommunityPriority

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,000
  • ai_summaries -- included, limit 10

Pro plan ($49/mo, billed in advance):

  • api_calls -- included, limit 50,000
  • ai_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

  1. Customer signs up -- create a customer, subscribe to Free plan
  2. Customer uses your API -- check entitlements, track events
  3. Customer hits their limit -- return 403 with upgrade prompt
  4. Customer upgrades -- create Pro subscription, redirect to Stripe Checkout
  5. Payment completes -- subscription activates, limits increase
  6. 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.