Bills.software · billing, invoicing, and payouts for schools and the studios that serve them · early access

Any billing system can show you a green checkmark. Ours tells you why the money did not move.

The moment that decides whether you can trust a billing system is not the successful charge. It is the one where the transfer cannot happen — the destination account is not onboarded, the retry computed a different amount, the provider key was never set — and the system has to choose between telling you and looking healthy. A great many of them choose looking healthy, and you find out at reconciliation.

We built the other thing. Every path in this stack that cannot complete refuses with a fixed machine reason code, at a real HTTP status, that your code can branch on and your auditor can read. A payout to a rep who has not finished onboarding is held, in place, resumable, with the amount still pinned from finalize — not settled to nowhere, not quietly dropped. There are ten of these codes. All ten are printed on this page, next to the file each one lives in.

The same discipline runs the other way, at our own expense. Of the nine capabilities described below, 4 are built and work today with no payment key set at all, and 2 are named on this page precisely because they do not work: one is a correctly gated console with no tables behind it, and one is a finished engine with nobody calling it. Both are labelled where you cannot miss them. A capability list you cannot check is an advertisement; this one is a citation list.

10machine reason codes that fire instead of a fabricated success — each listed below with the file it is defined in
2independent settings, both off by default, standing between this stack and a real charge — a live key alone moves nothing
4capabilities that do their whole job with no payment provider configured at all
0fabricated settlements, invented invoices, or synthetic portal links anywhere in the refusal paths

The refusal board — the whole argument, in one table

A refusal with a reason beats a success that lied.

Software that cannot complete an operation has exactly three options. It can crash, which is loud and useless. It can return something plausible, which is quiet and much worse, because the damage surfaces weeks later in a reconciliation nobody scheduled. Or it can refuse deliberately, in a shape a caller can act on.

Every one of these is the third option. They are string literals in the files named in the last column, mapped to real HTTP statuses, and none of them leaks anything about a person — a reason code is a fixed enum, not a message about a family. Read the table and you have read the product.

Every machine reason code a billing operation can refuse with, the HTTP status it maps to, and the file it is defined in.
StatusReason codeWhat it meansWhere it lives
503seam_unavailableNo payment provider key. The portal session and the subscription mint refuse instead of fabricating one.apps/api/src/routes/billing-admin.ts
503ach_rail_not_enabledLive bank-debit activation is founder-gated and the flag is off by default. Held, not silently skipped.apps/api/src/routes/billing-admin.ts
503payments_honest_offNo usable payment key, or a live key still held by the go-live switch, so the layer runs its offline stand-in whose transfer ids exist only in process memory. The payout is refused before it mutates anything, because marking a statement paid off those ids would be a settled payout with no money behind it.apps/api/src/services/payout-rail-guard.ts
409rep_payout_account_pendingThe destination account cannot receive payouts. The payout is HELD; no transfer fires; nothing is faked.apps/api/src/routes/org-statements.ts
409payout_amount_divergentA retry derived a different amount than the one pinned at finalize. Refused rather than silently re-derived.apps/api/src/routes/org-statements.ts
409statement_not_finalizedPay was called on a draft. The state machine refuses out of order rather than skipping the finalize pin.apps/api/src/routes/org-statements.ts
409recovery_invoice_unissuedA studio-funded commission has no recovery invoice yet. The platform holds rather than fronting the money.apps/api/src/routes/org-statements.ts
409prepay_rail_disabledA credit sourced from a real payment was attempted. The store refuses; the rail constant is false.apps/api/src/routes/prepay.ts
401claim_requiredA guardian read arrived with no verified claim. Fail-closed, and no enumeration signal either way.apps/api/src/routes/prepay.ts
422invalid_auto_draft_scheduleA proposed installment schedule did not reconcile. The engine verdict is returned, not swallowed.apps/api/src/routes/tuition-collections.ts

Scope bound, stated once and meant literally: these are measurements of the source on the canonical branch, and of the production call sites in it. They are not measurements of a running process. Nothing on this page says a capability is live in production, because no running process was probed to write it.

Nine capabilities, four statuses — because two is how a hollow surface passes as a working one

Built, honest-off, no data layer, inert. Every card cites the file it came from.

Most status vocabularies have two words in them, and the second word is doing far too much work. Built here means reachable, persisting, and doing its whole job with no payment key set. Honest-off means complete and refusing on purpose until a key or a founder flag is set. No data layer means correctly gated, validating through a real engine, and storing nothing. Inert means written, tested, and called by nobody. The last two are not features and this page never counts them as any.

Past-due state and the way back

Dunning as a state read, not an email cadence — and the recover path is never locked behind the gate it clears

The dunning endpoint is a pure read over the billing state machine: it resolves the account’s current standing (past due, suspended, cancelled) and hands back the recover action the interface should offer. It has no payment-provider dependency, which is why it does its whole job with no key set at all — this is the surface that works on day one. One design decision inside it is worth reading the file for. The billing-management routes are deliberately NOT put behind the billing gate, and the file says why in its own words: a past-due school must be able to reach the portal to fix its payment, and gating the recover path behind the very gate it clears would trap the payer. Most billing suites do the opposite — they lock the account and then ask you to sign in to pay.

Source: apps/api/src/routes/billing-admin.ts → resolveDunningState
apps/api/src/routes/billing-admin.ts → requireBillingAdmin

Built · works with no payment key set

Commission statements and payouts

A payout that cannot settle is HELD, with a reason code — never a fabricated settlement

Statements move draft → finalized → paid through a one-winner compare-and-set, and the amount is PINNED at finalize rather than re-derived at pay time. A pre-existing payout whose pinned amount diverges from the statement total is a 409 payout_amount_divergent — never a silent re-derive under the same idempotency wall. The part that matters most is what happens when the destination is not ready. A representative with no payouts-enabled connected account does not get a fake success and does not get a transfer to nowhere: the request returns 409 rep_payout_account_pending, the statement stays finalized, the payout row stays finalized, and no transfer fires. The held payout drains later, through the same row and the same deterministic transfer key, so onboarding the rep resolves it without a double-pay. A studio-funded payout is held the same way until its recovery invoice is paid — the platform never fronts an unrecovered commission. And a rep never finalizes or pays their own statement; separation of duties is checked before anything else. The same rule binds the payment rail itself, and this is the one we had to go back and fix. With no usable payment key — or a live key present while the go-live switch is still off — the layer runs its offline stand-in, whose transfer ids exist only in process memory. Marking a statement paid off those ids would be a settled payout with no money behind it, so the route now refuses with a 503 before it mutates anything: the statement stays finalized, the amount stays pinned, no payout row is created, and no audit record claims a transfer. It is the identical hold to the one above, applied to the rail instead of the destination, and it drains the same way once a key is set.

Source: apps/api/src/routes/org-statements.ts → rep_payout_account_pending
apps/api/src/routes/org-statements.ts → payout_amount_divergent
apps/api/src/routes/org-statements.ts → assertPayoutRailCanSettle
apps/api/src/services/payout-rail-guard.ts → PAYOUT_HONEST_OFF_CODE

Built · with no key set, the payout is refused before it mutates — never marked paid

Installment plans

Plan and schedule that reconcile to the penny, behind an idempotency wall

A plan is materialized against the order’s server-authoritative total — never a number the client sent — and the schedule’s integer cents sum to that total exactly, with the rounding remainder front-loaded so nobody is asked to pay a fractional cent. The plan id is deterministic in the order id, so one order has exactly one plan: a re-initiate returns the same plan instead of minting a second. On top of that it carries the same Idempotency-Key claim/commit/fail discipline order creation uses. The gates run in the right order: a minor is denied commerce entirely, only a finance role or admin may initiate financing, and a suspended or past-due school gets a 402 BEFORE the idempotency key is claimed — so a gated caller never burns a key. What it deliberately does NOT do: charge. This route owns the plan and the schedule; the charge rides the order’s payment intent, and settlement stays processing until the webhook confirms. The storefront never reports paid off a freshly created plan.

Source: apps/api/src/routes/installments.ts → requireIdempotencyKey
apps/api/src/routes/installments.ts → requireBillingActive

Built · plan and schedule persist; the charge is a separate, honest-off path

Prepay credits

Comp credits that really mint and really audit — and a paid credit that is structurally unreachable

A platform administrator can mint a comp credit for a specific student, and it is a real write to a real table with a real migration behind it, plus exactly one audit row per grant attributing the comp to the administrator who made it. The role gate is strict: an account manager is denied outright, and even an eligible administrator must have deliberately elected admin scope first. The guardian side is claim-token gated. A blank token is a 401 claim_required, and the store re-verifies the claim INSIDE the query that lists the credits — so the route cannot bypass the wall even by accident. The result is a display-only quote of what the credits would cover: nothing is redeemed, nothing is charged, and the charged amount is structurally zero. A credit sourced from a real payment cannot be minted at all. The rail flag is a compile-time constant set to false, the store refuses the mint, and the route maps that refusal to a 409 prepay_rail_disabled. A paid credit is not merely disabled — it is unreachable.

Source: apps/api/src/routes/prepay.ts → mintPrepayCredit
packages/db/prisma/migrations/0957_photo_prepay_credit

Built · comp grants persist and audit; the paid rail is a hard-off constant

Bank debit, disclosed before it is charged

Store the bank-debit method and disclose a zero fee — then refuse to debit until a founder flips it

Selecting a bank-debit method stores it and returns the fee disclosure up front, at zero. What it does not do is debit: the stored method comes back with status queued_not_charged and railProvisioned:false, because there is no live bank rail behind it. No account or routing number is ever accepted — only a display last-four and an opaque mandate reference. The separate activation route is founder-gated and returns 503 ach_rail_not_enabled while the flag is off, which is the default. Flipping that flag lifts the 503 and still does not move money, because no rail is provisioned. Both halves are built; neither is switched on.

Source: apps/api/src/routes/billing-admin.ts → ach_rail_not_enabled
apps/api/src/routes/billing-admin.ts → queued_not_charged

Honest-off · stored and disclosed, never debited; activation is founder-gated

Hosted portal and invoice list

The provider-hosted billing portal and the invoice list — complete, and refusing until a key exists

Both routes are written, role-gated, and audited. With no provider key set, the portal-session route refuses with a 503 carrying the machine code seam_unavailable and the message “billing portal unavailable” — never a crash, and never a fabricated session URL pointing somewhere that does not exist. The invoice list degrades to an empty list rather than inventing a charge. That distinction is the whole discipline: an empty invoice list is a true statement about a school with no invoices, so it is allowed to be a 200; a portal URL is either real or it is a lie, so its absence has to be a refusal.

Source: apps/api/src/routes/billing-admin.ts → seam_unavailable
apps/api/src/routes/billing-admin.ts → createBillingPortalSession

Honest-off · 503 seam_unavailable with no key; never a fabricated session

License subscriptions

Subscription lifecycle, built end to end, refusing at the mint

The subscription routes are complete and carry their own minor-wall security test. Without a provider key the payments port has no subscription creation method at all, so the service throws a structured seam_unavailable and the route maps it to a 503 — the mint refuses rather than pretending. Cancellation still flips the local row, because that is a real local state change that does not require a provider. Set a test key and the same routes mint against test mode with no code change. Nothing about going live is a rewrite; it is two configuration values, and the page says which two.

Source: apps/api/src/routes/subscriptions.ts → SubscriptionError
apps/api/src/routes/subscriptions.ts → seam_unavailable

Honest-off · 503 at mint with no key; local cancel still applies

Tuition collections console

Reachable, correctly gated, validating through a real engine — and storing nothing at all

This is the one to read carefully, because it is the one that would be easiest to oversell. The console is real in every way except the way that counts. The tenant wall holds, the finance entitlement is a 402 module-access gate, the finance-officer role check is fail-closed and builds its actor entirely from server-resolved context, and the write path binds the shipped installment engine so a schedule that does not reconcile is a genuine 422 rather than a silent accept. And then nothing is written. The three read endpoints return hard-coded empty structures. The write endpoint validates, succeeds, and honestly answers recorded: false with an empty schedule reference, because there is no family-account ledger, no installment-slot table, and no aging table to write to. The route file states this in its own header: no persistence in this kit. We are not going to describe this as tuition billing. It is a correctly built front half waiting on a back half, and that is exactly what it will be called until the tables exist.

Source: apps/api/src/routes/tuition-collections.ts → recorded: false
apps/api/src/routes/tuition-collections.ts → NO PERSISTENCE IN THIS KIT

No data layer · gated and reachable, stores nothing — not a working capability

Tuition billing engine core

Four hundred and sixty-nine lines of correct pure math with nobody calling it

The tuition billing engine — per-period billing, discount application, fee handling before the split, plan subtotals — is written and unit-tested as a pure module. It has zero callers anywhere in the application tree. Its only consumer is a sibling pure module whose own exports also have no application callers; both are reachable only as re-exports from the shared barrel. The measurement was run with a positive control in the same invocation, so the zero is a provable absence rather than a grep that quietly failed. It is listed here because leaving it off the page would be the dishonest choice. When it is wired to a route, it moves up this list and this card says so. Until then it is inert, and inert is not a feature.

Source: packages/shared/src/tuition-billing-engine-core.ts → billOnce
packages/shared/src/tuition-billing-engine-core.ts → planSubtotalCents

Inert · zero production callers — not a working capability

Named comparisons — because a comparison you cannot check is not a comparison

Stripe Billing, Bill.com, Chargebee, Recurly, QuickBooks, Sage, Skyward. Named, and each beaten on a mechanism — except one.

Marketing convention says you describe the incumbents as “legacy tools” and never write a name down, so nobody can check the claim. We are going to name them, say concretely what they do well, say concretely how we beat them, and say where we do not. One of the five rows below is a row we lose, and it stays in.

Stripe Billing

Subscription and invoice billing

How the category works
Excellent software, and the fastest way to bill if you are already all-in on one processor. It takes a percentage of your recurring revenue on top of the processing fee, and the billing model and the payment rail are the same company by design.
How we beat it
Two ways, both structural. We take no percentage of your billing volume — the license is the price. And the processor sits behind a payments port that every route talks to through an interface, so it is an adapter, not the architecture. Swapping or adding a provider is an adapter, not a migration of your billing model.
Where we do not
Their invoice rendering, tax handling, and revenue recognition are far ahead of ours today. We are not claiming parity on the accounting surface.

Bill.com

AP / AR automation

How the category works
Payables and receivables workflow priced per user, with per-transaction fees on top. Collections are approval chains and email cadences layered over an invoice table.
How we beat it
Past-due handling is a state read, not a mail merge: one call returns the account’s standing and the recover action, with no provider dependency, so it answers correctly even with the payment rail entirely off. And the recover path is deliberately not put behind the billing gate — a past-due payer can always reach the surface that fixes the payment. Locking the account and then asking the payer to sign in to pay is the trap we refused to build.
Where we do not
They own a real accounts-payable side. We do not have one, and this page is not going to imply we do.

Chargebee and Recurly

Subscription lifecycle and dunning

How the category works
Deep subscription lifecycle tooling with retry schedules and dunning campaigns, priced on a percentage of billed revenue above a floor.
How we beat it
Our failure behaviour is the product. Every refusal carries a fixed machine reason code the caller can branch on — ten of them, listed on this page with the file each one lives in. A dunning campaign tells your customer something went wrong; a reason code tells your engineer exactly what, and tells your auditor that nothing was fabricated in the meantime.
Where we do not
Their retry intelligence and revenue analytics are a category we have not entered.

QuickBooks and Sage

General-ledger accounting with invoicing

How the category works
A general ledger first, with invoicing as one module of many. The subject of a record is a customer, and every user of the system is an adult employee of the business.
How we beat it
In a school the payer is a guardian and the subject is a minor, and a general ledger has no concept of either. Ours does, at the route: minors are denied commerce and financial data before any handler body runs, and a guardian’s read is gated on a verified claim that the data store re-checks inside the query itself — so the route cannot bypass the wall even by mistake. Account managers are denied the payer surfaces entirely, and a representative can never finalize or pay their own commission statement.
Where we do not
They are accounting systems and we are not one. Nothing here replaces a general ledger, and we will hand you clean data to put into theirs.

Skyward

School information system with a fee and tuition module

How the category works
Student fees and tuition receivables bolted to the student information system, sold and implemented per district, with a real persistence layer behind the console.
How we beat it
Nowhere yet, and this is the one place on this page where the honest answer is that they are ahead. Our tuition collections console is correctly gated and validates through a real engine, and it stores nothing at all. Their module has tables. Ours does not.
Where we do not
Stated plainly because a comparison table that never loses a row is marketing, not a comparison. When the ledger, slot, and aging tables land, this row changes and the page will say when.

These companies are named here once each, factually, as category references for the billing market this product competes in. None of their code, copy, branding, or data is used anywhere in this product. The characterizations above are our reading of how each category works as of this writing, not a tested claim about any specific plan or contract, and any of them may ship something tomorrow that changes a row. Verify pricing and capabilities with the vendor before you decide anything on the strength of a comparison table, including this one.

What is not built — named on our own front page, in plain words

Two things here do not work. Here they are.

The tuition collections console has no persistence layer. It is reachable. Its tenant wall holds, its finance entitlement is a real module-access gate, its role check is fail-closed and builds the acting identity entirely from server-resolved context, and its write path binds the shipped installment engine so a schedule that does not reconcile is a genuine validation failure rather than a silent accept. And then nothing is stored. The three read endpoints return hard-coded empty structures. The write endpoint validates, succeeds, and honestly reports that nothing was recorded, because there is no family-account ledger, no installment-slot table, and no aging table for it to write to. The route file says so in its own header. We are not going to call this tuition billing, and if a demo ever appears to show it working, ask what table the row landed in.

The tuition billing engine has zero callers. Four hundred and sixty-nine lines of pure billing math — period billing, discounts, fees before the split, plan subtotals — correct, tested, and reached by nothing in the application tree. Its only consumer is a sibling pure module whose own exports are equally uncalled. Both are visible only as re-exports from the shared barrel, which is exactly the shape that makes an inert module look wired to a careless search. It is listed on this page because a capability inventory that quietly omits the unwired parts is not an inventory.

Neither of these is a bug and neither is an accident. They are work in a real order: the gates and the engines first, the tables next. What would be dishonest is the alternative — letting a well-gated, well-written, entirely hollow surface stand in a feature list next to things that actually persist. That has happened elsewhere in this fleet and it is the specific failure this page is built to refuse.

Money posture — two settings, both off, and neither one is enough on its own

Nothing on this stack can charge a card today, and it takes two deliberate acts to change that.

The payment provider key defaults to an empty string, and an empty key selects a fake payments service — so with a stock configuration there is no real processor behind any route on this stack. That alone would be the usual arrangement, and it is not sufficient, because a key can be pasted into an environment by accident.

So there is a second, independent switch that also defaults off, and it is the one that decides whether a live key is honoured at all. With that switch off, a live key is held in the fake service in every environment — the configuration file states the rule in its own comment: a live key alone never moves real money. Going live is both settings, together, changed on purpose by the founder. It is not a deploy, it is not a merge, and it is not something an engineer can do by forgetting something.

The consequences run right through the page above. Portal sessions refuse. Subscription mints refuse. Bank debits are stored and disclosed and never debited. Booking and tuition fees compute as reserved lines with a charged amount of zero. Prepay credits sourced from a real payment are unreachable because the store’s rail constant is false, not merely because a route declines. This is what “honest-off” means in practice, and it is why every card that touches capture on this page carries that label rather than a green one.

Who it is for — three payers, one ledger discipline

Schools, districts, and the studios that bill through them.

A school business office

You need to know what is past due, what the recover path is, and that nobody in the building can see a family’s financial record who should not. The past-due read answers the first two with no payment provider involved at all. The third is enforced before any handler body runs: minors are denied commerce and financial data at the route, account managers are denied the payer surfaces entirely, and a guardian’s own view requires a verified claim the data store re-checks inside its own query.

A district finance team

You need separation of duties that is real rather than procedural, and an audit trail that exists whether or not anyone remembered to turn it on. Every money-adjacent write here leaves an audit row attributing the act to the person who performed it, and the state machines refuse out-of-order transitions with named codes rather than best-effort guesses. A statement cannot be paid before it is finalized. An amount cannot drift between finalize and pay.

A studio billing through schools

You need commission statements that pin an amount and payouts that never invent a settlement. A representative whose connected account is not ready has their payout held, not failed and not faked, and it drains through the same row and the same transfer key once onboarding completes. A studio-funded commission is held until its recovery invoice is paid, because the platform does not front money it has not recovered. And no representative ever finalizes or pays their own statement.

Pricing — the planned launch tiers, display-only. Not a live checkout.

A license, not a percentage of your money.

The billing platforms in the comparison above are largely priced as a share of what flows through them, which means the better your year is, the more the tooling costs you for doing nothing different. We price the software. Your billing volume is not our revenue model, and there is no commission skim on any transaction that rides this stack. These figures are the plan. There is no checkout on this site, no card is charged here, and nothing below is a purchase option today.

School

Single school

$900 – $2,400 / yr

Per school · planned

  • Past-due state and recover path
  • Installment plan and schedule modelling
  • Prepay credits, granted and audited
  • Full audit trail on every money-adjacent write
  • No percentage of billing volume

Enterprise

Enterprise and white-label

Custom

Self-host · licensable · planned

  • Self-hosted or managed
  • Your own payment provider adapter
  • White-label per-tenant licensing
  • Support agreement

Display-only. No money changes hands through this site, and the payment rail on this stack is off behind two independent settings. What a license actually costs for your operation is a conversation, not a form.

FAQ

Common questions

Can bills.software charge a card today?

No, and it takes two deliberate changes rather than one. The payment key defaults to empty, and an empty key selects the fake payments service. Separately, a live-mode switch defaults to false, and the configuration file states in its own comment that a live key alone never moves real money in any environment while that switch is off. Both would have to be set, together, by the founder. Until then every capture path on this stack refuses with a machine reason code, which is exactly what the refusal board on this page is showing you.

Then what can we actually use on day one?

Four things, and they do not need a payment key at all. The past-due state read and its recover action. Installment plan and schedule modelling, with the idempotency wall, the audit row, the minor wall, and the billing gate. Prepay comp credits, which really mint, really persist against their own migration, and really write one audit row each, plus the claim-gated guardian view of them. And commission statements with finalize, the pinned amount, and the fail-closed payout hold. Everything else on this page is labelled honest-off, no-data-layer, or inert, and those labels are the point rather than a disclaimer.

What does “honest-off” mean, precisely?

Built, reachable, tested, and refusing on purpose. An honest-off route is not a stub and not a placeholder: it runs its gates, resolves its context, calls its service, and the service declines because a key or a flag is absent. The refusal is structured, carries a fixed machine code, and is mapped to a real HTTP status. The alternative — returning a plausible-looking success — is the specific behaviour this whole product is organized against.

Why is a hollow console listed on your own marketing page?

Because the failure mode we most want to avoid is shipping something real-looking with nothing behind it, and the only reliable defence is naming it out loud where it is uncomfortable. The tuition collections console is gated correctly and stores nothing. The tuition billing engine has zero callers. Both are on the page, in their own band, labelled so that no reader could mistake them for working capabilities. If either changes, this page changes with it.

How do you know the engine has zero callers? Absence is easy to get wrong.

By carrying a positive control in the same invocation. A search that finds nothing proves nothing on its own — it could be a broken pattern, a wrong root, or a tool silently skipping a file. So the same search, in the same run, was pointed at a symbol known to be called from application code, and it returned hits. The zero for the engine symbols is therefore an absence that was measured, not one that was assumed.

Is any of this artificial intelligence?

No. There is no model, no generation, and no inference anywhere in this stack, and no AI claim is made anywhere on this page. Billing is arithmetic, state machines, and refusals, and all three should be boringly deterministic.

Do you hold SOC 2, PCI, FERPA, or COPPA certification?

No, and none is claimed on this page. What is described is posture, which is a different thing and a checkable one: full card numbers and bank routing numbers are never accepted, only a display last-four and an opaque reference; minors are denied commerce and financial data at the route; a guardian read requires a verified claim the store re-checks inside its own query; and every money-adjacent write leaves an audit row.

How does a held payout eventually settle?

Through the same row. The amount was pinned at finalize, the payout row exists in a finalized state, and the transfer key is deterministic, so once the destination account is able to receive payouts the same operation drains it without any risk of paying twice. Holding is not a dead end and it is not a manual cleanup task; it is a resumable state.

What happens to my data if we stop using it?

It is your ledger and it leaves with you. The stack is ours end to end, without a third-party billing product sitting in the middle owning the record of what you charged, so there is no vendor in the export path with its own opinion about your data. Ask about export format in the conversation and we will show you the shape before you commit to anything.

Can we see it?

Yes, and the walkthrough starts with the refusal board rather than the happy path. We will show you what each code does, what is written to the audit log, what a held payout looks like from the administrator’s side, and the two places where there is nothing behind the console yet. There is no checkout on this site and no card is charged here; the next step is a conversation.

Early access · schools · districts · studios

Start with the part that breaks.

Most billing demonstrations walk the happy path, which is the least informative twenty minutes available. Ours starts at the refusal board. We will show you what each reason code does, what lands in the audit log, what a held payout looks like from the administrator’s side, how the amount stays pinned across a retry, and the two places on this page where there is nothing behind the console yet. Then you decide whether the parts that are built are worth having on their own, because they have to be.

There is no checkout on this site and no card is charged here. Bring your actual awkward case — the family on a plan who moved districts, the rep who never finished onboarding, the refund that has to reconcile against a statement already finalized — and we will walk it through what exists rather than what is planned.