← All standards
Open specificationStablePublished November 2026Changelog

The Month-End Close Build Standard

Close the month the same way every time, for every company you run: each item on the checklist checked against your accounts with the evidence written down, then you sign off, and only then do the month’s journals post and the month lock. Works with Xero, MYOB or QuickBooks. Written so you can build it yourself.

Used by

  • Bookkeeper
  • Accountant
  • Financial Controller
  • Finance Manager
  • Accounts Officer

11 min read · Published by AI Pathway

Using Claude? This standard installs as a skill. Guides for ServiceM8, Xero and Simpro.

What this is

An open build standard for closing the books each month. The close is a checklist, like "every bank account matches its statement at the 31st". An agent works the list for each of your companies and writes down, for every item, what it found in your accounts and where it found it.

The core rule

The agent checks and proposes. A person reviews the list and signs off. Only after that does anything go into your accounts: the month's accrual journals, once each, and the lock date, which stops anyone changing a closed month by accident.

What it does not do

It never makes up an accrual amount, never calls a difference too small to matter, never ticks an item it could not read, never recodes a transaction, and never moves the lock date backwards. Those stay with you.

The hard part

Writing into accounts we do not own: the approved journals posted exactly once even when the connection drops halfway, and the lock date set only after a person said yes, and only forward.

It is the fifth working day of the month. The bank feeds are mostly done, there are a few transactions nobody can explain sitting in suspense, the audit fee accrual is in last month’s spreadsheet, and the accountant wants to know if October is closed. Somebody ticks the list from memory. Three weeks later a late bill lands in October and the report you already sent is wrong. The list is the same every month. Saying it is done, and meaning it, is yours.

One company's month end, from the checklist to the closed month

  1. At month end the build closes each entity in turn.
  2. It works the checklist against the ledger and records every item as pass, fail or could not check, with the read behind it.
  3. Every uncoded or suspense line is handed to the Uncoded Query standard, once.
  4. Each accrual and prepayment is proposed as a journal, with its amount from the schedule the business keeps or a person's entry.
  5. Before anything is written into the ledger: has a named person signed off this entity's close? If no, nothing is written and the pack waits.
  6. If yes, each journal the person approved is posted once and read back from the ledger.
  7. Does the lock date read back at the period end or later? If yes, the closed period is handed to the Recurring Report standard for the client pack.
  8. If no, the build sets it forward to the period end where the ledger's API allows it, or raises one task for a person to set it, and reads it back. It never moves the lock date backwards.
The filled diamond is your sign-off: before it, the agent only reads your accounts and fills the close pack. Everything written into your accounts comes after it, and the lock date only ever moves forward.

Take the spec to Claude, ChatGPT or Copilot. The sentence to paste is one click away.

1. What this standard covers

A business, or a bookkeeper or accounting practice working for one, that closes the month in a cloud ledger, for one entity or many. For each entity and month the build works an eight-item checklist against the ledger, records a result with its evidence for every item, proposes the month’s accrual and prepayment journals from the schedule the business keeps, and holds the pack for a person. After that person signs off, it posts the journals they approved, once each, moves the lock date forward to the period end, and hands the closed month to the Recurring Report standard for the monthly pack.

Out of scope. Year-end adjustments, tax returns and lodgement, payroll itself, and coding transactions. An uncoded line is handed to the Uncoded Query standard, which asks the person who knows what it was. Whether a difference matters is the reviewer’s call, never the build’s.

How it connects. Straight to your ledger, through the ledger’s own sign-in and API, approved by you. No product of ours sits between the build and your accounts, and Office Voice is not the ledger connection.

What cannot meet it, and where a person steps in. All three ledgers below let an app read what the checklist needs and post a journal. None of them, as far as their own API references show, lets an app set the lock date: Xero and MYOB Business publish it as read only, and Intuit’s reference does not confirm a write for QuickBooks Online. So on all three today the lock step stays with a person: the build raises one task for the reviewer to set it, then reads it back before the month counts as closed. Where a ledger cannot give the statement balance or an aged report to an app, that item is recorded as could not check, or worked out from the open invoices and bills, never assumed.

Facts that can change, and when we checked them

Checked as at 12 November 2026

Xero. Manual journals can be created as a draft or posted through the Accounting API, and a post can carry an Idempotency-Key header so a retry lands once. The period lock date and end of year lock date are readable on the Organisation endpoint, which has no write method. Bank transactions carry whether they are reconciled; statement lines sit in a separate API with restricted access.

MYOB Business. General journals are created through the General Ledger API with no draft status, so creating one posts it. No idempotency key is documented, so the intent id goes in the journal memo and the build reads back before any retry. The lock periods date is readable in the company preferences, which are read only. Journal transactions carry the date they were reconciled. No aged receivables or payables report endpoint is documented.

QuickBooks Online. A journal entry posts when it is created. A request id on the call makes a repeat return the first answer instead of posting again. The closing date is readable in the company’s accounting preferences; Intuit’s own reference does not confirm it can be written. Reconciled status is not a field on transactions.

These change. Check the vendor’s current API reference before you build, and treat a lock date write as possible only once a test on your own company shows it reads back.

Check these against the primary instrument before you rely on them, and store the date you checked beside the value rather than hard-coding it. This standard tells you the shape and where to look. It is not legal advice and it does not tell you that anything complies.

2. What closed means, item by item

Every item ends as one of three results. Pass and fail both name the ledger read they came from, when it was made and the figures compared. Could not check says why. A blank is never a pass.

ItemIt passes when
bankEvery bank and card account is reconciled to a statement line dated on or after the period end, the ledger balance equals the statement balance, and nothing is left unreconciled.
uncodedNo line is left uncoded or in a suspense account. Each one found is handed to Uncoded Query, which asks the person who knows.
receivablesThe aged receivables at period end equal the receivables control account, to the cent.
payablesThe aged payables at period end equal the payables control account, to the cent.
payrollEvery pay run the business expects in the month has been posted.
accrualsEvery line on your accruals and prepayments schedule has a proposed journal, with an amount from the schedule or from a person's entry this month.
tax_controlThe tax control account (GST in Australia) agrees with the figure for the period's return.
clearingEvery clearing account, such as payroll clearing or undeposited funds, is at zero.

3. The close pack, in the shape your developer needs

One pack per entity per period. Money is whole cents. The build fills everything except the schedule, a person's amounts and the sign-off.

For your developer. Show the fields for your developer
entity
  - entity_id, legal_name
  - ledger                          # xero | myob | qbo
  - lock_writable                   # yes only after a test read it back

item_result                       # one per entity, period, item
  - item                            # bank | uncoded | receivables |
                                    #   payables | payroll | accruals |
                                    #   tax_control | clearing
  - status                          # pass | fail | could_not_check
  - evidence?                       # required for pass and fail
      read_id, read_at, detail      #   the figures compared
  - detail

schedule_line                     # kept by the business
  - line_id, kind                   # accrual | prepayment
  - debit_account, credit_account
  - amount?                         # blank = a person enters it
  - entered?  amount, by, at        # this period only

journal_proposal                  # written by the build
  - intent_id                       # close:<tenant>:<entity>:<period>:<line>
  - amount, source                  # schedule | person_entry
  - entered_by?

signoff                           # a person, never the build
  - by, at
  - approved_proposal_ids[]

after sign-off
  - posted[]   intent_id, ledger_journal_id   # read back
  - lock_date_read_back             # period end or later
  - handed_to                       # recurring-report

There is no field for a materiality threshold and no status the build can set on a sign-off. The pack records what the ledger showed; a person decides what it means.

4. The checks, in order

The order is the argument. Evidence comes before any result, the schedule before any journal, and a person’s sign-off before anything is written into your accounts. A close that looks finished and is not is worse than one that says plainly what it could not check.

  1. Every item on the checklist gets a result. For every entity and every period, each of the eight items (bank, uncoded, receivables, payables, payroll, accruals, tax control, clearing) is recorded as pass, fail or could not check. None is skipped, and a blank is never read as a pass.
  2. A pass or a fail carries its evidence. Every pass and every fail names the ledger read it came from, with the time of the read and what it showed: the balances compared, the lines found, the dates missing. A pass with no read behind it is not a pass.
  3. What could not be read is recorded as could not check. When the ledger does not answer, or a figure the item needs is not available, the item is recorded as could not check with the reason. It is never a pass because nothing was found, and never a fail the build made up.
  4. Every bank account is reconciled to a statement dated at period end. Each bank and card account passes only when its last reconciled statement line is dated on or after the period end, the ledger balance equals the statement balance at that date, and no statement lines are left unreconciled. One account short fails the item, and the result names the account.
  5. Uncoded and suspense lines are handed to Uncoded Query, once. Every line left uncoded or sitting in a suspense account fails the item and is raised once, with its line id, to the Uncoded Query queue, which asks the person who knows what it was. The close never recodes a line itself, and a second run does not raise it again.
  6. An accrual amount comes from the schedule or a person, never a guess. Each accrual and prepayment is proposed as a journal for a person to approve, with its amount taken from the schedule the business keeps or from a person's entry for the period, carrying who entered it. A line with no amount is not proposed: it is raised once to the bookkeeper as no_amount. Last month's figure, an average or a model's estimate is never an amount.
  7. A difference is reported, never judged small enough. The tax control account agrees with the return figure for the period, each clearing account is at zero, and the aged receivables and payables equal their control accounts, to the cent. Any difference fails the item with both figures and the difference. Whether a difference matters is the reviewer's call, not the build's.
  8. Nothing is written into the ledger before a person signs off. No journal is posted and no lock date is set or asked for until a named person has signed off that entity's close for that period. Only the journals the sign-off approves are posted. An entity nobody signed off has nothing written.
  9. Each approved journal posts exactly once, and is read back. Each approved journal is posted with the intent id close:<tenant>:<entity>:<period>:<schedule line>. A retry carries the same id and lands once. A post with no answer is treated as not posted, raised as write_failed to the bookkeeper and tried again. The journals are read back from the ledger before anything else happens.
  10. The lock date moves forward to period end, only after sign-off. Once the approved journals read back, the lock date is set to the period end and read back. It is never set before sign-off and never moved earlier than it already is. Where the ledger's API cannot write the lock date, the build does not try: it raises one task for the reviewer to set it by hand, and reads it back.
  11. The closed period is handed to the Recurring Report, once. Only when the lock date reads back at or after the period end is the period handed to the Recurring Report standard for the client pack, once per entity and period, with the lock date it read.
  12. It survives the builder. Someone other than the builder can explain what it does, and it runs on an account the business owns.
  13. The pass test passes. In the business's own ledger: run the close for one entity with one accrual on the schedule. With no sign-off, nothing is posted and the lock date does not move. After a recorded sign-off, the accrual posts exactly once and reads back, a second run posts nothing, and the lock date reads back at the period end, set by the build where the ledger allows it or by the reviewer on the task it raised.

5. One company at the end of October

An illustration, not data, using the figures the runnable stub uses. On 5 November the agent works October for a plumbing company. The operating account is reconciled to the 31st and agrees. The business card is only reconciled to the 27th, so bank fails and names the card. Two card payments sit in suspense, so uncoded fails and both go to Uncoded Query. Aged receivables are $10.00 away from the control account, and payroll clearing holds 40 cents: both fail, with the figures, and nobody is told they are too small to matter.

The schedule holds the audit fee accrual at $1,200.00, so that is proposed. Electricity has no fixed amount; the bookkeeper enters $450.00 for October and the next run proposes it, with her name on it. Insurance has no amount and nobody has entered one, so it is not proposed: it goes to the bookkeeper, even though last month’s figure is sitting right there.

The reviewer reads the pack, accepts the differences, and signs off approving both journals. The first post times out after it landed; the next run reads the journals back, finds it by its intent id, and does not post it again. The ledger cannot set its own lock date, so the reviewer gets one task, sets it to 31 October, and the next run reads it back and hands October to the monthly report.

6. Writing into your accounts, once

This is the step not to hand-roll, and the reason the standard exists. Each approved journal carries the intent id close:<tenant>:<entity>:<period>:<line> in its narration or memo, and in the ledger’s idempotency key or request id where it has one. Before any retry the build reads the journals back and looks for that id. A post with no answer is not a post: it is raised to the bookkeeper and tried again only after the read back. Like a cheque book stub, the id is how you know the cheque was already written.

The lock date is written last, only after the journals read back, and only forward: if the ledger already holds a later date, the build leaves it alone. Where the ledger’s API cannot write it, the build raises one task for the reviewer and waits for the read back. The month counts as closed only when the lock date reads back at the period end or later.

7. What stays with a person

  • Reviewing the close pack and signing off by name, approving each proposed journal or leaving it out.
  • Keeping the accruals and prepayments schedule, and entering any amount it does not hold, each month.
  • Deciding whether any difference or failed item matters, and what to do about it.
  • Setting the lock date by hand where the ledger's API cannot, and never moving it back without a reason you would write down.
  • What each uncoded line was, through the Uncoded Query standard.

8. Run it your way

The same checks and the same pass test on each. Two rules hold whichever you pick: nothing is written into your accounts before a person signs off, and the lock date only moves forward.

  • Google Workspace. A sheet with tabs for your entities, the figures you take from your ledger’s reports, your accruals schedule, the close results, the proposed journals and the sign-off. It records every item with its evidence, builds the approved journals as rows to import into your ledger once, and asks you to set the lock date. It never connects to your ledger and sends nothing. Make your own copy
  • Microsoft 365. SharePoint lists for the pack and the sign-off, a flow that works the checklist through your ledger’s own connector, and a second flow that runs only after sign-off. The Signing Off the Month build plan lays out the lists and flows.
  • Your own Grok Bot or Claude. To read the pack and explain a failed item, not to sign off or post. Ask it why bank failed or what is still in suspense. Keep the posting and the lock date in code that reads back, never in a chat.
  • Your own code. This page. Hand it to a coding agent: it gets the checks as data, the stub world, and a runner that tells it when the build is right. See running a standard anywhere.

9. Join It to Your Own Ledger: What to Stub, and What Not to Hand-Roll

There is no hosted step. The build connects straight to your ledger through the ledger's own sign-in, and the production check runs there. The parts that need care are the read back before any retry, the sign-off before any write, and a lock date that only moves forward. All of it runs against the stub first.

For your developer. Show the setup for your developer
provider:
  stub:       local.mock_ledger_close
  production: none                       # no hosted step; runs on your own systems

the live step                        # yours, on your own ledger
  approved journals posted once, read back
  lock date moved forward, or a task to a person

the ledger connection                # direct, the ledger's own sign-in
  Xero, MYOB Business or QuickBooks Online API
  scopes: read reports and transactions,
          write journals; settings read only

ports the stub exposes
  readSettings, readClose, readBank, readUncoded,
  readAged, readPayroll, readControls, readSchedule,
  recordResult, raiseException, proposeJournal,
  postJournal, readJournals, readLockDate,
  setLockDate, raiseTask, handOff

no port exists for
  signing off, deciding materiality,
  or recoding a transaction

Anyone selling a month-end close agent should be able to answer one question: show me the sign-off that came before last month's journals, and the read back that proves each posted once.

The pass test. In your own ledger, not a sample. Take one entity with one accrual on the schedule and run the close for the month. With no sign-off, nothing is posted and the lock date does not move. Sign off, approving the accrual. Run it: the journal posts once, with the intent id in its narration, and reads back. Run it again: nothing new is posted. The lock date reads back at the period end, set by the build where your ledger allows it or by you on the task it raised. A stub cannot pass this: on a stub the journal and the lock date are rows in the runner’s memory. Only your own ledger, through its own sign-in, proves the write landed once, after a person’s sign-off, and survived a retry.

10. A checklist to hold any build or supplier to

Hold a DIY build or a vendor to this. If a box is empty, it is not in production.

  • 1. Every item on the checklist gets a result. For every entity and every period, each of the eight items (bank, uncoded, receivables, payables, payroll, accruals, tax control, clearing) is recorded as pass, fail or could not check. None is skipped, and a blank is never read as a pass.
  • 2. A pass or a fail carries its evidence. Every pass and every fail names the ledger read it came from, with the time of the read and what it showed: the balances compared, the lines found, the dates missing. A pass with no read behind it is not a pass.
  • 3. What could not be read is recorded as could not check. When the ledger does not answer, or a figure the item needs is not available, the item is recorded as could not check with the reason. It is never a pass because nothing was found, and never a fail the build made up.
  • 4. Every bank account is reconciled to a statement dated at period end. Each bank and card account passes only when its last reconciled statement line is dated on or after the period end, the ledger balance equals the statement balance at that date, and no statement lines are left unreconciled. One account short fails the item, and the result names the account.
  • 5. Uncoded and suspense lines are handed to Uncoded Query, once. Every line left uncoded or sitting in a suspense account fails the item and is raised once, with its line id, to the Uncoded Query queue, which asks the person who knows what it was. The close never recodes a line itself, and a second run does not raise it again.
  • 6. An accrual amount comes from the schedule or a person, never a guess. Each accrual and prepayment is proposed as a journal for a person to approve, with its amount taken from the schedule the business keeps or from a person's entry for the period, carrying who entered it. A line with no amount is not proposed: it is raised once to the bookkeeper as no_amount. Last month's figure, an average or a model's estimate is never an amount.
  • 7. A difference is reported, never judged small enough. The tax control account agrees with the return figure for the period, each clearing account is at zero, and the aged receivables and payables equal their control accounts, to the cent. Any difference fails the item with both figures and the difference. Whether a difference matters is the reviewer's call, not the build's.
  • 8. Nothing is written into the ledger before a person signs off. No journal is posted and no lock date is set or asked for until a named person has signed off that entity's close for that period. Only the journals the sign-off approves are posted. An entity nobody signed off has nothing written.
  • 9. Each approved journal posts exactly once, and is read back. Each approved journal is posted with the intent id close:<tenant>:<entity>:<period>:<schedule line>. A retry carries the same id and lands once. A post with no answer is treated as not posted, raised as write_failed to the bookkeeper and tried again. The journals are read back from the ledger before anything else happens.
  • 10. The lock date moves forward to period end, only after sign-off. Once the approved journals read back, the lock date is set to the period end and read back. It is never set before sign-off and never moved earlier than it already is. Where the ledger's API cannot write the lock date, the build does not try: it raises one task for the reviewer to set it by hand, and reads it back.
  • 11. The closed period is handed to the Recurring Report, once. Only when the lock date reads back at or after the period end is the period handed to the Recurring Report standard for the client pack, once per entity and period, with the lock date it read.
  • 12. It survives the builder. Someone other than the builder can explain what it does, and it runs on an account the business owns.
  • 13. The pass test passes. In the business's own ledger: run the close for one entity with one accrual on the schedule. With no sign-off, nothing is posted and the lock date does not move. After a recorded sign-off, the accrual posts exactly once and reads back, a second run posts nothing, and the lock date reads back at the period end, set by the build where the ledger allows it or by the reviewer on the task it raised.
  1. 1

    Hold the build to the checklist

    The checks in this standard are what a working build looks like. Implement it yourself, hand it to a developer, or hand the page to an AI with the button below.

  2. 2

    Do not hand-roll

    There is no product for this step. Highest option: none. Checklist plus a build.

  3. 3

    If you are stuck

    Exceptions, three systems, a team that will not maintain a Zap, or an owner who wants it done. We implement when it has to survive the business.

Related reading

Want the whole thing by email?

This paper, plus the rest of the library. One click to stop them.

Run these checks yourself

Every check on this page is published as structured data. This prints them with what to inject, what to assert, and which ones can be proved on your own machine rather than against the live thing.

npx @aipathway/conformance show month-end-close-build-standard

Free and open source, CC BY 4.0. The checks are fetched from this page rather than bundled, so a correction here reaches you without a release, and you cannot end up conformant against a stale copy. The raw JSON is there if you would rather wire it into your own harness, and the package shows how to drive your build through the ports.

Built it, and one check failed?

Tell us which one. You have already read the standard and tried it, so the answer is a fixed price to finish that step, not a call to work out what you need.