Tokens, quotas, LMC and PAYG
A token is not a ruble and not LMC. What is charged to the subscription quota, what to the balance, and how to get through the 402 payg_topup_required fork without paying twice.
Übersetzung in Arbeit
Billing in the public API involves three different quantities, and they are regularly confused with one another.
| Quantity | What it is | Where to see it |
|---|---|---|
| Token | an accounting unit of a service's work volume. Not money and not LMC | total_tokens, tokens_spent in the order; GET /usage |
| Subscription quota | the plan's token allowance for the billing period. Spent first, free of charge | GET /subscriptions → items[].tokens_remaining |
| LMC | the internal currency. Only what the quota didn't cover is paid from it | shortfall_lmc in the 402 response; price_formatted in the order |
The charge order is always the same and can't be configured: subscription quota first, LMC only for the
remainder. If the quota is sufficient, the balance isn't touched at all, and the confirm_payg_topup flag
changes nothing.
Stolperfalle
The public API doesn't show your LMC balance: GET /wallets and GET /ledger-transactions
return 404 in the standard configuration. The public view of your state is GET /usage and
GET /subscriptions, and you learn the top-up amount from the 402 body, not from the balance in advance.
The 402 fork: the server asks for consent
If the order costs more than the tokens left in the quota, and the service allows pay-as-you-go
(Service.payg_access ≠ disabled), the server doesn't create the order and charges nothing; instead it
returns 402 with the top-up price. It's not a rejection but a question.
The 402 body:
| Field | Meaning |
|---|---|
reason | always payg_topup_required — branch on it, not on message |
shortfall_tokens | how many tokens the quota didn't cover — exactly these are paid from LMC |
shortfall_lmc | the top-up price in LMC, ready to show to the user |
shortfall_lmc_minor | the same amount in minor units — for exact calculations |
quote_token | a signed quote; send the string back unchanged |
expires_at | the price is locked until this moment (the token lives ~60 seconds) |
If you agree, repeat the exact same request with two added fields:
# 1. Regular attempt → HTTP 402 with a quote
curl -s -X POST "$BASE/orders" -H "X-API-KEY: $KEY" \
-H "Content-Type: application/json" \
-d '{ "template_id": "<UUID>", "input_text": "…" }'
# 2. Consent: the same body + confirmation and the same quote_token → HTTP 201
curl -s -X POST "$BASE/orders" -H "X-API-KEY: $KEY" \
-H "Content-Type: application/json" \
-d '{ "template_id": "<UUID>", "input_text": "…",
"confirm_payg_topup": true, "quote_token": "q_9f8b2c1a4d3e" }'If you don't agree, simply don't repeat the request: the order wasn't created, nothing was charged, there's nothing to cancel.
- Was passiert ist
- The order was not created and no money was charged — instead, the server sent the price of the top-up. Body: `reason: "payg_topup_required"`, `shortfall_tokens`, `shortfall_lmc`, `shortfall_lmc_minor`, `quote_token`, `expires_at`.
- Was jetzt zu tun ist
- Show the user `shortfall_lmc` and get explicit consent — an automatic retry here means silently charging money. The quote is short-lived (`expires_at`, on the order of a minute); if it has expired, a repeat request WITHOUT confirmation returns a fresh 402 with a new `quote_token`.
Four confirmation traps
- The flag alone charges nothing.
confirm_payg_topup: truewithout aquote_token— or with one that has expired, been modified, or was issued for a different order — returns402again with a new quote. - Consent is an upper bound, not a fixed amount. If the top-up has become cheaper by the time of the charge,
the actual price is charged. If it has become more expensive, you get
409quote_mismatchwith the fieldsactual_lmc_minorandquoted_lmc_minor. - Repeating without confirmation is safe and is a standard step: it issues a fresh
402. That's exactly how you refresh a stale or more expensive price — re-sending the oldquote_tokenis pointless. POST /orders/{order}/retryhas the same flow — an order retry goes through the same billing and can likewise answer402and409.
- Was passiert ist
- The confirmed top-up did not go through: the order was not created and nothing was charged. Body: `reason: "quote_mismatch"`, `actual_lmc_minor` (what it costs now), `quoted_lmc_minor` (what it was in the quote).
- Was jetzt zu tun ist
- The difference `actual_lmc_minor − quoted_lmc_minor` is the price increase; it is in minor units, and what you should show the user is `shortfall_lmc` from the next quote. The old `quote_token` is no longer valid: submitting it again guarantees the same 409.
Three different branches under code 402
These get confused most often: the code is the same, the reason differs, and the remedies are incompatible.
reason | What happened | What to do |
|---|---|---|
payg_topup_required | the quota ran short, a top-up is allowed | confirm the top-up with the quote |
insufficient_balance | the LMC balance lacks funds for the order's reserve | top up the wallet or reduce the order volume |
insufficient_allowance | the quota ran short, and PAYG is disabled for the service | there's nothing to top up with: wait for the new period, change the plan or cut the text |
You can tell insufficient_allowance apart from a top-up offer by the body's contents: the
shortfall_*, quote_token and expires_at fields are absent. A confirm_payg_topup sent
along with such a request is simply ignored by the server.
Two more domain rejections live nearby, but with code 403: no_subscription and
entitlement_missing — there's no subscription, or no required plan feature, at all. And limit_exceeded
with code 429 isn't about money but a subscription usage limit; see
Limits and the 429 code.
Minor units: why shortfall_lmc_minor can't be shown as is
All monetary fields in the API are integers in the asset's minor units. The real value is calculated
as minor / 10 ** precision. In the spec example, shortfall_lmc_minor: 360000000 corresponds to
shortfall_lmc: 3.6, which means the divisor here is 10^8, not 100.
Vorsicht
precision is not two and is not a constant — it lives in the data: on an order in
currency_asset.precision, on a service rate in the lumc_precision field. A formula dividing by 100
gives an amount that is off by a factor of a million, and showing 360000000 to the user as rubles is even worse.
For display, use the ready-made shortfall_lmc and price_formatted; the minor-unit fields are only for
arithmetic.
Where to check the remainder before ordering
Get subscriptions of API key owner
Only live subscriptions are returned (status active, not expired). The remainder is in
items[].token_allowance, tokens_used, tokens_remaining. There are no monetary fields here, by
design.
Get Usage Limits
GET /usage lists limits: limit_type, limit_value, used, remaining,
period_start / period_end / resets_in, plus token_unlimited and is_exceeded. It also shows
which services are available to you at all — the set is defined by the subscription's entitlements. For a single service, use
GET /usage/{service}, where {service} is the
service_code from the GET /usage output (elevenlabs, heygen, lumimg, sfx, music,
voice_search), not an arbitrary string.
Stolperfalle
The LLM reference (§7.7) calls {service} the service's integer id. That's wrong:
in the spec the parameter is a string, and the path accepts the service code — the same one found in the template's
service_key. A numeric id gives 404.
You can estimate the top-up in advance from the service rate: shortfall_tokens × lumcoin_per_token
(the lumcoin_per_token field is also in LMC minor units).
What's visible after the fact, in the order itself
total_tokens— the full cost in tokens, including retries and regenerations;tokens_spent— actually spent (retryrepeats aren't charged again);tokens_from_quotaandtokens_from_payg— the split into the free and paid portions;price_units— the locked PAYG amount in minor units,price_formatted— the same amount for display.
Stolperfalle
A zero portion arrives as null, not 0. For an order fully covered by the quota,
tokens_from_payg is null; for pure PAYG, tokens_from_quota is null.
The check if (order.tokens_from_payg > 0) won't behave on null the way you expect.
Richtig
Show the user shortfall_lmc, wait for explicit consent, and repeat the request with the
quote_token from that same response. If the quote has expired, request a new one by repeating without confirmation.
Falsch
Automatically retry a 402 with confirm_payg_topup: true: that's silently charging money
behind the user's back, and the first price discrepancy will turn the loop into a stream of 409s.
Topping up the balance and buying a plan aren't available with an API key — the public surface doesn't process payments. That's done in the dashboard: wallet, plans, account limits.
Diese Seite einer KI geben
Aktualisiert 04.09.2026 · Spezifikation 0.0.1