Pricing units
Per-call, credit packs and time passes are one record with different nulls.
One model
An entitlement has two nullable fields, and they carry the entire variation:
| unit | remaining | expiresAt |
|---|---|---|
per_call | 1 | null |
credit_pack | N | null, or a horizon |
time_pass | null (unlimited) | now + ttl |
definePrice({ sku: 'once', unit: 'per_call', amount: '0.05' });
definePrice({ sku: 'pack', unit: 'credit_pack', amount: '10.00', credits: 250 });
definePrice({ sku: 'day', unit: 'time_pass', amount: '20.00', ttlMs: 86_400_000 });definePrice validates the invariants so the rest of the system can treat a price as coherent: a per-call price grants exactly one credit; a credit pack needs an integer count; a time pass needs a ttl and must not also grant credits. Amounts are decimal strings — a number is rejected at the type level and again at runtime, because 0.1 + 0.2 is not 0.3 and money should not go near a float.
One consume
Because the units share a record, spending is one function:
if (expiresAt !== null && now >= expiresAt) → expired if (remaining !== null && remaining < cost) → insufficient_credits if (remaining !== null) remaining -= cost
It is atomic — a compare-and-swap on the record’s version, inside a transaction on the durable stores — and the conformance suite proves two concurrent calls cannot spend the same credit.
Per-tool cost
A credit pack can charge 1 for a cheap tool and 25 for an expensive one. That is the main reason packs are the default: the tool author reprices without reissuing anything, and a cost greater than one is all-or-nothing — a rejected consume never partially spends.
The $5 minimum, and why
definePrice rejects a credit pack below 5.00 unless allowBelowMinimum: true. The floor is notabout the provider’s fee. Moove’s protocol fee is 0.02%Moove documentation — a tenth of a cent on a $5 pack — and same-chain, same-token payments are free.
- What the payer bears.Network gas, and a bridge relayer fee if they arrive from another chain. Those are the provider’s documented costs, not ours to quantify; on a small pack they can rival the purchase.
- What a purchase costs a human. Reading the challenge, opening a browser, connecting a wallet, approving, coming back. A pack should be sized to keep the human out of the loop for a working session.
The escape hatch exists for tenants who can guarantee same-chain settlement, where the fee is nil and gas is a fraction of a cent. The reference server uses it for one deliberately uneconomic $1 trial pack, pinned by a test to exactly one.
Which entitlement is spent first
Unlimited entitlements first, so a valid time pass is spent before a credit pack and credits are not burned needlessly. Then soonest-expiring. Then smallest balance. The effect is that credits the buyer would otherwise lose get used first.