2026-11-09 — Bank transfers
POST /bank_transfers(MCPcreate_bank_transfer) records money moved between two of the account's own bank accounts as a Bank Transfer, through the same model as the web form: a line and a bank reconciliation row on each bank. Posted on create. Refused, with the reason, for an account that is not an active bank account of this account, the same account on both sides, two currencies, an amount that is not positive or has more than two decimal places, and a date on or before the lock date.GET /bank_transfers,GET /bank_transfers/:id,GET /bank_transfers/:id/transactionsandDELETE /bank_transfers/:id(refused while reconciled). Scopesjournal_entries:readandjournal_entries:write. MCPlist_bank_transfers,get_bank_transfer,delete_bank_transfer.
2026-11-06 — Bank balances agree
- The year-end review adds a
bank_balances_agree:<bank account id>check per bank, card and loan account: every balance the bank feed reported in the year (one per day) and every statement's closing balance, set against the ledger. Red names the first that disagrees (facts.first_disagreement) and the day after the last that agreed (facts.since);lineslists each that disagrees with the difference. bank_reconciled:<id>is green without a year-end statement when the feed proves it: every bank balance in the year agrees, through the first after year end (facts.proven_by_feed).
2026-11-03 — A note goes on any year-end check
POST /year_end_reviews/:financial_year/checks/:check_key/notes(MCPadd_year_end_review_note) takes every key the review returns, including keys with a colon (bank_reconciled:42,year_end_adjustment:income_tax), plain or percent-encoded (bank_reconciled%3A42). This has worked since the 31 Oct release; a note written to another check because a bank check refused it can now go on the bank check itself.
2026-11-03 — Quotes start finalised
- Default changed:
POST /quotes(MCPcreate_quote) creates the quote finalised and numbered, as the web app does. Passstatus: "draft"to keep one back for review, thenPOST /quotes/:id/finalise(finalise_quote). A finalised quote stays editable until it is marked accepted. Invoices and expenses still start as drafts.
2026-11-02 — A project names its client
GET /active_account/projectsandGET /active_account/projects/:idaddclient_idandclient_name.GET /active_account/companies/:idgives the client's address and contacts (companies:read). Changing a project's client moves itsupdated_at; renaming the company doesn't.
2026-10-31 — Year-end checks that list their items
- Every red or amber check now returns
linesfromget_year_end_review_check. director_loanslines show each loan's opening balance, drawings, interest charged, repayments and closing balance. With a written agreement's year and term recorded on the web, they also show the Division 7A minimum repayment, and the check is red when repayments fall short.balance_sheet_balances: the retained earnings line says how the figure is worked out and that no Retained Earnings account is needed.
2026-10-31 — Financial reports, help and the chart of accounts
- New
GET /reports/trial_balance?as_at=,GET /reports/profit_and_loss?from=&to=&basis=accrual|cash&compare_from=&compare_to=andGET /reports/balance_sheet?as_at=: one total per account with ids and tax return labels, from the code behind the web reports. Requiretransactions:readand a role that may read the Ledger. - New
GET /help,GET /help?q=andGET /help/:id: the help library as markdown, in the account's region. Any usable token. - New
POST /active_account/transaction_accountsandPATCH /active_account/transaction_accounts/:id, with the newtransaction_accounts:writescope. Tax return labels are checked against the account's type. Chart of accounts rows now carrytax_form_labelandtax_form_label_description. GET /active_account/transaction_accounts/:id/transactionsaddstransaction_count, andsummary=truereturns the totals with no lines.DELETE /raw_journal_entries/:idsays what to do instead when the journal is finalised.- Connector:
trial_balance,profit_and_loss,balance_sheet,get_help,search_help,create_transaction_account,update_transaction_accountanddelete_manual_journal, help topics as MCP resources atbeeswax://help/<id>, andsummary,pageandper_pageonlist_account_transactions.
2026-10-31 — One ledger entry, one bank transaction
POST /reconciliations/:id/confirm: eachticked_elsewhererow now also carries amessagenaming the entry and the statement it is ticked on.POST .../statement_lines/:line_id/matchanswers 409 with that message.- New reconciliation
statevaluedouble_counted: the difference is zero, but an entry ticked here is also ticked on another statement, so the bank lines are not all in the ledger. It never reads asreconciled. bank_reconciledis red when an entry is ticked on two statements even if the ledger and the year-end statement agree. It also lists statement lines not in the ledger, and addsfacts.not_in_the_ledger.get_year_end_review_checkopens keys with a colon when they arrive percent-encoded (bank_reconciled%3A42).
2026-10-31 — A tax statement's date can be chosen
2026-10-30 — Entries that balance, and year-end checks that name the items
- Behaviour change: an expense's lines now always debit and an invoice's lines always credit, whatever class of account they name; a negative line takes the other side. Lines used to follow the account's class, so a bill coded to a loan account was credited on top of Trade Creditors.
- Behaviour change: finalising an invoice, expense, refund or manual journal whose debits and credits differ is refused with a message saying by how much. Foreign-currency documents are not checked yet.
- Behaviour change:
POST /reconciliations/:id/confirmleaves a row unticked when its entry is already ticked on another statement, and reports it in the newticked_elsewherelist.POST .../statement_lines/:line_id/matchanswers 409 for the same row. - Behaviour change: a bank feed line can no longer be allocated to a manual journal or bank transfer whose bank line is already matched to a different feed line.
get_year_end_review_checknow lists the items behind a red figure.balance_sheet_balancesnames each unbalanced entry and any opening balances with no other side;receivables_agreeandpayables_agreelist payments with no control line, documents posted short of their total and journals posted straight to the account;bank_reconciledlists entries ticked on two statements and postings on no statement. Each gives the left-over amount asfacts.unexplained.bank_reconciledis amber, not red, when the only gap is payments from the statement's last seven days that haven't cleared yet.
2026-10-29 — Activity statement periods: the statement against the books
2026-10-02 — Invoice uploads say why they are in review
- Invoice uploads (
/invoice_uploads, andlatest_invoice_uploadon the incoming and processed expense lists) gainmatch_status,review_notes,potential_matchesandai_reasoning. match_status: "needs_review"means the created expense is waiting for a person.potential_matcheslists existing expenses it may duplicate (expense_id,confidence,reasoning);review_noteslists things to check on the expense itself, such as low extraction confidence. A review can have either or both.potential_matchesandai_reasoningdescribe other expenses in the account, so only the roles that see the web review panel (Owner, Admin, Accountant, Manager) get them. For a Basic user's own uploads,potential_matchesis always[]andai_reasoningisnull;match_statusandreview_notesare returned to everyone.- The
InvoiceUploadschema'sstatuslist now includesduplicate,attachedandexpense_deleted, which the API already returned.
2026-09-15 — Reads follow the user's role
- Behaviour change: every reader now applies the token user's role on
the account, as the web app does. Before, only writes checked the role, so
a token read every row its scopes reached. That included the
all-scope token that signing in through/sessionsissues, whatever the user's role. - Clients read the invoices and quotes of the projects visible to them, and
the milestones and project documents shown to them. They read no expenses,
payments, manual journals, tasks, time entries, companies, chart of
accounts, tax codes, products & services or tax returns, and see only
themselves in
/active_account/users. - Basic users read the tasks and milestones assigned to them, the projects and documents visible to them, and their own expense-inbox uploads. They read no accounting documents, companies, chart of accounts, tax codes, products & services or tax returns. The milestone list under a project still offers every open milestone, as the task form does.
- Managers no longer see manual journals, payrolls or bank transfers through
GET /journal_entries, the per-account ledger or tax returns. Their chart of accounts, tax codes and products & services reads are unchanged. - A resource the role cannot see at all answers
403("Your role does not permit reading …"). A single record it cannot see answers404. Writes find their record the same way, so they can no longer touch one the role cannot read. On a plan without accounting, manual journals and the per-account ledger
now answer403, as their web pages do.2026-09-15 — Conversation endpoints reach only the token's account
Behaviour change:
POST /conversations/:id/messages,DELETE /conversations/:id/messages/:message_idandPOST /conversations/:id/mark_readaccept only a conversation that belongs to the token's account. Another account's conversation, or a direct message, now returns404, even when the token's user takes part in it. Before, a token could post there, delete its user's own messages and mark the conversation read.GET /conversations/for_task/:task_idand the tax return conversation endpoints already worked this way and are unchanged, so beeswax-mcp is unaffected.2026-09-15 — Tokens stop working when their user leaves the account
Behaviour change: a token is accepted only while its user still belongs to the token's account. Once they are removed from it (by an Owner, or when their employment ends in payroll), every request made with their tokens returns
401 {"error":"Invalid or expired token"}, on the API and through the MCP server alike.Removing someone also revokes their tokens for that account, so adding the person back later does not revive them; they create new ones.
A token cannot be created, re-enabled or given a new secret for someone who is not a current member of the account, including a person whose invitation is still pending. Revoking a token always works.
A person's tokens for their other accounts are unaffected, and switching between accounts in the web app changes nothing.
2026-09-11 — Bank reconciliations: read them, tick and untick rows; possible-duplicates report
- New
reconciliations:readandreconciliations:writescopes. GET /reconciliations,GET /reconciliations/:id(header, every row of the table, the processed statement's lines),POST /reconciliations(from figures, or from a statement PDF that Beeswax extracts and matches),PATCH /reconciliations/:id.POST /reconciliations/:id/confirmand/unconfirmtick and untick rows byremittance_ids/line_item_ids;POST .../statementuploads a PDF for the AI statement run,GET .../statement_statuspolls it;POST .../statement_lines/:line_id/matchand/unmatchtie a statement line to a row with your reasoning.- Every write is pushed to the reconciliation page over Turbo Streams, so a person watching it sees the rows tick off as the API client works.
GET /reconciliations/possible_duplicates: a bill paid twice, a bill entered twice, or the same movement twice on the statement.- Behaviour change: a payment allocated from a bank-feed line now reads
as
reconciled: true(on/journal_entries,/paymentsand the web badges) even before a statement PDF covers its month. The feed line is the bank's evidence; the bank reconciliation still ticks it separately. - beeswax-mcp gains
list_bank_reconciliations,get_bank_reconciliation,create_bank_reconciliation,update_bank_reconciliation,reconcile_bank_rows,unreconcile_bank_rows,upload_bank_statement,get_bank_statement_status,match_bank_statement_line,unmatch_bank_statement_lineandfind_possible_duplicate_payments.
2026-09-07 — Tokens hashed at rest; new tokens carry a beeswax_ prefix
- API tokens are no longer stored in clear. Beeswax keeps a SHA-256 digest and the first 12 characters (shown in the token list); the full value is shown once, at creation, and cannot be retrieved afterwards.
- Newly created tokens start with
beeswax_(8 + 43 characters) so secret scanners can recognise a leaked one. Existing tokens keep working unchanged. - Reusing a device login (
POST /sessions) or re-dispatching a bug report always issues a fresh secret, since the old one cannot be read back. - No request or response shape changed.
2026-09-07 — Per-IP limit no longer caps authenticated API traffic; allowance headers on every response
- Requests to
/new_api/v1/that carry a Bearer token are exempt from the general 300-per-5-minutes per-IP limit that protects the web app. They are limited per token (300 per minute, unchanged) and by a per-IP backstop of 3,000 per 5 minutes. Unauthenticated API requests, includingPOST /sessions, stay under the general limit and the login throttles. - Every API response now carries
X-RateLimit-Limit,X-RateLimit-RemainingandX-RateLimit-Reset(Unix time) for the per-token limit, so a client can pace itself instead of discovering the limit through a 429. The 429 response is unchanged.
How to read this
Entries are newest first. Behaviour change marks anything that alters what an existing request returns or does; everything else is additive and safe to ignore until you want it. The MCP server version that mirrors each change is noted where relevant.
Within v1 nothing is removed. See Versioning and compatibility.
2026-09-07 — Developer documentation
- The API documentation moved into this help centre as the Developers category: overview and quickstart, authentication and scopes, request and response conventions, the ledger rule, sync and idempotency, recipes, automation tools, best practices for apps, an endpoint reference and this changelog. The in-app API Tokens page now links here instead of carrying its own copy of the endpoint table.
- Documented for the first time: the per-IP rate limit alongside the per-token one, money being returned as decimal strings,
X-Request-Id,GET /meta, and thatPOST /sessionsis reserved for first-party clients. - No API behaviour changed.
2026-09-06 — Idempotency-Key on every write
- Every
POST,PATCHandDELETEaccepts anIdempotency-Keyheader (up to 255 characters). The first request with a key runs and its response (2xx or 4xx) is stored against the token for 24 hours; a retry with the same key and the same request replays it withIdempotent-Replayed: trueand writes nothing. Same key, different request: 422. A retry overtaking the in-flight original: 409, with abandoned claims released after 90 seconds. See Sync & safe retries. - MCP 1.7.0: create tools and
apply_paymenttakeidempotency_key.
2026-09-06 — updated_since on every listing (incremental sync)
- Every listing accepts
?updated_since=<ISO 8601>and returns rows whoseupdated_atis on or after that instant. Line and contact-person edits touch their parent, so a changed line surfaces the document. meta.filter.updated_sinceechoes the applied instant andmeta.server_timeis the value to pass next time. The unpaginated listings (/active_account/projects,/active_account/transaction_accounts) now carry ametaobject too.updated_atadded to the list rows that lacked it: journal-entry listings, manual journals, milestones, projects, time entries, chart of accounts.- A malformed
updated_sinceis a 400. - Fixed: editing, adding or removing a line did not move the parent document's
updated_at. It now does, which also means such edits reliably schedule a version capture. - MCP 1.7.0: list tools take
updated_since.
2026-09-05 — Narration limit; column overflows are 422s
POST /raw_journal_entries: anarrationlonger than 255 characters is rejected with a 422 naming the limit (previously a 500). Keep the narration short; detail belongs on the lines.- Any value too long for its column anywhere in the API now returns a 422 naming the column instead of a 500.
2026-09-04 to 2026-09-05 — Invoice and expense writes, payments, contacts, attachments, versions, per-account ledger
- Invoices and expenses are writable: create (whole document, lands as a draft),
PATCHheader, sections and lines under/transaction_groups,POST …/finalise,POST …/void(finalised and unpaid),DELETE(drafts only). No unfinalise. Nothing emails anyone. Expenses takeexternal_number. - Payments:
POST /paymentsapplies a payment from a bank account to one or more finalised invoices (or expenses) in one all-or-nothing request;DELETE /payments/:idremoves one. Scopepayments:write. - Contacts:
POSTandPATCH /active_account/companies, with contact people. Scopecompanies:write. A company must be a client, a supplier, or both; owner and system companies stay read-only. - Attachments:
GET/POST/DELETE …/filesandGET …/files/:id/versionson invoices, expenses and quotes; multipartassetor JSON base64. - Version history:
GET …/versions,GET …/versions/:n,POST …/versions/:n/restoreon invoices and expenses; restore writes the next version. - Per-account ledger:
GET /active_account/transaction_accounts/:id/transactions?from=&to=returns posted postings oldest first with opening balance, running balance, period totals and closing balance. Requirestransaction_accounts:readandtransactions:read. - Reference data:
GET /active_account/transaction_accounts(+/default),GET /active_account/taxes. GET /metaadded: token introspection (account, user, scopes, server time, latest MCP version). Every response now carriesX-Beeswax-MCP-Latest-Version.- MCP 1.6.0 (131 tools) mirrors all of the above.
2026-09-04 — posted flag; unposted entries excluded from listings by default
Behaviour change for every journal-entry listing.
An API consumer reconciling a ledger counted a draft payroll template as real wages because the API returned it, and its would-be postings, indistinguishably from posted entries. The contract is now explicit; see The ledger rule.
- New field
posted(boolean) on every journal-entry-shaped response:/journal_entries,/invoices,/expenses,/payments,/quotes,/raw_journal_entries, the project-scoped readers, and every/…/{id}show. - Default changed: listings return
posted: trueentries only. Opt in with?posted=false(unposted only),?include_drafts=trueor?posted=all(both). An explicit?state=switches the default off./quotesis exempt. The applied filter is echoed asmeta.filter.posted. - Transactions endpoints: an unposted entry returns
{ "transactions": [], "posted": false, "reason": "draft|voided|template|quote" }.?preview=truereturns the would-be lines, each markedpreview: true. Posted entries return{ "transactions": [...], "posted": true }. Quotes return their lines withposted: falseand each lineledger: false. - Numbering is documented as not a signal: numbers are issued on create, drafts included. Use
posted. - MCP 1.4.0 mirrors all of the above.
Endpoints whose default result set changed: GET /journal_entries, GET /invoices, GET /expenses, GET /payments, GET /raw_journal_entries, GET /projects/{id}/invoices, GET /projects/{id}/expenses, and GET /journal_entries?type=… for every type except quotes. Endpoints whose response shape changed: every GET /…/{id}/transactions.
Earlier
Dates are the production release dates; the MCP server versions are approximate.
- 2026-09-03 —
GET /active_account/taxesfor tax-code lookup, so a product or line can be priced with the right tax. - 2026-08-28 — Products & services (
/transaction_templates) writable; a quote line can price itself from the catalogue viatransaction_template_id. - 2026-07 — API access and MCP included on every plan, Free included. Payments reader, version-aware MCP client, outdated-connector notifications.
- 2026-06-23 — Manual journals (
/raw_journal_entries): create posts by default,status: "draft"stages;finalise;DELETEdrafts. - 2026-06-05 — Read API under
/new_api/v1for invoices, quotes, expenses, payments, ledger transactions, journal entries, time entries, events, milestones, projects, users and accounts. The older/api/…(Grape) prefix removed. - 2026-05-07 — Quote write API (whole-quote create, header
PATCH, sections and lines,finalise,DELETE) and the first OpenAPI specification. - 2026-02-16 — Companies reader, expense inbox endpoints and invoice uploads for the iOS client.