Token billing without building the infrastructure
Sell prepaid tokens, granted at purchase. Salable burns the balance down as your customers use it, lets them buy a top-up when they run out, and resets or rolls it over each cycle. Salable handles the whole flow, so your application never has to react to a Stripe webhook. All you do is create the checkout, check entitlements and call consume.
You define the token
A token in Salable is a unit you define: an image generation, a report, an API call, or any other action in your application. Create the token once, add it to any plan that includes the feature, and set the quantity and price. Below, the same image_generations token is priced five different ways.
Monthly reset
$29 /month
1,000 generations
≈ $0.03 a generation
Resets at the start of each cycle.
Accumulating
$99 /month
5,000 generations
≈ $0.02 a generation
Accumulates unused tokens, each batch expiring after 90 days.
One-off top-up
$20 one-off
1,000 generations
$0.02 a generation
Joins the balance they already have and never expires.
Free allowance
$0 /month
50 generations
nothing, until they buy
Enough to build against before anyone pays.
Bought once
$499 one-off
100,000 generations
≈ $0.005 a generation
Bought once, drawn down for as long as it lasts.
Example figures. You set the price and the quantity.
What you don't build
From the first purchase onwards, Salable manages token accruals, balance records, consumption, reset or accumulation, subscription renewals and top-ups.
Gate features in your application
Salable uses grantees to represent who receives access: a user, a workspace, a project, or any other identifier in your application. When a customer buys a plan, the grantees named at checkout get access to its entitlements. Perform entitlement checks against the grantee in your code to gate features.
Grant tokens at purchase
The token quantity you configure on the plan’s line item becomes the balance granted to the plan owner. Nothing to reconcile on your side: consume tokens as they’re used.
Keep each tenant’s balance separate
A user can belong to several tenants, each with its own balance. The owner in Salable is the tenant the purchase belongs to. Scope each consume request to the owner, and the right balance is spent. Tokens never cross tenants.
Read the balance in one request
A single request returns the owner’s total balance for a token, aggregated across every purchase. Each purchase is recorded separately, with its source and expiry date.
Top-ups join one balance
Sell a one-off plan for the same token and it adds to the existing balance: two separate 500-token purchases leave the owner with a single balance of 1,000. Salable always spends the soonest-expiring records first, preserving longer-lived tokens for later.
Never record the same consumption twice
Consume tokens as owners use them. Send an Idempotency-Key with each call: retry within 24 hours, and Salable replays the original response instead of recording the usage twice.
Reset or roll over the balance
Choose whether unused tokens roll over to the next cycle or reset, with optional expiry days per batch.
Sell it, check entitlements, consume
Create the checkout, check entitlements and consume. Read the balance and sell a top-up when an owner runs low.
const response = await fetch('https://salable.app/api/checkout', { method: 'POST', headers: { Authorization: 'Bearer YOUR_SECRET_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ planId: 'plan_01KHRQ9T3W4WFDARCVX3QG70EB', owner: 'org_acme', grantee: 'user_jo', interval: 'month', intervalCount: 1, currency: 'USD', successUrl: 'https://yourapp.com/welcome', cancelUrl: 'https://yourapp.com/pricing' }) }); const { data } = await response.json();
const response = await fetch('https://salable.app/api/entitlements/check?granteeId=user_jo&owner=org_acme', { headers: { Authorization: 'Bearer YOUR_SECRET_KEY' } }); const { data } = await response.json(); const hasAccess = data.entitlements.some(e => e.type === 'token' && e.value === 'image_generations');
const response = await fetch('https://salable.app/api/tokens/consume', { method: 'POST', headers: { Authorization: 'Bearer YOUR_SECRET_KEY', 'Content-Type': 'application/json', 'Idempotency-Key': '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d' }, body: JSON.stringify({ token: 'image_generations', owner: 'org_acme', amount: 10 }) });
const response = await fetch('https://salable.app/api/tokens/balance?owner=org_acme&token=image_generations', { headers: { Authorization: 'Bearer YOUR_SECRET_KEY' } }); const { data } = await response.json(); const { balance, records } = data[0];
const response = await fetch('https://salable.app/api/checkout', { method: 'POST', headers: { Authorization: 'Bearer YOUR_SECRET_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ planId: 'plan_01KM5C5J4W8G2R7N9XQ6DPVTBA', owner: 'org_acme', grantee: 'user_jo', interval: null, intervalCount: null, currency: 'USD', successUrl: 'https://yourapp.com/tokens/added', cancelUrl: 'https://yourapp.com/tokens' }) }); const { data } = await response.json();




