Billing
Adjustments, refunds, invoice lifecycle, billing up front, the wallet, and user suspension. Endpoint signatures: HTTP API.
Adjustments and refunds
Manual billing corrections are first-class events, not embedded fields on other records. POST /adjustments appends adjustment.posted; amounts are signed integers
(negative = credit, positive = charge) with no currency code. kind is prorate, credit, or debit (inferred from sign if omitted for
manual entries). Plan changes with refund_immediately append refund.issued automatically — refund amount is always positive; the invoice line is negative. Query
history with GET /adjustments and GET /refunds.
Invoice lifecycle
Invoices move through explicit states: draft → finalized → paid, or void from draft/finalized.
POST /invoices/generate— draft with line items for any billing windowPOST /invoices/{id}/finalize— lock amounts before collectionPOST /invoices/{id}/apply-wallet— optional: pay part or all of it from the walletPOST /invoices/{id}/mark-paid— record payment withpayment_refPOST /invoices/{id}/void— cancel an unpaid invoice
Events: invoice.generated, invoice.finalized, invoice.paid, invoice.voided. Replay restores status, timestamps, and payment_ref.
Generation semantics: subscription periods reconstructed from the event log; base fee proportional to
active time (trials and pauses excluded); plan segments billed separately; billing_interval: year applies annual discount; usage scaled for partial windows; metric groups and usage pools share included across members; included_rollover adds unused included from the previous period at invoice time;
the same metric across multiple subscriptions for one user is billed only once;
built-in JSON billing or Luau pricing. JSON classify runs only at usage ingest.
Billing up front
A Luau plan can bill a period up front with advance(ctx) — seats, a platform fee — and
its arrears at the end with pricing(ctx). The advance lines go on the invoice
whose window contains the period's start (window start exclusive, window end inclusive), so one
invoice per closed period carries both:
- the invoice for
[P.start, P.end]= arrears ofP+ advance of the next period, which starts atP.end; - the first period is billed by a window that ends at the subscription start, e.g.
[start − 1s, start]; - a subscription canceled at period end has no next period, so no advance.
Gauge readings such as the seat count come from latest(ctx, code): the last reported
quantity at or before the period start. Details and an example: Luau pricing ctx.
Wallet (prepaid balance)
Each user has a wallet: money paid up front that invoices draw from — prepaid usage, credits bought in advance, a top-up that covers overage. HUME does not take payments; your app charges the card and credits the wallet once the payment provider confirmed it.
POST /users/{id}/wallet/credit— a paid top-up (amountin billing units, the PSPreference); the payment id as Idempotency-Key makes a retried webhook harmlessGET /users/{id}/wallet— the balance and its entries; use it for your own gates (e.g. how much overage the balance still covers)POST /invoices/{id}/apply-wallet— pays a draft or finalized invoice from the balance:min(balance, amount due, max_amount);max_amountlimits it to, say, the usage lines- collect
total − wallet_appliedwith your PSP (nothing, if the wallet covered it), thenmark-paid
The invoice keeps its total; wallet_applied shows the part the wallet paid. Events: wallet.credited, wallet.debited — they reach webhooks and replay like any
other. Endpoint signatures: HTTP API → Wallet.
User block, unblock, and delete
Blocking is for non-payment of your customers: the user can still send POST /usage and StatsD events (metering continues), but your app must treat them as
suspended. Invoice APIs are tenant-admin — generate, finalize, mark-paid, and void
work while a user is blocked so dunning can record payment without unblocking first.
| Operation | Blocked user | Deleted user |
|---|---|---|
POST /usage, StatsD | allowed | rejected |
| Entitlements, usage list, subscription GET, adjustments | 403 blocked | 403 deleted |
| Invoice generate / read / finalize / mark-paid / void | allowed (tenant admin) | generate rejected; existing invoices readable |
GET /users, GET /users/{id}, ?external_id= | listed / returned with flags | hidden from list; GET by id still works |
| Unblock | yes | no (permanent) |
Events: user.blocked, user.unblocked, user.deleted.
Tenant invoice suspension (platform non-payment)
When a HUME project is past the unpaid grace period, the site suspends POST /invoices/generate on that tenant via POST /tenant/suspend-invoices. Metering, plans, and webhooks keep working.
After payment: POST /tenant/resume-invoices. Inspect with GET /tenant (invoices_suspended).