Tanso Billing (Payment Provider Agnostic)

How billing works when Tanso handles the full lifecycle — no Stripe required. You collect payment however you like and tell Tanso when invoices are paid.

If you choose not to connect Stripe, Tanso is payment provider agnostic. In this case, Tanso is your billing system of record. There is no external payment processor — you manage payment collection yourself (bank transfer, enterprise invoicing, or any external processor) and use the API to mark invoices as paid.

This is the default mode. No setup required.


How it works

1. Create a customer

Register your end user with a customerReferenceId — any string you already use. No external sync needed.

await fetch(`${TANSO_API}/customers`, {
  method: 'POST',
  headers,
  body: JSON.stringify({
    customerReferenceId: 'user_456',
    email: '[email protected]',
    firstName: 'Jane',
    lastName: 'Smith',
  })
})

2. Subscribe the customer to a plan

List available plans, then create a subscription.

// List plans to get the plan ID
const plans = await fetch(`${TANSO_API}/plans`, { headers })
  .then(r => r.json())

const proPlan = plans.data.find(p => p.plan.key === 'pro_monthly')

// Subscribe the customer
const subscription = await fetch(`${TANSO_API}/subscriptions`, {
  method: 'POST',
  headers,
  body: JSON.stringify({
    customerReferenceId: 'user_456',
    planId: proPlan.plan.id,
    gracePeriod: 3
  })
}).then(r => r.json())

// For in-advance plans, this returns an invoice that needs to be paid
const invoiceId = subscription.data.invoice.id
const invoiceStatus = subscription.data.invoice.status  // "DUE"

What happens next depends on billing timing:

  • In-advance plans — Tanso creates an invoice immediately with status DUE. The subscription stays inactive until the invoice is paid. Free plans ($0) are auto-activated.
  • In-arrears plans — The subscription activates immediately. Tanso creates a PENDING invoice due at the end of the billing period.

3. Pay the invoice

Collect payment through whatever channel works for your business, then tell Tanso:

await fetch(`${TANSO_API}/billing/invoices/${invoiceId}/mark-paid`, {
  method: 'POST',
  headers,
})
// Subscription is now active. Entitlements and credits are granted.

When the invoice is marked paid:

  • The subscription activates (for in-advance plans)
  • Entitlements are granted — the customer can now access all features in the plan
  • Credits are allocated if the plan includes credit grants

4. Check entitlements before serving requests

app.post('/api/chat', async (req, res) => {
  // Check if the customer can use this feature
  const entitlement = await fetch(
    `${TANSO_API}/entitlements/${req.userId}/ai_chat`,
    { headers }
  ).then(r => r.json())

  if (!entitlement.data.isAllowed) {
    return res.status(403).json({
      error: 'Usage limit reached',
      usage: entitlement.data.usage
    })
  }

  // Process the request
  const result = await openai.chat.completions.create(req.body)

  // Report usage
  await fetch(`${TANSO_API}/events`, {
    method: 'POST',
    headers,
    body: JSON.stringify({
      eventIdempotencyKey: crypto.randomUUID(),
      eventName: 'chat_completion',
      customerReferenceId: req.userId,
      featureKey: 'ai_chat',
      costInput: {
        model: 'gpt-4o',
        modelProvider: 'openai',
        inputTokens: result.usage.prompt_tokens,
        outputTokens: result.usage.completion_tokens,
      },
      usageUnits: result.usage.total_tokens,
    })
  })

  res.json(result)
})

Events feed into usage-based billing calculations and analytics. At the end of the billing period, Tanso tallies usage and applies pricing rules to generate the next invoice.

5. Pre-flight check with usage simulation

Before consuming expensive resources, simulate whether the usage would be allowed — without recording real usage:

const evaluation = await fetch(`${TANSO_API}/entitlements`, {
  method: 'POST',
  headers,
  body: JSON.stringify({
    customerReferenceId: 'user_456',
    featureKey: 'ai_chat',
    track: {
      eventName: 'chat_completion',
      usageUnits: 5000  // estimated tokens for this request
    }
  })
}).then(r => r.json())

if (evaluation.data.simulation.wouldExceedLimit) {
  // Don't make the expensive API call
  return res.status(403).json({ error: 'Would exceed usage limit' })
}

6. Check credit balance

const pools = await fetch(
  `${TANSO_API}/credits/user_456/pools`,
  { headers }
).then(r => r.json())

pools.data.forEach(pool => {
  console.log(`${pool.denomination}: ${pool.balance} remaining`)
})

Renewals

Tanso automatically creates the next invoice when the billing period ends:

  • In-advance — A new DUE invoice is generated. Entitlements remain active during the grace period while you collect payment.
  • In-arrears — The PENDING invoice transitions to DUE with usage charges calculated from the period's events.

Credits roll over based on the plan's rollover policy (NONE, FULL, or CAPPED).


Upgrades and downgrades

Upgrade — Takes effect immediately. For in-advance plans, Tanso creates a prorated adjustment invoice for the price difference. Credits are granted for any increase in the new plan's allocations.

// Get the customer to find their subscription ID
const customer = await fetch(
  `${TANSO_API}/customers/user_456`,
  { headers }
).then(r => r.json())

const subscriptionId = customer.data.subscriptions[0].id

// Upgrade to enterprise plan
const enterprisePlan = plans.data.find(p => p.plan.key === 'enterprise_monthly')

await fetch(`${TANSO_API}/subscriptions/${subscriptionId}/plan-change`, {
  method: 'POST',
  headers,
  body: JSON.stringify({
    changeToPlanId: enterprisePlan.plan.id,
    changeType: 'UPGRADE'
  })
})
// Takes effect immediately. Prorated adjustment invoice is created.
// Pay the adjustment invoice the same way:
// POST /billing/invoices/{adjustmentInvoiceId}/mark-paid

Downgrade — Scheduled for the end of the current billing period. The customer keeps their current plan until then.

const starterPlan = plans.data.find(p => p.plan.key === 'starter_monthly')

await fetch(`${TANSO_API}/subscriptions/${subscriptionId}/plan-change`, {
  method: 'POST',
  headers,
  body: JSON.stringify({
    changeToPlanId: starterPlan.plan.id,
    changeType: 'DOWNGRADE'
  })
})
// Scheduled for end of current billing period.
// Customer keeps current plan until then.

Cancellation

End of period — Subscription remains active until the current period ends, then cancels automatically.

await fetch(
  `${TANSO_API}/subscriptions/cancellation/${subscriptionId}?cancelMode=END_OF_PERIOD`,
  { method: 'POST', headers }
)

Immediate — Subscription deactivates now. Outstanding invoices are voided. For in-advance plans, a prorated credit is issued for the unused portion. Entitlements are revoked and plan-included credits are clawed back.

await fetch(
  `${TANSO_API}/subscriptions/cancellation/${subscriptionId}?cancelMode=IMMEDIATE`,
  { method: 'POST', headers }
)

Undo a scheduled cancellation — If the customer changes their mind before the period ends:

await fetch(
  `${TANSO_API}/subscriptions/cancellation/${subscriptionId}/scheduled`,
  { method: 'DELETE', headers }
)

Grace period

Each subscription has a configurable grace period (default 3 days). If an invoice passes its due date without payment, the customer retains access for the grace period. After that, the invoice moves to PAST_DUE and entitlements are revoked until payment is received.


List invoices

const invoices = await fetch(
  `${TANSO_API}/billing/invoices/user_456`,
  { headers }
).then(r => r.json())

invoices.data.forEach(inv => {
  console.log(`${inv.id}: $${inv.amount} ${inv.currency} — ${inv.status}`)
})