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
PENDINGinvoice 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
DUEinvoice is generated. Entitlements remain active during the grace period while you collect payment. - In-arrears — The
PENDINGinvoice transitions toDUEwith 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-paidDowngrade — 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}`)
})Updated about 1 month ago