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, tick and untick statement checks; 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 statement check 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.