Skip to content

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.

The window total never resets. The amount is what every team starts with, here 100 free credits:

Terminal window
curl -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"}'

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:

Terminal window
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": "t_42" }, "amount": 5000,
"note": "5,000-credit pack" }'

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 limits.guard(
{
subject: { kind: "user", id: user.id },
shared: [{ kind: "team", id: user.teamId }],
action: "report",
cost: 25,
},
() => buildReport(params),
);

However many teammates spend at the same moment, the balance never goes below zero.

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.

Related: Pools, balances and budgets, Idempotency.