API Changelog

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

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, tick and untick statement checks; 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 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_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-03GET /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.
Browse Topics