Skip to content

DEVELOPERS

API Changelog

Dated record of every change to the Beeswax API and MCP server. Behaviour changes are marked; additions are listed.

2026-11-09 — Bank transfers

  • POST /bank_transfers (MCP create_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/transactions and DELETE /bank_transfers/:id (refused while reconciled). Scopes journal_entries:read and journal_entries:write. MCP list_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); lines lists 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 (MCP add_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 (MCP create_quote) creates the quote finalised and numbered, as the web app does. Pass status: "draft" to keep one back for review, then POST /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/projects and GET /active_account/projects/:id add client_id and client_name. GET /active_account/companies/:id gives the client's address and contacts (companies:read). Changing a project's client moves its updated_at; renaming the company doesn't.

2026-10-31 — Year-end checks that list their items

  • Every red or amber check now returns lines from get_year_end_review_check.
  • director_loans lines 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= and GET /reports/balance_sheet?as_at=: one total per account with ids and tax return labels, from the code behind the web reports. Require transactions:read and a role that may read the Ledger.
  • New GET /help, GET /help?q= and GET /help/:id: the help library as markdown, in the account's region. Any usable token.
  • New POST /active_account/transaction_accounts and PATCH /active_account/transaction_accounts/:id, with the new transaction_accounts:write scope. Tax return labels are checked against the account's type. Chart of accounts rows now carry tax_form_label and tax_form_label_description.
  • GET /active_account/transaction_accounts/:id/transactions adds transaction_count, and summary=true returns the totals with no lines.
  • DELETE /raw_journal_entries/:id says 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_account and delete_manual_journal, help topics as MCP resources at beeswax://help/<id>, and summary, page and per_page on list_account_transactions.

2026-10-31 — One ledger entry, one bank transaction

  • POST /reconciliations/:id/confirm: each ticked_elsewhere row now also carries a message naming the entry and the statement it is ticked on.
  • POST .../statement_lines/:line_id/match answers 409 with that message.
  • New reconciliation state value double_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 as reconciled.
  • bank_reconciled is 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 adds facts.not_in_the_ledger.
  • get_year_end_review_check opens 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/confirm leaves a row unticked when its entry is already ticked on another statement, and reports it in the new ticked_elsewhere list. POST .../statement_lines/:line_id/match answers 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_check now lists the items behind a red figure. balance_sheet_balances names each unbalanced entry and any opening balances with no other side; receivables_agree and payables_agree list payments with no control line, documents posted short of their total and journals posted straight to the account; bank_reconciled lists entries ticked on two statements and postings on no statement. Each gives the left-over amount as facts.unexplained. bank_reconciled is 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, and latest_invoice_upload on the incoming and processed expense lists) gain match_status, review_notes, potential_matches and ai_reasoning.
  • match_status: "needs_review" means the created expense is waiting for a person. potential_matches lists existing expenses it may duplicate (expense_id, confidence, reasoning); review_notes lists things to check on the expense itself, such as low extraction confidence. A review can have either or both.
  • potential_matches and ai_reasoning describe 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_matches is always [] and ai_reasoning is null; match_status and review_notes are returned to everyone.
  • The InvoiceUpload schema's status list now includes duplicate, attached and expense_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 /sessions issues, 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 answers 404. 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 answer 403, 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_id and POST /conversations/:id/mark_read accept only a conversation that belongs to the token's account. Another account's conversation, or a direct message, now returns 404, 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_id and 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:read and reconciliations:write scopes.
  • 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/confirm and /unconfirm tick and untick rows by remittance_ids / line_item_ids; POST .../statement uploads a PDF for the AI statement run, GET .../statement_status polls it; POST .../statement_lines/:line_id/match and /unmatch tie 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, /payments and 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_line and find_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, including POST /sessions, stay under the general limit and the login throttles.
  • Every API response now carries X-RateLimit-Limit, X-RateLimit-Remaining and X-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 that POST /sessions is reserved for first-party clients.
  • No API behaviour changed.

2026-09-06 — Idempotency-Key on every write

  • Every POST, PATCH and DELETE accepts an Idempotency-Key header (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 with Idempotent-Replayed: true and 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_payment take idempotency_key.

2026-09-06 — updated_since on every listing (incremental sync)

  • Every listing accepts ?updated_since=<ISO 8601> and returns rows whose updated_at is on or after that instant. Line and contact-person edits touch their parent, so a changed line surfaces the document.
  • meta.filter.updated_since echoes the applied instant and meta.server_time is the value to pass next time. The unpaginated listings (/active_account/projects, /active_account/transaction_accounts) now carry a meta object too.
  • updated_at added to the list rows that lacked it: journal-entry listings, manual journals, milestones, projects, time entries, chart of accounts.
  • A malformed updated_since is 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: a narration longer 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), PATCH header, sections and lines under /transaction_groups, POST …/finalise, POST …/void (finalised and unpaid), DELETE (drafts only). No unfinalise. Nothing emails anyone. Expenses take external_number.
  • Payments: POST /payments applies a payment from a bank account to one or more finalised invoices (or expenses) in one all-or-nothing request; DELETE /payments/:id removes one. Scope payments:write.
  • Contacts: POST and PATCH /active_account/companies, with contact people. Scope companies:write. A company must be a client, a supplier, or both; owner and system companies stay read-only.
  • Attachments: GET/POST/DELETE …/files and GET …/files/:id/versions on invoices, expenses and quotes; multipart asset or JSON base64.
  • Version history: GET …/versions, GET …/versions/:n, POST …/versions/:n/restore on 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. Requires transaction_accounts:read and transactions:read.
  • Reference data: GET /active_account/transaction_accounts (+ /default), GET /active_account/taxes.
  • GET /meta added: token introspection (account, user, scopes, server time, latest MCP version). Every response now carries X-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: true entries only. Opt in with ?posted=false (unposted only), ?include_drafts=true or ?posted=all (both). An explicit ?state= switches the default off. /quotes is exempt. The applied filter is echoed as meta.filter.posted.
  • Transactions endpoints: an unposted entry returns { "transactions": [], "posted": false, "reason": "draft|voided|template|quote" }. ?preview=true returns the would-be lines, each marked preview: true. Posted entries return { "transactions": [...], "posted": true }. Quotes return their lines with posted: false and each line ledger: 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/taxes for 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 via transaction_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; DELETE drafts.
  • 2026-06-05 — Read API under /new_api/v1 for 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.
← All resources Still have a question? ↗
Browse Topics