Developer Docs · 08

The Token System

Tokens are an internal currency: users buy bundles, plugins debit for usage, and every movement is an auditable transaction.

Concepts & data model

Four entities: UserTokenBalance (the current balance), TokenTransaction (an immutable ledger row), TokenBundle (an admin-configured purchasable package) and TokenBundlePurchase (a purchase moving through payment → completion).

TokenService API

service.get_balance(user_id) -> int
service.credit_tokens(user_id, amount, transaction_type, …)
service.debit_tokens(user_id, amount, transaction_type, …)
service.refund_tokens(user_id, amount, …)
service.get_transactions(user_id, limit=50, offset=0)

Tokens are credited on subscription activation and on bundle purchase completion; they are debited by plugins that meter usage (e.g. a chat bot charging per word).

Atomic debit pattern

Create the usage record and the debit in the same transaction so a failure rolls back both — never debit then create:

with db.session.begin_nested():
    record = MyUsage(user_id=user_id, cost=cost)
    db.session.add(record)
    service.debit_tokens(user_id, cost, TxType.USAGE,
                         reference_id=record.id)
# both committed, or neither
If the balance is insufficient, debit_tokens raises and the API returns 402 insufficient_tokens — surface a “buy tokens” path to the user.

Reading cost from config

Never hardcode prices. Read the per-action cost from the plugin's config (which the admin can edit), defaulting to DEFAULT_CONFIG so a fresh install still works.