Sell prepaid credits
Say customers buy credits in packs, and every member of a customer’s team spends from the same balance. That’s a balance limit on the team, with grants when they buy.
1. Create the balance
Section titled “1. Create the balance”The window total never resets. The amount is what every team starts
with, here 100 free credits:
import { createClient } from "@limitry/sdk";
const limitry = createClient();
await limitry.limits.create({ name: "Credits", subjectKind: "team", action: "*", amount: 100, unit: "credits", window: "total",});from limitry import Limitry
limitry = Limitry()
limitry.limits.create( name="Credits", subject_kind="team", action="*", amount=100, unit="credits", window="total",)limitry limits create --name Credits --subject-kind team --action "*" \ --amount 100 --unit credits --window totalcurl -X POST https://api.limitry.com/v1/limits \ -H "Authorization: Bearer $LIMITRY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name":"Credits","subjectKind":"team","action":"*", "amount":100,"unit":"credits","window":"total"}'2. Add credits when a customer pays
Section titled “2. Add credits when a customer pays”In your payment webhook, grant what they bought. Use the payment’s id as the idempotency key, so a redelivered webhook can’t add credits twice:
import { createClient } from "@limitry/sdk";
const limitry = createClient();
await limitry.limits.grants.create( "lim_3f0c…", { subject: { kind: "team", id: "team_42" }, amount: 5000, note: "5,000-credit pack", }, { idempotencyKey: "payment_8231" },);from limitry import Limitry
limitry = Limitry()
limitry.limits.grants.create( "lim_3f0c…", subject={"kind": "team", "id": "team_42"}, amount=5000, note="5,000-credit pack", request_options={"idempotency_key": "payment_8231"},)limitry limits grants create lim_3f0c… --subject '{"kind":"team","id":"team_42"}' \ --amount 5000 --note "5,000-credit pack"curl -X POST https://api.limitry.com/v1/limits/$LIMIT_ID/grants \ -H "Authorization: Bearer $LIMITRY_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: payment_8231" \ -d '{ "subject": { "kind": "team", "id": "team_42" }, "amount": 5000, "note": "5,000-credit pack" }'Credits go to the balance in the key’s environment. Your payment
handler in production needs a live key (lim_live_…), with
$LIMIT_ID the live limit’s id. A test key adds test credits, which
suits your payment provider’s test mode. See
Test and live.
3. Spend from the team’s balance
Section titled “3. Spend from the team’s balance”Check as the user, and name the team as the shared subject. Any limits you set on the user still apply as well:
const report = await limitry.guard( { subject: { kind: "user", id: user.id }, shared: [{ kind: "team", id: user.teamId }], action: "report", cost: 25, }, () => buildReport(params),);with limitry.guard( subject={"kind": "user", "id": user.id}, shared=[{"kind": "team", "id": user.team_id}], action="report", cost=25,): report = build_report(params)However many teammates spend at the same moment, a check never takes the balance below zero: Limitry refuses a cost that doesn’t fit. Two things can still take it below zero: committing a reservation at more than it held, which Limitry charges in full (see Reservations), and taking back credits the team already spent (below).
4. When the balance runs out
Section titled “4. When the balance runs out”The check says no, with retryAfterSeconds: null, because waiting won’t
help. Use that to offer a top-up rather than “try again later”.
To show the balance in your product, read remaining from any check’s
answer, or make a preview check, which counts nothing.
5. When you refund a payment
Section titled “5. When you refund a payment”In your refund webhook, take the credits back with a negative amount, a note, and the refund’s id as the idempotency key:
import { createClient } from "@limitry/sdk";
const limitry = createClient();
await limitry.limits.grants.create( "lim_3f0c…", { subject: { kind: "team", id: "team_42" }, amount: -5000, note: "Refund of payment_8231", }, { idempotencyKey: "refund_311" },);from limitry import Limitry
limitry = Limitry()
limitry.limits.grants.create( "lim_3f0c…", subject={"kind": "team", "id": "team_42"}, amount=-5000, note="Refund of payment_8231", request_options={"idempotency_key": "refund_311"},)limitry limits grants create lim_3f0c… --subject '{"kind":"team","id":"team_42"}' \ --amount -5000 --note "Refund of payment_8231"curl -X POST https://api.limitry.com/v1/limits/$LIMIT_ID/grants \ -H "Authorization: Bearer $LIMITRY_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: refund_311" \ -d '{ "subject": { "kind": "team", "id": "team_42" }, "amount": -5000, "note": "Refund of payment_8231" }'If the team already spent the credits, the balance goes below zero and their checks say no until they buy more. A chargeback works the same way.
Related: Pools, balances and budgets, Idempotency.