Billing

Adjustments, refunds, invoice lifecycle, 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: draftfinalizedpaid, or void from draft/finalized.

  1. POST /invoices/generate — draft with line items for any billing window
  2. POST /invoices/{id}/finalize — lock amounts before collection
  3. POST /invoices/{id}/mark-paid — record payment with payment_ref
  4. POST /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.

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.

OperationBlocked userDeleted user
POST /usage, StatsDallowedrejected
Entitlements, usage list, subscription GET, adjustments403 blocked403 deleted
Invoice generate / read / finalize / mark-paid / voidallowed (tenant admin)generate rejected; existing invoices readable
GET /users, GET /users/{id}, ?external_id=listed / returned with flagshidden from list; GET by id still works
Unblockyesno (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).