MyBooks.software · general ledger and fund accounting for school business offices · early access

Your books. Your name on them. And a posting rule you can read in full.

The month a number has to be explained to a board is the month you find out whose books these really are. If the explanation depends on somebody who knows where the adjusting entries were made, and that somebody is not in the room, they are not really yours.

So the rule that decides what goes into these books is written to be read. It checks that every line is a positive exact-cents amount, that every account exists, that the currency matches, and that debits equal credits — and if any of those fails it refuses with one of five named reasons and writes nothing. No override, no suspense account, no warning state that lets an unbalanced entry sit in the books until somebody notices at close. It is a pure function with no database and no clock inside it, about thirty lines long, and a business manager can read the whole thing in one sitting and then say exactly what it will refuse.

What is stored, and it is worth leading with rather than burying: a chart of accounts you register once, journal entries that stick, and a trial balance read back out of those stored entries — entity-wide or for one fund, because the fund is a real dimension on the account rather than a tag you remember to apply. Alongside that, a per-club activity funds ledger that persists, and spend-approval runs whose three sign-offs must be three different people. Underneath all of it, integer cents everywhere, so nobody spends an evening in June hunting eleven of them.

And what CALCULATES without storing, said in the same breath rather than four screens later: period close, budget-to-actual, payables and receivables aging, bank reconciliation, the 1099 worksheet, and payroll gross-to-net. Those are eight-plus endpoints of correct, tested arithmetic that you feed on each request and that keep no record between calls. Genuinely useful, genuinely not a set of books yet, and every card below carries which of the two it is on its face.

Three more honest notes before you read further, because they belong at the top rather than in a footnote. MyBooks does not move money — there is no pay, expend, or disburse endpoint anywhere in it, and that is structural rather than a switch. There are three capabilities written but not yet wired: the school purchasing chain, four of the five budget lifecycle verbs, and the bridge that would post payroll into the ledger. All three are named on their own cards below, with the measurement that established it. And the name is a claim about being able to read the rules, not a claim about data export — we have not built one, so we do not mention one.

The posting rule, in the order it actually runs

Four checks. Any one of them fails and nothing is written.

The board beside the headline is an illustration, not a reading from any system — there are no real balances on this page and no customer numbers behind it. What it shows is the shape of the outcome: two columns that foot to the same integer, or a refusal with a name on it.

Here is the thing that makes this different from a validation message. The check is not a screen in front of the ledger, which is where most systems put it and which is why most systems can be worked around by an import, an API call, or an adjusting entry made by somebody with enough permissions. It is the posting function itself. There is no second door into the ledger that skips it, because there is no code path that writes an entry without going through it first.

  1. 1 — Every line is a real amount

    Each line must carry a positive, exact, integer number of cents. Zero fails. A negative fails — you express a reduction by putting the amount on the other side, which is what double-entry is for. A fractional cent cannot be represented at all, so it cannot be entered and then silently rounded. The rejection reason is invalid_amount.

  2. 2 — Every account exists, in the right currency

    Each line names an account that must already be in the chart of accounts, and that account’s currency must match the entry’s. An account that was deleted, renamed, or never created does not get created implicitly to make the posting succeed. The rejection reasons are unknown_account and currency_mismatch.

  3. 3 — Debits equal credits, exactly

    The two totals are compared as integers. Not within a tolerance, not rounded to the nearest cent first, not with a rounding line auto-inserted to make them agree. If they differ by one cent the entry is refused. The rejection reason is not_balanced, and nothing is written.

  4. 4 — Only then does anything post

    On success the caller gets the entry back along with the balanced total. The rule itself touches no database, which is what lets it be read and tested on its own — and it also means the guarantee has to be made one layer down, at the write. It is. There is exactly one piece of code that inserts an entry into the database, the insert sits inside a function that runs these four checks first, and on a refusal that function returns before the insert is reached. Zero rows, not a row written and then withdrawn. Every downstream number is folded from entries that passed all four checks, and the fold defensively skips anything that somehow did not. Two of those downstream numbers read from the stored books; the rest are computed from values you send with the request, and the cards above say which is which rather than leaving you to assume.

What is built, what is not switched on, and how we know

Every card names the file it was read in.

A capability list is worth nothing if the labels are marketing. So these four labels have exact meanings, and each card carries the source file the claim was read in rather than asking you to take it on trust. Built means the function is in the engine and a real application route calls it. API only means it is built and persisted and no screen renders it. Built, not switched on means the function is written and tested and nothing calls it — that is a real gap, and there are 2 of them below. Honest-off means deliberately not connected, and not connectable without new code being written. Of the 12 capabilities here, 5 are built and reached; the rest carry a weaker label on purpose.

Reach of 12 capabilities: all 12 are in the engine, 9 are reached by a production call site, and 6 persist anything through a store. A filled dot is a rung reached; a dashed ring is a rung not reached. Each capability's exact label is on its card below.FILLED = REACHED · DASHED RING = NOT REACHEDIN THE ENGINEREACHED BY A ROUTEPERSISTS TO A STOREThe rule the whole ledger is built onWhy the pennies stay putThe thing general bookkeeping cannot doMonth endCommitted money, before it is spentStated plainly, because it would be easy to h...The other gap, and the bigger onePayables and receivablesThe unglamorous half of the jobPayroll, and exactly how far it goesRestricted moneyClub and activity money
Figure 1 How far each capability gets, read straight off the label on its card. Every one of them is in the engine. Fewer are reached by a production call site. Fewer still persist anything through a store, and that last column is the difference between a calculator and a set of books. A capability relabelled on its card moves in this figure in the same edit, because both are drawn from the same list.

The rule the whole ledger is built on

An entry that does not balance does not post. There is no second path.

The posting function walks every line of a journal entry once. A line whose amount is not a positive exact-cents integer fails. A line pointing at an account that does not exist in the chart fails. A line in a currency the entry is not denominated in fails. Then it compares the debit total to the credit total, and if those two numbers are not identical it returns “not_balanced” and writes nothing at all. There is no override, no suspense account it quietly parks the difference in, and no warning state that lets an unbalanced entry sit in the books until somebody notices at close. Every rejection is a named machine reason a caller can branch on, not a string a human has to read: no_lines, invalid_amount, unknown_account, currency_mismatch, not_balanced. The whole rule is about thirty lines of arithmetic with no database, no clock, and no network call in it, which is why it can be read in full by a business manager who wants to know exactly what the software will and will not accept. And because the rule itself touches no database, the place it matters is the write path, so here is that path exactly. There is one piece of code in the whole system that inserts a journal entry into the database, and the insert sits INSIDE a function that runs the posting rule first and returns before reaching the insert if the rule refused. A rejected entry does not write a row and then get cleaned up; it writes zero rows, because the write is downstream of the rule rather than beside it. That is the one claim on this page we will not soften.

Built and persisted · the only write path runs the rule first

Read in back-office-general-ledger.ts:208 · the not_balanced return at :234 · the sole insert path at finance-gl-repo.ts:309, :336

Why the pennies stay put

Exact integer cents, end to end. No floating-point dollar anywhere.

Every amount in this ledger is an integer number of cents. Not a dollar figure with two decimal places, which is a floating-point number wearing a costume and which will, over a year of thousands of postings, quietly invent or destroy money in the last digit. A line for four hundred and ninety-nine cents is four hundred and ninety-nine cents when it posts, when it folds into an account balance, when it rolls into a trial balance, and when it closes. This is not a preference. Rounding drift is not a small bug in a ledger — it is the bug, the one that turns a clean reconciliation into three hours of hunting for eleven cents — so the type system refuses the shape that causes it rather than correcting for it later.

Built · enforced in the posting guard

Read in back-office-general-ledger.ts:218 (positive exact-cents guard)

The thing general bookkeeping cannot do

A fund is a real dimension, not a tag you remember to apply

Schools do not have one pile of money. They have a general fund, a food-service fund, a capital-projects fund, restricted grant money that may only be spent on the thing it was given for, and activity money that belongs to a club rather than to the district. A trial balance can be pulled for the whole entity or for a single fund, from the same posted entries, because the fund is carried on the transaction rather than reconstructed from a label afterwards. Fund balance is classified the way governmental accounting actually asks for it — nonspendable, restricted, committed, assigned, unassigned — rather than collapsed into one equity line that a board has to take on faith. To be exact about what that sentence claims: we implement the classification. We are not certified by anyone, no auditor has issued an opinion on this software, and this page does not imply otherwise.

Built · per-fund trial balance and fund-balance classification reached

Read in finance-trial-balance.ts:136 · finance-fund-compliance.ts:177

Month end

Trial balance over the stored books. Period close and budget-to-actual are calculators.

The trial balance has both halves, and the difference between them is worth knowing before you buy. Over the STORED books it is a real report: the persisted route reads the chart and the posted entries back out of the database and folds the trial balance from them, entity-wide or for one fund. And the account-balance fold defensively skips any entry whose debits and credits do not match, so even a row corrupted by something outside this engine cannot push a wrong number into a balance. Period close and budget-to-actual are a different thing, and calling them the same thing is what this card used to do. Both are CALCULATORS: you send the chart, the entries, and the budgets in the request, and you get the arithmetic back. Period close does not close anything in a database — it returns the balanced closing entries for you to post. Budget-to-actual ties a budget you supply against actuals you supply. The arithmetic is right and it is tested. Nothing about it is stored, and there is no month-end state anywhere that remembers you ran it.

Trial balance persists · period close and budget-to-actual store nothing

Read in finance-trial-balance.ts:106 · finance-period-close.ts:169 · persisted read via finance-gl-repo.ts readTrialBalance · stateless via finance-ledger.ts:25-31

Committed money, before it is spent

Over-budget is blocked when the commitment is made, not discovered at close

Budget authority, minus what is already encumbered, minus what has already been expended, is the available balance. When an approval encumbers against a budget, the check is fail-closed: a commitment that would push the account past its authority is refused with a named reason rather than recorded and reconciled later. This one has the most real machinery behind it of anything on the page, so here is what is actually durable. The APPROVAL RUN is stored: a spend request needs three separate sign-offs — department, admin, and budget owner — they must be three different people and none of them may be the person who filed the request, and that is enforced in the engine rather than by a settings screen. Every step reloads the request from the database and drives the decision from the stored row, so a client cannot forge somebody else’s sign-off or replay a stale state. When there is no database the transition fails closed instead of trusting the client. The encumbrance itself is appended to an audit table. What is NOT stored is the budget. You send the budget on the encumbrance call and we hand back the new encumbered budget for you to save wherever you keep it. So the over-budget refusal is real arithmetic against the authority you supplied, and the running balance between calls is yours to hold, not ours. And the shape of the lifecycle matters. All five budget event kinds — appropriate, deappropriate, encumber, disencumber, expend — are modelled, are accepted as caller-supplied events, and are folded correctly. But only encumbrance is ever constructed by the server. The other four are foldable, not driven: nothing in the running system today raises an appropriation, releases an encumbrance, or books an expenditure on its own. That is a real gap and it is written on the card below, not buried.

Approval run persists · the budget is caller-held; encumbrance is server-driven

Read in back-office-general-ledger.ts:398, :491 · mosaic-budget-approval.ts:464 (durable instance + three-party SoD) · the caller-owned budget at its header :39-42

Stated plainly, because it would be easy to hide

Appropriate, disencumber, and expend are written and tested — and nothing calls them

The four functions exist in the engine: appropriate, deappropriate, disencumber, expend. They are unit-tested. Searching the entire application tree for a call to any of them returns nothing — and that search was run in the same pass in which the searches for postJournalEntry, encumber, and the finance authorization gate all returned dozens of hits, so the zero is a real absence rather than a search that was pointed at the wrong place. What this means in practice: the budget lifecycle is complete as arithmetic and incomplete as a workflow. A school could supply those events today through the API and the balances would fold correctly. No screen in the product constructs one. Calling this “built” would be a true statement with the scope taken off it, so we do not.

Built, not switched on · zero production callers, measured

Read in back-office-general-ledger.ts:449, :467, :513, :536

The other gap, and the bigger one

The school purchasing and requisition chain is written and is not wired

There is a separation-of-duties purchasing chain in the codebase: record a fund transaction, request a purchase, approve a purchase, receive a purchase. Four functions, written, tested, and with zero production callers between them — measured the same way, in the same pass, against the same positive controls. The module they live in is not dead, which is why this needs saying carefully. Its READ half is genuinely live: school fund balances and budget variance are computed from it and are reached by the finance command centre today. Its WRITE half is not. Nothing in the running system files a requisition through this chain or drives one through approval and receipt. So: do not buy MyBooks today for school purchasing workflow. Buy it for the books underneath, and hold us to a date for the rest.

Built, not switched on · write half has zero callers; read half is live

Read in sch-fund-accounting.ts:230, :349, :412, :485

Payables and receivables

Aging and never-pay-unapproved — correct arithmetic, and no invoice store behind it

Send a set of invoices and a date and you get back each one’s outstanding balance, its aging bucket, the approved-payable versus unapproved-held split, and the total outstanding and overdue on the receivable side. All exact cents, no clock of its own — you supply the as-of date, so the same input always gives the same answer. Never-pay-unapproved is a real function and it does refuse: an unapproved payable returns blocked_not_approved and a zero payable amount, and there is no setting that turns that off because it is not a setting. Here is the scope on it, which the earlier version of this card left off. The function reads an approved flag on the invoice you send it. There is no invoice table in this product — not a thin one, none — so it is not verifying your approval against a stored record of who approved what and when. It refuses to call an unapproved invoice payable. It cannot tell you whether the approval you asserted actually happened. That is a genuine control at the arithmetic layer and it is not a substitute for an approval system of record, and we would rather you know which one you are buying.

Calc reached · no invoice store; approval is an input, not a stored fact

Read in back-office-ap-ar.ts canPay:153 (branches on inv.approved:163) · caller back-office-erp.ts:226 CanPayBody · :247 aging · no *invoice* store in apps/api/src/store

The unglamorous half of the job

Bank reconciliation and a 1099 vendor year-to-date worksheet — both run per request

Bank reconciliation produces an audit record rather than a checkbox: send the book entries and the bank statement lines and it matches them deterministically, oldest first, each line matched once, then reports what matched, what is outstanding on each side, and the remaining difference. It passes only on an exact zero difference with every amount an integer. The 1099 worksheet sums vendor year-to-date reportable payments per box, flags who crosses the reporting floor, and lists the excluded rows with reasons. It emits no form and files nothing. Both are per-request calculations and neither keeps a record. There is no reconciliation you can come back to next month and no stored vendor year-to-date that accumulates as you go — you send the year’s payments each time you want the total. That makes these two a strong worksheet replacement and not yet a filing system, and January is still a review you drive.

Calc reached · nothing stored between requests

Read in cbuild-bank-reconciliation.ts · cbuild-1099-vendor-ytd-accumulation.ts · callers back-office-erp.ts:277, :292 (both stateless, per :61-66)

Payroll, and exactly how far it goes

Gross-to-net is computed. Nothing is disbursed, and nothing can be.

The payroll engine computes gross-to-net and builds a pay stub: withholding, contributions, deductions, net pay, all in exact cents. Eight endpoints sit on it — preview, taxable basis, federal withholding, FICA, stub compute, a NACHA file preview, filing reconciliation, and a four-eyes approval decision. It never pays anybody. The disbursement check is called against a partner that is not wired, so the verdict it returns is always “honest off, no partner” and the disburses flag it hands back is always false. That is not a configuration left switched off — there is no wired partner to switch on, and the route’s own test asserts the false. The eight are also stateless: you send the earnings and deduction lines each time and no pay run is stored, so there is no payroll history here to look back at. Read this as a calculation and preview surface that a payroll bureau’s numbers can be checked against, not as a replacement for the bureau. We say more about that below, under the heading where we lose.

Calc built · disbursement structurally off, not flag-off

Read in back-office-payroll-calc.ts:242 · back-office-erp.test.ts:286

Restricted money

A grant fund that keeps its own append-only record

Restricted grant money gets its own fund ledger with an append-only audit trail, so the question an auditor actually asks — show me that this grant paid only for what it was given for — is answered from the record rather than reconstructed from memory and email. One caveat, measured: there is a spend-gate function in that module with zero production callers. The audit trail and the fund append are reached; an automatic gate that refuses an out-of-scope spend is not wired. The record will show you the violation. It will not currently stop it.

Built · audit and append reached; the spend gate has zero callers

Read in cbuild-grants-fund-accounting.ts:399, :491 · the unwired gate at :351

Club and activity money

A per-club activity-funds ledger, persisted — with no screen in front of it yet

Student activity funds are the money most likely to be kept in a spreadsheet on one person’s laptop, and the money a district is most likely to be embarrassed by later. There is a real per-club ledger here: post an entry, read a club balance, pull an audit trail, and it persists through a repository rather than living in memory. It has no user interface. No console renders it, and that is a measured absence rather than an oversight in this description. Today it is an API. If a club treasurer is meant to touch it, somebody has to build the screen first, and that somebody is us, on a date we will give you rather than a quarter we will gesture at.

Built and persisted · API only, no console

Read in cbuild-activity-funds-ledger.ts:309, :399, :528 · store/activity-fund-repo.ts

Where this beats what a school business office runs today

Parity is the floor. Here is the mechanism, in each case, that clears it.

Every comparison below is against a category rather than a company, for the simple reason that we have not run a controlled test against any specific product and will not claim we have. What we can be exact about is the mechanism on our side, and a business manager who knows their current system can do the comparison themselves in about a minute.

Against the small-business bookkeeping package

A fund is a dimension, not a tag. Say what your books do with restricted money.

This comparison is about a MODEL, not about any company’s code, and we are only going to claim the side of it we can show you. Governmental fund accounting asks a question ordinary single-entity bookkeeping is not shaped to answer: this dollar is restricted to one purpose, this dollar belongs to a club rather than to the district, this dollar is the general fund, and the board needs each of those reported separately rather than as one equity line. Here is our side of it, and every word is checkable in the source. The fund is a field on the account, so a trial balance for one fund is the same function as the trial balance for the entity with a fund argument passed in — not a filtered export. Fund balance classifies into the five governmental categories: nonspendable, restricted, committed, assigned, unassigned. And every amount is an integer number of cents at every step, with a guard that refuses any other shape, so there is no rounding drift to hunt at year end. The question to take to whatever you run now is the direct one: can it produce a trial balance for one fund without exporting to a spreadsheet, and does it classify fund balance five ways? We are not going to tell you how your current system stores a number internally, because we have not read its source and could not.

Against the legacy school ERP finance module

They do fund accounting properly. We are not claiming otherwise, and we lose to them on breadth.

This is the comparison where we have to be most careful, so we will start by conceding the thing a sales page is tempted to fudge. We claim parity on the accounting model, not a win on it: a legacy school ERP finance module does real fund accounting, real encumbrances, and real board reporting, and anyone claiming otherwise has not used one. They have been through decades of real audits and they do a great many things this product does not do at all. On breadth of feature and depth of deployment we lose, today, and anyone telling you a first-release ledger beats a mature district suite on coverage is selling you something. What we can put on the table is LEGIBILITY, and it is not a small thing when the books are your responsibility. The posting rule is a pure function with no database, clock, or network call inside it, short enough to read in one sitting. Every refusal it can issue is one of exactly five named reasons a caller can branch on. The engine is testable without standing up a database, so we can tell you precisely what it refuses rather than asking you to trust a certification — and we hold no certification, which this page says in three other places. The pricing is printed further down instead of quoted after a discovery call, and the two capabilities that are written and not wired are named on their own cards. We are deliberately not characterising anyone else’s contract, quote, or implementation timeline. We have not seen your quote and cannot read another vendor’s source, so any number we published about them would be invented. Ask them the same questions you should ask us: what exactly does the posting rule refuse, can I read it, and what is written but not switched on. That is the difference the name is pointing at. A set of books you are responsible for and cannot fully read is a worse position than a smaller set of books you can. Same accounting model, different custody.

Against AP automation and billing tools

Never-pay-unapproved is a function here, not a setting. It is also narrower than it sounds.

Our side, stated without reaching into anyone else’s product. The refusal to treat an unapproved payable as payable is a function in the accounting engine that returns a named blocked reason and a zero payable amount. It is not a workflow step, not a role permission, and not a configuration flag, so there is no screen anywhere that switches it off and no administrator who can reorder around it. When a control has to survive a bad Friday afternoon, the layer it lives at is the whole question, and this one lives in the arithmetic. Now the part that keeps this comparison honest, because it is also the reason we are NOT claiming a win over dedicated payables tooling. We have no invoice store. The approval status arrives as a field on the invoice you send us; we refuse to act on an unapproved one, but we are not the record of who approved it. A product built around an approval ledger keeps that record and we do not. So: we beat a configurable workflow on the layer the control sits at, and we lose to a real payables system on having the approval history at all. Both halves of that are true and you need both to decide. Where we are genuinely different in kind is that there is no separate AP database here to drift out of step with the general ledger, because there is no separate AP database at all — which is a real property and also plainly a consequence of the gap in the paragraph above, not a feature we are dressing up.

Against Payroll bureaus

We compute the same gross-to-net. We do not move the money, and we will not pretend to.

Gross-to-net, withholding, contributions, deductions, and the pay stub are computed here in exact cents, by an engine you can read, against inputs you control. That is real and it is useful for checking a bureau’s numbers before you approve a run. An earlier version of this card went one sentence further and said that having the payroll arithmetic beside the ledger means payroll lands in the general ledger as a posted entry instead of a monthly import somebody reconciles. We checked that sentence against the code while auditing this page and it was not true, so it is gone. The payroll-to-ledger bridge is written — there is a function that builds a balanced payroll accrual entry and one that distributes labour cost — and nothing in the running application calls either of them. Searching the whole application tree for them returns zero, in the same pass where the posting function, the encumbrance function, and the trial balance returned 22, 83, and 25 hits, so the zero is a real absence and not a search pointed at the wrong place. So today the payroll numbers and the ledger are two things you connect, not one thing that connects itself. Read the next section before you weigh any of this.

Where we do not win, said first rather than last

A payroll bureau still cuts the checks. We do not, and we are not close.

MyBooks computes gross-to-net accurately and puts the result in the same ledger as everything else. It does not file your returns, it does not remit your withholding, it does not produce your W-2s, and it does not send a single dollar to a single employee. The disbursement check runs against a partner that is not wired, so it always comes back saying exactly that, and the flag it returns is always false. A test asserts the false, so it cannot quietly become true.

If you are evaluating this against a payroll provider, the honest framing is that it is a calculation and preview surface you can check a bureau’s numbers against and post cleanly from — not a replacement for the bureau. Anyone who tells you a first-release school accounting product replaces a national payroll processor is telling you something that will be discovered in your first quarter-end.

The same discipline applies to the two unwired capabilities above. We would rather lose a deal on this page than win one and have you find the school purchasing chain empty in week three.

One note on the four categories named above, since this page compares itself against them. The small-business bookkeeping packages (QuickBooks, Xero, Sage Intacct), the legacy school ERP finance modules (Skyward, Tyler Munis, PowerSchool eFinancePlus, Frontline), the payroll bureaus (Gusto, ADP, Paylocity) and the AP automation and billing tools (Bill.com, Stripe Billing) are named here once, in this paragraph and deliberately nowhere else on this page, as factual references to the market this product competes in. None of their code or copy is used anywhere in this product, and no affiliation or endorsement is implied. The category descriptions above are our reading of a market and not tested claims about any individual product. We have not run a controlled comparison against any of these, we have not read their source and could not, and we make no claim about how any of them is built internally, what any of them costs, or how long any of them takes to implement — a comparison we cannot source is not one you should weigh. Every comparison above is a claim about OUR side, with the file it was read in printed on the card. The questions we invite you to put to us are the same ones to put to them. If you work on one of these products and we have described your category wrongly, tell us and we will correct this page.

What is claimed, what is measured, and what is neither

The scope on every claim above, stated once, plainly.

What was measured. Every capability on this page was checked in the source on our canonical branch: the function exists, and a real application route calls it. Where a card says a function has no callers, that was established by searching the whole application tree — and in the same pass, searching for functions we expected to find, which all returned plenty. A zero standing next to those non-zeros is an absence. A zero on its own would only have proved the search was aimed badly.

What was not measured, and therefore is not claimed. Nothing on this page was verified against a running production server. We did not probe a live process, and we cannot tell you from here whether any particular deployment has its database wired or its migrations applied. “The code is there and something calls it” and “it is returning your district’s numbers today” are two different sentences, and this page only writes the first one. That is why the whole site says early access instead of available now.

Money is off structurally, not by a flag. There is no pay endpoint, no expend endpoint, and no disburse endpoint in this product. There is no payment-processor integration in this lane — the only mention of one in these files is a comment recording that the module does not import it. A requisition holds at approved and cannot reach paid. Payroll computes and never disburses. There is nothing to switch on by accident, which is a stronger guarantee than a setting somebody promises to leave alone.

One thing we will tell you before you find it. Every finance route sits behind a module entitlement check and a fail-closed role gate. The role gate does fail closed. The entitlement layer, however, currently treats a missing capability row as enabled, for backward compatibility with tenants provisioned before that layer existed — so the paywall is not closed by default. It is on the list. We are telling you rather than letting you find it, because a vendor who volunteers this one is a vendor you can believe about the rest.

No certification, and no claim of one. We implement a governmental fund-balance classification because the accounting model requires it. No standards body has certified this software, no auditor has issued an opinion on it, and we hold no FERPA, COPPA, SOC 2, or VPAT certification. There are no customer counts, adoption numbers, testimonials, or dollar figures anywhere on this page, because there are none to publish.

Pricing — published, and display-only

Three tiers, printed on the page. No card is charged on this site.

A business office should be able to find out what accounting software costs without booking a call and sitting through a discovery meeting first. So the tiers are here. They are the planned launch tiers, there is no checkout on this page, and nothing is charged.

Single school

Books

~$390/mo

Planned · display-only, not a live checkout

  • General ledger, balanced-or-nothing posting
  • Chart of accounts and funds
  • Trial balance, per fund and entity-wide
  • Period close and budget-to-actual

District

District

Per school, published on request

Planned · a real number, not a discovery call

  • Everything in Back office, per school
  • Restricted grant fund ledger and audit trail
  • Activity and club funds (API today, no console)
  • Consolidated trial balance across schools

Common questions

Can I keep my books here today?

No, and this page is not going to imply otherwise. MyBooks is early access. The engine described here is written, tested, and reached by real application routes on our canonical branch — that is a statement about code, and we can show you the file and the line for every claim on this page. It is not a statement that a production instance is serving your district today, and we have not measured that from here. The honest next step is a conversation and a date.

In what sense are they MY books? Be specific.

In one specific sense, and we are going to bound it rather than let the name do work it has not earned. Every rule that decides what goes into these books is one you can read: the posting rule is about thirty lines of arithmetic with no database, clock, or network call inside it; every refusal it can issue is one of five named machine reasons rather than a modal saying the entry could not be saved; there is no second code path into the ledger that skips it; and the two capabilities we have not wired are printed on this page next to the ones we have. That is custody in the sense of being able to defend a number without phoning anybody. What the name does not claim: it is not a statement about data export, and the exact shape of that matters more than a flat denial. There IS one endpoint named export in these files, so saying we have not built one would be false. What it does is serialize what you hand it: you post a chart of accounts and a set of posted entries in the request body, and it returns those back with a trial balance computed over them and a flag saying it pushed nothing anywhere. It reads nothing out of any store, so it cannot give you back a set of books you were not already holding — which is not what a business manager means by the word. A real one, reading the stored ledger, is not built. If you need that before signing, ask us and we will give you a date instead of a slogan.

Does MyBooks move money?

No, and it cannot without new code being written. There is no pay endpoint, no expend endpoint, and no disburse endpoint anywhere in this lane. There is no payment-processor integration in it either — the only place a processor is mentioned in these files is a comment stating that the module does not import one. A requisition run holds at approved; “paid” is a state the flow cannot reach. Payroll computes and never disburses. This is money being structurally off rather than switched off, and the difference matters: there is no toggle somebody could find and flip by accident.

What exactly is not built yet?

Four things, all of them on the cards above rather than in a footnote. One: MOST OF THIS PRODUCT DOES NOT STORE ANYTHING YET. The chart of accounts, the posted journal entries and the trial balance over them do persist, as do the club funds ledger and the approval runs. Everything else — period close, budget-to-actual, payables and receivables aging, bank reconciliation, the 1099 worksheet, all eight payroll endpoints — is a calculator: you send the values with the request and you get the arithmetic back, and nothing is kept. The arithmetic is right and tested. It is not yet a set of books for those areas, and this page used to blur that, which is the main thing this revision fixes. Two: the school purchasing and requisition chain — record transaction, request, approve, receive — is written and tested and has zero production callers. Three: four of the five budget lifecycle verbs — appropriate, deappropriate, disencumber, expend — are written and folded correctly but are never constructed by the server; only encumbrance is. Four: the payroll-to-ledger bridge is written and has zero production callers, so payroll does not post itself to the general ledger today. Separately, the activity and club funds ledger is real and persisted but has no user interface at all. It is an API today.

How do you know those things have zero callers? That is a strong claim.

It is, so here is the method rather than the conclusion. Each symbol was searched for across the whole application tree. In the same pass, the same search shape was run for symbols we expected to find — the posting function, the encumbrance function, the finance authorization gate, the trial balance, the payment-approval check — and every one of them returned a substantial number of hits. A zero next to those non-zeros is evidence of absence. A zero on its own would only have been evidence that the search was pointed somewhere useless.

Is this certified? Has an auditor signed off?

No. We implement a governmental fund-balance classification — nonspendable, restricted, committed, assigned, unassigned — because that is what the accounting model calls for. No standards body has certified this software, no auditor has issued an opinion on it, and we hold no FERPA, COPPA, SOC 2, or VPAT certification. Anyone telling you their accounting software is GASB certified is describing something that does not exist in that form.

Who can see the finance data?

Every finance route sits behind a module entitlement check and a fail-closed role gate, and the records themselves carry opaque finance references — account, fund, vendor, budget — rather than student identities. One thing we are going to tell you before you find it: the entitlement layer currently treats a MISSING capability row as enabled, for backward compatibility with tenants provisioned before it existed. The role gate is fail-closed; the paywall is not closed by default. That is on our list, it is not a secret, and you would rather hear it from us.

What happens if the database is not provisioned?

You get an honest error, not a fabricated number — on the surfaces that use a database at all, which is the part worth separating. The stored surfaces (the chart of accounts and posted entries, the club funds ledger, the approval runs) return an explicit not-provisioned response: the persistence layer says it is unprovisioned rather than handing back an empty ledger that reads like a zero balance. A blank balance and a missing database look identical on a screen and are completely different facts, so the system refuses to let them look the same. The approval-run path goes further and fails the transition closed rather than trusting whatever state the client claimed. The calculation surfaces never touch a database, so the question does not arise for them — they compute over the values in your request and hand the answer back. That is also the honest reason they cannot go stale or drift: there is nothing there to go stale.

Does it replace our student information system?

No. This is the business office: general ledger, funds, budgets, encumbrances, payables and receivables, bank reconciliation, and payroll calculation. It carries no student roster and no student identity, and it is not trying to. If a school runs our wider platform the two connect; if it runs somebody else’s, that is fine, and this ledger does not require it.

Is any of this AI?

No. There is no model, no assistant, and no generated number anywhere in this product. The posting rule is arithmetic and the classifications are rules. A ledger is the last place anyone should want a probabilistic answer.

How much does it cost?

Tiers are shown on this page and they are display-only: there is no checkout here and no card is charged on this site. They are published rather than quoted because a business office should be able to find out what software costs without booking a call, which is the opposite of how this category usually sells.

Join the waitlist

There is no checkout on this page and no card is charged here. If what your business office actually needs is a set of books that refuses an unbalanced entry, keeps funds as a real dimension, counts in exact cents, and does not hide the rule that made any of those decisions, the honest next step is a conversation — and we will bring the same list of what is not built that you have just read, not a shorter one.