StabisoDocs

Start here

ERP release notes

Changes that operators, integrators or users notice, newest first. The API contract is the OpenAPI document; /v1 changes additively, and a breaking change goes to /v2 (the exceptions are listed as "breaking (bug fix)" below: the refusal in the chart templates section of 2026-10-02, the controls on reversing entries of 2026-10-03, the close review's empty answer of 2026-10-05, tax schedules that end with the asset of 2026-10-05 and the accuracy fixes of 2026-10-05).

Each change writes its note as a file of its own in release-notes/, and this page is put together from those files.

2026-10-29: The product is called Stabiso

Before: the app, the API, the MCP connector and the documentation called the product "ESoR Headless".

After: the product is Stabiso, an Enterprise System of Record (ESoR): one system of record for finance and sales. The name changes in the app in all five languages, the sign-in and installed-app name, the documentation, the API reference (title of the OpenAPI document), the MCP server's name and instructions, e-mails, PDFs and audit files, and the label of the HubSpot property group and its fields. Nothing a caller relies on changes: the HubSpot fields keep their internal names (esor_lead_number and the others, in the group esor_headless), so reports and workflows built on them keep working. An AI assistant may show the connector under its new name after it connects again. Webhook calls now say User-Agent: Stabiso-Webhooks/1.

2026-10-29: One landing page: the pitch with the brief woven in

Before: the brief at /brief and the pitch at /pitch were two pages that told overlapping stories under the name ESoR Headless.

After: /pitch is the one landing page, under the name Stabiso. Around the pitch's story it now holds the brief's substance: how Stabiso acts as one unified Enterprise System of Record and the four ways to run it, what it covers, its countries, its controls, the audit and a look inside. It links the documentation and the app's log-in from its header and its close, and says that high availability, with a standby database in a second data centre, is part of each customer's service setup. /brief now leads to the landing page, so old links keep working. The app, the documentation and the API keep the name ESoR Headless for now.

2026-10-29: The landing page and the brief say how the data is kept safe

Before: neither the landing page nor the brief said where the data is kept or how it is protected.

After: a section "Safe on Google Cloud" on both says that ESoR Headless was built on Google Cloud, relies on Google Cloud's published certifications and SOC reports and on Cloud Run's published availability, and lists what is added on top: data, backups and documents in Zurich, daily backups with a restore to any second of the last seven days, a monthly restore drill, passkeys, the audit trail with its chained entries, auditors' own access and the complete archive export.

2026-10-29: The landing page shows companies talking to their data

Before: the landing page at /pitch named the MCP server only among what is built.

After: a section "Talk to your data" shows a company connecting its own ChatGPT or Claude to the books and asking in its own words: sales asking whom to call this week, leaders asking for every company's cash as a chart, accounting asking for the VAT report, and external auditors pulling their reports through their own read-only access and AI.

2026-10-29: The pitch is a landing page for investors

Before: the pitch at /pitch explained the step ESoR Headless skips and why now, then spoke mostly to outsourcing providers and companies thinking of outsourcing.

After: the pitch opens with where this goes, every organization running its back office as software, and keeps the step it skips and why now. New sections show what that means for any organization, what is built and running today, and how to be part of it: investors and holdings who run their own firms on it, owners, and outsourcing providers and accounting firms. The number of outcomes it states is checked against the app, like the modules, tools and API operations.

2026-10-29: ESoR Headless moves to stabiso.com

Before: everything was at erp.grunte.com: the app at /app/, the API, the MCP connector, the brief, the pitch and the documentation.

After: the public site is stabiso.com, with the pitch as its home page and the brief and the documentation at /brief and /docs. The app, the API and the MCP connector are at app.stabiso.com (https://app.stabiso.com/mcp). erp.grunte.com and www.stabiso.com forward to the new addresses. A passkey works only at the address it was made at: the first time you sign in at app.stabiso.com, touch your old passkey and then create the new address's one when your browser asks (passkeys). An AI assistant connected with erp.grunte.com/mcp is connected again with the new address.

2026-10-29: The pitch and the brief explain why the intelligence belongs in the system of record

Before: the pitch at /pitch spoke to outsourcing providers and the brief opened with the books keeping themselves; neither said plainly that ESoR Headless skips the step of adding AI agents to existing software, nor why that matters now.

After: both pages open with the idea in three steps (software people work in, AI agents bolted on, intelligence in the system of record) and a "Why now" section: Salesforce has announced it will charge outside AI agents per call for reaching its platform (the price is not set yet, and it is in talks with its partners), while in ESoR Headless the work is done where the records are and every caller uses the same API and MCP tools under the same rules. The pitch now speaks to any entrepreneur, with the ways to be part of it, and names its sources. The figures on both pages, including the number of modules, are checked against the code.

2026-10-29: The assistant answers sooner

Module-unchanged: the assistant is part of the core.

Before: every step of an answer thought at the model's highest level, and most tools had to be looked up in two extra steps before the assistant could use them; answers on erp.grunte.com took up to 100 seconds.

After: the app tells the assistant which page you are on, and it is given the tools your question needs straight away (page in POST /v1/agents/assistant): picked by TypeSafe's decision model Jev in a quarter of a second, in any language, once the deployment has its key, and by word rules until then. Everyday questions think briefly, which answers in a fraction of the time; closing a period with the assistant still thinks hard. How long each answer took, step by step, is logged.

2026-10-29: Connecting a Shopify shop explains each field

Module: shopify 1.4.1

The company and the app's Client ID now say what they are and where to find them in Shopify.

2026-10-29: Reservation and season forms explain what to enter

Module: hospitality 1.1.1

Reserve explains each field and sets the stay in one click (1, 2, 3 or 7 nights). A new season offers its usual names (high season, low season, summer, winter) and says that its price is per room and night, without tax.

2026-10-29: The booking form explains what to enter

Module: capacity 1.2.1

Every field of Book says what goes in it; for rooms it says that the departure day is not a night booked. Chips set the length in one click (1, 2 or 7 nights; 1, 2 or 5 days).

2026-10-29: The purchase order form explains what to enter

Module: purchasing 1.0.3

Every field of a purchase order says what goes in it. The expected delivery has chips (in a week, in two weeks, in a month, counted from the order date), the currency field names the vendor's currency it takes when left empty, and cancelling, closing or refusing an order says where the reason goes.

2026-10-29: Stock, delivery note and goods receipt forms explain what to enter

Module: inventory 1.3.0

  • Warehouses: type the name and the code is made from it (Lager Zürich is LAGER-ZURICH); a new warehouse is in the company's country. Every address field has an example.
  • Adjust stock offers the usual reasons as chips (damaged goods, lost or stolen, found in a count, opening stock); the write-off of goods received not invoiced offers its usual reasons too.
  • Every field of delivery notes, goods receipts, stock counts, transfers and adjustments says what goes in it. Filters above the lists stay as they were.
  • API and MCP create_warehouse: code may be left out; it is made from the name, with -2, -3 when the company has it.

2026-10-29: Subscription, recurring invoice and contract forms explain what to enter

Module: revenue 1.1.0

Before: a new subscription, recurring invoice or revenue contract asked for terms such as "Invoiced", "Spread" or "How it is delivered" without saying what the choices mean.

After:

  • Every field says what goes in it, with an example, on subscriptions (new, change, end early, renew, cancel), recurring invoices and revenue contracts (new, change, cancel, delivered).
  • One click for the usual values: a start of today or the 1st of next month, terms of 12, 24 or 36 months, renewal notice of 30, 60 or 90 days, an uplift of 3 or 5 %, a contract's last day 1, 2 or 3 years on, payment terms of 10, 30 or 60 days, and how a promise's own price was set.
  • A new recurring invoice is named after its customer and frequency until you type a name.
  • Suggest with AI on a new revenue contract proposes, for each promise, whether its revenue comes over time or on delivery and how it is spread, with a reason each.

2026-10-29: Sales forms explain what to enter and offer the usual values

Module: crm 1.1.0

Before: the deal, lead, quote and order forms had fields with only a label, and the currency, the close day and the reasons were typed by hand.

After:

  • Every field says what goes in it, with an example: new and edited deals, moving a deal, notes and tasks, leads and their conversion, website forms, quotes and sales orders and what you do with them (accept, invoice, record as sent, decline, cancel, close the rest), sales quotas and the customer's own page for answering a quote.
  • One click for the usual values: a deal's close day (end of this or next month or quarter), its currency (the customer's, the company's, EUR, USD), common reasons a deal or quote is lost, next steps, a quote's validity (1, 2 or 3 months), the country an e-mail address tells.
  • Sensible defaults: a new deal is yours unless you pick a colleague; picking a customer brings its currency; converting a lead takes the currency of its country; a new website form's page speaks your language and offers your company's website as an allowed address.
  • Suggest with AI proposes a deal's next step and a website form's name and thank-you text, with a reason each; nothing is saved until you save.

2026-10-29: Receivables forms explain what to enter, and the AI can propose allowance rates

Module: invoicing 1.1.0

Before: reminder letters, stopping reminders, write-offs and the allowance for doubtful receivables had fields with only a label.

After:

  • Every field says what goes in it, with an example, and the Include VAT switch says what off means.
  • One click for the usual values: common reasons to withdraw a reminder or stop reminding, and 25, 50, 75 or 100 % for a doubtful invoice.
  • Suggest with AI on the allowance settings proposes a percent per aging bucket for your company, with a reason each; it is saved only when you save.

2026-10-29: Asset and lease forms explain what to enter

Module: assets 1.1.0

Before: registering an asset or entering a lease showed fields such as Useful life, First and last year or Payments before commencement with only a label, and a new tax book asked for a code before anything else.

After:

  • New asset: every field says what goes in it, with an example ("3 for computers, 5 for vehicles and furniture"). The useful life has one-click chips (3, 5, 8, 10 years), and once the asset has a description, Suggest with AI proposes the depreciation method, the useful life and the first and last year for it, each with a reason. Nothing is saved until you register the asset.
  • New lease: every field is explained; the term has chips (12, 24, 36, 60 months, only up to 12 for a short-term lease), and the accounting and classification say what each choice books.
  • Tax books: choose the tax rules first and the book is named after them (Tax book Germany (AfA)); the code is made from the name as you type. This book carries deferred tax says what on and off mean.
  • The dialogs to sell, dispose of, impair, revalue or reverse an asset, and to change, end or book a lease, explain each field.
  • API: code of POST /v1/entities/{entity}/depreciation-books may be left out; it is made from the name (Tax CH is TAX-CH, then TAX-CH-2).

2026-10-29: VAT and sales tax forms explain themselves

Module: tax 1.1.0

Before: the VAT settings, VAT adjustments and sales tax setup had fields with only a label, its switches did not say what "off" means, and a sales tax area or local jurisdiction needed a code typed by hand.

After:

  • Every field says what goes in it, with an example; each VAT switch says what happens when it is off (the filing extension, the One-Stop Shop, booking at the VAT rate).
  • Sales tax areas and local jurisdictions: the name comes first and the code is made from it as you type (City of Los Angeles: CITY-OF-LOS-ANGELES); change it if you like.
  • API: code of POST /v1/entities/{entity}/sales-tax/areas and POST /v1/entities/{entity}/sales-tax/jurisdictions may be left out; it is then made from the name, -2, -3 ... when taken.

2026-10-29: Consolidation and intercompany forms explain themselves

Module: consolidation 1.1.0

Before: a new consolidation group needed a code typed by hand, and the intercompany loan, dividend and relationship forms had fields with only a label.

After:

  • Every field says what goes in it, with an example.
  • New group: the code is made from the name as you type (Demo Group: DEMO-GROUP); change it if you like. Suggest with AI proposes the framework and the goodwill treatment for your company.
  • New intercompany relationship: usual cost-plus mark-ups (3, 5, 8, 10 %) are one click, and Suggest with AI proposes the pricing method, the mark-up and the benchmark to document.
  • New intercompany loan: the currency is the lender's once you pick it (the borrower's is one click away), and the maturity is one click 1, 3 or 5 years after the start.
  • API: code of POST /v1/consolidation-groups may be left out; the group then gets one made from its name, -2, -3 ... when taken.

2026-10-29: Close forms say what to enter, and the AI proposes the close calendar

Module: close 1.1.0

Before: the close schedule, close tasks, recurring journals, the year-end appropriation and the reopening of a period had fields with only a label.

After:

  • Every field says what goes in it, with an example (a close task "Count the cash box" due on working day 2; a pause reason; the minutes a profit appropriation rests on).
  • Close schedule: Suggest with AI proposes the working day of each close step for your company, with a reason each; it saves nothing until you press Save.
  • Year-end appropriation: with the date of the resolution set, one click fills the reference "Minutes of the general meeting of" that date.
  • Period close: opening the first fiscal year and reopening a period say what to enter.

2026-10-29: Banking forms say what to enter

Module: banking 1.1.0

Before: the bank statement import, the reconciliation, the payment proposal, direct debits and mandates asked for dates, accounts and reasons with only a label.

After:

  • Every field of these screens says what goes in it, with an example: which file to import and in which format, what the statement date of a reconciliation is, what a bank line booked to an account or ignored needs, which day a payment run or a collection takes the money.
  • The filters above the lists (statement, status) stay short and need no explanation.
  • Settings → Bank accounts: a new account starts in the company's own currency instead of EUR, and its name, IBAN, BIC and address fields explain themselves too.

2026-10-29: Core forms explain every field, and the AI proposes an account's type

Before: most forms outside the support desk showed a field's label and nothing else. Setting up a company, a new customer or vendor, a payment, an invoice or bill, a journal entry, a price list, a cost centre or project, a user or an approval rule left you to guess what to type, a country was two letters you had to know, and a cost centre or price list asked for a code you had to invent.

After:

  • Every field of the core forms says what goes in it, with an example, in all five languages: company setup and new companies, customers and vendors and their contacts, invoices and bills, payments and refunds, journal entries and reversals, products and price lists, cost centres and projects, users and approvers, the approval policy, API keys, webhooks, the archive export, the data import and sign-in. A VAT number field shows the format of the country chosen.
  • New account on the chart of accounts has Suggest with AI: from the name you typed it proposes the type and the report line, with a sentence on why. A new field under Settings › Your own fields has it too: it proposes whether the field takes text, a number, a date, yes or no or a choice, and the choices.
  • A new customer's or vendor's country is picked from a list of every country by name, your company's own first.
  • Codes are made from names: leave the code of a cost centre or project, of one of its values or of a price list empty and it is made from the name (Wholesale CHF is WHOLESALE-CHF, with -2 when that code is taken).
  • API: code is optional on POST /v1/dimensions, POST /v1/dimensions/{code}/values and POST /v1/price-lists; left out, it is made from the name in the same way.

2026-10-29: Company sign-in: the client id says where to find it

Module: company_sign_in 1.1.0

Before: Before: the client id of the identity provider's app had only a label.

After: After: the field says it is the id the identity provider shows for the app you registered, with an example.

2026-10-29: Salesforce import: the file field says what to upload

Module: salesforce 1.1.0

Before: Before: the import asked for a file with nothing more.

After: After: the field says to upload a CSV or Excel export from Salesforce, one object per file.

Module: hubspot 1.1.0

Before: Before: linking HubSpot asked for a company with nothing more.

After: After: the field says it is the company whose customers and deals HubSpot is linked to.

2026-10-29: Roles: the role and approver forms explain each field

Module: roles 1.1.0

Before: Before: a role's name, what it is for and its sales records, and an approver's name, had only labels.

After: After: each says what goes in it, with an example (Sales manager, a sentence on who gets the role, the name as in the commercial register).

2026-10-29: Auditor access: the invitation explains each field

Module: auditors 1.1.0

Before: Before: inviting an auditor asked for an e-mail and a name with nothing more.

After: After: each says what goes in it, with an example of a work address and of a name as the access log shows it.

2026-10-29: Plans and forecasts: the plan forms explain each field

Module: analytics 1.1.0

Before: Before: renaming a plan, sending it back, rolling it forward and adding an account had fields with only a label.

After: After: each says what goes in it; rolling forward says when the average of recent periods fits and when the same period last year does. The period picker above the figures is unchanged.

2026-10-29: Saved reports: the report builder explains each choice

Module: saved_reports 1.1.0

Before: Before: the builder's records, columns, grouping, sums, order and conditions, and the schedule and sharing, had only labels.

After: After: each says what it does, with an example (group by Customer, a condition on Status or Due date, the hour the e-mail comes in the company's time zone).

2026-10-29: Expenses: every field explains what goes in it

Module: expenses 1.2.0

Before: Before: the expense, receipt-less expense, policy and card forms had fields with only a label.

After: After: each field says what goes in it, with an example (the receipt's date and total, what a category decides, when a receipt is too old, the card's last four digits, the IBAN you are paid back to).

2026-10-29: Support cases: every field explains itself, and the AI proposes a subject, priority and queue

Module: support 1.2.0

Before: Before: the new-case, triage, reply and close forms of a case had fields with only a label; the priority gave no hint which one to choose.

After: After: every field says what goes in it, with an example, and the priority says what urgent, high, normal and low mean. Once a new case has a description, Suggest with AI proposes a short subject, the priority and the queue, each with a reason; check them, then open the case. The filters above the case list are unchanged.

2026-10-29: Forms explain what to enter, and the AI can propose it

Module: support 1.1.0

Before: the support desk's New queue form asked for a code, a name and eight empty service-level boxes with nothing to say what a sensible value is, and its switches did not say what "off" means. Many other forms had fields with only a label.

After:

  • Support queues: every field says what goes in it, with an example. The code is made from the name as you type (Customer support: CUSTOMER-SUPPORT). A new queue starts with standard service levels counted in office hours (Monday to Friday 08:00 to 17:00); Fast, Standard, Relaxed and No targets fill all eight boxes at once, each box shows its unit (h). With a contact e-mail on the company, the form offers an inbox address of its domain.
  • Suggest with AI on the queue form proposes a name, service levels and working hours for your company, with a sentence on why for each. It saves nothing and never makes up names, addresses, amounts or bank details; when the AI is slow, the form stays as it was and the button asks you to try again.
  • The other forms follow area by area; a check keeps new fields from shipping without an explanation.
  • API: POST /v1/agents/form-suggestions proposes values for a form's fields (read scope; no answer in time is status waiting with retry_after_seconds, never 502).

2026-10-28: Invoicing and receivables are a module of their own

Module: invoicing 1.0.0

Before: customer and vendor statements, reminder letters with their fees, interest and blocks, customer credit limits, write-offs and what follows them, the allowance for doubtful receivables, and sending invoices to customers were part of the core of ESoR Headless, so a change to them meant changing and testing the core with them.

After: they are one module of their own, with their tables, rules, operations, AI tools, close check, screens and tests together, and their own version, so they can be changed, tested and shipped on their own. Everything works as before: the Customer statement, Customer activity, Vendor statement, Reminders, Reminder settings, Credit exposure and Receivable allowance pages, reminders, write-offs and dunning blocks on an invoice, a customer's credit limit on their card and on sales orders, sending an invoice or recording its hand-over, the allowance in the close and the AI tools for statements, reminders, invoice output and the allowance. The invoices, bills and credit notes themselves, their open items and payments stay in the core, as the one engine of documents every other part of ESoR Headless posts through.

2026-10-28: Consolidation operations keep their place in the API

Module: consolidation 1.0.2

Before: the consolidation module's operations were listed after the reminder settings in the API reference.

After: the reminder settings moved into the invoicing and receivables module, so the consolidation operations are placed after the price lists, where they were. Nothing changes in what they do.

2026-10-28: Autopilot reads reminders through the invoicing module

Module: autopilot 1.0.4

Before: Autopilot's customers paid on time outcome read the reminders due from the core, and its month closed outcome ran the allowance for doubtful receivables itself.

After: it reads the reminders due through the invoicing and receivables module, which it now builds on, and the allowance runs as that module's period-end run in the close. Nothing changes in what the outcomes do.

2026-10-28: The support desk no longer reads the CRM and sales code

Module: support 1.0.3

Before: a case's customer and the people a queue may hold were checked with the core's CRM code.

After: CRM and sales is a module of its own, and the support desk does not need it: it checks the customer and the people through the core's checked interface, and reads a customer's open deals from the customer's card. It works exactly as before.

2026-10-28: The Salesforce import builds on the CRM and sales module

Module: salesforce 1.0.1

Before: the Salesforce import wrote deals and their timeline through the core's own CRM and sales code.

After: CRM and sales is a module of its own, so this module builds on it and reads it through its public face, the way one module may use another. It works exactly as before.

2026-10-28: Saved reports build on the CRM and sales module

Module: saved_reports 1.0.3

Before: a saved report of deals, quotes or sales orders read them through the core's own CRM and sales code.

After: CRM and sales is a module of its own, so this module builds on it and reads it through its public face, the way one module may use another. It works exactly as before.

2026-10-28: Revenue builds on the CRM and sales module

Module: revenue 1.0.2

Before: subscriptions read a won deal, and renewals opened their deals through the core's own CRM and sales code.

After: CRM and sales is a module of its own, so this module builds on it and reads it through its public face, the way one module may use another. It works exactly as before.

2026-10-28: Inventory builds on the CRM and sales module

Module: inventory 1.2.1

Before: delivery notes read the sales orders they deliver through the core's own CRM and sales code.

After: CRM and sales is a module of its own, so this module builds on it and reads it through its public face, the way one module may use another. It works exactly as before.

2026-10-28: HubSpot builds on the CRM and sales module

Module: hubspot 1.0.1

Before: HubSpot contacts became leads, and their deals were reported back through the core's own CRM and sales code.

After: CRM and sales is a module of its own, so this module builds on it and reads it through its public face, the way one module may use another. It works exactly as before.

2026-10-28: Gmail and Calendar build on the CRM and sales module

Module: mailbox 1.0.1

Before: e-mails and meetings were put on the timelines of deals through the core's own CRM and sales code.

After: CRM and sales is a module of its own, so this module builds on it and reads it through its public face, the way one module may use another. It works exactly as before.

2026-10-28: CRM and sales is its own module

Module: crm 1.0.0

Before: CRM and sales (deals and the pipeline, calls, meetings, notes and tasks, leads and website forms, quotas and the sales reports, quotes and sales orders with their online answers) was part of the core of ESoR Headless, so a change to it meant changing and testing the core with it.

After: it is one module of its own, with its tables, operations, MCP tools, screens and tests together and its own version, and it uses the core only through the checked interface (ADR-0102). Everything works exactly as before: the pipeline, tasks and their reminders, leads and website forms, the sales reports, quotes sent and answered online, sales orders invoiced in full or in parts, a customer's credit limit, Find records, merging customers and handing a person's records over. Contacts, the organization's own fields and sales teams stay in the core, because customers obey them too.

2026-10-28: Autopilot builds on the CRM and sales module

Module: autopilot 1.0.3

Before: the outcomes "leads followed up" and "won deals billed" read the leads and deals through the core's own CRM and sales code.

After: CRM and sales is a module of its own, so this module builds on it and reads it through its public face, the way one module may use another. It works exactly as before.

2026-10-28: Auditors build on the CRM and sales module

Module: auditors 1.0.4

Before: the auditors' evidence pack read the quotes and sales orders through the core's own CRM and sales code.

After: CRM and sales is a module of its own, so this module builds on it and reads it through its public face, the way one module may use another. It works exactly as before.

2026-10-28: The tax demo receives its statements through the bank and payments module

Module: tax 1.0.1

Before: the demo of the US company read in its bank statement through the core.

After: it hands the statement to the bank and payments module, which reads it in and books what the matcher left, as before. Nothing changes in the demo's books.

2026-10-28: Bank and payments are a module of their own

Module: banking 1.0.0

Before: bank statements, their matching and reconciliation, payment proposals and payment files, and SEPA direct debits were part of the core of ESoR Headless, so a change to them meant changing and testing the core with them.

After: they are one module of their own, with their tables, rules, operations, AI tools, demo data, close checks, screens and tests together, and their own version, so they can be changed, tested and shipped on their own. Everything works as before: the Bank statements, Bank reconciliation, Vendor payments, Payment proposal and Direct debits pages, a customer's mandates on their card, the close job's bank matching, the AI's reconcile proposals and the AI tools for the bank, payment runs and direct debits. Recording, applying and reversing a payment stay in the core, so invoices, bills and write-offs keep recording payments as before.

2026-10-28: Autopilot reads the bank through the bank and payments module

Module: autopilot 1.0.2

Before: Autopilot's bank, payables and receivables outcomes used the bank matcher, the payment proposal and direct debits of the core.

After: they use the same work through the bank and payments module, which Autopilot now builds on. Nothing changes in what the outcomes do.

2026-10-28: The slowest part of the tests after go-live is as quick as the others

Before: one of the four parts of the tests after go-live held the longest test file on top of its share, so it took over 30 minutes while the others took about 20. The check for a stuck machine then restarted it, and a release took an hour to be confirmed.

After: each part is dealt its share first and only then cut into its four smaller pieces, so the four parts take about as long as each other. A machine counts as stuck only after 40 minutes. Testing describes it.

2026-10-27: Tests after go-live no longer run the test machines out of memory

Before: since the afternoon of 27 October one part of the tests after go-live stopped every time, because its test workers outgrew the machine's memory. The releases since stayed live but were not confirmed by those tests.

After: each part runs in four smaller pieces with fresh workers, and only as many workers as the machine's memory holds. A test that takes more than 10 minutes now says where it waits. Testing describes it.

2026-10-27: Sign in with your company account

Module: company_sign_in 1.0.0

Before: People signed in with Google or a password, then with their passkey. A company that manages its people in Okta or Microsoft Entra ID had to remove leavers in two places.

After: An administrator connects the company's Okta, Microsoft Entra ID or other OpenID Connect provider on Settings → Company sign-in, following the steps the page shows. People choose Use your company account on the sign-in page, sign in at their company's page and then with their passkey. Once it has been used, it can be made the only way in for the company's domains, so switching someone off at the provider switches them off here. See Company sign-in.

2026-10-27: Autopilot builds on the close module

Module: autopilot 1.0.1

Before: Autopilot's "The month is closed", "The group's reports are ready" and recurring bookings read the close calendar, the close memo and the recurring journals from the core.

After: those are the close module's, so Autopilot builds on it and reads them there. Every outcome works exactly as before.

2026-10-27: What bank, payments, CRM and sales read of invoicing is named for modules

Before: bank and payments, and CRM and sales, read invoicing's open items, documents, cash discounts, credit limits and document output straight from the core's inside, so neither could become a feature module before invoicing had.

After: each of those 35 names is named in erp.sdk, the interface a feature module uses: open items, settlements, order lines and the order's credit check in erp.sdk.trade, cash discounts in erp.sdk.payments, a document's output and its sending in erp.sdk.output and erp.sdk.pdf, and the company query parameter in erp.sdk.api. A check (tests/architecture/test_invoicing_seam.py) refuses a new read of invoicing from either area that erp.sdk does not name. Nothing the platform does changes; the moves of invoicing, bank and payments, and CRM and sales into modules (ADR-0102 §17) build on it.

2026-10-27: The documentation home, the brief and the pitch name what went live this week

Before: the documentation home listed the areas of the system without the forecasts, expenses or the hotel front desk, and named the EU without Norway; the brief's order-to-cash line read as if Shopify orders came in; the pitch's list of what the system does left out receipts, expense checks and the forecasts.

After: the documentation home names the 13-week cash forecast, the rolling 12-month forecast, expenses from a receipt's photo and the hotel front desk, the countries include Norway, and it links Industries and Modules. The brief says a Shopify shop's products are kept in step (orders do not come in from Shopify yet) and that each company's industry puts its screens first. The pitch lists reading receipt photos, checking expenses against the policy and keeping both forecasts among what the system does.

2026-10-27: Each merge is tested sooner, and the next one goes live without waiting

Before: after a change went live, the next change waited until the whole test run on it ended, about 20 minutes and sometimes over an hour when a test machine stopped answering. The Python tests ran in four uneven parts (25, 11, 17 and 13 minutes on 27 October), and each change also ran the whole suite twice on its own before it was sent.

After: the next change goes live as soon as the one before serves, and the newer release's tests cover both. The four parts are dealt by how long each test file takes, so they end at about the same time. A test machine that is still busy after 30 minutes is restarted instead of holding the release for an hour. Before sending, a change runs the whole suite only when a rollback could not take it back (a database change or the test set-up). Every release is still tested in full right after it goes live and rolls back on its own when that fails. Testing describes it.

2026-10-27: Intercompany dialogs use the core's reason dialog

Module: consolidation 1.0.1

Before: the intercompany screens borrowed the dialog that asks for a reason from the year-end close screen.

After: the dialog is part of the app's core, so the intercompany screens keep it now that the year-end close is a module of its own. Intercompany works exactly as before.

2026-10-27: Recurring invoices run in the close module's job

Module: revenue 1.0.1

Before: the nightly close job that makes due recurring invoices was part of the core.

After: the close job is the close module's; recurring invoices and renewal deals are made by it exactly as before.

2026-10-27: Analytics operations keep their place beside the close module

Module: analytics 1.0.2

Before: the plans and analytics operations were listed after the core's year-end close operations.

After: the year-end close is the close module's, so they are placed after the close module's operations instead. Plans and analytics work exactly as before.

2026-10-27: Auditors read closed years through the close module

Module: auditors 1.0.3

Before: the auditors' evidence pack and access scope read the year-end closes from the core.

After: the year-end close is the close module's, so they ask the core which years are closed instead. What an auditor sees is exactly as before.

2026-10-27: Month-end and year-end close are their own module

Module: close 1.0.0

Before: the close calendar and its tasks, the nightly close job, the year-end close and recurring journals were part of the core of ESoR Headless, so a change to any of them meant changing and testing the core with it.

After: they are one module of their own, with their tables, operations, MCP tools, close checks, screens and tests together and their own version, and it uses the core only through the checked interface (ADR-0102). The close calendar, the steps the nightly job runs on their working day, closing a fiscal year, booking the shareholders' resolution, catching up earlier years and recurring journals work exactly as before. Periods, closing and reopening a period and the close checklist stay in the core.

2026-10-27: Autopilot is its own module

Module: autopilot 1.0.0

Before: Autopilot (its outcomes, its passes and the questions its AI asks, the Processes page) was part of the core of ESoR Headless, so a change to it meant changing and testing the core with it.

After: it is one module of its own, with its tables, operations, MCP tools, screens and tests together and its own version, and it uses the core only through the checked interface (ADR-0102). All twelve outcomes, switching them on and off, Run now, the daily pass, the passes and their steps, the questions and their answers work exactly as before; the work other modules give its outcomes (deliveries and stays to invoice, the VAT returns, the group close) still reaches it the same way.

2026-10-27: Analytics follows the areas a role opens

Module: analytics 1.0.1

Before: Analytics showed above the sidebar's groups for everyone, also for a person whose role does not open Reports.

After: Analytics shows only to people whose role opens Reports, as its figures are refused to the others anyway. See Roles.

2026-10-27: Shopify refunds become credit notes paid back out of the balance

Module: shopify 1.4.0

Before: when a shop refunded an order, or cancelled one after part of it had been invoiced, the order waited for you: the credit note and the money back to the customer had to be recorded by hand.

After: every round reads the refunds of the shop's refunded orders. A refund of goods that were sent becomes a credit note of their invoice (with the shipping when the shop gave it back), approved like any credit note, and once the invoice was paid in, a payment out of the payment provider's balance pays the customer back. Goods refunded before they were sent are simply not invoiced, and what is left of the sales order is closed. The order's row under Settings > Shopify > Orders shows its credit notes. A refund without goods, one whose credit note differs from what the shop refunded, or one of goods on more than one invoice waits for you, with the reason on the page. See Shopify.

2026-10-27: Bills and receipts are kept when the AI is slow

Module: expenses 1.1.0

Before: when the AI provider was slow for a few minutes, reading an uploaded bill failed after about 85 seconds with "the model could not complete this request" (an error the monitoring counts as the service failing), even though a backup model was asked: it ran at the same provider and thought as hard, so it was cut off too. A receipt photo was lost the same way.

After:

  • The backup model of a bill or receipt read reads with little thinking, so that it answers in time when the first did not. A person checks every reading before anything is booked.
  • When no model answers at all, the bill upload says so in plain words, counts down and reads the file again by itself, up to three times in all; then the file stays in the list with Read again. Nothing is read, stored or booked meanwhile.
  • A receipt is kept all the same: it becomes your draft expense with nothing read from it, and the draft says to fill in what the receipt says.
  • API: POST /v1/agents/bill-capture/read answers status waiting with retry_after_seconds (and extracted null) instead of 502 when no model answers in time; status is read otherwise. Every agent names a model that did not answer with the error model_unavailable (before: agent_failed).

2026-10-27: Shopify payouts: fees and chargebacks booked, the bank line matched by itself

Module: shopify 1.3.0

Before: the receipts of a shop's orders went into the payment provider's balance, but what Shopify kept when it paid that balance out (its fees, the chargebacks) had to be booked by hand, and so did the payout's line on the bank statement.

After: every round reads the shop's Shopify Payments payouts of the last 30 days and lists them under Settings > Shopify > Payouts. Once a payout is on its way to the bank, one entry books what Shopify kept against the provider's balance: the fees to bank fees (KMU 6940, IFRS 7100) and chargebacks to losses on receivables (KMU 3805, IFRS 6600), approved like any entry. When the bank statement shows the payout, with its exact amount and naming Shopify, the line is posted to the provider's balance by itself. A payout whose amounts do not add up waits for you, with the reason on the page. Refunds stay in the balance until the next step books them as credit notes. See Shopify.

2026-10-27: Shopify drop shipping: suppliers get a purchase order for each order

Module: shopify 1.2.0

Before: a drop shipper whose suppliers send the shop's goods straight to the customers ordered from them outside ESoR, and each supplier's bill was booked without an order to check it against.

After: under Settings > Shopify > Drop shipping, the page lists the vendors the shop's products name, and you choose which of your vendors ships each one's goods. From then on every order of those goods becomes a purchase order of that vendor: at the cost per item in Shopify, sent to the customer's address, naming the order and approved like any purchase order. The vendor's bill naming it is matched to it and books the cost to your purchases of goods (a new account role goods_purchases: France 607000, SKR04 6100; else the cost of goods sold, KMU 4200). A product without a cost per item in Shopify keeps its order waiting, with the reason on the page; an order cancelled before it was sent cancels its purchase orders too. See Shopify.

2026-10-27: VAT, sales tax and withholding are their own module

Module: tax 1.0.0

Before: VAT returns, the VAT report, US sales tax and withholding tax were part of the core of ESoR Headless, so a change to any of them meant changing and testing the core with it.

After: they are one module of their own, with their tables, operations, MCP tools, close check, screens and tests together and their own version, and it uses the core only through the checked interface (ADR-0102). VAT returns of every country, their filing files, filing and payment, the VAT report, the lock a final return puts on its period, sales tax setup and returns, and withholding tax on vendor bills and payments work exactly as before; so does the VAT on every invoice and bill, which stays in the core.

2026-10-27: Purchasing tools keep their place beside the tax module

Module: purchasing 1.0.2

Before: the purchasing MCP tools were listed after the core's VAT return tools.

After: VAT returns are the tax module's, so the purchasing tools are placed after the month-end close tools instead. Purchasing works exactly as before.

2026-10-27: Auditor operations keep their place beside the tax module

Module: auditors 1.0.2

Before: the auditor access operations were listed after the VAT return operations of the core.

After: VAT returns are the tax module's, so the auditor access operations are placed after the e-invoicing operations instead. Auditor access works exactly as before.

2026-10-27: The rolling forecast reads plans through the analytics module

Module: rolling_forecast 1.0.1

Before: the rolling forecast made and rolled each company's forecast with the core's plans.

After: plans are the analytics module's, so the rolling forecast builds on that module and reaches its plans through the module's checked face. The forecast, its roll after each close and Home's figure are the same as before.

2026-10-27: Plans and analytics become their own module

Module: analytics 1.0.0

Before: budgets and forecasts and the Analytics screen's figures (trends, key figures, breakdowns and what moved) were part of the core of ESoR Headless, so a change to any of them meant changing and testing the core with it.

After: they are a module of their own (analytics, version 1.0.0): their tables, rules, operations, AI tools, screens and tests sit together, and they work with the core only through the checked interface. Budgets and forecasts with their approval, roll and comparison, the budget column of the income statement, the forecast roll of the month-end close, the Analytics screen and the AI tools work exactly as before, and the menu is unchanged. The close's flux analysis and unusual entries, the dimensions and the cash flow statement stay in the core, since the close, every posting and the consolidation rely on them.

2026-10-27: Shopify orders become sales orders, invoices and receipts

Module: shopify 1.1.0

Before: a linked Shopify shop brought its products in, but every order was still typed in by hand: the customer, the sales order, the invoice once the goods were sent, and the payment.

After: every 5 minutes the orders placed since the shop was linked come in. A buyer becomes a customer (guests one per country, as consumers who pay at once), the order a confirmed sales order with the shop's prices net of tax, and each shipment an invoice of what it sent, the shipping with the first one. Once the invoice is posted and the buyer has paid, a receipt pays it to the payment provider's balance, a new account role provider_balance (KMU 1092, SKR04 2480, IFRS, UK, US and Canada 1080), added from the company's chart the first time. An order cancelled before anything was sent cancels its sales order; an invoice that differs from what the shop charged, or an order cancelled after it was invoiced, shows on the Shopify page for a person. Payouts, fees and refunds follow. See Shopify. The shop is now read in pages small enough for what Shopify allows one request: before, a real shop refused the reading of its products, so they never arrived; when Shopify asks ESoR to slow down, what was read is kept and the next round goes on from there.

2026-10-26: The brief and the docs say what the AI reviewer decides now

Before: the brief, the pitch and the documentation home said the AI reviewer decides only new customers and vendors, and that every other controlled step waits for a second person.

After: they say what it decides since the reviewer took on more steps: it clears a new customer or vendor, a period-end run's entry (depreciation, FX revaluation, deferred tax) and an expense claim when every check passes, and holds the rest for another person, or for the owner who runs the company alone, who releases it with a passkey touch (who approves).

2026-10-26: Goods sold book to the revenue from goods

Before: a new product without a revenue account booked its sales to the default revenue account, which in every chart is the revenue from services (KMU 3400 Dienstleistungserlöse, IFRS 4100). A shop's goods landed among its services.

After: a product sold as goods (supply_type: goods) without an account of its own books to the chart's revenue from goods, a new optional account role goods_revenue (KMU 3200 Handelserlöse, IFRS, UK, US and Canada 4000, PCG 707000, PGC 700000, Italy 5100, Denmark 1010, Finland 3000, BAS 3001). SKR04 and NS 4102 keep one revenue account for all sales. A company set up before this keeps its default revenue account until it gives an account the role. See Products and Account roles.

2026-10-26: Release your own held purchase order with a passkey

Module: purchasing 1.0.1

Before: on a purchase order waiting for approval, a person who runs the company alone saw only "another person decides" and an invitation to bring someone in.

After: under the AI reviewer, that person approves the order on its card themselves, with a passkey touch, as on Approvals. In a company with more people, or in four-eyes mode, another person still decides.

2026-10-26: Capacity offers its bookings to the modules that build on it

Module: capacity 1.1.1

Before: other modules reached the capacity module's bookings through a shortcut in the core, and without the capacity module a booking was refused with "capacity is not kept in this installation".

After: the capacity module offers adding bookable items, what is free, booking and cancelling to the modules that build on it (the hotel), through its public interface. Bookings work exactly as before; the "not kept" refusal is gone because a module that books always comes with capacity.

2026-10-26: Hotel stays build on the capacity module

Module: hospitality 1.0.1

Before: a hotel stay booked its rooms through a shortcut in the core that the capacity module answered, so the core carried a piece that only two modules used.

After: the hospitality module builds on the capacity module directly, through its public interface, and is always installed with it. Reserving, changing and cancelling a stay work exactly as before.

2026-10-26: Connect your Shopify shop: its products arrive as products

Module: shopify 1.0.0

Before: a company selling in a Shopify shop typed its shop's products into ESoR by hand, and kept their names and prices in step itself.

After: under Settings → Shopify an administrator connects the company's shop with an app the company makes in Shopify's Dev Dashboard in a few minutes (its client ID and secret, or on Shopify's own page; a custom app's token from before 2026 works too). Every 5 minutes the products the shop sells arrive as the company's products, one per variant: a variant whose SKU is already a product's code is linked to that product, a new one is made otherwise, with the shop's price as its list price (net, when the shop's prices include tax). New names and prices are taken over and a product the shop stops selling is set inactive. The page lists the shop's products and what each is sold as here. The secret is kept sealed and a test copy never connects (Shopify). The shop's orders, drop shipping and payouts follow.

2026-10-26: Stock comes first for trade, retail and manufacturing

Module: inventory 1.2.0

After: for a company in wholesale and distribution, retail or manufacturing, the Stock group follows the industry's group at the top of the menu, and Delivery notes joins the industry's group in wholesale and manufacturing. Nothing else changes. See Industries.

2026-10-26: Capacity comes first for hotels and services firms

Module: capacity 1.2.0

After: Capacity and bookings moves into the menu's industry group at the top for a company in hospitality or professional services. Nothing else changes. See Industries.

2026-10-26: A hotel's Home shows today at the hotel

Module: hospitality 1.1.0

After: in a company whose industry is hospitality, Home shows Today at the hotel: how many guests arrive, are in house and leave today, and the reservations ahead, with a link to the front desk. Sales → Hotel moves into the menu's Hotel group at the top. See Industries.

2026-10-26: Choose your industry, and the app puts its screens first

Before: every company saw every screen in the same order. A hotel looked for Hotel among some sixty entries, and there was nowhere to say what the company does.

After: each company chooses its industry: hospitality, professional services, wholesale and distribution, retail, online shop and drop shipping, manufacturing, or general. The setup wizard asks in a new Industry step, Add company asks too, and Settings → Company → Industry changes it at any time. The menu then starts with a group of that industry's screens (a hotel's Hotel group holds the front desk, capacity, customers, invoices and price lists), the stock group follows where it fits, and Home shows a "where to start" list. Nothing is switched off or hidden: every other screen stays where it was, and the API and assistants work the same. Industries that are not fully built say so: retail has no till yet, and online shop and manufacturing are marked as coming. Existing companies start as General, the layout they had.

API: industry on GET /v1/entities, POST /v1/setup, POST /v1/setup/companies, POST /v1/entities and PATCH /v1/entities/{entity}; GET /v1/industries lists the industries, how far each is built and the modules that serve it. See Industries and ADR-0108.

2026-10-26: Modules have their own guide, and the controls are described as they are

Before: nothing in the documentation said which functions are modules of their own or how a module ships, and the brief, the documentation home and the features page said the AI reviewer decides every controlled step.

After: the new guide Modules lists every module (purchasing, inventory, support desk, auditors, capacity, hospitality, branding, saved reports, Salesforce import, HubSpot, Gmail and Google Calendar), what each does, what it builds on and its guide, and explains that each module has its own version, tests and release notes on the same books and rules. A module added to the system must be on that page: the documentation check says so. The brief, the pitch, the documentation home and the features page now say what the AI reviewer decides today, a new customer or vendor, and that other controlled steps wait for a second person.

2026-10-26: Autopilot's depreciation no longer waits for an outsider

Before: the AI reviewer decided only new customers and vendors. A depreciation run that Autopilot or the close job booked, and every other posting that needed approval, still said "A second person must approve" and asked a company run by one person to invite someone from outside. The banner mixed two text sizes, and the setting that chooses who approves had no screen.

After: the AI reviewer also decides postings. The entry of a period-end run (depreciation, the FX revaluation, deferred tax) is posted at once when it is the run's own entry, unchanged and booked only once. Any other posting that would wait is held under Approvals, and the person who runs the company alone approves it with a passkey touch. Requests that were already waiting are decided the same way. The banner is now one sentence in one style. Settings → Approval policy → Who approves switches between the AI reviewer and a second person.

2026-10-26: Cash forecast leaves out entries that move no cash

Module: cash_forecast 1.0.1

Before: only recurring journals on a cash account were scheduled.

After: recurring journals with no cash line (an allocation) are scheduled too, for the rolling forecast's profit and loss; the cash forecast leaves them out, so it shows what it showed before. See Cash forecast.

2026-10-26: A rolling forecast for every company

Module: rolling_forecast 1.0.0

Before: a company had a rolling forecast only when someone created one, and a roll filled new periods from the average of past periods alone.

After: every company has a Rolling forecast of its profit and loss for the next 12 periods without any setup. The system makes it and rolls it again after each month's close: closed periods become actuals, open ones are worked out again from the run rate and are never below what is already scheduled (the invoices of recurring invoices and subscriptions, recurring journals, depreciation), and figures a person changed are kept. Home shows the net income it forecasts; each roll is a draft a person approves on Reports → Budgets and Forecasts. Every forecast's roll now uses what is scheduled (method schedule), POST /v1/plans/{plan_id}/roll takes refresh, and setting a draft's lines keeps the method of a line whose amount did not change. New: GET /v1/entities/{entity}/rolling-forecast and the MCP tool rolling_forecast. See Rolling forecast.

2026-10-26: Saved reports read subscriptions through the revenue module

Module: saved_reports 1.0.2

Before: a saved report of subscriptions read the subscription records from the core.

After: subscriptions are the revenue module's, so saved reports builds on that module and reads them through its public face, the way one module may use another. A report of subscriptions shows the same rows and columns as before.

2026-10-26: Expenses from a photo of the receipt

Module: expenses 1.0.0

Before: what people paid for the company was typed in as a vendor bill by hand, someone checked each one, and paying it back was a separate step.

After: Purchasing → Expenses → Photograph a receipt opens the phone's camera. The receipt is read, and what was paid, when, the VAT per rate, the category, the project and the company card fill in by themselves; each field says where it came from. The person checks the draft and submits it. The AI reviewer checks it against the company's expense policy (Settings → Expense policy and cards: a limit per receipt, lower limits for meals or gifts, the oldest receipt taken, when a project is needed): within the policy it is approved and booked at once as a bill; anything it flags waits in Approvals for a person who did not pay it. What a person paid with their own money is paid back in the next payment run, to the IBAN they gave once. Company cards get their statement read, each payment matched to its receipt by itself; their purchases are never paid in a payment run, and payments still without a receipt are listed for the card holder. A receipt from abroad is booked without VAT at what it cost in the company's currency. Demo AG's sales team has expenses to look at, and Sophie Laurent a company card. Over the API it is /v1/expenses and /v1/expense-cards; AI assistants have list_expenses, expense_policy, claim_expense_without_receipt, update_expense, submit_expense and list_expense_card_lines. See Expenses.

2026-10-26: Save reports you ask for

Module: saved_reports 1.0.0

Before: a list beyond the standard reports (unpaid invoices of one sales person, deals closing this quarter, last month's spending by account) was answered by the assistant once, in words, and had to be asked for again next time; nothing could be kept, pinned, shared or e-mailed.

After: ask the assistant for a list in a sentence and it shows the list beside its answer; one click saves it. Reports are also built field by field under Reports → Saved reports, over 11 kinds of record, with conditions, grouping (per currency, never adding currencies together), sums and order, and dates such as "start of last month" that keep a report current. A saved report can be pinned to Home, shared with everyone or some people, downloaded as CSV, opened group by group down to each record, and e-mailed each day, week or month with a CSV attached. Every reader sees it as themselves, in their language: only the records they may see. The same works over the API and the MCP tools run_report, save_report and the others (Saved reports). The assistant's answer has a new field cards with what a module shows beside it.

2026-10-26: Roles that say what they allow

Module: roles 1.0.0

Before: a person's access was a list of rights (read, draft, post, approve, audit, admin) set on Settings, Users; everyone with read opened every part of the app, and nothing showed which rights were no longer used.

After: Settings → Roles gives people ready-made roles (bookkeeper, accounts payable and receivable clerk, controller, sales rep and lead, support agent, warehouse clerk, internal auditor, viewer, administrator) or your own made from one, each saying in plain words what it can and cannot do. A role also sets the areas of the app a person opens: a closed area is gone from their sidebar and refused in the API and the AI assistants alike. See it as shows the app as a person sees it, and Unused rights suggests, after 90 days, taking away the rights and areas someone did not use; each is one click, with the audit trail. People without a role keep exactly the access they had. See Roles.

2026-10-26: Cash forecast for the next 13 weeks

Module: cash_forecast 1.0.0

Before: how much cash the company would have in the coming weeks had to be worked out by hand.

After: Reports → Cash forecast shows the next 13 weeks of cash from what the books already know, with nothing to type in: open invoices and bills on the day each customer and vendor really pays (learnt from the last 12 months of payments), payments waiting for approval, recurring invoices, subscriptions and recurring journals. Home shows the lowest point among its key figures. Each Monday the forecast is kept, and the screen shows what changed since last week; Explain with the assistant tells the story behind it. Assistants read it with the MCP tool cash_forecast, programs with GET /v1/entities/{entity}/cash-forecast. Nothing posts. See Cash forecast.

2026-10-26: Consolidation and intercompany are their own module

Module: consolidation 1.0.0

Before: intercompany and consolidation were part of the core of ESoR Headless, so a change to either meant changing and testing the core with it.

After: they are one module of their own, with their tables, operations, MCP tools, close checks, screens and tests together and their own version, and it uses the core only through the checked interface (ADR-0102). Relationships between your companies, mirrored invoices, the reconciliation, loans and their interest, group dividends, consolidation runs, the group statements and the group close checklist work exactly as before; so do reports in another currency, whose translation stays in the core.

2026-10-26: Fixed assets and leases are a module of their own

Module: assets 1.0.0

Before: fixed assets (the asset register, depreciation, tax books and deferred tax, changes in value, disposals) and leases were part of the core of ESoR Headless, so a change to them meant changing and testing the core with them.

After: fixed assets and leases are one module of their own, with their tables, rules, operations, AI tools, demo data, close checks, screens and tests together, and their own version, so they can be changed, tested and shipped on their own. Everything works as before: the Fixed assets and Leases pages, the depreciation, deferred tax and lease runs on the close page and in Autopilot, and the AI tools for assets and leases.

2026-10-26: The support desk reads subscriptions through the revenue module

Module: support 1.0.2

Before: a case's customer panel counted the customer's active subscriptions from the core's own tables.

After: subscriptions are the revenue module's, so the support desk asks for the count through the checked interface every module shares. The count on a case is the same as before.

2026-10-26: Auditor access reads deal billing through the revenue module

Module: auditors 1.0.1

Before: the evidence an auditor sees for a won deal read the subscriptions that bill it, and their invoices, from the core's own tables.

After: subscriptions are the revenue module's, so auditor access asks for a deal's subscriptions and invoices through the checked interface every module shares. Auditors see the same evidence as before.

2026-10-26: Revenue contracts and subscriptions become their own module

Module: revenue 1.0.0

Before: recurring invoices, revenue contracts, subscriptions, renewal deals and recurring revenue (MRR and ARR) were part of the core of ESoR Headless, so a change to any of them meant changing and testing the core with it.

After: they are a module of their own (revenue, version 1.0.0): their tables, rules, operations, AI tools, close checks, screens and tests sit together, and they work with the core only through the checked interface. Recurring invoices, revenue contracts and their recognition, subscriptions with their changes, renewals and billing, renewal deals, the recurring revenue report, the close checks, Autopilot and the AI tools work exactly as before, and the Sales menu is unchanged.

2026-10-26: Saved reports builds on the purchasing module

Module: saved_reports 1.0.1

Before: a saved report on purchase orders read them from the core.

After: the saved reports module reads purchase orders through the new purchasing module's public interface. Reports on purchase orders work exactly as before.

2026-10-26: Goods received builds on the purchasing module

Module: inventory 1.1.1

Before: goods received on purchase orders used purchasing as part of the core.

After: the inventory module uses the new purchasing module through its public interface. Recording a receipt, the GR/IR account, its report and its write-off work exactly as before.

2026-10-26: Purchasing becomes its own module

Module: purchasing 1.0.0

Before: purchase orders, receipts and the matching of vendor bills to their orders were part of the core of ESoR Headless, so a change to them meant changing and testing the core with them.

After: purchasing is a module of its own (version 1.0.0): its tables, rules, operations, AI tools, screens and tests sit together, and it works with the core only through the checked interface. Purchase orders and their approval, receipts, the matching tolerances, a variance that blocks a bill's payment until it is approved, finding an order by search and the AI tools work exactly as before; the Purchasing group of the menu is unchanged.

2026-10-26: Masked test copies keep the company branding

Module: branding 1.0.1

Before: branding was missing from the classification of the company's own setup in a masked test copy. A privacy check could mistake characters in the encoded logo for a partner's name, and branding metadata was treated as business text.

After: the branding module declares its table as company setup. A masked copy preserves its logo, colours, theme and metadata exactly, as it preserves the company and chart. The test verifies that setup is unchanged while business-partner data and history remain masked.

2026-10-26: Run a hotel: room types, seasons, check-in and check-out, the stay invoiced

Module: hospitality 1.0.0

Before: rooms could be booked by the night under Sales → Capacity and bookings, but a hotel still had to price every night, keep track of who arrived and left, and write each guest's invoice by hand.

After: Sales → Hotel is a front desk. A room type (single, double, suite) has its rooms, how many guests a room sleeps and its price per night, at the accommodation VAT rate where the country has one (3.8 % in Switzerland). Seasons give a room type another price for a range of nights; a customer's price list comes first. Reserving a stay shows the price of every night and how many rooms are free before it is saved, and never takes more rooms than are free; the agreed prices stay with the stay. Guests are checked in from their arrival day, with their room number, and checked out with what else they had (breakfast, the minibar). Check-out drafts the stay's sales invoice at once: the nights at their agreed prices and the extras, in the customer's currency, ready to check and send like any other invoice. A guest who leaves early pays only the nights spent. If an invoice is rejected, Autopilot drafts it again. Assistants in Claude or ChatGPT quote, reserve, check in and out under the same rules. Demo AG runs a small hotel this way.

API: GET/POST /v1/room-types, GET/PATCH /v1/room-types/{product_id}, GET /v1/room-types/{product_id}/quote, POST /v1/room-types/{product_id}/rates, DELETE /v1/room-rates/{rate_id}, GET/POST /v1/hotel-stays, GET /v1/hotel-stays/{stay_id} and its check-in, check-out, cancel and invoice. See Hospitality and ADR-0104.

2026-10-26: Bookings a module holds can only be cancelled through it

Module: capacity 1.1.0

Before: every capacity booking could be cancelled under Sales → Capacity and bookings.

After: a booking made by another module, such as a hotel stay, is held by it and shows which; it is cancelled only through that module (cancelling the stay), so a stay and the rooms it holds never part. POST /v1/capacity-bookings/{booking_id}/cancel answers 409 booking_held for such a booking. Bookings made under Capacity are unchanged. See Capacity.

2026-10-26: Review captured accounts and credit only the remaining quantities

Before: bill capture favoured a vendor's earlier account even for a different purchase, and an absent or invalid suggestion silently used the default expense account. A second credit note copied the original invoice's full quantities, requiring the person to correct them after a partial credit.

After: account suggestions follow what was purchased, with vendor history only a hint. The bill wizard shows account names and reasons and lets the person correct each choice before creating the bill. Missing or invalid accounts wait for a choice; the original reading stays unchanged and reviewed accounts are recorded with the bill.

Credit defaults exclude posted and pending credits, omit fully credited lines and keep the exact remaining discount and VAT, including the last rounding cent. The form shows the remaining allowance; a fully credited invoice offers no further credit. Price corrections or credits without line links that leave ambiguous quantities need manual lines. Explicit credit lines and the final over-credit guard remain in force.

The credit form waits for the current invoice and account details before showing editable lines. A late default can no longer overwrite a quantity typed while those details were loading; reopening also checks the remaining allowance again.

Test-copy timestamps now consistently use UTC, so repeating a removal request returns the same timestamp after a database read. CI runner startup dates are read portably on macOS and Linux, preserving the wait for a runner switching itself off.

The account form clears a refused save's old error when its fields change, so a corrected number no longer carries the duplicate-number warning of the previous attempt. Its current field validation remains visible.

An unfinished General Journal survives in-app navigation for the signed-in session, and refreshing asks before discarding it. Loading or saving a draft clears that warning until the next change. Unsaved work is kept in memory, never on disk.

A receipt or payment opened from an invoice waits for its current open balance before prefilling the amount. Reopening it immediately after a partial payment no longer copies the old invoice total while the balance refreshes.

After submitting drafts in bulk, the action stays busy until the list has refreshed. A draft refused because someone changed it can then be selected again with its current version, instead of repeating the same stale-version refusal. The version check is never bypassed and a retry still needs the person's selection.

Module: support 1.0.1

Before: a reply to a customer from the support desk was plain text.

After: when the company has a logo or colours (Settings → Branding), the reply also carries an HTML version with the logo and the accent band, as every e-mail to customers does; the plain text is unchanged. See Branding.

2026-10-25: Your logo, colours and theme

Module: branding 1.0.0

Before: every company looked the same in the app and on what it sent: the product's teal, no logo, plain-text e-mails.

After: Settings → Branding gives each company its own logo, a brand colour and an accent colour, and a theme (light, dark, or the device's own). The app takes them on for everyone in the company: the logo in the top bar, the colours on buttons, links and the band along the top, and the dark theme throughout. Invoices, quotes, order confirmations, delivery notes and reminders print with the logo and the colours; e-mails to customers get an HTML version with the logo beside the plain text; the online quote page and website forms show both. A colour is taken only when it can be read on white (4.5 to 1 for the brand colour, 3 to 1 for the accent). An invoice already issued keeps the look it was printed with. Every sample company has a logo of its own. Over the API it is /v1/entities/{entity}/brand and /brand/logo; AI assistants have get_company_brand, update_company_brand, set_company_logo and remove_company_logo. See Branding.

2026-10-25: Duplicate supplier bills give a usable next step

Before: a refused duplicate supplier bill told the person to void one of the bills, even when the existing bill was posted or paid and the attempted duplicate had not been saved.

After: the refusal in all five interface languages directs the person to review the existing bill and explains that a posted bill is corrected with a credit note. Duplicate detection and posting controls are unchanged.

2026-10-25: Documents fill in what the system already knows

Before: an invoice, quote, payment or purchase order needed every field, even those the system already knew: the date, and the name and price of a product. An agent over MCP had to look them up first, or guess.

After: give only what you know (What the system fills in).

  • Dates: a sales invoice, credit note, quote, sales order, payment, purchase order and goods receipt without a date is dated today where the company is. A bill's date stays required: it is the date the vendor printed (422 document_date_required).
  • Product lines: a line that names a product takes its name and its price: the customer's price from its price list, else the list price on the document's day. A product without a price is refused (422 product_unpriced), never guessed.
  • What was filled: a document keeps it in provenance.filled (field, value and where it came from), and the MCP tool create_document returns it as filled.

2026-10-25: The system fills in country, currency and language by itself

Before: a new customer or vendor needed its country and currency typed in, even when its VAT number already said where it is; a price list, a product price, a reminder letter, an intercompany loan, a new company and a consolidation group each asked for a currency the system already knew; an import file without a country or currency column was refused; a lead without a country could not become a customer.

After: whatever is left out and can be worked out is filled in, the same way in the app, in imports, over the API and for AI assistants (MCP), and every answer shows what was set (what the system fills in):

  • Customers and vendors: the country from the VAT number's prefix, else the company's; the currency of that country when the company has exchange rates for it, else the company's; the language where the country has one of ours (Germany German, France French, Sweden Swedish, the UK English, ...). The New customer and New vendor dialogs leave the currency empty for this. Import files may leave out the country, currency and language columns.
  • Price lists, product prices and standalone selling prices: the company's currency.
  • Reminder letters: the currency the customer is due a reminder in (422 reminder_currency_needed when it is due in several, none of them its own).
  • Intercompany loans: the lender's currency. Consolidation groups: the parent's currency.
  • Companies: the currency of the country (422 entity_currency_needed where it is not known); the setup wizard's company also takes the country's first accounting standard, and its answer now says the standard and currency it was set up with.
  • VAT number checks: the country the number's prefix names.

API: country, currency and language of POST /v1/entities/{entity}/parties, currency of POST /v1/price-lists, POST /v1/products/{product_id}/prices and /ssps, POST /v1/entities/{entity}/customers/{party}/reminders and POST /v1/ic/loans, functional_currency of POST /v1/entities, standard and currency of POST /v1/setup and /v1/setup/companies, reporting_currency of POST /v1/consolidation-groups and country of POST /v1/vat-registers/check are optional. The lead_customer_country refusal is gone.

2026-10-25: Customers, vendors and products are numbered by themselves

Before: whoever created a customer or vendor had to make up its number (or the system made one from the name, such as V-PAPETERIEM), on the screen, over MCP and in every import file.

After: a new customer gets the company's next number, C-00001, C-00002, ..., and a new vendor V-00001, ..., counted per company without gaps. Nobody types one: not on the screen, not over the API or MCP, not for a vendor made from a bill or e-invoice, a customer made from a lead, or a Salesforce or file import. An import that brings the numbers of the system before keeps them (leave the code column empty, or leave it out, to have them numbered). A product, fixed asset or lease takes your own article number, asset tag or contract number, or, when you leave it empty, the next number: P-00001, FA-00001, L-00001.

2026-10-25: Sell rooms, seats and people's time by the night, day or hour

Module: capacity 1.0.0

Before: ESoR Headless kept only goods in stock. A hotel room, a meeting room or a consultant's hours could be invoiced, but nothing showed what was free on a day or stopped the same room being sold twice.

After: a service product can be sold by the slot: by the night (hotel rooms), by the day (meeting rooms, seats, a person's day) or by the hour (people's time, in hours a day), with how many units each slot has. Sales → Capacity and bookings shows what is free on each of the next two weeks, with fully booked days in red, and books an item for a customer from a first to a last day (for rooms: arrival and departure). A booking that would take more than is free on any day is refused, and cancelling one frees its units. Assistants in Claude or ChatGPT read availability and book under the same rules. Demo AG sells its installation hours and its training room this way. Capacity is its own module, the base for the hospitality and professional services packs that come next.

2026-10-25: Salesforce import is its own module

Module: salesforce 1.0.0

Before: the Salesforce import was part of the core of ESoR Headless, so a change to it meant changing and testing the core with it.

After: the Salesforce import is a module of its own, with its operation, rules, screen and tests together and its own version, and it uses the core only through the checked interface (ADR-0102). Importing users, accounts, contacts, products, price books, deals and their activities works exactly as before.

2026-10-25: HubSpot is its own module

Module: hubspot 1.0.0

Before: the HubSpot link was part of the core of ESoR Headless, so a change to it meant changing and testing the core with it.

After: HubSpot is a module of its own, with its tables, operations, rounds, screen and tests together and its own version, and it uses the core only through the checked interface (ADR-0102). Connecting HubSpot, its contacts coming in as leads, what became of each going back, and a lead's "Open in HubSpot" link work exactly as before.

2026-10-25: Gmail and Google Calendar are their own module

Module: mailbox 1.0.0

Before: linking a person's Gmail and Google Calendar to the CRM was part of the core of ESoR Headless, so a change to it meant changing and testing the core with it.

After: the mailbox link is a module of its own, with its table, operations, scheduled reading, screen and tests together and its own version, and it uses the core only through the checked interface (ADR-0102). Linking, reading every 15 minutes, the e-mails and meetings on the timelines and a support inbox's mail reaching its cases work exactly as before.

2026-10-25: Automatic vendor activation names the AI reviewer

Before: when the AI reviewer cleared a vendor, the activation event and the vendor card named the person who created it as the activator. The creation form also said a second person always had to activate a vendor.

After: automatic activation is attributed to system:reviewer, with the requesting person kept separately in the audit event. The creation form explains both controls modes. Existing audit events remain unchanged.

2026-10-25: The support desk becomes its own module

Module: support 1.0.0

Before: the support desk (queues, cases, service levels, replies and the mail to a queue's inbox) was part of the core of ESoR Headless, so a change to it meant changing and testing the core with it.

After: the support desk is the second module of its own (version 1.0.0), after inventory: its tables, rules, operations, AI tools, demo data, screens and tests sit together, and it works with the core only through the checked interface. Cases, queues, replies, the customer card's case counts, the Gmail inbox and the AI tools work exactly as before; the Service group of the menu is unchanged.

2026-10-25: The AI reviewer decides vendors that were already waiting

Before: a customer or vendor whose activation was requested before the AI reviewer became the default kept waiting for a second person, and Approvals still said that nobody else could approve. A step the AI reviewer held could not be released from Approvals by the person who runs the company alone.

After: the AI reviewer also decides the activations that were already waiting, at once when an administrator switches to it and on every deploy: a clean one is activated, one with a warning waits with the reviewer's reasons. Approvals says that the AI reviewer decides, and the owner who runs the company alone releases what it held with Approve and a passkey touch.

2026-10-25: Auditors become their own module

Module: auditors 1.0.0

Before: auditor access (Settings → Auditors and the access log) and the audit files (FEC, GoBD, SAF-T and the evidence pack) were part of the core of ESoR Headless, so a change to them meant changing and testing the core with them.

After: they are the Auditors module, version 1.0.0, with their operations, screens and tests together, changed, tested and shipped on their own. Everything works exactly as before. Recording what an auditor opens stays in the core, so every request of an auditor is still logged.

2026-10-25: Enter the invoice address when creating a customer or vendor

Before: the creation form did not ask for an address. A new customer needed a second edit before an invoice could be printed or sent.

After: New customer and New vendor accept the street, building number, postal code and town. Enter the address once, when creating the record; it is saved with the other details. Address fields remain optional, and invoice output still checks the required content.

2026-10-25: Each function becomes its own module, inventory first

Before: each function of ESoR Headless (stock, delivery notes, and every other area) was spread over the whole application, so changing one meant changing and testing the core with it.

After: a function is a module of its own, with its tables, rules, operations, AI tools, demo data, screens and tests together, and its own version. It works with the core only through one small, checked interface, and the core never reaches into it, so each module can be changed, tested and shipped on its own. Inventory and delivery notes are the first module: warehouses, stock items, stock postings, counts, valuation, delivery notes and the stock an invoice ships work exactly as before. The other functions move to modules next.

2026-10-25: Goods received post to GR/IR and bills clear them

Module: inventory 1.1.0

Before: a purchase order receipt counted quantities for invoice matching, but posted nothing. The cost and the debt reached the books only with the bill, and goods bought on an order did not enter stock.

After: a receipt posts on the day the goods arrive (ADR-0077, section 7; Goods received):

  • A purchase order line can name a stock item (item). Its receipt brings the goods into stock in the chosen warehouse (warehouse on the receipt), at the line's net price converted at the rate of the receipt day: Dr inventory / Cr GR/IR.
  • Any other line that needs a receipt books Dr its account / Cr GR/IR.
  • A negative receipt takes the goods out at the value they came in with.
  • When a bill or credit note on the order posts, it clears GR/IR at the receipt value.
    • For a stock item, the price or exchange rate difference revalues the stock, and the share already sold goes to cost of goods sold.
    • Over-billing clears only what was received.
    • A revaluation is not dated before the item's latest stock posting. A bill dated earlier clears on that later day.
    • A credit note at the bill's unit price clears the quantity back. At any other price, it is a price credit.
  • New operations:
    • POST /v1/purchase-orders/{order}/receipts/preview: what a receipt would post.
    • GET /v1/purchase-orders/{order}/goods-received: per line received, billed and open.
    • GET /v1/entities/{entity}/grni: the GR/IR open items on a day, tied to the ledger.
  • Screens:
    • The order form has a stock item per line.
    • The receipt dialog shows the warehouse and the value it will post.
    • The order card has a GR/IR tab.
    • The new report Reports › Goods received not invoiced (WE/RE-Konto, Marchandises reçues non facturées, Merci ricevute da fatturare).
  • A receipt waiting for a second person is not counted by invoice matching until it is approved: the bill says it waits for approval of its receipt (beyond_received_waiting). A rejected receipt no longer counts as received, and the order card shows it as rejected.
  • Write-off of what will never be billed (goods sent free of charge, or billed without the order's number): POST /v1/purchase-orders/{order}/grni-write-off, the button "Write off what will never be billed" on the order's GR/IR tab. It takes the open value off GR/IR against the line's own account (or its bills' account), leaves the stock as it is, needs a reason, is dated from the last receipt to today and passes the posting approval. The GR/IR tab shows a write-off that waits, and the tab and the report name a line billed after its write-off.
  • A bill dated ahead clears GR/IR on its own date; its stock revaluation is dated today.
  • A company without a GR/IR account gets it with its first receipt.
  • The period close checks that GR/IR ties to the ledger (also a GR/IR balance with nothing received fails) and warns of order lines still open and of lines billed after their write-off (grni_reconciled).
  • New refusals: stock_item_line_goods, return_before_receipt_posted, return_while_write_off_waits, return_after_write_off, return_beyond_received, grni_nothing_open, grni_line_nothing_open, grni_write_off_pending, grni_write_off_future, grni_write_off_before_receipt, grni_write_off_control_account and grni_write_off_account_missing.

Also fixed in delivery notes

  • An agent's invoice with several lines of one stock item values them one after the other, as the posting books them, so the cost of goods sold it shows is the one booked.
  • A draft delivery note reserves the order lines it ships. Another note or an invoice can no longer ship the same quantity again: refusal delivery_order_line_on_draft_note (post or cancel the draft first).
  • A new delivery note can start from a sales order: Sales order fills the lines with what is left to ship. The order's card shows what is left to ship next to what is left to invoice.
  • An invoice drafted with "Invoice this delivery note" no longer puts the note's number in "Order no. (PO)": that field takes the customer's reference (of the note, else of the order), and the invoice links its delivery notes.
  • Invoicing an order line whose goods left on posted delivery notes names those notes and says to invoice their lines (delivery_order_line_shipped_on_notes).
  • Goods a customer returns on an order line no longer reopen the order for shipping (Delivery notes, "Goods returned").

2026-10-25: Changes go live first and are tested right after

Before: a merged change waited for the whole test run before it went live, about half an hour for a change of the app and as long for one of the brief's text.

After: a merged change is live about 8 minutes after its merge (the app's image, the check of the new version before it gets traffic, then the switch). The full tests run right after, on the version that serves, and a version that fails them goes back to the one before on its own; a change of the database is fixed forward instead. Deployment describes it.

2026-10-25: Reload works in every window

Before: with the app open in more than one window or tab, pressing Reload on "A new version is available" in one window switched every window to the new version, and Reload in the other windows then did nothing.

After: Reload reloads the page in every window, also when another window took the new version first. See the web UI.

2026-10-25: Service as a Software on the pitch and the brief

Before: the pitch at /pitch went from the shift in outsourcing straight to its two readers, and the brief's business model spoke of "service as software" without a source.

After: the pitch has a section of its own on the shift from Software as a Service to Service as a Software: the tool bought apart from the work against the finished work bought as one, with Julien Bek's line from Sequoia Capital's "Services: The New Software" (2026), linked to the essay: "The next $1T company will be a software company masquerading as a services firm." The brief's business model names the same shift and quotes the same line.

2026-10-25: Passkeys for every sign-in

Before: people signed in with their Google account or a password Google keeps, and that was all. A stolen password, or a fake sign-in page that passed on a Google sign-in, opened the books.

After: every sign-in ends with the person's passkey: their phone, computer or password manager asks for their fingerprint, face or PIN. The first sign-in after this release asks everyone to create their passkey, and everyone was signed out once so that no session without one stays open. My passkeys in your menu adds a passkey for another device and removes one you no longer have. An administrator resets a person's passkeys under Settings → Users when they lost every device. Risky steps can now ask for a fresh touch of the passkey; adding, removing and resetting passkeys already do. API keys and connected apps work as before.

2026-10-25: Small fixes from the first tester

Before: a quantity field that started at 1 became 101 or 110 when you typed 10; a new company's Home showed "9 periods in close" for months in which nothing was ever booked; a mistyped address quietly opened Home; and the operator portal let you invite a customer's administrator on our own Workspace domain, an address that can never get a password, without a word.

After: every number field chooses its whole value when you enter it, so what you type replaces it (the screens). Periods in close and the Autopilot outcome "the month is closed on time" count the months from the company's first posted entry on: a new company has nothing in close, and one that books its history from January closes from January (the close tracker). An address the app has no page for shows "Page not found" with a link to Home. In the operator portal, an administrator's address on one of our own Workspace domains shows a warning that its person signs in with Google and never gets a password link.

API: GET /v1/platform/me gives the deployment's own Workspace domains (workspace_domains); periods_in_close of GET /v1/entities/{entity}/overview counts from the period of the entity's first posted entry.

2026-10-25: AI reviewer replaces a second person as the default control

  • AI reviewer (default controls mode): activating a customer or vendor is now decided by an AI reviewer instead of waiting for a second person; new bank details and other changes still need a second person. The reviewer runs fixed checks (VAT register, possible duplicates, own bank accounts) on every step, clears clean ones at once as system:reviewer, and holds warned ones until a person releases them. Every review, every check with its evidence and the reasoning are in the audit trail and readable by auditors over MCP. An administrator switches to four-eyes mode on Settings when the law or auditor requires a second person (Swiss ordinary audit, SEC registrant). See ADR-0101.
  • Passkey-protected release: a person who runs the company alone releases a held step with a passkey touch (within 5 minutes), after any waiting time the hold requires (24 hours for new bank details). In a company with more than one person, a held step still needs another person.
  • Migration 0140: adds organization.controls (ai_reviewer or four_eyes; default ai_reviewer).

2026-10-25: The pitch for outsourcing

Before: the only public page about ESoR Headless was the brief at /brief, written for investors and customers.

After: a second public page at /pitch, in the brief's look, for two readers: outsourcing and shared-services providers who want to run their clients' finance and sales processes on Autopilot, with their own people as the approvers, and companies thinking of outsourcing those processes, who can run them in their own system of record instead. It explains the shift from hours to results, what each reader gets, the twelve Autopilot outcomes, the three lanes of control (Autopilot prepares, a second person approves, only a person decides), what is not there yet (one screen across all clients, payroll) and how a pilot starts. Its figures and outcome names are checked against the code like the brief's.

2026-10-25: Statutory holiday calendars in close and support, customer context on cases, and 19 webhook events

  • Statutory public holidays in the close tracker and support desk: the close tracker and the support desk now skip the statutory public holidays of the company's country (from the data-vNext calendars) alongside weekends when counting close working days and support SLA clocks.
  • Cross-function customer context: a support case (GET /v1/support/cases/{id}) now includes customer_context (open_invoices, overdue_invoices, active_subscriptions, open_opportunities) when a customer is linked, and a customer's card (PartyCard) includes open_support_cases, breached_support_cases, active_subscriptions and open_opportunities.
  • 19 webhook events and 15-minute retry sweeps: webhooks now also emit lead.created, lead.converted, quote.accepted, quote.declined, subscription.activated, subscription.cancelled, case.created and case.closed, and pending webhook retries and due task reminders are swept by the 15-minute mailbox sync job in addition to the daily close job.
  • Declarative approval policy for support and CRM controls: close_support_case, send_support_reply, record_quote_answer and erase_personal_data are now evaluated through the approval policy decision table (agents_no_support_or_crm_controls).

2026-10-25: Companies in Sweden, Finland, Denmark and Norway at onboarding

Before: The setup wizard put a company in Sweden, Finland or Denmark on the generic IFRS chart with no VAT return form, and did not offer Norway.

After: The setup wizard (and Add company) offers each of the four countries its national chart as the recommended choice, with IFRS as the other: the Swedish BAS chart (K2/K3), a Finnish chart after the Kirjanpitoasetus, a Danish chart in the order of the standardkontoplan, and the Norwegian NS 4102, named in the country's language with English, German, French, Italian and Swedish translations. The books are kept in SEK, EUR, DKK or NOK, and the VAT number is checked against the country's format. The VAT return is prepared from the books: the Swedish momsdeklaration, the Finnish kausiveroilmoitus, the Danish momsangivelse and the Norwegian MVA-melding, with import VAT reported on the return and the due date of each period. Norway is outside the EU: its VAT is filed per term of two months (2026-T1 to 2026-T6) or per year, and it has no One-Stop Shop. Returns are still filed by hand on the authority's e-service.

For integrators: GET /v1/setup/options lists Norway (region no) and the standards se_gaap, fi_gaap, dk_gaap and no_gaap with the charts se_bas, fi_kpl, dk_standard and no_ns4102. VAT settings take the period two_months (Norway only; other periods a Nordic country does not use are refused with vat_period_country), and returns take labels such as 2026-T3. See ledger.md and ADR-0100.

2026-10-24: A new look for the brief and the documentation

Before: the brief at /brief showed the product three sections down, and the documentation at /docs had a look of its own, in blue, with the system's fonts.

After: the brief reads as a pitch, with the Processes screen in the first view and ten numbered sections (problem, how it works, Autopilot, controls, audit, assistants, scope, countries, business model, a look inside). It has a ledger look: warm paper, ink rules, Newsreader headings, Geist text and Geist Mono labels, light and dark after the reader's system. The Autopilot pass names who acts at each step (code, model, person), the twelve outcomes are grouped by phase, and the controls are a table of what agents may do, what only a person may do and what always needs a second person. The documentation shares the same look, colours and typefaces. The facts are unchanged.

2026-10-24: Leads from HubSpot, and HubSpot hears what became of them

New: Settings > HubSpot connects a company to its own HubSpot account, with a private app's key from that HubSpot (and HubSpot's consent page once this system has its HubSpot app). Contacts that reach the hand-over stage (Marketing qualified lead by default, or Lead, or Sales qualified lead) arrive as leads with the source HubSpot every 5 minutes or on Read now: once each, linked to an open lead with the same e-mail instead of copied, owned by their HubSpot owner when that person is here too. The lead's dialog opens the contact in HubSpot.

New: each contact is told what became of its lead: HubSpot's lead status and lifecycle stage (Sales qualified lead once worked, Opportunity with a deal, Customer once won or invoiced) and ESoR's own fields in a group ESoR Headless: the lead number, its status, the deal won and the revenue invoiced, so marketing sees which campaigns bring customers.

Security: the token is sealed with a key that is never in the database, never exported and never copied into a test copy; a test copy never connects to HubSpot.

API: GET /v1/hubspot, POST /v1/hubspot/connections, POST /v1/hubspot/authorize, POST /v1/hubspot/connections/oauth, PATCH and DELETE /v1/hubspot/connections/{connection_id}, POST /v1/hubspot/connections/{connection_id}/sync; leads have the source hubspot and hubspot_url (HubSpot). New command erp hubspot-sync.

2026-10-24: Swedish for the whole app

New: Swedish (Svenska) is the fifth language, beside English, German, French and Italian.

  • Every screen in Swedish. Choose Svenska in the language menu at the top (the globe). Every page, message and plain-language error then reads in Swedish, with the words a Swedish bookkeeper uses (moms, bokföring, verifikation, huvudbok, leverantörsreskontra, kundreskontra), and numbers and dates as written in Sweden. The screens say du to you. A browser set to Swedish opens the app in Swedish until you choose.
  • Standard account names in Swedish. The accounts of the shipped charts show their Swedish names; a name your company chose itself still shows as you wrote it.
  • Documents and e-mails in Swedish. A customer or contact whose language is Swedish gets invoices, credit notes, quotes, order confirmations, delivery notes, statements and reminders in Swedish, as does a customer in Sweden without a language set. Notification e-mails, web forms, the organization export's README and Autopilot's notes and questions follow too. The Swiss QR-bill keeps its own standard's wording in English.
  • The assistant answers in Swedish when the app is in Swedish.

This is the language only: no Swedish tax or country rules come with it.

API: PATCH /v1/me takes the locale sv-SE; the party and contact language, the web form, export, statement, invoice output and Autopilot languages take sv.

2026-10-24: Approvers are declared, confirmed and registered for the auditor

New: giving someone the right to approve is now an audited act (ADR-0098). When you invite a Controller or Administrator, or raise someone to one of those roles, you say who the person is (full name, relationship to the company such as external accountant or board member, and their firm) and confirm that it is a different real person and that your declaration is recorded for the auditor. A second account of your own is not a second person: an address that is your own mailbox (for example with a +tag) or your own name is refused. The person you named confirms once, at sign-in, that their approvals carry their name and are recorded; they decide nothing before. Settings, Approver register lists everyone who may approve, what was declared, by whom and when, when they confirmed, and every change of their access. Administrators and auditors see it. An AI agent still never approves anything.

API: approver (person_name, relationship, organisation, note, confirmed) on POST /v1/users and PATCH /v1/users/{user_id}; GET /v1/approvers; POST /v1/me/approver-acknowledgement; approver_acknowledgement_needed on GET /v1/me. New error codes approver_declaration_missing, approver_same_person and approver_acknowledgement_needed.

2026-10-24: A company one person runs invites a second person to approve

New: when you run a company alone, what you ask for (a new vendor or customer, bank details, a payment above your limit and the other steps a second person must do) can still never be approved by you, but ESoR now says so and shows the way out (ADR-0096). Approving your own request reads "nobody else in this company may approve yet" and links to Invite a person to approve; Approvals and Users show the same note while you are the only one who may approve. The link opens the invitation with the Controller role, for your accountant or another person you trust: they get an e-mail, sign in, and approve under Approvals with one click.

UI: a screen no longer offers Approve on your own request, only to refuse it. On a customer or vendor, a journal entry, a purchase order, a recurring invoice and the year-end close, your own request reads "You asked for this or prepared it: another person decides", with Invite a person to approve while nobody else may. Who may decide is the server's answer (the same four-eyes rule), so a request made through a connected app or an agent acting for you counts as yours too.

Fix: approving the activation of a customer or vendor you created yourself (when someone else asked for it) is refused with the same four-eyes words as every other approval, not a technical message.

API: the four_eyes refusal carries nobody_else (true when no other person may approve yet); GET /v1/me gives other_approvers.

2026-10-24: Audit files for every company: the SAF-T and the audit evidence pack

Before: Reports → Audit files was there only for companies in France (the FEC) and Germany (the GoBD data carrier). A company anywhere else had no file for its tax auditor, and no company could hand its external auditor what an audit pulls from the books: the journal with who approved each entry, a trial balance that ties to it, who has access, the audit trail, the approvals and the period closes.

After: Reports → Audit files is there for every company and offers, for a fiscal year or a range of its closed periods, every file that fits the company, each with its own download. Each download is recorded in the audit trail. See API and UI.

  • SAF-T (every country but France and Germany), SAF-T_<company>_<year>.xml: the OECD Standard Audit File for Tax 2.0 in UTF-8: the header (company, address, VAT number, currency, the selected periods, whether the year is closed, ESoR Headless as software), the accounts with opening and closing balances, customers and suppliers with their balances on the receivables and payables accounts, the tax table of the codes used, and every line of every posted entry by journal, with its document, customer or supplier, the amount, currency and rate of a line in another currency, and its tax. The total debit equals the total credit and the balances tie to the trial balance.
  • Audit evidence pack (every country), Audit_Evidence_<company>_<year>.zip: pipe-delimited UTF-8 tables laid out after the AICPA Audit Data Standards, with a README that describes every file and column, the scope, whether the year is closed and the control totals: GL_Detail (every journal line with who entered, approved and posted it), Trial_Balance (ties to GL_Detail account by account), Chart_Of_Accounts, Business_Unit_Listing, User_Listing (every person, their rights and status, invited, last sign-in and when their rights changed), Sign_Ins (sign-ins and security events), Audit_Trail, Master_Data_Changes (customers, vendors, bank details, mandates, accounts), Approvals (four-eyes requests and decisions), Period_Closes (closes and reopenings with their reasons), Receivables and Payables (with what is open at the end), Sales_Deals (deals won, with the invoices of the subscriptions they were won for) and Ledger_Integrity (the hash chain's check when the pack was made).

What could not be verified. The SAF-T follows the OECD's 2.0 schema; it has not been validated against a national schema (Norway, Portugal, Poland and the others publish their own) or imported into audit software. The evidence pack follows the ADS column names where the standard has one; its import into an audit tool has not been tested. A deal is linked to invoices only through a subscription: quotes, orders and other invoices carry no link to a deal in the books, so the pack shows none.

For integrators: GET /v1/entities/{entity}/audit-files/saft?fiscal_year= (application/xml) and GET /v1/entities/{entity}/audit-files/evidence?fiscal_year= (application/zip), with optional from_period and to_period; scope read. The overview GET /v1/entities/{entity}/audit-files now lists saft or the national format first, then evidence, in formats, and has the name of each file in the new file_names (file_name stays the first format's). A company in France or Germany asking for the SAF-T gets 422 audit_file_country.

2026-10-24: Claude's connector no longer keeps requests open for 15 minutes

Fixed. On the newest MCP protocol (2026-07-28) the server announced that its tool list can change, so Claude's connector opened a subscriptions/listen stream on every connection and held it until Cloud Run cut it after 15 minutes. Each one was a 900-second request on the request log, which set off the ERP latency alert again and again from 23 October, 22:19 (Swiss time), and kept an instance busy all night. The tools never change while the server runs, so /mcp now says listChanged: false and answers a subscriptions/listen at once with "method not found". Tool calls are unchanged.

Changed. A path outside /v1/ and /mcp that is not public (the web UI, the docs, sign-in, OAuth, website forms) is answered 404 not_found without asking for a credential, instead of 401. Internet scanners that try /.env, /.git/config or /wp-config.php no longer set off the alert on rejected credentials; nothing was ever served on those paths.

2026-10-24: External auditors get read-only access that ends on a date, and a log of what they open

New. An administrator invites an external auditor on Settings → Auditors with the date their access ends. The auditor reads every screen, report and audit file and changes nothing; from the end date they cannot sign in and a running session stops. The auditor may connect their own AI (Claude or another MCP client) to ESoR: it can only read and stops with their access. Everything they and their app open is in the access log on the same page.

API: POST /v1/users and PATCH /v1/users/{id} take access_until (required for the auditor role), UserOut returns it, and GET /v1/access-log lists the auditors' requests. See External auditors and ADR-0099.

2026-10-24: Public documentation at /docs

New: the whole platform is documented at erp.grunte.com/docs, for customers, integrators and anyone evaluating ESoR Headless: every feature in plain language, the guides of each area, Autopilot and a new page on the routines that run by themselves and when, connecting an AI assistant over MCP with every tool, webhooks, and the REST API with its full reference. It has search, works on a phone and in dark mode, and needs no sign-in. The API reference and the MCP tool list are generated from the code on every release.

Changed: the interactive API explorer that used to be at /docs is now at /docs/explorer; /openapi.json and /redoc are unchanged.

For changes to the platform: a change of the API, the MCP tools, Autopilot, the routines, the webhooks or the app's screens now has to change the guide that describes it, or CI fails (python -m tools.docsite --changes).

2026-10-24: A public brief for investors and customers at /brief

New: /brief is a public page, no sign-in needed, that explains what ESoR Headless is and how it works: the four ways to run it (the app, AI assistants over MCP, the REST API and Autopilot), how an Autopilot pass works and the outcomes it keeps true, the four-eyes controls, how it is built for the audit (records that cannot be rewritten, every number traced to its source, enforced controls, the auditor's files and read-only access), what the system covers, the countries, and where the product goes ("service as a software"), with screenshots of the app. It is a static page with no script and no data from anyone's books (the brief).

2026-10-24: Autopilot follows up new leads and bills won deals

New: two more outcomes Autopilot keeps true (Autopilot, ADR-0095). "New leads are followed up": when a lead arrives, a lead nobody owns gets an owner (its form's, a known contact's, or yours), and the AI reads what they wrote and what the records know of them, then plans the owner's first step as a task due the next working day and sets the lead to working; plain spam it disqualifies with the reason. "Won deals are billed": when a deal is won, the AI reads the deal and its history and drafts what bills it, a subscription for a recurring service or else a quote, linked to the deal, or asks you when the records do not say what was sold. Sending the quote and approving the subscription stay yours, as links on the Processes page.

UI: a lead's dialog shows what was done with it and its tasks; tasks on leads are listed with the pipeline's open tasks. A won deal in the pipeline offers New quote beside New subscription and links to the quote or order made for it; it stays listed until a subscription, quote or order bills it. In English, German, French and Italian.

API: activities can be about a lead (lead_id on POST /v1/activities, GET /v1/activities?lead_id=, log_activity and list_activities); a quote or order can be for a deal (opportunity_id on POST /v1/entities/{entity}/quotes and /sales-orders and on create_quote, shown on the document; the order made from a quote takes it). The task.created and task.completed webhooks name the lead. New error codes opportunity_other_customer and sales_document_deal_customer.

2026-10-24: Autopilot invoices customers and sees that they pay on time

New: two more outcomes Autopilot keeps (Autopilot). "Customers are invoiced": when a delivery note posts, and once a day, it drafts the invoice of everything delivered and not yet invoiced at the order's prices, and makes the recurring invoices that are due; you check, issue and send them, and bill the subscription periods, each as a link. "Customers pay on time": it collects by direct debit what is due under a mandate, and before a customer is reminded, the AI looks at the unmatched money in that could be their payment and matches it when it clearly is (or asks you), so nobody is reminded of what they paid; you make and send the letters and release the collections to the bank.

Changed: an invoice that a direct debit is collecting is no longer proposed for a reminder letter; it is reminded again only if the collection is returned.

UI: both outcomes on the Processes page, with their steps for you, in English, German, French and Italian.

2026-10-23: Autopilot prepares the month-end close, the VAT return and the report pack

New: three more outcomes Autopilot keeps true (Autopilot, ADR-0094).

  • The month is closed on time. For each month that has ended and is still open (the latest three), Autopilot makes the runs the close needs: the recurring journals and invoices due, depreciation, FX revaluation, deferred tax, revenue recognition, leases, the allowance for doubtful receivables and the bank matcher. What they post waits for a second person. The AI looks at each close check still failing that is not only a person's, finds what holds it and asks you one question where only you know. You get the approvals that hold the close, the checks left to you and, once a month's checklist passes, the link to close it, with the working day the close calendar sets. Autopilot never closes a month.
  • The VAT return is ready. For each VAT period that has ended and is not filed, Autopilot prepares the return from the books and recomputes a draft when something was posted into its period since. The AI looks at a draft whose computation found an error: it asks you for an amount only you know (import VAT paid to customs) and recomputes with it, or names the booking to correct. Finalising, the approval of the settlement and submitting stay yours, each a link with the due date.
  • Group and reports are ready. For the month that ended last, Autopilot makes a new consolidation run of each group the company heads when the latest one is missing or older than the books. The AI looks into each intercompany relationship whose difference is unexplained and writes the month's report pack (the close memo with its commentary) once the checklist passes or the close calendar's day for it has come. You get the intercompany differences to resolve, what holds a group, and the report pack when it names blockers.

Closing a month now makes the VAT and group outcomes due at once.

UI: the three outcomes on the Processes page, with their steps for you in English, German, French and Italian; a step with a day shows when it is due.

API: GET /v1/entities/{entity}/autopilot lists month_closed, vat_ready and group_reports; a hand-off (Handoff) has a new optional due date and new kinds (approve_close, close_checks, close_period, prepare_vat, finalise_vat, approve_vat, submit_vat, amend_vat, ic_differences, group_checks, read_pack).

2026-10-23: Autopilot pays vendors on time, makes the recurring bookings and depreciates the assets

New: three more outcomes Autopilot keeps true by itself (Autopilot, ADR-0094). Vendors are paid on time: when a bill is posted and once a day, it makes the payments of the bills due in the next two days (a setting, 0 to 30) or whose cash discount ends by then, vendor by vendor, as the payment proposal proposes them. It pays only bills without a warning (no IBAN, a different IBAN, money to the vendor's credit, a vendor not active), only while the bank account stays above zero, and never again a bill whose payment a person rejected. Recurring bookings are made: every recurring journal due by today is booked, except one whose entry a person rejected. Assets are depreciated: every month that has ended and is still open gets its depreciation run, oldest first. The AI is never asked for these: the rules do the work. Everything Autopilot books waits for a second person's approval, and releasing the payment file to the bank stays a person's act.

UI: on Processes, the three outcomes with their switch and Run now; what waits for you now also names the payments to release to the bank, the bills it left to you, the recurring journals it could not book and the month whose depreciation to run, each a link. In English, German, French and Italian.

API: outcomes vendors_paid (setting days_before_due), recurring_bookings and depreciation in GET /v1/entities/{entity}/autopilot and PUT /v1/entities/{entity}/autopilot/{outcome}; an outcome without settings refuses any (validation_failed). New step kinds pay_bills, release_payments, book_recurring, run_depreciation; approve now counts only the bookings of its own outcome.

2026-10-23: Autopilot keeps the bills coded

New: a second Autopilot outcome, "bills are in and coded" (Autopilot): every bill that came in as a file is coded, matched to its purchase order and posted or waiting for approval. Bill capture and the e-invoice import already submit what they can; a bill they leave a draft (its new vendor not yet activated, a dimension it needs, an unsure reading) is now picked up. When a vendor is activated, once a day or on Run now, the rules send on each draft whose vendor is active, as capture would have, and the AI looks at each one left with why it is still a draft, the vendor's usual accounts and open orders: it codes the lines and submits the bill, or asks you one question. Amounts, dates and VAT stay as the bill says. Its bills wait for a second person like any agent's. What only you do comes as a link: activate the new vendors, approve the bills and the differences against their orders, and finish what is left.

MCP: code_bill sets the accounts and dimensions of a draft bill's lines and the purchase order it names, then submits it.

2026-10-23: Autopilot keeps the bank matched by itself

New: Autopilot, a fourth way to run ESoR beside the app, MCP and REST (Autopilot, ADR-0094). Switched on for a company, it keeps an outcome true by itself: the first is "the bank is matched". When a statement arrives, once a day, or when you press Run now, the bank matcher settles what it can, the AI looks at each line that is left with its candidate documents (it matches one the records leave no doubt about, books a bank fee, or asks you one question), and the check runs again. What only you do (approve its postings, mark the account reconciled) comes as a link. Every pass is recorded, and a company's AI use is capped at 50 items a day.

UI: the new Processes page (beside Analytics in the sidebar, and in Tell me as "autopilot" or "processes") shows what waits for you first (the links and the AI's questions, answered in place), then each outcome with its state, the switch and Run now (each step shows as it happens), then what every pass did; a pass opens with its steps. In English, German, French and Italian.

API: GET /v1/entities/{entity}/autopilot, PUT /v1/entities/{entity}/autopilot/{outcome} (a person signed in switches it on: its passes act for them), POST /v1/entities/{entity}/autopilot/{outcome}/passes (and /stream), GET /v1/entities/{entity}/autopilot/passes, GET /v1/autopilot/passes/{pass_id}, POST /v1/autopilot/questions/{question_id}/answer. MCP tools autopilot_status and autopilot_passes. The daily job (erp close-job) runs what is due and Autopilot's daily passes.

2026-10-23: The assistant shows what it is doing, and keeps going when the AI model is busy

Before: asking the assistant for a graph could fail after a minute with "The assistant did not answer", with nothing on screen while it worked.

After: the assistant shows each step as it works and finishes the answer even when the AI model is briefly overloaded.

Fixed: an answer that took several steps could fail with "The AI model could not complete this request": every step sent the model the descriptions of all of the assistant's tools (some 200), which used up the AI service's quota within a minute. The model now gets the tools it uses most and a short index of the others, which it reads up on when it needs one.

Fixed: when the AI model is busy in the middle of an answer, the assistant waits and asks again (after 5, 15 and 30 seconds), and if the model still does not answer, the fallback model (Gemini 3.7 Flash) takes the answer over with what was looked up so far, instead of the answer failing. If no model can answer, the assistant says the AI model is busy and to try again in a minute.

New: while the assistant works, its drawer shows what it is doing, step by step ("Fetching figures and trends", "Looking at VAT", "Drawing the chart"), in the language you use the app in.

API: POST /v1/agents/assistant/stream is POST /v1/agents/assistant answered as the assistant works: one JSON object per line (application/x-ndjson), a step for each tool it calls (tool, area), working every few seconds, then done with the answer or error with the status and error an ordinary request answers. A busy model is the error model_busy (502). The model now reaches most MCP tools through describe_tools and call_tool; tool_calls still name the tools that ran.

2026-10-23: A Salesforce re-import follows merged customers and contacts

Fixed: after a customer or contact brought over from Salesforce is merged into another (a duplicate), a later Salesforce import puts the deals, contacts, tasks and notes about it on the one that stays instead of the merged one, and a re-imported account is not matched to a merged customer.

2026-10-23: AI assistants work with the group, exchange rates and cost centres

New: an AI assistant connected over MCP (Claude, ChatGPT or the in-app assistant) can now work on the group consolidation: list the groups and their subsidiaries, check the group close checklist, make a draft consolidation run at a month end and read what it holds (group balance sheet, profit and loss, cash flow, eliminations, rates, translation reserve, exceptions, each entity's translated trial balance). Creating a group and admitting a subsidiary stay a person's decision with a second person's approval, so there is no tool for them.

New: the assistant reads exchange rates the way the books use them: the ECB rates of a day against the company's currency, cross rates, a currency pair's history with its IAS 21 month rates, a conversion, the official VAT rates beside the accounting rates, and the foreign-currency exposure with what the period-end revaluation would book. A rate of its own for one entry goes on the journal line (fx_rate); a rate far from the ECB's is only used after the person confirms it.

New: the assistant finds the cost centres, profit centres and projects, puts them on the lines it drafts (journal entries, invoices and bills, bank lines booked to an account) and reads the results per cost centre with the postings behind them. Adding or changing cost centres stays an administrator's act.

API: MCP tools list_consolidation_groups, list_consolidation_runs, run_consolidation, consolidation_run, consolidation_trial_balance, group_close_checklist, exchange_rates, exchange_rate_history, convert_currency, vat_exchange_rates, fx_exposure, list_dimensions, dimension_results, dimension_lines; create_journal_entry takes confirm_rate and reconcile_bank_line takes dimensions (MCP tools).

2026-10-23: The assistant prepares vendor payments, direct debits, invoices and tax returns

New: the in-app assistant and an AI assistant connected over MCP (Claude, ChatGPT) now work on payments, invoicing and tax, not only on the ledger, the close and sales. Ask it "which bills should we pay this week?": it proposes the payments (due and overdue bills, cash discounts still open, what blocks a payment, the bank balance after the run), asks which to pay and makes the payments once you agree. The same for SEPA direct debits: it shows a customer's mandates, proposes the invoices to collect on a day and makes the collections. Before an invoice goes out it checks what the company's or the customer's record still lacks by law, how the invoice would be sent, and for a German company whether the XRechnung passes its rules. It reads the withholding tax kept back from vendors in a period and prepares a US state's sales tax return from the ledger, with the check against the state's account.

What stays a person's act is handed over as a link that opens the right page in the right company and organization: releasing the payment file or the direct debit file to the bank, recording or changing a mandate, sending an invoice or recording that it was handed over, and recording a sales tax return as filed (a second person approves the record) and its payment. An agent's payments wait for a person's approval under the default policy, and the agent key is refused on every release, send and filing operation, as before (ADR-0036, ADR-0083).

New MCP tools: propose_vendor_payments, create_payment_run, payments_to_release, customer_mandates, propose_direct_debits, create_direct_debit_run, list_direct_debits, invoice_content_rules, invoice_output_check, withholding_tax_codes, withholding_tax_report, sales_tax_setup, sales_tax_periods, sales_tax_return, customer_sales_tax and sales_tax_payment_candidates. Each mirrors existing REST operations.

Changed: a link to the payments, direct debits, withholding tax or a sales tax return page may name the company (?entity=CH01), as the bank reconciliation already could: the page switches to that company before it shows anything.

2026-10-23: An AI assistant works purchasing, stock and deliveries

Added: an AI assistant connected over MCP, and the assistant in the app, now works beyond sales. It drafts purchase orders, changes them and submits them for approval, reads an order with what arrived and what was billed, explains how a bill matches its order and why its payment is blocked, cancels an order or closes the rest when the person says so. It keeps warehouses and stock items, answers what is in stock and what it is worth (and whether that ties to the ledger), moves goods between warehouses, books damaged, lost or found goods, runs a stock count with the person, ships goods on a delivery note and drafts the invoice of what was delivered.

Controls: the assistant prepares, a person decides. A second person approves a purchase order or a bill's price or quantity variance, and the person records the goods that arrived; no tool does any of these, and the answer gives the link to the page where the person does it with one click. A stock adjustment or a count difference the assistant books changes the value of stock, so it waits for a person on Approvals. The assistant asks for quantities, prices and reasons it does not know and never guesses them.

API: new MCP tools list_purchase_orders, get_purchase_order, purchase_order_history, create_purchase_order, update_purchase_order, submit_purchase_order, cancel_purchase_order, close_purchase_order, get_bill_match, match_bill, purchase_settings, list_warehouses, create_warehouse, update_warehouse, list_stock_items, create_stock_item, update_stock_item, stock_valuation, list_stock_postings, adjust_stock, transfer_stock, list_stock_counts, open_stock_count, enter_stock_count, post_stock_count, cancel_stock_count, list_delivery_notes, get_delivery_note, create_delivery_note, update_delivery_note, post_delivery_note, cancel_delivery_note, invoice_delivery_note and delivered_not_invoiced, each the REST operation it names in _meta.rest. create_document now describes order_reference, the purchase order a bill names.

2026-10-23: AI assistants keep fixed assets, revenue contracts, subscriptions and leases

New: AI assistants (the in-app assistant, Claude, ChatGPT and any MCP client) now work beyond sales and the close. They look up a fixed asset with its book values, register a new one with its depreciation schedule, dispose of an asset and sell one to a customer on an invoice. They draft revenue contracts and subscriptions, and work out what a change of plan, an early end or a renewal would cost before anyone asks for it. They enter a lease, book its start, record a new payment (an index increase) and end it early. They ask you for what only you know (the useful life, the price, the lease rate) instead of guessing.

Controls: what only a person may do stays with a person. Activating a revenue contract, asking for a subscription's terms or a change of plan to be approved, and impairing or revaluing an asset are never done by an assistant: it prepares them and gives you a link to the record, where you do it in one click, and a second person approves as before. An assistant's disposal or sale of an asset always waits for a person's approval, whatever its amount.

API: MCP tools get_asset, register_asset, dispose_asset, sell_asset, create_revenue_contract, preview_revenue_contract_change, create_subscription, preview_subscription_change, create_lease, activate_lease, change_lease_payments, end_lease (MCP tools); POST /v1/assets/{asset}/dispose by an agent now always waits for approval.

2026-10-23: AI assistants read the sales reports, recurring revenue and your notifications

New: an AI assistant (the one in the app, or Claude or ChatGPT connected to ESoR Headless) answers questions about sales with the same figures as the Sales performance and Recurring revenue pages, and can chart them: the pipeline by owner, stage and expected-close month, the win rate, each person's activity, the forecast against quota, the quotas, and MRR, ARR and churn month by month. It sees only what the person it works for sees (their own or their team's deals, where that is set), and asks which company and period you mean when you do not say. It can also list the renewal deals of subscriptions on their own.

New: an assistant of yours answers "what waits for me": it reads your notifications (tasks given to you, due or overdue, your deals won or lost, your approval requests decided) and counts the approvals you may decide. It never marks them read; that stays yours.

API: GET /v1/opportunities takes kind (new_business, expansion, renewal). GET /v1/me/notifications with a key or agent of a person now returns that person's list instead of an empty one (marking read and the e-mail settings still answer 409 notifications_person_only). MCP tools sales_pipeline_report, sales_win_rate, sales_activity, sales_forecast, list_sales_quotas, recurring_revenue, list_notifications, and kind on list_opportunities (MCP tools, notifications).

2026-10-23: Lists keep the order things were made in

Before: records made by one action (a loan's drawdown, interest and settlement, or two invoices of one delivery note) all carried the same time, so a list sorted by time could show them in any order, and not always the same one.

After: every record keeps the exact moment it was written, so such lists show them in the order they were made, the same way every time. Records made before today keep their times.

2026-10-23: Sales teams, record owners and "only my records"

New: every customer, contact, deal and activity has an owner. New records belong to whoever creates them; the owner shows on the customer card, on each contact and on each activity in the timeline. The owner of a customer, contact or deal can be changed; an activity stays with who did it.

New: Settings > Sales teams: group your sales people into teams and decide, per person, whether they see every customer, contact and deal, only their team's, or only their own. It applies on every screen, in the API and for AI assistants alike; someone who sees only part gets a note on the pipeline and customer list saying whose records they see. Invoices, payments and the books are not affected.

New: Hand over records moves everything one person owns to another when someone leaves or changes territory, each change in the record's history.

New: the Salesforce import keeps each account's, task's, meeting's and note's owner. The demo company has two sales teams.

API: owner_user_id on customers, contacts and activities and as a list filter; /v1/sales-teams, /v1/record-access, PUT /v1/people/{user_id}/record-visibility, GET /v1/me/record-access, POST /v1/record-access/hand-over (sales teams).

2026-10-23: Leads and website forms

New: Sales > Leads: the people who may become customers, kept apart from customers. List them by status (new, working, disqualified, converted), source and owner, with tiles for new, being worked, nobody owns and converted in 30 days. Add a lead, work it, disqualify it with a reason or take it back, and erase a person's data on request. In all four languages.

New: Convert a lead in one step into a customer (new, or one you have; a known e-mail proposes its customer and contact), the person as its contact and a deal owned by the lead's owner, with the person's message as the deal's first note.

New: Settings > Website forms: a form with a page of its own in English, German, French or Italian. Put it on your website as a link, embedded, or as a form of your own; whoever fills it in becomes a new lead. Spam is filtered (a hidden field and a signed opening time), submissions are limited per address and per form, and a form can be limited to your websites, switched off or given a new key.

New: AI assistants can list, add, change and convert leads (list_leads, create_lead, update_lead, convert_lead); only a person erases a lead or sets up a form. Leads are found by the search, exported and masked in test copies.

API: GET/POST /v1/leads, GET /v1/leads/summary, GET/PATCH /v1/leads/{lead_id}, POST /v1/leads/{lead_id}/convert and /erase; GET/POST /v1/lead-forms, PATCH /v1/lead-forms/{form_id}, POST /v1/lead-forms/{form_id}/new-key; GET/POST /forms/leads/{key} without credential (leads). The record search finds leads (type lead).

2026-10-23: Notifications, task reminders and webhooks

New: a bell in the top bar shows how many notifications are unread and the latest of them: a task someone gave you, a task of yours due today or overdue, a deal someone made yours, a deal of yours won or lost by someone else, your approval request approved or rejected, and how many approval requests wait for you. Each opens where it happened. The Notifications page (from the bell or the account menu) lists them all; there you can also choose which ones come by e-mail too (none by default; one message with everything new). A test copy never sends e-mail. In all four languages.

New: task reminders: an open task reminds the person it is given to on its day, and once more when the day passed undone.

New: Settings > Webhooks (administrators): tell other systems what happens here. Each event (deal created, won or lost; task given or done; invoice, credit note, vendor bill or vendor credit note posted; payment received or sent) is sent to the address you give, signed with a secret shown once (Standard Webhooks), and tried again for about 42 hours until it arrives. Send a test, stop and start, give a new secret, and see every delivery with its attempts and message, and send it again. A webhook that keeps failing for a day is stopped and the administrators are told. Only public https:// addresses are accepted.

API: GET /v1/me/notifications, POST /v1/me/notifications/read, GET and PUT /v1/me/notification-settings; GET and POST /v1/webhooks, GET, PATCH and DELETE /v1/webhooks/{id}, POST …/rotate-secret, POST …/test, GET …/deliveries, POST /v1/webhook-deliveries/{id}/send-again, GET /v1/webhooks/event-types (notifications and webhooks, ADR-0090).

2026-10-23: A support desk: cases, queues and service levels

New: Service > Cases keeps what customers ask, by e-mail or phone: each case with its customer and contact, the person who works it, what was said, and two service-level chips (first reply and resolution) that show the working time left, turn amber in the last hour and red once breached. The list sorts the next one due first and filters by queue, status, person, customer, text and breached only. A customer's card links its cases. In all four languages.

New: on a case, reply from the queue's inbox (the case number in the subject brings the answer back), record a reply given by phone or another mailbox, keep a draft, add an internal note, triage (queue, priority, person, customer, contact), set it waiting on the customer (the resolution clock stands still), close it with how it was resolved, or reopen it. A test copy never sends.

New: Service > Support queues (administrators): an inbox address per queue, service levels per priority in working hours, the working days and hours they count in, members, and automatic assignment to the member with the fewest open cases.

New: mail to a queue's inbox opens a case or continues the one it answers, matched to the contact or customer by the sender's address; a customer's answer reopens a waiting case or one closed within 30 days. It arrives through a linked Gmail mailbox (off until the operator sets the link up) or any mail service posting to the API.

New: AI assistants list, open and triage cases, add notes and draft replies over MCP; only a person sends a reply or changes a case's status.

API: GET and POST /v1/support/queues, PATCH /v1/support/queues/{queue_id}, GET and POST /v1/support/cases, GET and PATCH /v1/support/cases/{case_id}, POST /v1/support/cases/{case_id}/status, /notes, /replies, PATCH .../replies/{message_id}, POST .../replies/{message_id}/send and /discard, POST /v1/support/inbound (support desk, ADR-0092). MCP: list_support_cases, list_support_queues, open_support_case, triage_support_case, add_case_note, draft_case_reply.

2026-10-23: Customers accept quotes online, and every sent version is kept

New: a quote e-mailed to a customer now carries a link. On it the customer sees the quote without signing in, in their language, downloads the PDF and accepts it with their name (and their order number) or declines it. Accepting makes the sales order, as accepting it on the quote's card does; above the customer's credit limit the order waits as a draft for a person. The name, the time, the internet address and browser, and the fingerprint of the PDF accepted are kept as evidence.

New: quote versions. Changing a sent quote (Revise) begins its next version; the earlier one, with the PDF the customer got, stays on the quote's new Online and versions tab. A link the customer already has shows the new version once it is sent; nobody can accept a version that changed since they read it. From version 2 the PDF names its version.

New: Make a link to share on a sent quote gives a link to pass on yourself; each link shows when it was last opened and can be turned off. Assistants draft quotes but never send them, make links or record an answer. In all four languages.

API: POST /v1/sales-documents/{id}/links, POST .../links/{quote_link}/revoke, GET .../revisions/{n}/pdf, the customer's GET /v1/public/quotes/{token} (and /pdf, /accept, /decline); revision on quotes and online on a quote's card (quotes and sales orders, ADR-0088).

2026-10-23: Recurring revenue: MRR, ARR, churn and renewal deals

New: Sales > Recurring revenue shows monthly and annual recurring revenue (MRR, ARR) at each month's end, read from the subscriptions in force, and what moved it: new customers, expansion, reactivation, contraction and churn, each customer with its subscriptions, plus the customer churn rate, the gross MRR churn rate and the net MRR retention. Each customer's MRR stays in the currency it is billed in; the totals are in the company's currency at each month's ECB rate, and the change of rates is shown as its own currency-effect line, so every month adds up. In all four languages.

New: renewal deals. When the renewal of a subscription that does not renew itself falls due (its notice days before the term ends), the daily job opens a renewal deal in the pipeline: with the customer, in the subscription's currency, worth the next term (uplift included), expected to close on the term's last day and owned by whoever owned the original deal. It is won when a second person approves the renewal and lost when the subscription is ended early. Winning it by hand asks for the renewal at the same time; a second person still approves it, and an assistant cannot win it. The deal and the subscription link to each other.

API: GET /v1/subscriptions/metrics (recurring revenue); subscription_id, subscription_number and renewal_term_start on every deal, renewal_deal_id and renewal_deal_number on every subscription. ADR-0087.

2026-10-23: Merge duplicates, your own fields on screens, the contact person on deals

New: a customer entered twice is merged on its card with Merge into…: its contacts, deals, activities and subsidiaries move to the customer you choose, and it stays inactive for good, with a note saying where it went. Only a customer without invoices, payments or other business records can be merged; one with records is set inactive instead. Vendors are not merged.

New: a person entered twice is merged from the contacts list (the merge button): the contact that stays takes the details it was missing, the deals and calls move, and the duplicate is erased.

New: Settings > Your own fields adds fields to companies, contacts and deals (text, number, date, yes/no or a list of choices); they appear on the customer and vendor card, the contact form and the deal form. A field no longer needed is retired, and the values records hold stay readable.

New: a deal names its contact person from the customer's contacts; the typed name and e-mail are only for a deal without one.

API: POST /v1/entities/{entity}/parties/{party}/merge and POST /v1/contacts/{contact_id}/merge (contacts); merged_into on a party's card and merged_into_id on a contact.

2026-10-23: An AI assistant keeps the people at your customers

Added: an AI assistant connected over MCP now looks up, adds and updates the people at customers and vendors: name, role, e-mail and phones, the language to write to them in, whether they want sales e-mails, and your own contact fields. It names the person on the deals and the calls, meetings and e-mails it records, and it looks a person up by e-mail before adding them, so nobody is entered twice.

Controls: erasing a person's data stays a person's decision: no tool does it, and an erased contact no longer changes.

API: new MCP tools list_contacts, list_custom_fields, create_contact and update_contact, each the REST operation it names in _meta.rest; create_opportunity, update_opportunity and log_activity take contact_id.

2026-10-23: Salesforce contacts, parent companies and your own fields

New: the Salesforce import brings contacts over (Contact.csv, step 3 after the accounts): each person becomes a contact of the customer their account became, with role, email, phones, email opt-out and language. A person already at the customer (same email, else same name) is linked rather than doubled.

New: deals keep the contact they are with (ContactId), and tasks, events and notes the contact they were with (WhoId); a task or note about a contact only lands on that contact's customer. An account's parent account becomes its parent company, whatever the order of the file. Your own fields of companies, contacts and deals are read from their Salesforce custom fields (Industry__c for industry) or a column you choose, and checked like any value.

API: POST /v1/entities/{entity}/imports/salesforce takes object contacts; the mapping lists your own fields as custom.<key> with their label.

2026-10-23: Sales performance: forecast, quotas, win rate and activity

New: Sales > Sales performance shows this month, quarter or year at a glance: won against each person's quota, the forecast (won plus commit) and best case, the open pipeline by stage, by expected-close month and by owner, the win rate by owner (by count and by value, the average deal and the days to win), and what each person or agent logged. Every amount is in the company's currency, each deal converted at one ECB rate the page names, with the deals' own currencies kept beside it. In all four languages.

New: monthly quotas per person: a manager (approve permission) sets them on the same page; every change is audited.

New: a deal's forecast category (commit, best case, pipeline or omitted) on its edit form; by default it follows the stage.

New: the demo company's sales team works deals in CHF, EUR, USD, SEK and GBP against quotas.

API: GET /v1/sales/pipeline, /win-rate, /activity, /forecast, GET and PUT /v1/sales/quotas (sales reports); forecast_category on POST and PATCH /v1/opportunities and forecast_category, forecast_category_set on every deal.

2026-10-23: An AI assistant in the app that answers with figures and charts and closes a month with you

Before:

After:

New: an Assistant button in the top bar, on every page, opens a conversation with the built-in AI model (Gemini 3.8 Flash). Ask about your figures ("what did we invoice Basel Kunde AG this year?"), ask for a chart ("unearned revenue this year to date as a graph") and it draws one from the books' figures, or ask it to prepare something (a customer, an invoice draft, a journal entry, a period-end run). It says what it will change and waits for your yes, answers in the language you use the app in (English, German, French or Italian), and the conversation stays with you while you move between pages.

New: on an open period's close page, Close with the assistant starts a conversation about closing that month. The assistant works through the close checklist, runs the period-end steps it may run (depreciation, FX revaluation, deferred tax, revenue recognition, leases), looks into variances and unusual entries, asks what it needs to know, and lists the steps that are yours with a link to each.

New: the analytics figure unearned revenue: what customers paid or were billed for goods and services not yet delivered (contract liabilities), at each month, quarter or year end.

Controls: the assistant acts for you as an AI agent and is held to the agent rules: what it posts waits for approval where your rules say so, it approves nothing, closes no period, sends nothing and pays nothing, and you cannot approve what it prepared for you. A person who may only read gets an assistant that only reads. A chart value that none of its lookups returned is named under the chart, to check. The conversation is not stored; each answer is recorded in the audit trail with your message and what the assistant looked at.

How it works: the assistant uses the very tools an assistant like Claude or ChatGPT uses over MCP, so both work the same way (ADR-0093).

API: POST /v1/agents/assistant answers the person's last message: the request carries the conversation so far, entity, language and, to close a period, period_id; the answer the reply, its charts, the period's close state with the person's open steps, and the tools called. The analytics series (GET /v1/entities/{entity}/analytics/series, MCP analytics_series) take the metric unearned_revenue.

2026-10-23: Bring customers, deals and products over from Salesforce

New: Settings → Data Import → Import from Salesforce reads the files of a Salesforce export (Setup → Data Export, Data Loader or a report), one object at a time: users, accounts, products, price books and their prices, opportunities, tasks, events and notes. Accounts become customers, opportunities deals of the sales pipeline (open, won and lost), prices list prices or price lists, and tasks, calls, emails, meetings and notes land on the timeline of their deal or customer. See Importing a Salesforce export.

Each file is checked first without saving anything, listing every problem by line and by the file's own column; its columns can be mapped to other fields, such as a custom field. Each record keeps its Salesforce id, so importing the same file or a newer export again brings over only what is new, and an account that is already a customer here is linked to it rather than doubled.

API: POST /v1/entities/{entity}/imports/salesforce (multipart file, object, mode, optional mapping).

2026-10-23: Contacts of their own, parent companies and your own fields

New: the people at a customer or vendor are contacts of their own. On the card, under Contacts, Add contact takes a first and last name, role, e-mail, phone, mobile, the language to write to them in and whether they want sales e-mails; the pencil changes one, and the bin erases one: its personal data is gone for good, and the deals and calls that named the person keep only that someone was there. The contacts the cards had are carried over (the last word of a name became the last name).

New: a customer or vendor can belong to a parent company (on the card's General section, while editing); the parent's card lists its subsidiaries.

API: contacts (/v1/contacts, contacts), with contact_id on deals and activities; parent and custom on customers and vendors, custom on deals; the organization's own fields on companies, contacts and deals (/v1/custom-fields, admin), whose values are in each record's custom; GET /v1/entities/{entity}/parties?parent= and GET /v1/activities?contact_id=.

Changed (breaking): PATCH /v1/entities/{entity}/parties/{party} no longer takes a contacts list (422): add, change and erase each contact by itself. A party's contacts now each have an id; a new party's contacts take first_name and last_name (a name is still split).

2026-10-23: An AI assistant works the sales pipeline

Added: an AI assistant connected over MCP now does a salesperson's groundwork, not only reads the pipeline. It creates leads and deals (with a prospect or a customer), changes them, moves them through the stages (won only with a customer a person set up, lost with its reason), records notes, calls, meetings and e-mails, plans tasks and marks them done, finds the people to own a deal or a task, and drafts quotes and changes them. Everything it does carries its name in the history, like a person's.

Controls: an assistant drafts, a person decides. No tool sends a quote to a customer or records the customer's answer, the quote stays a draft until a person sends it, and nothing in the pipeline posts to the books. A key that may only read gets no write tool to work.

Fixed: changing a quote, sales order or delivery note answered the version it had before the change, so a program that changed it again with that version was refused as if someone else had changed it in between. The answer (and its ETag) now names the version as it is after the change.

Fixed: an MCP tool given an argument that breaks a rule (a lost deal without its reason, a quote without lines) answered only "Error executing tool"; it now says validation_failed and what is wrong, as REST does.

API: new MCP tools create_opportunity, update_opportunity, move_opportunity_stage, list_people, list_activities, log_activity, complete_task, list_quotes, get_sales_document, create_quote and update_quote, each the REST operation it names in _meta.rest. Writes that create something take an idempotency_key; update_quote takes the version read with get_sales_document and refuses a quote someone changed since.

2026-10-23: Close the month from Claude or ChatGPT

New: an AI assistant connected to the ERP's MCP endpoint can take a person through the whole month-end close. Ask it to "close September for CH01": it finds the period, lists what is still open in order (depreciation, FX revaluation, leases, recurring journals and invoices, the allowance for doubtful receivables, bank lines, VAT returns, approvals, the company's own close tasks), runs the steps it may once the person agrees, and asks what only the person knows (what a payment was, an amount for a VAT box, whether an accrual is right). What only a person may do it hands over as a link that opens the right page in the right organization: approving, finalising and submitting a VAT return, marking a bank account reconciled, and closing the period itself. See ADR-0083.

New MCP tools: month_end_close (the plan), request_period_close, list_periods, close_schedule, receivable_allowance_proposal, run_recurring_journals, run_recurring_invoices, review_close, vat_periods, list_vat_returns, get_vat_return, prepare_vat_return, recompute_vat_return, vat_box_lines, discard_vat_return, bank_reconciliation_status and bank_reconciliation_history; run_period_end also runs receivable_allowance. Each mirrors existing REST operations; the approval policy and four-eyes apply as before.

Changed: a link to another organization (?org=, a test copy's link or an assistant's) opens the page it names once you confirm the switch, instead of the home page. A link to the bank reconciliation may name the company (?entity=CH01).

2026-10-23: Gmail and Google Calendar logged on deals and customers

New: link your own Google account on Gmail and Google Calendar (the person menu, Settings, or the button on Sales pipeline). Your e-mails and meetings with the contact of an open deal, or with a customer or one of its contacts, are then logged on that deal's or customer's timeline every 15 minutes, the first time from the last 30 days, each once, with a link to open it in Gmail or Google Calendar. Only exact addresses match; nothing is ever sent from your account, attachments are never read, and a test copy never reads a mailbox. The link is offered once the operator has set it up (deployment); until then the page says so.

API: GET /v1/me/mailbox, POST /v1/me/mailbox/google, POST /v1/me/mailbox/sync, DELETE /v1/me/mailbox; activities carry source (google_mail, google_calendar) and external_url. ADR-0036 section 9; CRM.

2026-10-23: Connect Claude to the ERP with your own sign-in

New: Claude (the Claude apps and claude.ai) connects to ESoR Headless as a custom connector. In Claude, open Settings, Connectors, "Add custom connector", and enter the ERP's address followed by /mcp (for example https://erp.grunte.com/mcp). Claude opens the ERP's sign-in; signed in, you see which app asks, the organization it would work in and what it may do, and click Allow. Claude then uses the ERP's MCP tools as an agent tied to you: it reads, and prepares entries, invoices and bills that wait for a person's approval; posting routine entries within the approval policy is a choice on the same page. It never approves, closes periods, manages keys or records a VAT submission or payment, and it can never be the second person on what you started. It is listed under Settings, API keys as "Claude (connected app)", where an administrator disconnects it. ChatGPT connects the same way (Settings, Connectors, with developer mode on).

API: OAuth 2.1 with PKCE for MCP clients (ADR-0082, MCP): /.well-known/oauth-protected-resource, /.well-known/oauth-authorization-server, POST /oauth/register (dynamic registration), GET /oauth/authorize, POST /oauth/token. A request to /mcp without a credential answers 401 with WWW-Authenticate naming the resource metadata. The connect page uses GET and POST /v1/connectors/authorization. New setting ERP_OAUTH_REDIRECT_HOSTS (default claude.ai,claude.com,chatgpt.com): where an app may be sent back. Migration 0121.

2026-10-22: A customer or vendor of another company opens in that company

Fixed: a link to a customer or vendor of another company of the organization (from Approvals, which lists every company's requests, from a sales order, purchase order, recurring invoice, subscription or revenue contract of that company) showed "Not found customer '8d70…' not found". The card now switches to that company and opens the customer or vendor there, as a search hit already did. Its statement and activity do the same.

Fixed: switching the company in the top bar while a record is open (a customer, an invoice, a journal entry, an asset …) goes to that list in the company switched to, with its filters as last used there, instead of looking for the record in the new company.

Changed: a record that is not there at all (removed, or an old link) says so in plain words in the reader's language, names the company it was looked for in and links back to its list, on every record page.

API: GET /v1/entities/{entity}/parties/{party} (and a customer's statement and activity, a vendor's statement) asked with the id of a party of another company answers 404 with the code other_company and entity, party and kind: the company the party is of, its code and kind.

2026-10-21: One language picker for the whole app

Changed: the language is chosen in one place, the picker with the globe in the top bar (English, Deutsch, Français, Italiano, or the same as the browser). It switches every screen, message and standard account name at once and is remembered on the user's account, so it follows them to another browser. It is no longer hidden in the account menu.

Changed: account names are shown in the reader's language by the app itself, like every other text, from the app's own translation files: a standard name of the shipped charts (and the accounts the app makes itself, such as the US sales tax account of a state) reads in the chosen language, a standard name followed by a currency code keeps the code, and a name the company chose shows as it is. Nothing about language is tied to the chart of accounts any more.

Removed (breaking, all data is test data): the API no longer reads Accept-Language. An account's name is always the one name the company keeps its books under, everywhere (accounts, reports, ledger, bank, VAT and year-end answers); official_name is gone, and so are the translations in the chart templates (the UI's files hold them).

2026-10-21: An account has one name; other languages come from the chart

Changed: nobody types an account's name in other languages any more. The Edit account and New account dialogs on Chart of Accounts have one name field; the four "Name in other languages" fields are gone. A standard name of the company's chart (Swiss KMU, IFRS, HGB, US, UK, Canadian, French, Italian or Spanish) shows in English, German, French and Italian by itself, in the chart's proper accounting terms; so do the US sales tax accounts per state, the Swiss lease accounts, and a standard name followed by a currency code ("Bankguthaben EUR" reads "Bank EUR" in English). A name the company made up, or changed to something that is not a standard name, shows as it is typed in every language. Better words in the catalog reach every company with the next release; a test holds every standard name to all four languages.

Removed (breaking, all data is test data): the stored translations. The names column of the account table (migration 0110), the names field of the account in GET /v1/entities/{entity}/accounts, in adding an account and in changing one, and the account.names column of organization exports. name in the account still reads in the language the request asks for (Accept-Language); official_name is the one name.

2026-10-21: A refunded credit note of a blocked bill no longer says its credit stays open

Fixed: a vendor credit note of a bill blocked for payment kept saying "the credit stays open on this credit note and can be applied to the bill" after the vendor had refunded it and nothing was left open. The notice now shows only while some of the credit is still open.

2026-10-17: A journal says why it waits, and a credit note of a blocked bill says it settles nothing yet

Fixed: a journal entry posted above the company's posting approval limit was sent for approval, but the General Journal said it waited for "a person with the right to do it". It now gives the real reason, as the entry's page and Approvals do: the amount is above the posting approval limit, and a second person approves it. A period-end run that waits for approval says so the same way.

Added: a credit note of a bill that is blocked for payment (its difference from the purchase order waits for approval) posts but does not settle the bill. The credit note now says so, names the bill and links to it; the credit stays open and can be applied to the bill once the difference is approved.

2026-10-17: Money received after a write-off, and the VAT of a write-off corrected later

Before: If a customer paid an invoice after it was written off, there was no way to book it: a payment to the invoice was refused, and the bank and receivables accounts refuse manual entries. A write-off that kept the VAT could never have it corrected when the loss became final. On the cash basis, a foreign-currency write-off in Germany or Spain could take back VAT at the invoice's exchange rate that had been reported at the rate of the write-off's day, and "keep the VAT" made VAT due on money never received. The allowance for doubtful receivables took foreign-currency invoices at the rate they were booked at.

After: Under each posted write-off on the invoice's card, Money received after write-off books the money to the bank and back to the bad-debt account, and makes the VAT the write-off corrected due again in proportion to what came in, so the VAT return of that period shows it like a sale. Correct the VAT now corrects the VAT of a write-off that kept it, on what is still not received, once the loss is final. Both go through the approval policy. On the cash basis a write-off always corrects the VAT (keeping it is refused, and the screen does not offer it), and the correction is converted at the official rate of the write-off's day where the law converts payments at their own rate, so an unpaid invoice written off leaves nothing on the return. The allowance now values a foreign-currency invoice at the closing rate of the period end, as the FX revaluation does. See bad debts.

API: POST /v1/documents/{document_id}/write-off/recovery and POST /v1/documents/{document_id}/write-off/vat-correction. WriteOffOut has recovered, vat_corrected_later and follow_ups. Eight new error codes: write_off_keep_cash_basis, write_off_not_posted, write_off_follow_up_waiting, recovery_more_than_written_off, write_off_vat_corrected, write_off_all_recovered, follow_up_in_future and follow_up_before_write_off.

2026-10-17: German reminder interest follows the base rate; the EUR 40 flat fee is not lost; credit limit checks queue

Before: A German company's default interest was built on one stored base rate (1.27 %), so a letter charged every day at that rate, also days from before the rate changed, and someone had to change the rate by hand each 1 January and 1 July. The EUR 40 flat fee was only on the payment reminder: if that letter's charge invoice was rejected or voided, no later letter charged it. Two invoices of one customer submitted at the same moment could both pass its credit limit, each check counting without the other.

After: The German base rate is a dated table of the Bundesbank's rates (from 2016, including 1.62 %, 3.12 %, 3.62 %, 3.37 %, 2.27 %, 1.27 % and 1.52 % from 2023 to July 2026: 10.52 % for businesses and 6.52 % for consumers from 1 July 2026). With the defaults, each day is charged at that day's base rate plus 9 points (businesses) or 5 points (consumers): days that span 1 January or 1 July are split at the change, each part on its own line of the letter and the charge invoice, at its own rate (EUR 1,190.00 due 20 June 2025 and reminded on 10 July: 10 days at 11.27 % is 3.67, 10 days at 10.27 % is 3.35). Saving the settings (a fee, say) keeps the rates left at their default following the base rate; only a rate the administrator changed is fixed. The flat fee goes with the first letter whose charge stands: when the payment reminder's charge is rejected or voided, the second reminder charges the EUR 40. A credit limit check locks the customer while it counts, so invoices submitted at the same moment are checked one after the other and the second waits for a second person if together they go above the limit.

See Customer statements and reminders and Customer credit limits. New field interest_parts on the reminder proposal's invoices and on letters' items (additive), and the column dunning_notice_item.interest_parts (migration 0118, which also lets dunning_setting's rates be empty: following the base rate).

2026-10-12: Form checks in your language

Before: When the server refused a request whose values it could not read or that broke a rule of the form ("Line 2: the quantity must be above zero"), the screens showed that sentence in English, whatever the person's language.

After: The screens word these refusals in the person's language, with the field's label, numbers written as the person writes them and the allowed values by their labels: "Zeile 2, Menge: muss grösser als null sein". A rule a document checks itself (a line needs either a debit or a credit) still shows its own sentence as the server writes it, in English.

For integrators: each entry of a validation_failed refusal's fields now also carries items (name and number from 1), field (the name as sent) and the rule it breaks, with its limit, the count its words agree with and the choices of a choice, beside loc, type, msg and message. A validator's own sentence has no rule.

2026-10-12: A row opened right after typing a search stays open

Fixed: opening a row of a list right after typing in its search box (within a quarter of a second) opened the card and then went back to the list, because the search text still waiting was written to the address afterwards. The search now gives way once a row is opened. This applies to every list with a search box: customers, vendors, invoices and bills, purchase orders and journal entries.

2026-10-12: Cash basis VAT counts money on account from the day it came in

Before: Money received before its invoice is kept on account and applied once the invoice is there. On the cash basis of VAT the ERP took the day it was applied as the day it was paid. That day is typed when applying and defaults to today, so 1,081.00 received on 20 June for an invoice of 25 June and applied on 10 July was reported in Q3 instead of Q2. An application into a period whose VAT return was already final was not refused either.

After: Money kept on account counts from the day it came in, and never before the invoice's date. In the example above, the VAT is reported in Q2. Applying it after Q2's return is final is refused with vat_period_locked, so the return is amended instead. The open items still use the day of the application.

2026-10-12: The last part of a credited line takes the rest of its VAT

Fixed: crediting an invoice line in parts could leave the last part refused. 3 units at 333.33 less a 54.25 discount, at 8.1 % VAT, are 945.74 net and 76.60 VAT. A credit note of 1 unit took 25.54 VAT; one of the other 2 units rounded its own VAT to 51.07, and its 681.56 was refused with credit_more_than_invoiced ("681.56 exceeds the 681.55 left"). Now the credit note line that credits the rest of a line's quantity takes the rest of its net and the rest of its VAT (51.06), so the parts add up to the invoiced line to the cent. Other parts keep the VAT of their own net.

Fixed: the refusal approval_withdraw_admin_only said that another administrator rejects the request. Any second person with the approve permission can reject it; the message now says so, in all four languages.

Changed: when a change of the posting approval limit applies at once because it lowers the limit or because the approval policy allows it, the same person's own older request to raise or remove the limit that still waits is withdrawn, as it already was when she is alone. Approved later, it would have set a limit from before her change. It shows as withdrawn and is audited as approval.withdrawn (without alone); a request someone else made stays for a second person.

For integrators: PATCH /v1/entities/{entity} with a lower posting_approval_limit answers posting_limit_request: null when the waiting request was the caller's own; deciding that request afterwards answers 409 approval_decided. POST /v1/documents and PATCH /v1/documents/{document_id} for a credit note line with credits_line that credits the rest of the line return the rest of its tax_amount.

2026-10-12: The overview and the service health page answer faster

Before: The overview, the first page after sign-in, asked the reference-data service for every VAT deadline of two years separately, twice each: 18 requests for a Swiss company and 50 for a French one, one after the other. It sometimes took more than the 500 ms the read objective allows. The operator's Service health page read each objective's figures from Cloud Monitoring one after the other and took about five seconds, and that time counted against the ERP API's read objective.

After: Business days, and whether a VAT return form is in force, are kept for an hour (a VAT return itself is still computed from forms fetched fresh), so the overview asks it each question once and, on a second visit, nothing at all; a holiday the publisher adds still arrives within the hour. Service health reads the objectives' figures at the same time, about one second instead of five. Like a VAT register check, it waits on another provider's API, so GET /v1/platform/health and GET /v1/platform/models are now tagged heavy in the request log: they are charted and no longer count against the read objective.

2026-10-12: VAT can be due when paid (cash basis)

Before: VAT was always due with the invoice (or the date of supply). A Swiss company accounting on collected fees (vereinnahmte Entgelte), a German one with Ist-Versteuerung, a UK business in the cash accounting scheme, a Spanish one under the criterio de caja or an Italian one with IVA per cassa had to correct every return by hand. The VAT settings offered only the method (effective or net tax rate in Switzerland, the Quick Method in Canada).

After: The VAT settings have "VAT due": on the invoice, or when paid, in Switzerland, Germany, France (services), the UK, Spain and Italy, with the law and its condition shown. When paid, an invoice's turnover and VAT are on the return of the period in which the customer pays, in proportion to what is paid: an invoice of 1,000.00 + 81.00 VAT from September of which half is paid in October is on the October (or fourth-quarter) return with 500.00 and 40.50. An unpaid invoice is on no return. Credit notes, cash discounts and reversed payments follow their settlement. Input VAT is reclaimed when the bill is paid in Switzerland, the UK, Spain and Italy; in Germany it stays with the bill, as the law says, and in France so does the VAT on goods bought. In Spain what is still unpaid on 31 December of the following year, and in Italy one year after the sale, is due then. Italian sales to customers without a VAT number stay on the invoice. A payment dated before its invoice (a prepayment matched later) is on the return of the invoice's period: a payment of 1,081.00 on 20 September for an invoice of 1,000.00 + 81.00 dated 5 October is on the fourth-quarter return. In Germany and Spain an invoice in another currency is converted at the official rate of the payment, as the law says (UStG § 16 Abs. 6, Ley 37/1992 art. 79): a USD invoice of 1,000 + 190 from August paid in September at 1.25 USD per euro is on the September return with 800 and 152.00, and a reversal of that payment in October takes back exactly 800 and 152.00 at the September rate. In France the VAT on services bought from a French supplier is deducted when the bill is paid, on either setting (CGI art. 271): the supplier's VAT on services is due when paid unless it opted for the débits, which the ERP does not know, so the deduction is never early. The choice applies to documents posted from then on; when you change it, the settings warn that documents already posted keep their basis and say how many are still open on the cash basis. The reconciliation shows the VAT booked with the invoice but due with the payment as "other period", not as an unexplained difference, and a payment cannot be recorded when it would change a VAT return that is final. The conditions (turnover limits, applications) are shown but not checked.

For integrators: GET and PUT /v1/entities/{entity}/vat-settings have basis (invoice or cash; refused with cash_basis_unavailable where the country offers none); the settings also return cash_basis_law, cash_basis_condition, cash_basis_purchases and cash_basis_open (how many posted documents on the cash basis are not settled in full). Tax and base journal lines of a document posted on the cash basis carry tax.cash_basis: true (and tax.cash_until, the latest day, in Spain and Italy) instead of a tax point; in France so do the lines of a services bill from a French supplier, on either basis. The export package documents vat_settings.basis.

2026-10-12: Depreciation and revenue recognition notes in German, French and Italian

Before: what a depreciation schedule said about itself (where its method and rate came from, a residual value a depreciation table does not deduct, a rate limited by law, the year the rest is written off, an impairment or revaluation that changed it) was in English only, and the asset card did not show it at all. The revenue recognition preview's notes (an earlier entry or period to see to first, a contract waiting for the ECB rate) were partly in English only too.

After: the asset card shows these notes under each book's schedule, and they and the revenue recognition preview's notes read in English, German, French and Italian, with dates and amounts in the reader's format. Legal references (for example CAA 2001 s.56(1) for a UK rate that changes during a period) stay in the words.

For integrators: AssetBookOut has note_reasons and the revenue recognition run and preview (RecognitionRunOut) have note_reasons (each waiting contract a note_reason): the notes by code with their values, in the same order, as Reason objects (assets.scheduleNote and periodClose.recognitionNote). notes stays in English. Breaking in data-vNext: POST /v1/fixed-assets/depreciation-schedule returns each of data.notes as an object with code, details and text instead of a string (a recorded exception to the additive /v1, see API conventions), and so does the MCP tool depreciation_schedule. The schedules stored before (an asset's schedule and tax memo, its tax books) are rewritten the same way when the release is installed; a note that cannot be read back is left out and kept in the asset's audit trail.

2026-10-12: Contract revenue by cost centre

Before: An invoice line that billed a revenue contract could name a cost centre, profit centre or project. The value stayed on the contract liability, though, and the monthly revenue recognition posted without it, so contract and subscription revenue always showed as "Not assigned" in Results by cost centre. A journal draft naming a value that had been deactivated since was refused at posting with a technical English message.

After: The revenue recognition takes the cost centre, profit centre and project of the invoice lines that billed the obligation, even when a value has been deactivated since. When one obligation was billed under several cost centres, the revenue is split in proportion to what was billed under each, less credit notes. Both the revenue and the release of the contract liability carry the values, so contract revenue appears under its cost centre and the contract liability of each cost centre comes back to zero. Revenue recognised before the obligation was billed names none. A draft whose value has been deactivated or has ended since is refused in plain words when it posts, saying which line and value to change.

2026-10-12: Reports to Excel

Before: The reports downloaded as CSV files only. Opened in Excel, a CSV file depends on the computer's settings: amounts can turn into text or lose their decimals, and accented letters can come out garbled. The group statements of a consolidation run had no download at all.

After: The trial balance, income statement, balance sheet, cash flow statement, VAT report, aging report and results by cost centre have an "Export to Excel" button beside "Export to CSV". The file opens in Excel as it is: amounts are numbers with two decimals that add up, dates are dates, codes stay text, the first row is the heading, and the income statement and balance sheet carry their total rows. The prior-year and budget columns go into the file when they are shown. The group balance sheet, profit and loss and cash flow statement of a consolidation run can be downloaded to Excel and CSV too.

For integrators: Nothing changes in the API: the file is made in the browser from the report the screen shows.

2026-10-12: A group cash flow statement

Before: A consolidation run showed the group balance sheet and income statement, but no cash flow statement, which group accounts under IFRS need (IAS 7).

After: A run has a Cash flow tab. It explains the change of the group's cash from the start of the group year to the run's date by the indirect method, from the group's report lines: the profit before the minority's share, the changes in working capital, investing and financing. A subsidiary acquired in the year shows as one investing line, the price paid less the cash it held (IAS 7.39), not as changes of its receivables, goodwill and minority interest. Exchange rates are kept apart: the effect on the cash held in other currencies is its own line (IAS 7.28), and the translation differences on goodwill, the minority's interest and the other items are not shown as cash flows. The statement starts from the group's run at the end of the previous year; when the group has books before the year and that run has not been made yet, the tab says which run to make first. Each line opens to the report lines it comes from.

For integrators: GET /v1/consolidation-runs/{consolidation_run}/cash-flow returns the statement (opening_run, opening_cash, sections with their lines and each line's report_lines, net_change, closing_cash, balanced). It answers 409 group_opening_run_missing with as_of when the run at the end of the previous year is needed.

2026-10-12: Sell an asset to a customer, retire a part, impair it, reverse an impairment or revalue it

Before: A fixed asset could only be disposed of as a whole, with the proceeds booked straight to a bank account. Selling it to a customer meant a disposal and a separate invoice by hand, and the invoice's line had no proper place to go: either the VAT or the gain or loss came out wrong. A part of an asset could not be retired, a loss in value could only be booked by hand to the asset's accounts (it then showed as an "adjustment" and the depreciation went on as before), and a revaluation to fair value under IFRS was not possible at all.

After: The asset card has two new buttons. "Sell to a customer" makes an ordinary sales invoice with the VAT the sale carries, so the VAT is on the VAT return and the receivable on the customer; the asset (or the part sold) leaves the books against the net price, in the company's currency, with the gain or loss on the disposal accounts. When the invoice needs a second person's approval, the asset's entry waits with it: approving the invoice books the asset's entry too, and rejecting the invoice puts the asset back; on the Approvals page, the request to approve the invoice shows the asset's entry line by line and its gain or loss. A price reduced after the sale is an ordinary credit note on the invoice whose line goes to the account of gains on disposals. A sale by an agent always waits for a person: first the invoice, then the asset's entry; so does an asset's entry above the company's posting approval limit, once the invoice is approved. "Change value" retires a part of the asset (a percentage or an amount of its cost, sold or scrapped: its share of the depreciation leaves with it; proceeds go to a bank or cash account, and a part sold on credit is sold on an invoice), records an impairment with its reason (Dr impairment loss, Cr accumulated depreciation), reverses an impairment once its reasons are gone, or revalues the asset to its fair value (a rise to the revaluation surplus in equity, as income where it reverses an earlier loss; a fall against the surplus first, the rest as a loss). Each change books the month's depreciation first, posts one entry that follows the approval rules like a disposal, shows in the asset's history, and recalculates the depreciation of the later months on the new value. The asset shows the change at once; if a second person rejects the entry, the asset is put back as it was. An impairment, its reversal, a revaluation and a part retired without an invoice are recorded by a person: an agent is refused, whatever the organization's approval rules say.

The reversal of an impairment (the Wertaufholung of HGB § 253 (5), required there; allowed under IFRS, IAS 36.114, and Swiss law) writes the asset up as income, at most to the value it would have today, net of depreciation, had it not been impaired (IAS 36.117), never a cent above it, and the asset stays at cost. US GAAP and ASPE forbid it: there (whatever the chart: a company on an American or Canadian chart keeping IFRS books may reverse), on an asset never impaired, and once nothing of the impairment is left to reverse, "Change value" does not offer it. A revaluation is income only up to that value too; the rest goes to the revaluation surplus. Revaluation is offered on IFRS and FRS 102 books only: Swiss law (OR 960a), the German HGB (§ 253), the Italian, Spanish and US rules keep an asset at cost, and the refusal says so.

A revaluation is not a cash flow: neither it nor an impairment set against the surplus shows on the cash flow statement. The deferred tax on a revaluation is charged to the revaluation surplus, not to deferred tax expense (IAS 12.61A); the part that unwinds as the asset is depreciated is income. A Swiss tax book (ESTV) takes the commercial impairment as its deduction (DBG Art. 58 and 62, the Massgeblichkeitsprinzip) and follows its reversal: while an impairment stands, the tax value is at most the carrying amount in the books. The charts have new accounts for this: a clearing account for asset sales, an impairment account and, on the IFRS, UK and Canadian charts, a revaluation surplus. On the cash flow statement, an asset sold on credit counts as an investing inflow when the customer pays the invoice, not before.

For integrators: new POST /v1/assets/{asset}/sale, /partial-retirement, /impairment, /impairment-reversal and /revaluation (each needs an Idempotency-Key). The history (GET /v1/assets/{asset}/history) has the event kinds partial_retirement, impairment, impairment_reversal and revaluation. An asset's cost is now its current cost: less what partial retirements took out, or the revalued amount; GET /v1/assets/{asset} has value_changes, the changes the asset can take now under the law of the company's books. New account roles asset_sale_clearing (a control account), asset_impairment_loss and revaluation_surplus, and the report line bs.equity.revaluation_surplus. New policy action change_asset_value (an impairment, its reversal, a revaluation, a part retired without an invoice): agents are always denied (403, rule agents_no_asset_value_changes), and an organization's policy may send it to a second person (require_approval); a stored policy that does not list the action decides it by the default rules. A disposal dated before a change in the asset's value is refused (asset_changed_later); a part retired without an invoice to an account that is not cash is refused (proceeds_account_not_cash); the asset's entry of a sale is not decided alone while its invoice waits (sale_invoice_waits, 409). The invoice of a sale is the only sales document whose line books to the clearing account of asset sales; any other is refused as before (control_account_on_document). Once an asset's value changed, its acquisition is no longer reversed (POST /v1/assets/{asset}/reverse-acquisition answers 409 asset_value_changed with asset, kind and date): dispose of it instead. BookTotal of the deferred tax report has surplus_movement, the part of movement charged to the revaluation surplus.

2026-10-12: Vendor master-data controls: four eyes on key details, possible duplicates, bills recorded twice

Before: One person could rename an active vendor, change its VAT number or move its payment terms from 30 to 90 days, and the change took effect at once. Only a vendor that already had posted documents was protected, and payment terms never were. A second vendor with the same name, VAT number and IBAN as an existing one could be created and activated without a word, and vendor invoice ALP-1001 could then be booked once on each of them. Two bills of the same vendor with the same amount and date posted one after the other. A vendor could be created in "XXX", a currency without exchange rates, and its bills then failed. A vendor could be given the company's own bank account as its IBAN.

After: Once a customer or vendor is no longer a draft (active, blocked or inactive) or has a posted document, a new name, VAT number, country, region, currency, payment terms, cash discount, credit limit or withholding tax waits for a second person. The card shows the values now and after approval. A draft still changes at once, because its activation needs a second person anyway. When a customer or vendor is created, the ERP looks for others of the same company with the same VAT number, an IBAN in common or the same name (case, punctuation and AG, GmbH and similar ignored). It still creates the new one, and names the others as possible duplicates on the card, in the answer that creates it and in its activation request: "Possible duplicate of V-ALP Alpen Werkzeug AG: same VAT number and IBAN". A vendor invoice number is recorded once per supplier: vendors with the same VAT number or IBAN count as one supplier, and a second bill is refused with the bill and vendor it repeats. A bill with the same vendor, amount and date as another posted or pending bill waits for a second person, who reads why, for example "possible duplicate of BILL-2026-000012: same vendor, amount 777.00 EUR and date 2026-09-24". A currency without exchange rates is refused: use the company's currency or one the European Central Bank publishes. An IBAN or QR-IBAN of any of your own companies is refused as a customer's or vendor's bank account: "CH93 0076 2011 6238 5295 7 is your own company's bank account, not the vendor's".

For integrators: POST /v1/entities/{entity}/parties now answers PartyCreated, a party with possible_duplicates (id, code, name, status, same of vat_number, iban and name, message). GET /v1/entities/{entity}/parties/{party} has possible_duplicates too, and an activate_party request carries them in its summary. PATCH /v1/entities/{entity}/parties/{party} sends payment_terms_days, cash_discount_percent and cash_discount_days (with credit_limit, withholding_code and withholding_rate) to the change_party_details request along with the other key details, for every party past draft. Create and change refuse 422 party_currency_without_rates (currency, functional) and party_own_bank_account (iban, kind). POST /v1/documents and POST /v1/documents/{document_id}/submit refuse 409 vendor_invoice_twice_supplier (external_number, document, party, party_name) for a number another vendor of the same supplier holds. A bill held as a possible duplicate answers submit.status: "pending_approval" with submit.reason, and its approval request has reason_words with the code bill_twin (document, amount, currency, date) or bill_twin_waiting.

2026-10-12: Write off bad debts and keep an allowance for doubtful receivables

Before: A customer invoice that would never be paid stayed open forever, or was "settled" with a credit note that went to the customer and looked like a price reduction. Nothing covered the risk of unpaid invoices at a period end, and the charts had no bad-debt or allowance accounts.

After: An open customer invoice has a Write off action. You give the amount, the date and a reason, and you say whether the loss is final. If it is final, the output VAT on the amount is corrected and the VAT return shows the reduction where the form wants it: on the Swiss return as a deduction in Ziffer 235 with the turnover at the rate reduced, Ziffer 200 unchanged. Cash discounts granted now land in Ziffer 235 the same way. On the French CA3 the VAT of a write-off is deducted on line 21 (dont régularisation sur de la TVA collectée) and the sales stay as declared. If it is not final yet, the VAT stays owed and the whole amount goes to bad debts. It is an internal entry: nothing is sent to the customer. It always waits for a second person on Approvals. A foreign-currency invoice is written off at the rate it was booked at. The invoice then shows as written off, not paid, and so does the customer's account activity. Under Period close, Doubtful receivables works out an allowance at each period end. Invoices you mark doubtful get their own percent, and every other open invoice gets the percent of its aging bucket (suggested: 0 % not yet due, 5 % overdue, editable per company). One click posts the difference to the allowance account, and the close checklist reminds you while the allowance is out of date. Every chart template has the new accounts, for example 1109 Delkredere and 3805 Verluste aus Forderungen in the Swiss KMU chart. See bad debts.

API: POST /v1/documents/{document_id}/write-off, GET /v1/documents/{document_id}/write-offs, GET and PUT /v1/entities/{entity}/receivable-allowance/settings, PUT and DELETE /v1/documents/{document_id}/doubtful, GET and POST /v1/periods/{period_id}/receivable-allowance. A settlement of a document (AllocationOut) can now be of the kind write_off, with write_off_id and write_off_number, and an account activity line can be of the kind write_off. Seven new error codes: write_off_not_invoice, write_off_nothing_open, write_off_in_future, write_off_before_settlement, allowance_done, allowance_buckets and doubtful_not_invoice.

2026-10-12: Delivery notes and cost of goods sold

Before: A company with stock could receive, move and count its goods, but selling them did not take them out of stock: an invoice of a stock item booked the revenue and left the goods and their value in the warehouse.

After: Goods leave on a delivery note (guide, ADR-0077, section 6). A draft note to a customer from a warehouse is posted with a gap-free number (DN-2026-000001) and books the goods' cost at the moving average (Dr cost of goods sold / Cr inventory); each line keeps the cost it left with. Notes print in German, French, Italian or English, with the items and quantities and no prices. Invoice this delivery note drafts the invoice of what was delivered and not yet invoiced, at the customer's price-list price where it has one, and no invoice ever bills more than was delivered. Invoicing a sales order invoices what its delivery notes delivered from those notes, so the goods of an order line leave once. A stock item invoiced without a delivery note ships with the invoice, and so does a stock item on a recurring or subscription invoice, which is now refused when the goods are not on hand; the refusal comes in words, before anything posts. An invoice and the cost of goods sold of its goods are decided together: when the cost would wait for a person, the invoice waits with it. A credit note line can say the goods came back: they return to a warehouse at the cost they left with, not today's average. Delivered, not invoiced lists the goods shipped and not yet billed, at cost. Demo AG ships scanners and label rolls to Zürich Kunde AG from Zürich at the end of March and invoices them; a label printer shipped from Basel in September waits to be invoiced.

For integrators: new operations under the tag "delivery notes": /v1/delivery-notes (list, create, read, change a draft, post, cancel, pdf, invoice) and GET /v1/entities/{entity}/delivered-not-invoiced. Sales document lines take delivery_line (an invoice line of a delivery note line), goods_returned and warehouse; the stock posting kinds delivery and return now come from delivery notes, invoices and credit notes (source types delivery_note and trade_document). The org export has the tables delivery_note and delivery_note_line in the group "stock".

2026-10-12: Stock: warehouses, stock items, counts and valuation

Before: ESoR Headless booked services only. A company that sells goods had no warehouses, no quantities on hand and no inventory value in its books other than what it booked by hand.

After: A company keeps goods in stock (guide, ADR-0077). It adds warehouses, marks the products it sells per unit that are goods as stock items, and sees the quantity in each warehouse, the value and the moving average cost of one unit. Stock adjustments book damaged, lost or found goods (with a reason; an agent's adjustment waits for a person), transfers move goods between warehouses without touching the ledger, and stock counts book their differences at the moving average. No stock goes negative, not even through a back-dated posting. Every chart (Switzerland and Liechtenstein, Germany, France, Italy, Spain, the United Kingdom, the United States, Canada and IFRS) has accounts for inventory, cost of goods sold, inventory changes and goods received not invoiced; only stock postings book the inventory accounts, so the stock valuation ties to them to the cent, and the period close checks that it does. The demo company Demo AG now resells barcode scanners, label printers and label rolls from a Zürich and a Basel warehouse.

For integrators: new operations under the tag "inventory": /v1/warehouses, /v1/stock-items, /v1/stock-adjustments, /v1/stock-transfers, /v1/stock-postings, /v1/stock-counts and GET /v1/entities/{entity}/stock-valuation. New journal sources inv and inv_adjustment (refused for a reversal by hand), a new close check inventory_reconciled, the report line bs.assets.current.inventory, the account roles inventory, cogs, inventory_adjustment and grni, the policy rule agent_stock_adjustments, and the org export's tables in the new group "stock". The refusal account_codes_in_use now also names the roles the accounts would have had.

2026-10-12: Leases (IFRS 16, ASC 842)

Before: A leased office or company car existed only as the lessor's bills, booked as rent. A company reporting under IFRS or US GAAP had to work out the right-of-use asset, the lease liability, the interest and the depreciation in a spreadsheet and book them by hand, every month.

After: Leases under Assets & tax lists every lease the company holds as a lessee, with its liability (in the lease currency), the part due within 12 months, the right-of-use asset and the months not booked yet. New lease takes the lessor, currency, commencement date, term, payment (monthly, quarterly or yearly, in advance or in arrears), the discount rate (an effective annual rate), initial direct costs and prepayments; the accounts come from the chart (the IFRS and US GAAP charts now have 1720 right-of-use assets, 1729 their depreciation, 2750 lease liabilities and 7130 lease interest, the US chart also 6110 operating lease cost). A US GAAP company classifies each lease as an operating lease (the default), which books one straight-line lease cost a period, or a finance lease, which books interest and depreciation; under IFRS every lease is a finance lease. The lease card shows the schedule month by month to the cent, the liability split into current and non-current, the changes and the history. Activate books the asset and the liability at commencement, Post lease entries books a period's interest and depreciation for all leases at once (each lease once per period; in the period a lease commences, the part of its first month that ran by the period's end; a lease whose change of payments waits for approval waits too), Change payments remeasures the lease for an index increase at the original rate, and End lease derecognises it early with a gain or loss; all go through the approval policy. The lessor's bills are booked on the lease liability account. A lease in another currency is revalued at each period end like a loan; the asset stays at its original rate. Short-term and low-value leases are only listed, their bills stay expenses. A company whose framework is local (Swiss Code of Obligations, HGB and the like) enters no lease: it expenses its leases. A company that reports under IFRS or US GAAP on a local chart is reminded that its local rules usually expense them. The close checklist warns of leases whose months are not booked yet. The demo has Demo AG's Basel office rented in EUR, Demo GmbH's company car and Demo Inc.'s Austin warehouse, an operating lease.

Not built yet: lease incentives and restoration costs in the initial measurement, depreciation over the useful life when ownership transfers, lessor accounting, subleases and reassessing the lease term. See Leases and ADR-0072. Operations: tag "fixed assets" in the API reference, POST /v1/leases, POST /v1/periods/{period_id}/leases.

2026-10-12: Price lists per customer and currency

Before: A product had one list price per currency. A reseller or key account that was agreed other prices, or a lower price from ten pieces on, had its price typed by hand on every invoice, quote and order line, and nothing in the system said which price a customer had been agreed.

After: Sales > Price lists keeps price lists: each is in one currency and holds prices of the company's products, with lower prices from a quantity on (volume tiers), each from a day. A price is never changed; a new price from a later day replaces it. On the customer's card, the Price lists section gives the customer one list per currency (a CHF list and a EUR list, for example) and shows every change in the customer's history. A new invoice, credit note, quote or sales order line for that customer now proposes the customer's price for the line's quantity and says where it came from ("Customer price 209.00 CHF from the price list Wholesale CHF for 10 or more"), next to the list price. The price on the line is still yours to change, and nothing is posted by a price list. The demo company sells a barcode scanner and a label printer, with a wholesale CHF list for its Swiss customer and a partner EUR list for its German customer.

For integrators: POST /v1/price-lists, GET /v1/price-lists, GET and PATCH /v1/price-lists/{price_list_id}, POST /v1/price-lists/{price_list_id}/prices, and GET, PUT and DELETE /v1/entities/{entity}/customers/{party}/price-lists/{currency}. GET /v1/products/{product_id}/price takes party and quantity and then answers customer_price with the list and tier it came from. The MCP tool list_price_lists reads them. See the price lists guide and ADR-0070.

2026-10-12: Customer invoices collected by SEPA direct debit

Before: The ERP paid vendors with pain.001 files but could not collect from customers. A customer who had signed a SEPA direct debit mandate had nowhere to keep it, and each receipt was recorded by hand or matched from the bank statement.

After: A customer's card has a Mandates tab: reference, signature date, account holder, IBAN, optional BIC, scheme (CORE or B2B), recurring or one-off. A new mandate and a new IBAN wait for a second person in Approvals, and agents never record or approve one. A mandate shows when it expires (36 months without a collection) and can be revoked. The EUR bank account collected into gets the company's SEPA creditor identifier under Bank accounts, with its check digits verified. The new Direct debits screen (Sales) suggests the open EUR invoices of customers with an active mandate that are due by the collection date, and says which it left out and why. It collects the ones you confirm as one receipt per mandate, held in payments in transit until the bank statement shows the money. A second person releases them as a pain.008.001.08 file, under the rules of a payment file: never an agent, never whoever made the run, each collection once, the file kept with its SHA-256, and no file from a test copy. The collection date moves onto a TARGET2 business day, at the earliest one TARGET2 day after the bank gets the file (D-1). A return or refund on the statement (an R-transaction such as MD06, in camt or in MT940, where a debit marked RC or with the German code 108 or 109 counts as one) reverses the receipt, so the invoice is open again.

  • Sequence types: FRST is not used. Every collection under a recurring mandate is sent as RCUR, the first one too, as the rulebook allows since 2016. A one-off mandate's collection is OOFF, and FNAL is not sent.
  • Pre-notification: the invoice is the pre-notification. An invoice is collected only when its date is at least the mandate's notice period (14 days unless agreed otherwise) before the collection date.
  • Due date and cash discount: an invoice is never collected before its due date. Inside its cash discount period it is collected early, less the whole discount (1,190.00 with 2 % becomes 1,166.20), and the discount is booked as for any receipt. The collection date is the first day the bank can collect on, and a collection released so late that the bank would collect after the discount period is not sent.
  • Creditor identifier: changing it waits for a second person. The next collection under each mandate tells the debtor's bank the old identifier.
  • Returns with the bank's fee: a return that includes the bank's fee (up to 50.00) still reopens the invoice, and the fee is booked on bank charges. A return for a closed or blocked account (AC04, AC06) stops all collections from that account, including ones already made, until the customer's new IBAN is recorded. A return because the mandate is not valid, refused or the customer has died (MD01, MD07, MS02) stops the mandate for good: revoke it and record a new one.
  • Addresses: the customer's address is in the file when its bank or your account is outside the EEA (for example a EUR account at a Swiss bank), and your own address too when your account is.
  • Payments in transit: a company without a payments-in-transit account collects nothing until it adds one in Chart of Accounts with Add missing accounts.
  • Scope: SEPA in EUR only. Invoices in other currencies and other schemes (LSV+, CH-DD, Bacs) are not collected.

API: GET/POST /v1/entities/{entity}/customers/{party}/mandates, PUT /v1/mandates/{mandate_id}/account, POST /v1/mandates/{mandate_id}/revoke, POST /v1/entities/{entity}/direct-debit-proposals, POST /v1/entities/{entity}/direct-debit-runs, GET /v1/entities/{entity}/direct-debits, POST /v1/direct-debit-exports; GET /v1/payment-exports takes kind (credit_transfer or direct_debit); bank accounts have creditor_id. See SEPA direct debits and ADR-0076.

2026-10-12: Invoices and bills have a date of supply, and VAT follows it where the law says so

Before: A document had one date, the invoice date, and it decided everything about its VAT. Goods delivered on 28 September and invoiced on 2 October were on the October VAT return, also in Germany, where that VAT is September's; the VAT exchange rate of a dollar invoice was that of October; and the invoice did not show when the goods were delivered.

After: Sales invoices, credit notes and bills have a date of supply (Leistungsdatum, date de livraison, data di effettuazione), entered under the document date on the form and when an order is invoiced; empty means the document date. It is printed on every invoice (the invoice date when no other is given), shown on the document and in its history, sent in XRechnung and ZUGFeRD as the delivery date, and read from an e-invoice received. Where the company's country makes VAT due with the supply, the VAT return takes the document in the period of the supply: Germany, Spain and the UK (unless invoiced within 14 days), and goods in France and Italy. Switzerland stays with the invoice date (MWSTG Art. 40). Supplies to and from other EU countries are reported with the invoice, at the latest the month after the supply. Input VAT is still claimed with the invoice; a credit note is reported with its own date; an invoice issued before the supply is reported with its own date. Each rule names its legal basis. The VAT rates and the official VAT exchange rate (Germany: the BMF rate of the month of supply) are those of the date of supply. The return's reconciliation shows the VAT the date of supply moved between periods in a column of its own ("Other period"), and the VAT report and the turnover reconciliation show it as a labelled item, so nothing becomes an unexplained difference. A filed VAT month also locks a later invoice whose supply falls in it.

For integrators: POST /v1/documents, PATCH /v1/documents/{document_id} (null clears it) and POST /v1/sales-documents/{document}/invoice take supply_date; DocumentOut returns it (null: the document date). Tax and base journal lines whose tax point is not the entry's date carry tax.tax_point (ISO date). The VAT return reconciliation has other_period per account and in total, the VAT report other_period_output_tax and other_period_input_tax, and reconciling items may be other_period_sales or other_period_purchases. The export package documents trade_document.supply_date.

2026-10-12: Exempt supplies everywhere in Europe, and the rate's category on the line

Before: Only Swiss companies could invoice an exempt supply (rent, insurance, medical care, education, financial services: no VAT and no input VAT deduction). In Germany, France, Italy, Spain, the United Kingdom and every other European country the line was refused with "no 'exempt' rate in force in FR on ...". The line screens offered the same rate types in every country ("Super-reduced", "Parking", "Zero"), most of which then failed, while the Swiss special rate for accommodation (3.8 %) was not offered at all. In France, Italy and Spain "Reduced" covers several rates (France: 5.5 % for books, 10 % for restaurants), and the screens had no way to say which: the document was refused with an English text holding a Python dictionary. A Swiss exempt line took an arbitrary kind of exemption (agricultural produce) whatever was supplied.

After: Every EU country, the United Kingdom and Norway have an exempt rate with its kinds and legal basis (Germany UStG §4 Nr. 8 to 29, France CGI art. 261, Italy DPR 633/1972 art. 10, Spain LIVA art. 20, the United Kingdom VATA 1994 Sch. 9, the others under the VAT Directive Art. 132 and 135). The returns put exempt sales in their box: UStVA Kz 48, CA3 line E2, LIPE VP2 and Dichiarazione IVA VE33, Modelo 390 box 105. The invoice, credit note, bill, quote, order, purchase order, recurring invoice and product screens offer only the rate types the company's country has on the document date, each with its percent (Switzerland: Special 3.8 %). Where a rate type has several rates or kinds of exemption, a second field below it asks for the category (books 5.5 %, restaurants 10 %; insurance, healthcare, letting for a Swiss exemption); the category is kept on the line and shown again when the document is reopened, copied or credited. A Swiss exempt line names its kind. When a category is missing, the refusal says so in the person's language and lists the categories in words; a rate type the country does not have is refused the same way.

For integrators: GET /v1/entities/{entity}/vat-rate-types?date= lists the rate types in force for the entity, each with its rates (series, rate, categories, description) and category_needed. A line names one of those categories as tax_category (documents, sales documents, purchase orders, recurring invoices; up to 200 characters, purchase order lines took 40 before). A category that is a rate's exact label now wins over one that only contains the text, and of exact labels the one of the line's rate_type. A line whose rate type matches several rates, or several kinds of exemption (Switzerland), without a category is refused with tax_category_needed (422; country, date, choices); a Swiss exempt line without tax_category was accepted before and is refused now. A rate type the country does not have is refused with tax_rate_type_missing (404; rate_type, country, date). data-vNext's error body carries the same code and details for these two.

2026-10-12: Customer credit limits: an invoice above the limit waits for a second person

Before: a customer had no credit limit. The only way to stop invoicing a customer who owed too much was to block it by hand (a second person approving the block).

After: a customer can have a credit limit in its own currency, on the customer card and in the parties API (credit_limit; empty for none). Changing it once the customer has documents waits for a second person, like its name or currency. Its credit exposure is what it owes now (open invoices less credit notes and payments on account), plus its invoices waiting for approval, plus what is left to invoice on its confirmed sales orders, all in the customer's currency (other currencies at today's ECB rate). An invoice that would take the customer above its limit does not post but waits for a second person, saying for example "Credit limit: Stockholm Design AB would owe SEK 128,000.00, above its credit limit of SEK 100,000.00; a second person approves." A sales order above the limit is not confirmed (orders have no approval of their own), nor a quote accepted into one. Credit notes and receipts are never held. The customer card shows the limit, the exposure and the credit available; the new Credit exposure screen under Sales lists every customer with a limit, those over it first, and downloads it as CSV. See Customer credit limits.

API: credit_limit on POST and PATCH /v1/entities/{entity}/parties[/{party}], credit on GET /v1/entities/{entity}/parties/{party}, new GET /v1/entities/{entity}/credit-exposure (format=json|csv), new refusal 409 credit_limit_exceeded on POST /v1/sales-documents/{document}/confirm and /accept, 422 credit_limit_customers_only, and the approval reasons credit_limit and credit_limit_no_rate (reason_words). Migration 0114 adds party.credit_limit.

2026-10-12: Reminder letters charge fees and default interest; disputed invoices can be kept out of reminders

Before: A reminder letter only asked for the open amount. It printed no fee line and no interest line, and nothing was booked, so default interest the law allows (5 % a year in Switzerland, the base rate plus 9 points and EUR 40 per invoice to a German business) was never claimed. An invoice the customer disputed kept getting reminders; the only way to stop them was to leave it off by hand each time.

After: Payment Reminders → Fees and interest sets, per company and reminder level, a fee per letter and a flat fee once per invoice to a business customer (both in the company's currency, converted at the letter's day's rate for a letter in another currency), and a yearly default-interest rate for business customers and for consumers. Until an administrator saves, the country's defaults apply: Switzerland and Liechtenstein 5 % a year and no fee; Germany 10.27 % for businesses and 6.27 % for consumers (base rate 1.27 % plus 9 or 5 points, to be kept up to date on the screen) and EUR 40 on the first reminder; elsewhere nothing. Interest runs on what is open each day, actual days over 365, from the due date or from the last day an earlier letter charged, so no day is charged twice: CHF 8,000.00 overdue 45 days at 5 % is CHF 49.32. The proposal and the letter show each charge, and the letter prints a fee line and an interest line per invoice (rate, from, to, days) in the customer's language, the new total and the remark that the charges carry no VAT. When the letter is first sent or recorded, its charges are booked as a sales invoice outside the scope of VAT (debit receivables, credit the new account role "dunning fees and interest income", in every chart template: Swiss 6955, German 7305, French 763800, Italian 7120, Spanish 769100, others 7030) through the normal approval policy; that invoice is an open item the customer pays, it is on the statement and is reminded in turn; if the approver rejects it, or it is voided, the letter shows it as not charged and the next letter charges those interest days and the flat fee again. An open invoice's card has Stop reminders with a reason (a person, never an agent); a stopped invoice gets no letter and no charges, and the reminder run lists it under "Reminders stopped" with the reason until someone resumes it. Every stop, resume and settings change is kept with who did it.

See Customer statements and reminders. Operations: GET/PUT /v1/entities/{entity}/reminder-settings, GET/POST/DELETE /v1/documents/{document_id}/dunning-block; new fields fee, interest, interest_rate, interest_from, interest_until, flat_fee on the reminder proposal and letters, charge (with charged) on a letter and blocked on the proposal (additive).

2026-10-12: Group statements under Swiss GAAP FER and HGB

Before: A group could be set up under Swiss GAAP FER or the German HGB, but every group run was refused, with a message that named a settings file instead of saying what was wrong. Only IFRS groups could be consolidated.

After: Groups under Swiss GAAP FER (FER 30, as revised in 2022) and under HGB are consolidated like IFRS groups, with the rules each standard sets for goodwill:

  • Swiss GAAP FER: goodwill is offset against equity at the acquisition date (the default) or capitalised and amortised. Either way the group states a useful life of at most 20 years; left empty it is 5 years, the life FER 30 sets when it cannot be determined. With goodwill offset against equity, the run also shows what capitalising it would have given (the amortisation of the year and the carrying amount the notes must show). A negative goodwill is offset against equity too, or, with capitalised goodwill, kept as a liability and released to profit or loss over five years.
  • HGB: goodwill is amortised over its useful life, 10 years when it is left empty (§ 253 para. 3 HGB), kept in the subsidiary's currency and translated at the closing rate. A negative difference is shown after equity as "Unterschiedsbetrag aus der Kapitalkonsolidierung" and kept: the run warns that releasing it depends on its cause and is a manual adjustment. The statements say "Nicht beherrschende Anteile" and "Eigenkapitaldifferenz aus Währungsumrechnung".
  • Goodwill amortisation now starts in the month of the acquisition, as fixed assets do.
  • The group cash flow shows goodwill offset against equity, a negative goodwill and the HGB difference as part of the acquisition (not as a dividend or a loan), and a release of negative goodwill as income that is not cash.
  • The New group dialog asks for the goodwill treatment and the useful life.
  • A run under a framework whose rules have not been checked is refused in plain words: "group statements under ... are not available yet".

The cited paragraphs were checked against extracts of the published texts, not the full texts; a person reads them before the first final group close.

For integrators: POST /v1/consolidation-groups takes goodwill_years for the policies the framework gives a useful life (amortise, and under fer also offset_equity) and fills in 5 (fer) or 10 (hgb) when it is left out; it refuses years for other policies (goodwill_years_not_used, replacing goodwill_years_amortise_only) and too many (goodwill_years_too_long). The run's policy names negative_goodwill and negative_goodwill_years. GET /v1/consolidation-runs/{id}/eliminations adds per subsidiary theoretical_capitalisation and negative_goodwill, and the goodwill treatment negative_goodwill. The group balance sheet adds after_equity and total_after_equity (the HGB difference, line bs.after_equity.consolidation_difference). The exception bargain_purchase_unsupported is gone; the warning negative_goodwill_not_released is new. See consolidation and ADR-0075.

2026-10-12: An active revenue contract can be ended early or modified

Before: A revenue contract that no subscription bills could only run to its end. "Cancel" on an active contract answered "RC-2026-000005 is active; only a draft is cancelled" and offered nothing else. A customer who stopped a maintenance contract after four months could be credited by hand, but the contract kept booking revenue every month for service no longer given, and the gap grew as a contract asset.

After: The contract's window has End early and Modify, as SAP and Oracle offer a contract modification.

  • End early: choose the new last day and say why. The price becomes what was earned up to that day (the months through it, and the month it falls in by its days), so the customer pays for the service given. A higher price, such as a fee for ending early, is booked as revenue on the last day.
  • Modify: a new price and/or a new last day, earlier or later, from the day it takes effect: the day it was agreed, today unless you enter another. The service before that day keeps the old price, also in months whose revenue is not booked yet; the rest of the new price is spread from that day. The day cannot be before a day whose revenue is already booked. A further promise at its own price is a new contract.

Before asking, the window shows the price and last day before and after, from which day, what was invoiced, what will be credited on which invoice, what is still to invoice and which recurring invoices stop. A second person approves; until then nothing changes. Revenue already booked never changes. When the customer was invoiced more than the new price, a credit note per invoice is posted together with the change, each invoice line credited with its own VAT rate, and dated the day of the approval (never earlier: a price reduction belongs to the VAT period it is granted in); what was already paid stays open for a refund. When the new price is above what was invoiced, invoice the rest. A recurring invoice that bills the contract stops on its new last day: it invoices nothing after it, and ends when nothing is left. When it also bills other items, only the contract's lines stop and the other items go on being invoiced. The approvals list shows the request with its prices, last days and credit.

Example: CHF 12,000.00 of maintenance for 2026, invoiced in full and booked through April, ended on 30 April: the price becomes 4,000.00 and a credit note of 8,000.00 plus 8.1 % VAT (8,648.00) is posted; nothing more is booked from May and the contract liability is zero. The same contract booked through February only and modified to 10,800.00 on 12 October: March to September stay at 1,000.00 a month, 1 to 11 October is 354.84, and the rest, 1,445.16, is spread from 12 October (352.48, 546.34, 546.34); 1,200.00 plus VAT is credited on 12 October. "Cancel" on an active contract now says to end it early or modify it.

The credit notes of a subscription that is ended early or changed are now also dated the day of the approval, not the day the change takes effect.

A subscription's contract is still ended or changed from the subscription. A contract with a promise not yet delivered is refused until the delivery is recorded.

For integrators: POST /v1/revenue-contracts/{contract_id}/changes/preview, POST /v1/revenue-contracts/{contract_id}/changes (kind end or modify, end_date, price, effective_date for modify, reason; answers the change and the approval_id) and GET /v1/revenue-contracts/{contract_id}/changes. The approval request has object_type revenue_contract_change and is decided with POST /v1/approvals/{approval_id}/approve. New error codes: contract_active_not_cancelled, contract_end_not_earlier, contract_change_nothing, contract_change_pending, contract_of_subscription, contract_documents_unposted, contract_credit_not_possible, contract_credit_not_posted, contract_change_recognised, contract_change_outside_term. The preview's recurring_invoices names the recurring invoices that stop (ends false when other lines go on); a recurring invoice line has until, the last due date it is invoiced on. The new table revenue_contract_change is in the data export. See revenue contracts and ADR-0074.

2026-10-12: Recurring and accrual journals

Before: An entry that repeats every month (the office rent accrual, a cost allocation, the release of a prepaid insurance) was typed again by hand each period, and an accrual had to be reversed by hand in the next month. The General Journal page could not ask for a reversal at all, although the API could.

After: General ledger > Recurring Journals holds journal entries that repeat monthly, quarterly or yearly, from a first due date to an optional last one. Each has its lines (accounts, debit or credit, currency, the line's analysis tags) and can be an accrual: every entry it makes is reversed automatically on the first day of the next period. A recurring journal is created as a draft (its first entry is built and checked, not kept), and a person activates it; an agent cannot. Post due journals (on the list, on a card, or from the Role Center cue "Recurring journals due") makes the entry of every due date that has come, through the approval policy like an entry typed by hand: it is posted, or it waits for a second person. A due date is never made twice, also when two people post at once; a period that fails (for example because it is closed) stays due and shows its error under the entries made. The period-close checklist warns when recurring journals are due through the period. The General Journal page has a new Reverse automatically option with a Reverse on date, the first of next month unless you change it. The demo company Demo AG has two recurring journals due from 30 September: the office rent accrual and an insurance release in EUR.

Not built yet: posting due journals automatically in the scheduled close job. See recurring journals and ADR-0071. Operations: tag "recurring journals" in the API reference, POST /v1/recurring-journals, POST /v1/recurring-journals/run, GET /v1/recurring-journals/{recurring_journal_id}/runs; MCP tools list_recurring_journals, get_recurring_journal, recurring_journal_runs.

2026-10-12: Input VAT that is not fully deductible

Before: All the VAT on a domestic bill was booked as input VAT and reclaimed on the VAT return. There was no way to say that the law lets the company deduct only part of it, or none: gifts and other costs of § 4 Abs. 5 EStG in Germany, passenger cars and their fuel or staff lodging in France ("TVA non récupérable"), cars, telephones and entertainment in Italy ("IVA indetraibile"), vehicles, gifts and hospitality in Spain ("no deducible"), private use in Switzerland. The VAT return reclaimed too much, and the cost was too low.

After: A bill line says how much of its VAT is deductible, in percent ("Deductible VAT", under "VAT not fully deductible?" in the line's description cell): 100 % by default, 0 % for none, 50 % for a car used half privately. An account can carry a default ("Deductible VAT on bills" when you change an expense or asset account), e.g. 0 % on a gifts account, which every new bill line booked to it takes unless the line states its own; bills already entered keep theirs. The deductible part is booked as input VAT; the rest is booked as part of the cost on the line's own account, and no VAT return reports it: a German bill of 1,000 + 190 VAT at 0 % costs 1,190 and adds nothing to Kz 66; at 50 % it costs 1,095 and adds 95. A reverse-charge line still owes all of its VAT; only its input VAT is cut. A credit note of the bill takes back the same split, a cash discount corrects the cost part as cost. The VAT report and the reconciliation of each return list the cost part as a reconciling item ("Input VAT that is not deductible"), so nothing is unexplained. The yearly pro-rata of a partly exempt business is not computed: a line can state the provisional percent, and the yearly correction stays a journal entry. See ADR-0079.

For integrators: DocumentLineInput.vat_recovery_percent (0 to 100, two places; null takes the account's, else 100) on POST /v1/documents, PATCH /v1/documents/{document_id} and the preview; refused with vat_recovery_not_applicable (422) below 100 on a line without input VAT of its own (a sales line, a line outside VAT, foreign VAT booked as cost, US sales or use tax, a Canadian line of several taxes). DocumentLineOut.vat_recovery_percent is the percent the line took. vat_recovery_percent on AccountInput, AccountUpdate (PATCH /v1/entities/{entity}/accounts/{code}, null takes it away; never waits for a second person) and AccountOut. Journal lines that book the part not deductible carry the tax tag kind non_deductible with recovery_percent; the VAT report and the return reconciliation carry the reconciling item non_deductible_vat_purchases.

2026-10-12: Withholding tax kept back from vendor bills

Before: A company that must keep tax back from a vendor and pay it to the tax authority itself (the Italian ritenuta d'acconto on a professional's fee, the Spanish IRPF retención, the German tax of Paragraph 50a EStG on a royalty paid to a foreign licensor, the US 30 % NRA withholding) had no way to book it. A bill line cannot be negative, a payment carries only a cash discount, and a manual entry on the payables account is refused, so the vendor was shown as owed, and paid, the full amount.

After: A vendor carries a withholding code from a table for the company's country (DE-50A 15.825 %, IT-RA-20 20 %, ES-IRPF-15 15 %, ES-IRPF-7 7 %, US-NRA-30 30 %, and others) or a rate of its own (custom), on the vendor card. Its bills keep the tax back on their net amount before VAT: an Italian professional's EUR 1,000.00 + 22 % IVA books Dr cost 1,000.00, Dr VAT 220.00, Cr payables 1,020.00 and Cr the new withholding tax account 200.00. The bill shows the tax and what is paid to the vendor; payments, payment proposals and the pain.001 file pay the 1,020.00. A credit note gives the tax back in proportion, the last one to the cent. A cash discount lowers the tax too: paying the 1,020.00 bill with 2 % off (999.60) takes 20.00 off the cost, 4.40 off the VAT and 4.00 off the ritenuta, which ends at 196.00, as booked, as owed and as the report and the vendor's certificate show it. In a foreign currency the bill books the tax at its rate, and each payment moves the part it releases to the rate of the payment day, which is what the authority is owed and what the report by payment date shows. Changing a vendor's code or rate once a bill is posted needs a second person. The new report Reports → Withholding tax lists, per period, each bill with the vendor, the net amount, the rate, the tax in the bill's currency and as booked, and whom it is paid to, with totals per code, by bill date or by payment date, and exports it to Excel and CSV (the API answers CSV too). The company pays the authority by booking the bank line to the withholding tax account. Every chart template has the account (role vendor_withholding_payable): 2207 in the Swiss KMU chart, 4450 in the German, 4431 in the Italian, 475101 in the Spanish, 442800 in the French, 2231 in the others.

For integrators: withholding_code and withholding_rate on POST /v1/entities/{entity}/parties, PATCH /v1/entities/{entity}/parties/{party} and every party answer; withholding_code, withholding_rate, withholding_base and withholding_amount on every document answer, whose total is now net plus VAT less the withholding tax. The report's rows by bill date include kind cash_discount for a discount taken on such a bill. New: GET /v1/entities/{entity}/withholding-tax/codes and GET /v1/entities/{entity}/withholding-tax?start=&end=&basis=&format= (guide).

2026-10-12: Cost centres, profit centres and projects

Before: A journal line could carry free text under "dimensions" that nothing checked and no report read. There was no list of cost centres, profit centres or projects, invoices and bills could not name one, and there was no way to see revenue and expenses by cost centre or project.

After: General ledger > Cost centres and projects lists what postings are analysed by. Every organization has cost centres, profit centres and projects; an administrator adds their values (a code such as CC-100 and a name, for every company or one, from and until a day), renames them, deactivates them, and sets the accounts on which a line must name one. A value is never deleted and its code never changes, so a posted line always shows what it named. The general journal, invoice and bill lines, and a bank line posted to an account each have a choice per dimension. An invoice or bill line's cost centre goes to its revenue or expense posting, not to the VAT or the receivable; a credit note and a cash discount take back the values of the lines they correct, even when a value has been deactivated since. Reports > Results by cost centre shows revenue, expenses and result per cost centre, profit centre or project over a range, with the accounts behind each and the postings behind an account; lines that name none are a row of their own, so the report adds up to the income statement. The demo and test books have cost centres, profit centres and projects on their August invoices and bills.

For integrators: GET/POST /v1/dimensions, PATCH /v1/dimensions/{code}, POST /v1/dimensions/{code}/values, PATCH /v1/dimensions/{code}/values/{value} and PUT /v1/dimensions/{code}/required-accounts (admin scope; agents are refused). GET /v1/entities/{entity}/reports/dimension-balances and .../dimension-lines (read scope). Document lines (DocumentLineInput, DocumentLineOut) and POST /v1/bank-lines/{line_id}/post take dimensions, as journal lines did. A line naming an unknown dimension or a value not usable that day is refused with 422 dimension_unknown or dimension_value_unknown, and one lacking a dimension its account requires with dimension_required; the database refuses the same at posting.

2026-10-12: Postings above a company's limit need a second person

Before: there was no value limit for people. An accountant alone posted a manual journal of CHF 5,000,000.00 and a vendor bill of CHF 5,405,000.00, both at once, and reversed the journal herself.

After: each company has a posting approval limit in its functional currency. A new company starts at about CHF 100,000 in its currency as a round figure (100,000 CHF, EUR, USD, GBP or CAD; 1,000,000 SEK or NOK; 750,000 DKK; 15,000,000 JPY; ...), and every existing company got the figure for its currency. When a person submits an entry above it, it waits under Approvals with the reason Four eyes: CHF 5,405,000.00 is above the company's posting approval limit of CHF 100,000.00; a second person approves it. That is everything a person posts through the ledger: manual journals, bills, invoices, credit notes, payments, refunds and applications, bank and intercompany postings, asset postings, and the runs a person starts (VAT settlement, FX revaluation, depreciation, deferred tax, revenue recognition, year-end close). A bill shows "waiting for approval" until a second person with the approve scope approves it; then it posts and gets its number. The person who asked never approves it. A document in another currency is compared after conversion (a EUR 200,000.00 bill at 0.9438 is CHF 188,760.00, so it waits). Reversing an entry above the limit waits for a second person too. Amounts at or below the limit post as before. Agents keep the approval policy's own limits, and purchase-order approval is unchanged.

The limit is shown on Settings > Company with a one-line explanation. An administrator lowers it at once; raising or removing it (no limit, for a one-person company, after a confirmation) waits for a second person under the new policy action change_posting_limit, and the old limit holds until then, so one administrator alone cannot lift the limit, post and put it back. Each change is audited as entity.posting_limit with the value before and after. See ledger.

Relaxing the approval policy needs a second administrator. Before, one administrator alone could undo every second-person control through the policy itself: allow change_posting_limit, remove the limit, post CHF 5,405,000.00 alone and restore both; the same worked for bank details and account roles. Now a change of the approval policy that relaxes any decision (refused to second person or allowed, or second person to allowed, for people, agents or the system, at any amount or source), and a return to the built-in policy where it is looser, waits for a second person with the administrator permission under Approvals, with a summary of what it relaxes. The person who asked never approves it, and agents never do. Until then the policy in force holds; Settings > Approval Policy shows the waiting change. Only one waits at a time. A change that only tightens applies at once, and so does any change in an organization with fewer than two active people (audited with what it relaxes), unless another user was disabled or lost the administrator permission in the last 30 days: disabling the other administrator first does not let one person relax the policy alone.

A request nobody else can decide no longer blocks the next one. Before, a one-person company that clicked No limit had a request nobody could approve, could not reject it herself, and every later change of the limit was refused while it waited; an administrator whose colleagues are controllers, accountants or viewers was stuck the same way with a change that relaxes the policy. Now a person alone in the organization (one active person, nobody else disabled or demoted in the last 30 days) raises or removes the limit at once, audited with alone. And the person who asked to raise or remove the limit, or to relax the policy, withdraws her own waiting request with Withdraw on Settings > Company or Settings > Approval Policy: the limit or policy in force stays, a new request may be asked, and the withdrawal is audited as approval.withdrawn. Nobody withdraws for someone else (a second person rejects instead), and agents never do. API: POST /v1/approvals/{approval_id}/withdraw (admin scope; 403 approval_withdraw_own_only, 409 approval_not_withdrawable); the request becomes rejected with the comment withdrawn. When a person alone changes the limit or relaxes the policy at once, her own older request of that kind is withdrawn automatically; Approvals shows a withdrawn request with a Withdrawn badge instead of as her own rejection (withdrawn: true in the API); and someone without the admin scope who tries to withdraw is told in plain words that only the person who asked may (approval_withdraw_own_only, or approval_withdraw_admin_only for the person who asked but is no longer an administrator).

The posting-limit hint on Settings > Company now names everything the limit holds: manual journals, bills, invoices, payments and refunds, and the runs a person starts, such as the VAT settlement, depreciation and the year-end close.

API: PUT and DELETE /v1/approval-policy answer with the policy in force and, for a change that relaxes it, pending_change (the approval request, who asked, relaxed and relaxed_count); 409 approval_policy_change_waiting while one waits. POST /v1/approvals/{approval_id}/approve applies it (403 approval_policy_admin_only without the admin scope; 409 approval_policy_changed if the policy changed meanwhile). GET /v1/approval-policy shows pending_change.

API: GET /v1/entities/{entity} returns posting_approval_limit and the waiting posting_limit_request; PATCH /v1/entities/{entity} changes it (an amount, or null for no limit; only a person with the admin scope, 403 posting_limit_admin_only; 422 posting_limit_invalid; 409 posting_limit_change_waiting while a request waits). POST /v1/approvals/{approval_id}/approve applies a raise or removal (409 posting_limit_changed if the limit was lowered meanwhile). POST /v1/documents, POST /v1/journal-entries and POST /v1/journal-entries/{id}/reverse answer with the approval request when the limit holds the entry; approving it posts it.

2026-10-12: Prior year and budget beside the income statement and balance sheet

Before: The income statement by dates and the balance sheet showed one column only: no prior year, no budget and no change to read the figures against. A section opened to its accounts one at a time, and an account of the income statement opened its ledger from the first of January rather than from the report's first day.

After: The income statement shows the same dates a year earlier and the approved budget beside each account, each report line and the totals, with the change and the variance in money and in percent. A month end stays a month end, so a report through 29 February compares with one through 28 February. The budget is, for each fiscal year in the dates, the approved version of its budget (of several budgets, the one approved last), for the fiscal periods wholly in the dates; a note above the report says which budget it is, and says in plain words when there is none or when a period is only partly in the dates. The balance sheet shows the balances at the end of the previous fiscal year beside its own, with the change. A "Comparatives" switch hides the extra columns. "Expand all" opens every report line at once, and an account opens its ledger over exactly the report's dates, where each line opens its journal entry. A report in another currency compares with the prior year translated the same way; the budget, planned in the company's own currency, is left out with a note. These notes, and those of the income statement by fiscal period (no prior period, periods of different length), now read in the screen's language; they came in English before.

For integrators: GET /v1/entities/{entity}/reports/profit-and-loss takes compare=prior_year and budget=true; GET /v1/entities/{entity}/reports/balance-sheet takes compare=prior_year_end or compare_as_of. Each account and section then carries comparative (and budget): the comparative amount, the change and change_pct, and the report carries the totals in comparative (with the comparative dates) and budget (with the budget versions and periods), and caveats in notes. Each note, here and in the period income statement (.../reports/period-profit-and-loss), is now a coded reason (words reportNote, code, details, text) instead of an English string. An account with figures only in the comparative is listed with a zero balance. Without the new parameters the reports answer as before. There is no budget on the balance sheet: budgets plan revenue and expenses only.

2026-10-12: EU One-Stop Shop (OSS) return; sales to consumers abroad no longer block the VAT return

Before: A sale by a German, French, Italian or Spanish company to a private customer in another EU country charged that country's VAT from the first euro and booked it on the ordinary output VAT account. The German UStVA, the French CA3 and the Italian LIPE then showed an error ("fits no box") and could not be finalised. There was no One-Stop Shop (OSS) return to declare that VAT.

After: Such a sale is checked against the EU-wide threshold of 10,000 euros a year of distance sales of goods and electronic services (this year with the invoice, or last year if higher): below it the company charges its own VAT; from the sale that crosses it, the customer's country's VAT. The customer's VAT is booked to its own account, "VAT due under the One-Stop Shop" (Germany 4405, France 445740, Italy 4405, Spain 477200, other EU countries 2205), added to older companies on their first such sale. The VAT settings of a company in the EU have two new switches: registered for the One-Stop Shop, which adds a quarterly OSS return to the VAT returns list, due on the last day of the month after the quarter; and charging the customer's VAT below the threshold too. The OSS return lists the taxable amount and the VAT per country of consumption, goods or services and rate, ready to type into the tax office's portal, and is finalised, submitted and paid like any VAT return. The domestic returns leave that VAT out and can be finalised; the bases stay where the forms ask for them (UStVA Kz. 45, CA3 E3, Modelo 303 casilla 123). An invoice to a consumer abroad by a company not registered for the OSS says so.

For integrators: POST /v1/vat-returns takes the kind oss_return (period YYYY-Qn); its result has oss_rows (member_state, supply, rate, rate_type, base, vat). GET and PUT /v1/entities/{entity}/vat-settings have oss and oss_destination, and the answer has oss_available. The new account role is vat_oss. Document warnings may carry the code oss_not_registered, return issues oss_correction; the result has oss_corrections (period, member_state, vat); VAT settings have oss_from; a final OSS return locks only its own lines, and a posting into it is refused with oss_period_locked. Design: ADR-0068.

2026-10-12: Group close: a member version can be corrected, and a payment in transit no longer blocks

A subsidiary entered without its purchase data can be completed

Before: A subsidiary first approved without the price paid, its net assets at acquisition or the parent's investment account could never be repaired. The group run asked for a new version with them, but a new version (on the same date or a later one) was read as a change in the share, and every run stayed blocked with "a step acquisition or a partial disposal is not built". The only way out was a new group. The message also showed field names and paragraph numbers.

After: A new version that keeps the share and the acquisition date and adds or corrects the purchase data replaces the earlier version's purchase data from the acquisition on, whatever date it takes effect from. A new version on the same date as the one before replaces it entirely, so a share or a date typed wrong can be put right too. The corrected version stays in the history: the approval and the group structure screen say "corrects version 1". The group run then computes goodwill and the non-controlling interests as usual. A real change of the share (buying more shares or selling some) still stops the run, now in plain words: the share in DE01 changes (80 %, 90 %), and buying more shares or selling some is not supported yet. A version a final group close used cannot be corrected.

A payment between two group companies that only one has booked

Before: When one group company had paid an intercompany bill and the other had not booked the receipt yet, the intercompany reconciliation rightly called it a timing item, but the group run was incomplete with a blocking difference ("EUR 10000 against 0") until the other company booked.

After: The group run shows such a payment as cash in transit between group companies on the group balance sheet, at the closing rate, and warns; the run and the group statements are complete. Example: DE01 pays CH01's EUR 10,000.00 bill on 28 September, CH01 books the receipt in October: the group balance sheet at 30 September shows CHF 9,438.00 of cash in transit (EUR 10,000.00 at 0.9438) instead of the receivable. The eliminations tab labels the pair "Payment in transit". A difference that the payments in transit do not fully explain still blocks.

For integrators: a member version has corrects_version; a run's members have purchase_data_version; an elimination pair can have the treatment cash_in_transit with in_transit; the new exception ic_cash_in_transit (warning) and report line bs.assets.current.cash_in_transit; ownership_change_unsupported now names shares and dates; a correction after a final run is refused with 409 member_version_closed. See consolidation and ADR-0073.

2026-10-12: Credit notes show the discount they credit and settle no blocked bill

Before: A credit note line that credited an invoice line silently took over that line's discount: crediting 1 at a typed price of 200.00 against "10 x 150.00 less 10 %" posted 180.00 net, and the form showed the discount greyed out, so it could not be changed. A bill blocked for payment (its variance against the purchase order waiting for a second person) could not be paid, but a vendor credit note could still be applied to it and it became "partially paid". A credit note of a bill matched to a purchase order did not lower what the order invoiced: after crediting 1 of 12 hammers the order still said 12 invoiced, 1,675.00 instead of 11 and 1,575.00, so the next hammer billed looked like a quantity beyond the order.

After: A credit note line carries its own discount, visible and editable. In the credit note form each credited line starts with the invoice line's price and discount (where the invoice had a document discount, the line's share of it is included in the line's discount), and a discount amount follows the quantity you type: crediting 4 of 10 shows 4/10 of it. Left as filled in, the line credits the invoice line as it was, worked out exactly by the server; a price or discount you change is posted as you typed it. A saved draft remembers which lines credit the invoice line as it was: reopened with a new quantity, such a line is again worked out by the server, while a line whose price or discount you typed (0 included) keeps them and is posted as shown, its discount amount following the quantity. Over the API, a line that names the line it credits and gives no price credits the line as it was, and the response states the price and discount it took (150.00 less 10 %) and as_credited: true. A line that gives a price must say its discount when the credited line had one (0 for none): "line 1 of INV-… has a discount of 10 %: send the discount to credit (0 for none), or leave out the price to credit the line as it was". Customer and vendor credit notes behave the same. A bill blocked for payment is refused by every way of settling it, with the same words as a payment ("bill … is blocked for payment: its variance against purchase order … waits for approval"): a payment, or money on account or a credit note applied to it. A credit note of the blocked bill itself still posts, but it does not settle the bill: the credit stays open (the credit note says so) and is applied to the bill once the variance is approved. A posted credit note of a matched bill lowers the order line's invoiced amount on the order card, in the matching of later bills and in the order's status, and its invoiced quantity when it gives goods back (a line at the bill's price); a price reduction gives back no quantity, so an 11th chair on a 10-chair order is still a variance.

For integrators: DocumentLineInput.unit_price may be left out on a credit note line with credits_line (the line then credits that line as it was). DocumentLineOut.as_credited (new, stored, migration 0108) is true on a credit note line that gave neither price nor discount and so took them from the line it credits; send such a line again without price and discount to credit a new quantity as it was, or with them to credit them as given (the flag is then false). A change to a draft that leaves its lines alone keeps the flag. discount_percent and discount_amount take 0 for "no discount". A line with credits_line and a unit_price crediting a line that had a discount (its own or a share of the document discount) must give a discount: without one it is refused with 422 credit_discount_required (document, line, discount) instead of taking the credited line's discount as before, so an unchanged request never credits a different amount silently; lines crediting a line without discount behave as before. A credit note may give a document discount of its own. POST /v1/applications with a credit_note answers 409 bill_payment_blocked for a blocked bill, as POST /v1/payments does; a credit note of a blocked bill posts without an allocation to it; the database refuses any allocation to a blocked bill (migration 0107). GET /v1/purchase-orders/{order}: invoiced_amount is net of posted credit notes of the matched bills, invoiced_quantity net of their lines at the bill line's price. Operations: POST /v1/documents, PATCH /v1/documents/{document_id}, POST /v1/documents/{document_id}/submit, POST /v1/applications, GET /v1/purchase-orders/{order}. See ADR-0055.

2026-10-12: Audit files for the tax auditor: the FEC in France and the GoBD data carrier in Germany

Before: A company had no file to hand its tax auditor. A French company could not produce its FEC (fichier des écritures comptables), which it must hand over at the start of a tax audit, and a German company had no period- and company-scoped data carrier for the auditor's Z3 access: only the whole organization's archive export carried an index.xml.

After: Reports → Audit files (shown for companies in France and Germany) makes the file for one fiscal year, or for a range of its closed periods, from the posted entries only. A year that is not closed yet can be exported; the screen says so, and the German file states it in its index.xml. Each download is recorded in the audit trail. See API and UI.

  • FEC (France), <SIREN>FEC<closing date>.txt: the 18 fields of LPF art. A47 A-1 in their order with a header record, tab-separated, dates YYYYMMDD, amounts with a comma and no thousands separator; one record per journal line in posting order; EcritureNum is the entry's gap-free number in the books; the opening balances ("à-nouveaux", journal AN) first, holding the balance sheet accounts only: the profit and loss of a year not closed yet is one line "Résultat de l'exercice antérieur non affecté" on the result account; the customer or vendor on receivables and payables lines; the lettrage the app knows (a document settled in full by posted payments or credit notes inside the file); the amount (signed, negative on the credit side) and ISO 4217 currency code of lines in another currency. Labels are in French: what a person wrote stays as written, and the app's own lines read "Facture INV-2026-000001 Boutique Étoile SAS", "Règlement …", "TVA collectée 20 %", "Écart de change". The SIREN is taken from the company's French VAT number; a French company without one is refused with a plain message naming it.
  • GoBD data carrier (Germany), GoBD_<company>_<year>.zip: Sachkonten (accounts with opening balance, debits, credits, closing balance; profit and loss accounts open at zero, the result before the range on the result account), Buchungsjournal (every journal line with document, tax code (on a VAT line, the one of its base), who entered and who posted it, and when), Debitoren, Kreditoren, Steuerschluessel (the tax codes used) and Offene_Posten (documents open at the end), as CSV, described by index.xml (Beschreibungsstandard 1.6) and its DTD, with every column's meaning. It reuses the writer of the organization export's index.xml.
  • No SAF-T. None of the supported countries (CH, LI, DE, AT, GB, US, CA, FR, IT, ES) requires the OECD Standard Audit File for Tax the way Portugal, Norway, Poland, Romania or Lithuania do; in Austria it is optional. So none is made.

What could not be verified. Légifrance and the BOFiP (BOI-CF-IOR-60-40-20) were not reachable when this was built, and secondary sources disagree on whether the FEC may be UTF-8: the encodings commonly cited from the official text are ASCII, ISO 8859-15 and EBCDIC. The FEC is therefore written in ISO 8859-15; a character outside it is written without its accent, or as ?. The file has not been run through the DGFiP's test tool (Test Compta Demat); it meets the checks we know of (field names and order, dates, no empty mandatory field, balanced entries). The GoBD file is valid against the standard's DTD; an import into IDEA has not been tested.

For integrators: GET /v1/entities/{entity}/audit-files?fiscal_year= (what the file covers, the format of the entity's country, year_closed, open_periods, missing), GET /v1/entities/{entity}/audit-files/fec?fiscal_year= and GET /v1/entities/{entity}/audit-files/gobd?fiscal_year=, each with optional from_period and to_period; scope read. New error codes: audit_file_country (422, a format for another country), siren_missing (422), audit_period_range (422) and audit_periods_open (409, part of a year with open periods). The demo has a French company for trying it, Démo Lumière SARL (FR01), in its own organization on the seeded test server.

2026-10-12: Draft journal entries can be changed and deleted

Before: a journal entry saved with "Save as draft" could only be submitted or left alone. Its date, text or lines could not be corrected, and a wrong draft could not be removed: it stayed in the journal list and in the close checklist's "draft journals" warning, or was posted and then reversed, which left two needless entries in the ledger.

After: a draft entered by hand opens from the journal list (or from the close checklist's "Draft entries" link) and has Edit and Delete draft. Edit opens it in the General Journal with its date, description and lines (and its reversal date, when it has one); Save changes keeps it a draft, Post saves and posts it. A change is checked exactly like a new entry. Delete asks first. A draft has no entry number yet, so deleting one leaves no gap in the numbering; the audit trail keeps its date, description, amount and lines. Only drafts are changed: an entry waiting for approval is withdrawn, a posted one is reversed. A draft made by a process (FX revaluation, depreciation, revenue recognition and the others) is corrected by that process, and the screen says so. Whoever changed a draft cannot approve its posting (four eyes), like the person who created it.

For integrators:

  • PUT /v1/journal-entries/{entry_id} replaces a draft's content; the body is the one of POST /v1/journal-entries (the whole entry) and goes through the same checks. With submit=true it is submitted at once. The entry keeps its id, created_by and created_at; the audit event is journal.draft_updated (before, after). Idempotency-Key as on create.
  • DELETE /v1/journal-entries/{entry_id} deletes a draft (204); the audit event journal.draft_deleted carries its date, description, amount, currency and lines.
  • Both need the draft scope and answer 409 draft_not_editable (status) for an entry that is not a draft, and 409 draft_from_process (source) for a draft whose source is not manual.
  • JournalEntryOut has adjustment_period: the adjustment period the entry is in, or null.

2026-10-12: Fiscal years without gaps and a check of typed exchange rates

Before: opening fiscal year 2029 when only 2026 and 2027 existed was accepted, leaving 2028 without periods. An exchange rate typed on a journal line was booked as typed: EUR 100 at 5.0 (the ECB rate of the day was 0.9438) posted CHF 500.00 without a word. A mixed-currency entry whose converted amounts differed by more than the rounding allowance was refused without saying what the allowance is.

After: a new fiscal year must directly follow the last year (or directly precede the first); otherwise it is refused with the year to open first ("open 2028 first"). A new company opens its listed years in order. A rate typed on a manual journal line, or given in the fx_rate column of an opening balance or open item import, that differs by more than 10% from the reference rate (ECB) of the day is refused with both rates and the date, unless the person confirms it: the General Journal has a Rate column and shows "Use this rate anyway", and Data Import offers to use the file's rates anyway. A confirmed rate keeps the reference rate it was checked against on the journal line. Without a reference rate for the currency or day nothing is checked. The refusal of an entry off after conversion now says "the difference may be at most 0.05".

For integrators: POST /v1/entities/{entity}/fiscal-years answers 409 fiscal_year_gap (details year, missing, entity). POST /v1/journal-entries takes confirm_rate (default false); a line's fx_rate more than 10% from the reference rate answers 422 fx_rate_implausible (details line, currency, rate, reference, date, tolerance), and a confirmed line's fx carries confirmed, reference_rate, reference_source and reference_date. entry_off_in_currency carries limit. POST /v1/entities/{entity}/imports takes the form field confirm_rates; DataImportResult.rates_to_confirm counts the rows whose rate needs it (problems without it, warnings with it).

2026-10-12: Bank lines of a closed month and resetting a bank reconciliation

Before: a bank statement line dated in a month that was already closed (an August interest credit imported in September) could not be booked at all: posting it to an account or matching it to an invoice or payment was refused with "period 2026-08 is closed", and the line stayed unmatched. The September close check did not count it, so nobody was reminded. A bank reconciliation, once marked done, could never be corrected: a balance typed wrong stayed for good, and the right figure was refused with "account 1020 was already reconciled on 2026-09-20". On a 1440-pixel screen the "Reconciled dates" table was cut off at the right edge.

After: on Bank Statements, each line has a posting date. A line dated in a closed month is booked on the first day of the next open period by default, and the screen says why: "The bank booked this line on 28.08.2026, in period 2026-08, which is closed. It is therefore booked on 01.09.2026 …". You can choose another day in an open period, never one before the bank's date. The line keeps the bank's date on the statement and in the reconciliation; matched lines say on which day they were booked. Automatic matching still books only on the line's own day and leaves such a line to a person. The period close now counts unmatched lines dated before the period too ("1 unmatched statement line (1 dated before the period)"). On Bank Reconciliation, whoever may reopen a period (a controller or administrator) can reset the latest reconciliation of an account with a reason, then mark the date reconciled again with the right figure. The record that was reset stays in the list, with who reset it, when and why, and the reset is in the audit trail. An earlier reconciliation is reset only after the later ones. The "Reconciled dates" table fits the page.

For integrators: POST /v1/bank-lines/{line_id}/post, /match and /link take an optional on (posting date, ISO 8601; default: the booking date, or the first day of the next open period when the booking date's period is closed); a date before the booking date is refused with 422 bank_posting_before_line (date, booking_date), a date in a closed period with 409 period_closed. BankLineOut has posting_date and closed_period for unmatched lines, and match.booked_on when a line was booked on another day than its booking date. The close check bank_reconciled counts unmatched lines up to the period's end, with the codes bank_unmatched_earlier (count, earlier) and bank_unmatched_earlier_ignored. New POST /v1/entities/{entity}/bank-reconciliations/{reconciliation_id}/reset with {"reason": "..."} (approve scope and the policy's reopen_period; agents are refused): 409 reconciliation_reset_already (account, date) and reconciliation_later (account, date, later). ReconciliationDone has reset_reason, reset_by and reset_at; GET .../bank-reconciliations lists reset records too, the record in force first. Migration 0092 adds these columns to bank_reconciliation (also in the data export) and allows one record in force per account and date. See ADR-0056.

2026-10-12: A cash flow statement from the books

Before: The reports were the trial balance, the income statement and the balance sheet. There was no cash flow statement, which every financial statement pack needs (IAS 7, DRS 21 under the HGB, Art. 961b OR for larger Swiss companies).

After: Reports > Cash Flow Statement shows, for any date range, where the cash came from and went to, by the indirect method: the profit, the amounts in it that are not cash (depreciation, gains and losses on selling fixed assets, unrealised exchange differences on cash), the change in receivables, payables and the other working capital, then investing activities (fixed and financial assets) and financing activities (capital, dividends, loans), and the effect of exchange rates on cash held in other currencies. It starts at the cash at the beginning and ends at the cash at the end, both from the balance sheet's cash line, and says whether the two tie. Each line opens to the accounts it is made of, and each account to its entries for the same dates. Which line an account belongs on follows its chart: provisions and bank loans are told apart in the Spanish, French and Italian charts, Italian finished goods count as inventories, and under the German chart interest received and dividends are investing activities and interest paid financing (DRS 21). A machine bought on credit counts in investing activities when it is paid, not when the bill arrives, and the statement notes what of it is still unpaid (IAS 7.43); the VAT on it stays in operating activities. An asset sold on credit is invoiced to the buyer, so selling one now asks for a bank or cash account for its proceeds. Interest paid, interest received and income taxes paid are shown as notes (IAS 7.31-35), whether paid directly or through a bill. The Swiss, US, UK, Canadian and generic IFRS charts place bank loans and other borrowings in financing activities, declared dividends as dividends paid and provisions in operating activities; the US, UK, Canadian and IFRS charts gain accounts for them (2100 short-term borrowings, 2400 dividends payable, 2500 provisions, 2700 long-term borrowings). The range starts at the beginning of the fiscal year by default. Year-end closing entries are left out, and balances taken over from an earlier system count as the position at the start.

For integrators: GET /v1/entities/{entity}/reports/cash-flow?start=&end= returns the statement (opening_cash, sections with their lines and each line's accounts, net_change, closing_cash, balanced, notes); an end before start is refused with 422 end_before_start. POST /v1/assets/{asset}/dispose refuses proceeds on an account that is not cash with 422 proceeds_account_not_cash. The MCP server has a cash_flow tool. A chart template may set the line of its accounts with cash_flow rules (erp.modules.chart_rules).

2026-10-12: The "earlier periods not filed" warning names only periods without a final return; rounding to whole euros is shown on the return

Before: A VAT return warned that earlier periods "have no final return yet" for every period whose return had not been recorded as submitted by the first day of the period being prepared. Returns are submitted after their period ends, so a third-quarter return finalised and submitted on 12 October was still named in the fourth quarter's warning, as if it had never been filed. Returns of another length (three monthly returns for a quarter, after the company changed its period) did not count either, and a company that registered for VAT after its books started was warned about, and counted as overdue for, periods it never had to file. On the German UStVA, the French CA3 and the Italian Dichiarazione IVA, which are declared in whole euros, the return's amount payable differed from the VAT booked by a few cents (1,000.99 at 19 % is 190.19 booked, 190.00 on the UStVA), and the reconciliation showed only a "computational difference" without saying why.

After: The warning names only earlier periods (this and last year) that no final return covers, however late that return was finalised or submitted; a return being corrected still covers its period, and returns of another length count for the days they cover. The VAT settings take the day the VAT registration takes effect ("VAT registered from"): no return is listed, counted as due or named as unfiled for a period that ends before it; left empty, the company is registered from the day its books start. The reconciliation panel of a return declared in whole euros shows a line "of which rounding to whole euros" with the amount, apart from the difference from official VAT exchange rates, and explains that the form declares whole euros and computes the tax from them, and that finalising books the difference to the rounding account. The printed return shows the same line. Rounding to whole euros no longer triggers the warning about a computational difference above 1.00. Returns in cents (Switzerland, Spain, Canada, the Italian LIPE) show nothing new.

For integrators: VatReconciliation has whole_unit_rounding: the part of computation_difference that comes from declaring amounts in whole currency units (the amount payable minus the same return computed to the cent); zero for a form in cents. GET and PUT /v1/entities/{entity}/vat-settings have registered_from (a date or null): GET /v1/entities/{entity}/vat-periods leaves out periods that end before it. Like tax_number, a PUT without it sets it to null. The earlier_period_unfiled issue now means an earlier period has no final return (it used to mean no return recorded as submitted by the first day of the period).

2026-10-12: The UK VAT Return (nine boxes) from the books

Before: A company in the United Kingdom could not compute a VAT return: "data-vNext has no VAT return form for GB". Only the plain VAT report existed, and the setup said no return form was available. Goods bought from Germany or any other country abroad were booked at 0 with no import VAT, so boxes 1, 4 and 7 would have missed them.

After: The UK VAT Return (VAT100) is prepared from the books like the other countries' returns: box 1 VAT on sales plus the VAT the company accounts for itself (services from abroad, the domestic reverse charge for construction, imports), box 4 the VAT reclaimed, box 5 to pay or to reclaim, box 6 every sale without VAT (zero-rated, exempt, exports and services abroad included) and box 7 every purchase without VAT, both in whole pounds; boxes 2, 8 and 9 (Northern Ireland trade with the EU) are entered by hand where they apply. Imports are accounted for by postponed VAT accounting: the import VAT is booked on the bill and shown in boxes 1 and 4, the value in box 7. The return is quarterly by default, monthly by choice, due one month and seven days after the period; it is reconciled with the VAT accounts, settled to the VAT control account, printed in English, and its submission and payment are recorded. It is not sent to HMRC: the figures are filed through Making Tax Digital software. Quarters are calendar quarters; other HMRC staggers, the Annual Accounting Scheme, the Flat Rate Scheme and cash accounting are not supported yet.

For integrators: POST /v1/vat-returns works for GB entities (form gb.hmrc.vat100, boxes 1 to 9; the data-vNext form names each box's Making Tax Digital field). GET /v1/setup/options returns vat_return_form: true for GB. PUT /v1/entities/{entity}/vat-settings for a GB entity refuses a period other than month or quarter (vat_period_uk, period).

2026-10-12: A sale to a business in another EU country needs its VAT number

Before: A customer in another EU country marked as a business counted as one without a VAT number, so a German company's goods and services to a French business without a VAT number were invoiced at 0 % as an intra-Community supply and under the reverse charge, and the recapitulative statement (Zusammenfassende Meldung) would have needed a number the customer did not have. Only Swiss and German VAT numbers were checked: a wrong French, Italian or Spanish number such as FR00123456789, IT12345678900 or ESB12345679 was accepted.

After: Between two EU countries a sale is free of VAT or under the reverse charge only when the customer has a VAT number, as the law requires (EU VAT Directive art. 138, Implementing Regulation 282/2011 art. 18). Without one the sale is taxed like one to a private person: a service with the seller's VAT, goods with the VAT of the customer's country through the One-Stop-Shop (or the seller's below the EU threshold), and the invoice warns that the customer's VAT number should be recorded. A Swiss company's sales, and an EU company's sales to a business outside the EU, are unchanged: there the business mark still counts, with the warning that asks for the number (now also when the customer owes the acquisition tax, as a Swiss business buying a German service does). VAT numbers of every EU country, the United Kingdom, Norway, Switzerland and Liechtenstein are checked for their format, and for the check digit in Switzerland, Germany, France, Italy, Spain and Belgium; a wrong number is refused with an example of the right format, on the screen and by the server.

For integrators: POST /v1/entities/{entity}/parties and PATCH /v1/entities/{entity}/parties/{party} refuse a VAT number of these countries in the wrong format or with a wrong check digit (422 vat_number_invalid, details field, value, country, example). A sale to a customer in another EU member state without a VAT number is determined as B2C; its provenance.warning_reasons carries eu_customer_without_vat_number (details customer, country) instead of customer_not_business, and reverse_charge_without_vat_number is now also given when the customer accounts for acquisition tax.

2026-10-12: Documents refuse control accounts, zero totals and impossible dates; plainer error messages

Posting guards

Before: A manual journal refused the receivables, payables and bank accounts, but an invoice, bill or asset purchase did not. An invoice line booked to the receivables account or the bank, or a bill line booked to payables, the bank or a revenue account, posted without a word, and the aging then showed an unexplained difference that nothing could correct. An invoice or bill that added up to zero (a free item, a 100 % discount) failed with "something went wrong on our side". A due date before the invoice date, a credit note or a payment dated before the document it settles, a vendor payment dated weeks ahead and goods received next year were all accepted.

After: One rule decides which account a document line may book to, for invoices, credit notes, bills, purchase orders, products, revenue contracts and a customer's or vendor's default account: never a control account (receivables, payables, bank, payments in transit) or an accumulated depreciation account of any asset class, never a VAT or sales tax account (the tax lines fill those), a sales line only to revenue and a purchase line never to revenue. A bill may still buy a machine onto its fixed-asset account, and a revenue contract line still bills its contract account. A document that adds up to zero is refused in plain words ("there is nothing to book"), and a free line beside a billed one posts without an empty line. A due date before the document date, a credit note dated before its invoice, and a receipt or payment dated before the document it settles are refused (money paid before a document stays on account and is applied once the document is there). A payment is recorded once it is made, like a receipt: to have the bank pay later, choose the day under "Bank pays on" next to "Export payment file" on the vendor payments page. A payment that takes a cash discount is not sent for a day after the discount period, since the vendor counts the day the money arrives; a period that ends on a weekend or bank holiday runs to the next business day. A draft saved before these rules is checked again when it is posted. Goods received are recorded once they arrived. The payment and goods-receipt screens no longer offer a day after today.

For integrators: new refusal codes control_account_on_document, tax_account_on_document and document_account_type (account, name, use), nothing_to_book, due_before_document, credit_before_document, payment_before_document, tax_category_unknown (country, date, category, categories: no VAT rate applies to the line's category), payment_in_future, receipt_date_in_future, contra_account_on_document (an accumulated depreciation account) and execution_after_cash_discount (POST /v1/payment-exports with an execution date after the cash discount period of a bill whose discount the payment takes). not_revenue_account is replaced by the three account codes on document lines; not_an_expense_account now refuses only a stock item's cost of goods sold account that is not an expense account. POST /v1/payments with an outgoing payment dated after today now answers 422 payment_in_future (breaking (bug fix)).

Fixed assets: safer entry, a way to undo a wrong purchase, and depreciation month by month

Before: A residual value above the cost gave "something went wrong on our side". An asset could go into service before it was bought, and a disposal could be dated before it was in service or in the future. An asset purchase could be booked against customer receivables or revenue, could not be undone, and then blocked the month-end close. October's depreciation could be run before September's and took September's amount with it. An asset entered after its month had run was not depreciated for that month, and the close checklist did not notice.

After: These entries are refused with a message saying what to change, as is a cost account and an accumulated depreciation account given the wrong way round, or an accumulated depreciation account that is not one of the chart's. An asset purchase is credited to a clearing account or the liability it is owed on; receivables, payables, bank, tax, revenue and expense accounts are refused (an asset paid from the bank is bought with a bill, then paid like any bill). A wrongly entered asset with no depreciation yet can be removed with "Reverse acquisition" on its card: give a reason, and a second person approves where the policy asks for one. Depreciation runs month by month in order. When an asset is entered after its month has run, the close checklist shows that month's depreciation as not done until it is run again, and the second run books only the missing amount, in that month.

For integrators: new POST /v1/assets/{asset}/reverse-acquisition (body on, reason; answers AssetReversalResult with status reversed or pending_approval); an asset's status can be reversed. POST /v1/periods/{id}/depreciation refuses with depreciation_earlier_period while an earlier open month still has depreciation to book, and may be called again on a period to top it up. New codes salvage_not_below_cost, in_service_before_acquisition, disposal_before_in_service, disposal_in_future, acquisition_reversal_waits, asset_depreciated, asset_cost_account_contra, asset_accumulated_account_cost and asset_accumulated_account_not_contra; new close-check result depreciation_owed.

Error messages in plain words

Before: People signed in at a screen were told "this API key lacks the 'admin' scope", though they have no key. A form with a mistake could answer with a technical list ("Value error, ..." with field paths). VAT errors could show program data such as a list of rate categories in braces, or amounts with four decimals. Some messages named internal settings, API addresses, field names or file names. A bank CSV with an impossible date such as 31.13.2026 said the file had "no column 'date'".

After: A refusal says who may do the action ("only an administrator can manage users"), and approving your own request says a second person must do it. Form mistakes come as one plain sentence per problem, naming the line ("Line 2: the quantity must be above zero"). VAT messages say which rate is missing, or which rates apply and that the category of the supply decides, with amounts in the currency's decimals. Messages name screens, not settings or addresses. A bank CSV with an impossible date names the line and the date. An automatic check now refuses new messages that contain field names, program data, API addresses or file names.

For integrators: a missing right answers 403 missing_right (was forbidden) with scope and credential (session or key); API keys keep the "lacks the 'x' scope" wording. A request that fails validation answers 422 validation_failed with sentences in message and the problems in error.fields (loc, type, msg, message); FastAPI's detail stays. Document and intercompany validation errors carry fields instead of problems. New codes tax_category_unknown, csv_line_date_invalid, framework_unverified and framework_not_implemented; CSV amount errors are csv_line_unreadable. API, field and setting names moved from messages to details.hint: match on code, not on message text. The default approval policy's reasons are reworded.

2026-10-12: A credit note reverses exactly the VAT of the invoice line it credits

Before: A credit note line that credits part of an invoice line (it names the line it credits) had its VAT decided afresh from what the line itself said. A line that did not repeat the rate type got the standard rate: in Switzerland a book invoiced at 2.6% was credited at 8.1% (102.60 became 108.10, the customer was credited 5.50 too much and the output VAT was reduced too far). A line that did not repeat the supply type counted as a service: a German company's credit of goods sold to a French consumer (French VAT 20%) was taxed at German 19%, so 30.00 of French VAT stayed in the books and the invoice kept 1.50 open. The screen copies the rate type and supply type of the invoice line but not its tax category, so a French book (5.5%) credited from the screen failed with "2 different rates match". Only the US sales tax already took the invoice line's decision.

After: Every credit note line that names the line it credits takes that line's VAT decision as it was stored, in every country: its rate type, supply type, tax category and VAT scope, and its treatment, taxing jurisdiction and rate. The credit reverses exactly the VAT charged and posts to the same accounts and return boxes as the invoice did, even if the customer's details or the rates changed since. This holds for a whole-invoice credit, a partial credit sent over the API and one made on the screen. Only the amounts are the credit note's own: VAT on the credited net, split over the Canadian taxes as before; a vendor's credit note keeps the VAT it prints, with a warning where that differs. If a crediting line states a different rate type, supply type or category, the invoice line's decision still applies and the credit note shows a warning saying so. A credit note line that names no line is still a line of its own, decided as usual.

For integrators: DocumentLineInput.credits_line now also carries over the credited line's supply_type, rate_type, tax_category, vat_scope and tax. A stated value that differs is not used, and provenance.warning_reasons then holds credit_takes_credited_vat (line, credited, document, rate).

2026-10-12: Ending a subscription that starts mid-month credits exactly what its schedule has not earned

Before: A subscription that starts in the middle of a month bills by months from its start day (from 15 September: 15 September to 14 October is a month), but its revenue schedule counted calendar months. Ended early, it credited by the billing months and so a little less or more than its own schedule showed as not yet earned: EUR 2,600.00 a year from 15 September, ended on 31 January, had earned 982.22 by its schedule but the end kept 985.48 and credited 1,614.52, EUR 3.26 short.

After: Billing and revenue count a month the same way, from the subscription's start day. A part of a month weighs its days over the days of that month of service, so from 15 September, 1 to 14 October is 14/30 of a month and 15 to 31 October 17/31. The schedule of the example earns 985.48 through 31 January, which is what the end keeps; the credit of 1,614.52 is exactly the part not earned. Monthly amounts of a mid-month subscription now vary a little with the length of the months (219.92 for October, 213.41 for November of the example), as the days of service do. Subscriptions that start on the 1st and stand-alone contracts are unchanged. Existing mid-month subscriptions keep their current schedule until they renew or change plan; the new count applies from then on.

See subscriptions and ADR-0036.

2026-10-12: An FX revaluation can be run again after a late posting

Before: Once a month's FX revaluation had run, it could not run again ("has already run"), not even after reopening the month. A payment dated in the month but entered after the run (or an invoice entered late) left the books wrong for that month: a paid EUR invoice still carried its revaluation, so the same exchange difference was in the result twice (realised on the payment and unrealised on the revaluation), and the close checklist still said the revaluation was done. Manual correction was impossible because the receivables account takes no manual postings. The run's total also disagreed with what it posted when payables were revalued: a payable that shrank was subtracted although it is a gain (for example "total 9.25" for an entry that booked a gain of 9.95).

After: The close check "FX revaluation posted" notices a revaluation that postings made since then have changed, says by how much ("Postings since the revaluation change its result by -4.80 CHF: run it again"), and offers the run again. Running it again books only the change, at the same closing rate, dated the month's last day and reversed on the first day of the next month like the first entry; a paid invoice's revaluation is taken back, a late invoice is revalued. When nothing changed, it is refused as before; while the revaluation's entry waits for approval, it is refused until that is decided. The total of a run is now the result it books (a gain positive, a loss negative), the same amount the unrealised FX account receives, and each item shows its own result.

For integrators: POST /v1/periods/{period_id}/fx-revaluation runs again on a period whose revaluation stands when something changed, and returns the change: items (with booked, what the earlier entries booked for the item, and result) and total. 409 fx_revaluation_done now means it ran and nothing changed; 409 fx_revaluation_pending (period) while its entry waits for approval. Every item has result; total is the result posted (breaking (bug fix): it was the sum of the differences, which counted a payable's gain as a loss). The close check fx_revaluation fails with code fx_changed (change, currency). The close job runs the revaluation again when the check says so.

2026-10-12: Bank statements in MT940 can be imported

Before: Payments > Bank took camt.053 statements, camt.054 notifications and CSV files. Many German, Austrian and Dutch banks still deliver statements as SWIFT MT940, and those had to be converted or typed in.

After: Choose "Bank statement (MT940)" (or just pick the file: a file that starts with the MT940 fields is recognised) and import it like a camt.053 statement. A file may hold several statements, with or without the SWIFT envelope. Each transaction's details are read in the German structured form (the bank's booking text, the payer's or payee's name and IBAN, the end-to-end reference EREF+ and the purpose SVWZ+) or in the SWIFT form (/EREF/, /REMI/, /NAME/), so invoices are matched by their creditor or QR reference, end-to-end id and text exactly as from camt. A returned transfer (a reversed debit, or the German code 159) is marked as a return with its SEPA reason code (for example AC04) and offered for the payment it returns. A statement is named after its reference, number and closing date (STARTUMSE 00094/001 2026-09-30), so the same statement twice is refused and transactions already imported from camt are skipped by the bank's reference. A statement that names its account by German bank code and account number rather than IBAN goes to the bank account you choose. A statement whose transactions do not add up from its opening to its closing balance is refused, so a damaged file never imports half a day.

For integrators: POST /v1/entities/{entity}/bank-statements takes format=mt940; refusals mt940_balance_mismatch (statement), mt940_line_unreadable (line), mt940_balance_unreadable (value); a file without MT940 fields is statement_wrong_format. Bank statements list format mt940. BAI2 (US) is not supported.

2026-10-11: "Today" is the company's own day everywhere

Before: Many checks and defaults counted "today" in UTC, the server's day. For a company in Switzerland or the EU, between midnight and 02:00 (01:00 in winter) its own date was already the next day, so recording a bank reconciliation, a VAT submission, a satisfied revenue obligation, a closed deal or an invoice hand-over dated today was refused as "in the future", a period was "not started" on the first of the month, and defaults (a report's "as of", a price's first day, the day a payment export runs) were a day early. Only Switzerland, Liechtenstein, Germany, Austria, France, Italy and the United Kingdom had a time zone at all; the rest of the EU, the United States and Canada counted in UTC. The billing runs and invoice hand-overs allowed a day ahead of UTC to make up for it, which let a run go a day too far for a company west of London.

After: Every business date's "today" is the day it is where the company is: the time zone of its country (every country now has one, from the time zone database: Japan's is Tokyo, Australia's Sydney), and in the United States and Canada of its state or province. That covers period-end runs (depreciation, revenue recognition, FX revaluation, deferred tax, intercompany interest), VAT submission and the filing schedule, bank reconciliation and unmatching, revenue contracts and subscriptions, recurring invoice and subscription billing runs (no longer a day ahead), CRM, product prices, Swiss payment exports, sample data, the year-end close, the close tracker, reminders and statements, the setup wizard's first fiscal year, every report's default "as of", and the daily close job, which now runs each company on its own day, and the first fiscal year of a company the operator sets up. Intercompany interest is accrued on the lender's day. Screens fill in the company's today rather than the browser's or UTC's, the customer statement and aging pages no longer step back a day after midnight, and an invoice hand-over dated tomorrow is refused on the screen rather than by the server. Timestamps stay in UTC. An automatic check refuses any new code that reads the server's day for a business date.

For integrators: GET /v1/entities and GET /v1/entities/{entity} return time_zone (an IANA name such as Europe/Zurich): the zone the entity's dates are checked in. Every as_of, date and end that defaults to today now defaults to today there. POST /v1/recurring-invoices/run and POST /v1/subscriptions/run refuse a through after today where an entity they would bill is (billing_through_future), no longer a day past UTC. erp close-job without --date runs each entity on its own day; its report's run_date is then local. With --date, an entity where that day has not come yet is left alone and reported as date_ahead. POST /v1/platform/organizations with no fiscal_years opens the fiscal year that holds today where the company is, on its fiscal_year_start_month.

2026-10-11: Quotes handed over, accepted and invoiced late in the evening

Before: Recording a quote's hand-over or acceptance with today's date was refused between midnight and 02:00 in Switzerland ("A hand-over can't be in the future"), because the server counted days in UTC. Accepting the quote and drafting its order's invoice that late used the UTC day too. Canadian companies were on UTC time too.

After: The day is the company's own: today in Zurich is today, at any hour, for the hand-over, the acceptance, the order's invoice and whether a quote has expired. A Canadian company's day follows its province (Toronto, Vancouver, Halifax, St. John's and the others).

For integrators: no API changes.

2026-10-11: Canadian invoices show each tax with its own name and rate

Before: A Quebec invoice line read "Standard 14.975%", one rate for GST and QST together, under a "VAT" column (in French "TVA", with "Suisse/intérieur" for a sale in Canada). The rates printed with a dot in every language. The GST/HST return was listed as "VAT return". A company in Ontario did not see its own province in VAT settings, and a new customer could not be given a province, so the tax of a sale to them could not be worked out until the card was edited.

After: The line shows GST 5% + QST 9.975% (in French TPS 5 % + TVQ 9,975 %), under a column named after the taxes the invoice charges (GST/QST, HST). The VAT tab of the invoice is named the same way and has a row per tax. Rates follow the person's number format everywhere on invoice and journal entry cards. A domestic sale is National in French, whatever the company's country. VAT returns lists the GST/HST return under that name in all four languages. VAT settings shows the company's own province ticked and fixed, also where it has no provincial sales tax (Ontario). New customer and New vendor ask for the province (or the US state) for Canada and the United States.

For integrators: no API changes.

2026-10-11: Customers in the Canary Islands, Ceuta and Melilla are recognised from their postal code

Before: A Spanish customer or vendor counted as being in the Canary Islands, Ceuta or Melilla only if someone filled in its region by hand (ES-CN, ES-CE or ES-ML). A customer in Las Palmas entered with only its address was invoiced 21 % IVA instead of being treated as an export.

After: For a Spanish customer or vendor, the postal code fills in the region when it is left empty: 35 and 38 are the Canary Islands, 51 Ceuta and 52 Melilla. This works wherever customers and vendors come from: the form, imports, e-invoices and bills read by an agent. A shipment to a customer there is then an export without IVA. A region that doesn't fit is refused with a message saying so: one of another country (a French customer with ES-CN), or one the postal code contradicts (35001 with Melilla, a Madrid postal code with the Canary Islands). A mainland postal code leaves the region empty. Once a document of the customer or vendor is posted, a new region, like a new name, VAT number, country or currency, waits for a second person's approval.

For integrators: POST /v1/entities/{entity}/parties and PATCH /v1/entities/{entity}/parties/{party} fill subdivision from a Spanish postal_code and refuse a contradicting one with 422 party_subdivision_mismatch (subdivision, country, postal_code). subdivision joins the key details that go to a change_party_details request once the party has posted documents.

2026-10-11: Canadian PST returns, the Quick Method and GST/HST instalments

Before: A Canadian company collected PST for British Columbia and Saskatchewan and RST for Manitoba on accounts of their own, but had no return to file them on. A small business on the CRA's Quick Method, or an annual filer paying instalments, had to work out lines 103, 107 and 110 of its GST/HST return by hand. Adjustments to a return could be entered only through the API.

After: VAT returns lists the PST return (British Columbia) (FIN 400, boxes A to K), the PST return (Saskatchewan) (boxes A to D) and the RST return (Manitoba) (lines 1 to 3) for the company's own province and each province it is registered in. Each is filled from the books (sales in the province, PST collected, PST self-assessed on purchases for own use), reconciles to the province's account, settles to 2249 "Provincial sales tax remittances", and records its submission and payment like the GST/HST return. VAT settings gets a Return period per province, where it differs from the GST/HST period. BC and Saskatchewan returns are due on the last day of the month after the period, Manitoba's on the 20th. For a company in Ontario or a province without HST, VAT settings offers the Quick Method, with its rate for goods bought for resale or for services: line 103 is the rate times the GST/HST-included sales, line 106 takes only the input tax credits on equipment and other capital property, line 107 gets the 1% credit on the first $30,000 of the fiscal year, and the difference to the tax charged is booked to 4950 "GST/HST Quick Method difference". A return for a year credits on line 110 the instalments paid to 1406 "GST/HST instalments paid"; whether instalments are due, and when, is not tracked. The Quick Method return warns when the GST/HST-included sales of the last four fiscal quarters pass $400,000. A draft return now has an Adjustments panel: enter an amount on a line (the BC commission, the tax of Quick Method sales into another region), with its account, and see the amounts the return filled in itself. Filing is still done on the CRA's and the provinces' sites. See ADR-0067.

For integrators: additive. Return kinds pst_return_bc, pst_return_sk and rst_return_mb in VatReturnInput, GET /v1/vat-returns?kind= and VatPeriod. VatSettingsInput and VatSettingsOut gain provincial_periods (refused with provincial_period_unknown for a province whose return is not filed) and quick_method (resale or services); method quick (refused with quick_method_canada, quick_method_rate, quick_method_region or quick_method_wrong_method). VatAdjustment.automatic marks the adjustments a return fills in; they are replaced on every recompute. The GST/HST return's payable is now line 113C, so an adjustment on line 110 or 111 needs an account. New return issues quick_method_other_region, quick_method_rate_missing and the warning quick_method_turnover (amount, limit). New account roles pst_settlement and gst_instalments; the Canadian chart adds 1406, 2249, 4950 and 4960. data-vNext: return forms ca.cra.gst34.quick, ca.bc.fin400, ca.sk.pst, ca.mb.rst, line facts provincial and supply_region, and capital_goods read for Canada too, net tax rate scheme quick_method and threshold ca.gst.quick.turnover_limit. Migration 0084 adds vat_settings.provincial_periods and vat_settings.quick_method and widens the method and kind checks. Operations: PUT /v1/entities/{entity}/vat-settings, POST /v1/vat-returns, POST /v1/vat-returns/{return_id}/recompute.

2026-10-11: Italian VAT returns: LIPE and Dichiarazione IVA

Before: An Italian company had no VAT return: the VAT report showed its VAT per rate, and the setup wizard said there was no return form for Italy.

After: VAT returns has the two Italian returns, filled from the books:

  • The Comunicazione liquidazioni periodiche IVA (LIPE), one per month or per quarter (VAT settings offer only these two for Italy): VP2 to VP14, with the credit of the period before (VP8) and a debt of at most 100 euros carried to the next period (VP7) taken from the earlier returns, and 1 % interest on a quarterly filer's payments (VP12; for the fourth quarter it is shown separately and paid with the annual balance). Imports are entered from the customs bills (lines "Importazioni"), with their VAT settled from account 2155. The return shows the F24 tax code (6001 to 6012, 6031 to 6033, 6099) and the day it is to be paid (July's VAT of a monthly filer on 20 August), which in Italy comes before the filing day; the VAT returns list shows both days.
  • The Dichiarazione IVA (Modello IVA 2026), new kind "Annual VAT return", due 30 April: quadri VE, VF, VJ and VL in whole euros (each row rounded, the totals adding up the rows, the difference to the VAT booked in VE25 and VF26), with VL30 (periodic VAT due and paid) and the interest (VL23, VL36) taken from the year's LIPE returns, and a warning when its balance does not match the fourth quarter left to pay.

Finalising a LIPE settles the VAT accounts to Erario c/IVA (4420) and the interest to the new account 7520 "Interessi passivi su versamenti IVA trimestrali" (7120 on the IFRS chart); submission and payment are recorded as for the other countries. Nothing is sent to the Agenzia delle Entrate: file the figures there. The printable return is in Italian. The figures follow the official instructions; the agency's PDFs could not be opened here, so the row numbers were checked against extracts and professional summaries, and the form's notes say so.

Split payment (art. 17-ter DPR 633/1972): an Italian customer can be marked "Split payment" on its card. Its invoices show the VAT but ask only the net amount, with the legal note; the VAT is not booked as owed (it is in LIPE VP2 without tax, and in VE38). The e-invoice shows the VAT as already paid (BT-113).

For integrators: POST /v1/vat-returns takes kind: "annual_summary" with a period YYYY; results carry payment_code and payment_due_date, boxes carry filled, forms carry jurisdiction and tax_precision, and GET /v1/entities/{entity}/vat-periods rows carry payment_due_date. Parties have split_payment (customers in Italy only; split_payment_italian_customers_only). VAT settings refuse other periods for Italy (vat_period_italy). GET /v1/setup/options now says Italy has a return form. data-vNext serves the forms it.ade.lipe.2024 and it.ade.iva.2026. See ADR-0066.

2026-10-11: Spanish companies prepare their Modelo 303, 349 and 390 from the books

Before: A Spanish company booked VAT at the Spanish rates, but there was no return form: the setup wizard said to file with the tax office's own service using the VAT report.

After: Under Assets & tax > VAT Returns a Spanish company prepares its Modelo 303 for each quarter (or month), its Modelo 349 of intra-Community operations, and the annual Modelo 390, every casilla filled from the books and each one drilling down to its postings. Sales at 21, 10 and 4 %, credit notes (casillas 14/15 and 40/41), intra-Community supplies and acquisitions of goods and services, exports, purchases under the inversión del sujeto pasivo and capital goods (bienes de inversión from 3,005.06 euros a unit) land in their own casillas. Import VAT from the DUA is entered as an adjustment. A quarter with more IVA to deduct than to pay is carried forward to Hacienda Pública, deudora por IVA (470000) and is done once submitted; the next 303 fills casillas 110 and 78 with it by itself, and warns if an earlier return was amended since. A negative last quarter is refunded: it is booked as a receivable (470900) and the refund received is linked as its payment. The IFRS chart has these accounts too, for a Spanish company that keeps it. The 349 warns when it must be filed monthly (a quarter or one of the four before above 50,000 euros of supplies) and when a row is negative. The Modelo 303 is finalised and settled like the other countries' returns, then recorded as submitted and paid. The 390 settles nothing: it takes the import VAT, regularisations and results of the year's final 303s and warns when its totals differ from theirs; monthly (SII) filers are not asked for one. Filing stays by hand on the AEAT's sede electrónica. Due dates follow the AEAT calendar (the 20th after a quarter, 30 January for the last one). VAT settings default a Spanish company to quarterly 303 and 349. Customers and vendors in the Canary Islands, Ceuta and Melilla are treated as outside the Spanish VAT: shipments there are exports and services to businesses there are not subject.

For integrators: POST /v1/vat-returns takes the new kind annual_summary (period 2026), and recapitulative_statement works for Spain. GET /v1/setup/options now gives Spain vat_return_form: true. Journal lines of documents carry two new tax facts, capital_goods and correction. A recapitulative statement outside Germany gets the reference …-ESL-…. Two new optional account roles, vat_carry_forward and vat_refund_receivable, hold a carried-forward VAT result and a refund due. New issue codes: missing_carry_forward_account, carry_differs, negative_statement_row; monthly_statement names the quarter in period. See ledger.md.

2026-10-11: The French VAT return (CA3) is built from the books

Before: A French company had no VAT return form. VAT was only reported per rate in the VAT report, and the setup wizard said a French return would come later.

After: VAT returns builds the CA3 (formulaire n° 3310-CA3-SD, 2026 edition, régime réel normal) for a month or a quarter, like the German and Swiss returns. Every line of frames A and B is filled from the books and shown with its official case number: sales by rate (20, 10, 5.5 and 2.1 %), intra-Community supplies and acquisitions, exports, services to EU businesses, services bought from businesses not established in France (line A3, in or outside the EU), the reverse charge (CGI art. 283), and imports self-assessed on the return (autoliquidation, CGI art. 1695 II), which invoices and bills from abroad now book that way. Input VAT on fixed assets (class 2 accounts) goes to line 19, other input VAT to line 20. Bases and tax are in whole euros, and frame A always equals the bases of the rate lines, as the DGFiP requires. The return ties to the VAT accounts, settles to 445510 TVA à décaisser (a credit stays there and enters the next return on line 22), prints in French and is submitted and paid like the other returns, without e-filing. A French company files monthly by default; VAT settings offer it the CA3 periods (month, or quarter when its VAT for a year is below 4,000 euros) and no recapitulative statement. The annual CA12 of the régime simplifié is not built: its 2026 form could not be obtained from the DGFiP and the régime ends on 1 January 2027.

For integrators: GET /v1/setup/options marks FR vat_return_form: true; the return's form carries tax_precision (0 for the CA3); PUT /v1/entities/{entity}/vat-settings refuses a French period other than month or quarter with vat_period_french; a return whose frame A and rate lines differ by more than rounding has the error issue boxes_disagree. See api.md.

2026-10-11: Accounting standards, tax number names and report lines in your language

Fixed: In the setup wizard and Add company, the accounting standards ("French GAAP (Plan comptable général)", "HGB (German commercial code)" and the others) and the name of the tax number field ("VAT number", "UID (VAT number)") stayed in English for people working in German, French or Italian. Ten statement lines, such as other non-current assets and liabilities, goodwill and non-controlling interests, showed as a code-like English text ("Noncurrent other"). All of them are now shown in the person's language, and a test fails when a new standard, tax number or statement line arrives without its words in every language.

2026-10-11: US sales tax by state

Before: A US company was treated like a VAT company. Its invoices charged only the state's base rate, and only in California, New York or Texas; a sale into any other state was refused. No local rates, no exempt customers or products, no idea where the company is registered, one tax account for everything, no return per state. A vendor's sales tax on a bill was booked as if it could be reclaimed.

After: A US company has a Sales tax page. It records the states where it is registered (each gets its own payable account, such as 2200-CA), the local county, city and district rates with the day they apply from, tax areas such as "City of Los Angeles" (9.75%), and what a state does not tax (California: no tax on SaaS; Texas: 80% of it). A customer gets a tax area and, if it has one, an exemption certificate with the day it starts, its expiry and the states it covers; a product gets a category. Invoices then charge each customer the right tax, to the cent, only in states where the company is registered, and warn about sales into other states, which the page also lists for the year; a credit note takes back exactly the tax of the line it credits. Each state's return for its month or quarter (New York's quarters start in March) shows the day it is due and whether it is due soon or overdue (by the day where the company is, so a return due on the 20th is not overdue that evening), is built from the books (gross, exempt, taxable, use tax, tax per jurisdiction) and is checked against the state's account; a person records that it was filed, a second person approves it, and then the bank payment is linked. A vendor's sales tax on a bill is booked as part of the cost; on a bill with no sales tax for something used in the company's own state, the company ticks "use tax" and owes it to its state. Invoices from a US company say "Sales tax" instead of "VAT". VAT and its returns are unchanged and never include sales tax. A customer's or vendor's state or province is now a key detail in every country: once it has posted documents, changing it waits for a second person, like its name or VAT number. The demo organization has a new US company, Demo Inc., selling into four states in August with its Texas return filed and paid.

Not built yet: rates looked up automatically from an address, economic-nexus thresholds and e-filing. See US sales tax and ADR-0064. Operations: tag "sales tax" in the API reference, GET /v1/entities/{entity}/sales-tax, POST /v1/entities/{entity}/sales-tax/returns.

2026-10-11: "Complete the customer" opens the form with the missing fields marked, and forms say what invoices require

Before: An invoice that lacked content the law requires said so ("Customer: Street, Postal code, Town (§ 14 Abs. 4 Nr. 1)"), but Complete the customer opened the customer's card read-only: one had to find Edit, and nothing on the card said which fields were missing or mandatory. Complete the company profile opened the profile with nothing marked either, and a payment held for want of a vendor's IBAN said so in words only.

After: Complete the customer and Complete the company profile open the record ready to edit, with a banner and each missing field marked Missing: fill this in; saving returns to the invoice, whose warning is then gone. The customer form and the company profile always mark the fields an invoice of the company must show, by the law of its country: an asterisk and "Required on invoices (§ 14 Abs. 4 Nr. 1)", or "Required on some invoices" for a field only some need (the customer's VAT number for an intra-community supply). A customer card shows an empty required field as missing even when not editing. A vendor's card, Vendor Payments and the payment proposal offer Enter the bank details when a payment waits for a missing, invalid or unconfirmed IBAN; it opens the vendor's form with the IBAN marked (a second person still approves bank details). A person who may only read sees the card as before.

For integrators: additive. New operation GET /v1/entities/{entity}/invoice-content returns InvoiceContentOut (country, and seller[] and buyer[] of InvoiceField: field, basis, always), from the same rules as missing_content. Operations: GET /v1/entities/{entity}/invoice-content.

2026-10-11: Canadian companies file their GST/HST and QST returns

Before: A Canadian company could be set up, but an invoice to Quebec booked GST and QST as one 14.975% tax on one account, provincial sales tax was charged in every province whether or not the company collected it there, and there was no return to file: VAT returns covered Switzerland and Germany only.

After: Each invoice and bill line of a Canadian company carries its taxes one by one (GST, HST, QST, PST or RST), each rounded on its own and printed on its own row of the invoice (TPS/TVQ in French). VAT settings has Registered for provincial sales tax in with Quebec (QST), British Columbia (PST), Saskatchewan (PST) and Manitoba (RST); the company's own province always counts. A sale into a province the company is not registered in charges GST only, and the document says why. QST goes to its own accounts (2245 QST payable, 1405 QST receivable, settled to 2246), PST and RST (on goods) collected to the account of their province (2240 British Columbia, 2241 Saskatchewan, 2242 Manitoba), and PST paid on purchases is part of the cost. A Canadian company set up earlier gets these accounts when its VAT settings are saved; documents posted before this release are not split again. VAT returns shows the CRA's GST/HST return (GST34) with its line numbers (101 sales, 103 GST/HST collected, 106 input tax credits, 109 net tax, through 115) and, for a company in Quebec or registered there, the QST return (Revenu Québec) (203 QST collected, 206 input tax refunds, 209 net). Both work like the other returns: drill-down to the postings, reconciliation to the accounts, finalising by a second person, the settlement entry, recording the submission and the payment, and the close check. Both are due one month after a monthly or quarterly period. Filing itself is done on the CRA's and Revenu Québec's sites. Not covered yet: the provincial PST returns, the quick method and instalments. See ADR-0063.

For integrators: additive. A Canadian line's tax has components[] (tax_type, jurisdiction, rate, rate_type, amount). VatSettingsInput.registrations and VatSettingsOut.registrations (province codes; refused with registrations_canada outside Canada and registration_unknown for a province without a tax of its own). Return kind qst_return in VatReturnInput, GET /v1/vat-returns?kind= and VatPeriod. New document warnings customer_no_province and provincial_tax_not_registered. New account roles qst_output, qst_input, qst_settlement, pst_payable_bc, pst_payable_sk and rst_payable_mb. Migration 0083 adds vat_settings.registrations and the return kind. erp seed-demo --canadian adds Demo Canada Inc. Operations: PUT /v1/entities/{entity}/vat-settings, POST /v1/vat-returns, GET /v1/vat-returns.

2026-10-11: Backups of everything in Google Cloud are kept in Switzerland and restored every month

Before: The ERP database was backed up every night, but Google kept those backups in its EU multi-region rather than in Switzerland, and they were deleted with the database. Market data in Firestore had no backups, and the monthly test that restores the database from its backups had stopped when GitHub Actions was paused, so no restore had ever been proven.

After: The ERP database keeps a nightly backup for 30 days and can be restored to any second of the last 7 days; the backups stay in europe-west6 and survive even if the database itself is deleted. Firestore can be restored to any minute of the last 7 days and keeps a weekly backup for 14 weeks; BigQuery keeps 7 days of time travel; a destroyed secret stays recoverable for 30 days. Uploaded documents keep every version for 11 years, as before. On the 1st of each month Google Cloud restores the database from its backups into a temporary copy, checks that its books balance and its ledger chains verify, and deletes the copy; a failure sends the "restore drill failed" alert. See Backups.

2026-10-11: French, Italian and Spanish charts of accounts at onboarding

Before: A company set up in France, Italy or Spain started on the generic IFRS chart of accounts, and the setup wizard said national charts would come later.

After: The setup wizard (and Add company) offers each country its national chart as the recommended choice, with IFRS as the other: the French Plan comptable général (with the 2025 reform of règlement ANC 2022-06), an Italian chart in the order of the Civil Code balance sheet and income statement, and the Spanish PGC PYMES. Each comes with its receivables, payables, bank, VAT, payroll, equity, fixed-asset and group accounts, named in the country's language with English, German, French and Italian translations. Invoices and bills post VAT at the country's rates to the chart's own VAT accounts. There is no French, Italian or Spanish VAT return form yet: the wizard now says so for every country without one, and VAT is reported per rate in the VAT report. The operator portal's New customer wizard picks the same charts.

For integrators: GET /v1/setup/options lists the standards pcg, oic and pgc with the charts fr_pcg, it_civil_code and es_pgc, and each country carries vat_return_form. GET /v1/charts?country= and GET /v1/platform/charts?country= mark these charts standard for FR, IT and ES. See ledger.md.

2026-10-11: UK VAT numbers are checked with HMRC from the customer or vendor card

Before: a VAT number was checked in its public register only in the bill wizard, for a new vendor, and nothing was kept. A UK number without HMRC access said the register "did not answer".

After: every customer and vendor card with a VAT number has Check in the register. EU numbers go to VIES, Swiss ones to the UID register, UK ones to HMRC. The card keeps the last answer: valid or not known, the registered name, when, and for a UK number HMRC's consultation reference, the proof that the number was checked (given when your company has a UK VAT number of its own). Each check is also in the change history. While HMRC access is not set up, a UK number says "Not checked yet: HMRC access is not set up." Nothing is ever refused on the answer.

API: POST /v1/entities/{entity}/parties/{party}/vat-check; vat_check on the party card; reference and the new status not_configured on POST /v1/vat-registers/check. See ADR-0038.

2026-10-11: Payments the bank rejects or returns are reversed and their bills open again

Before: When the bank refused a vendor payment (its status report on the payment file) or sent the money back (for example because the payee's account was closed), nothing in the ERP changed: the payment stayed booked, the bill stayed paid, and a person had to find the payment and reverse it by hand, after which a second person still had to confirm the bank did not pay it.

After: Bank statements imports the bank's status report on a payment file (Format Payment status report (pain.002), recognised from the file) beside camt and CSV files. Each payment it rejects (the whole file, a batch or one payment) is reversed, the bills it paid are open again, and the import lists each payment with the bank's reason and what became of it. A credit on a statement that returns a payment the bank executed is matched to that payment by itself when the bank marks it a return, or when it carries the payment's number and comes from the payee; any other is offered on the line as Payments that came back. The payment is reversed by that line: the bank account shows the money back on the bank's booking day (with the exchange difference when the bank converted it), and the bills are open again. Vendor Payments says why: "Rejected by the bank on …: the account is closed. The bills it paid are open again." The four-eyes rules stay: a reversal of a payment someone else recorded, or asked for by an agent, still waits for a second person; the bank's message replaces only the confirmation that the bank did not pay. Importing the same report or statement twice changes nothing. See ADR-0062 and payments in transit.

For integrators: additive. New operation POST /v1/entities/{entity}/payment-status-reports (multipart file, a pain.002 report) returns StatusReportResult (rejected[] with outcome reversed, pending_approval, already_reversed, reversal_waits, executed or not_posted). PaymentRow.bank_refusal (kind rejected or returned, code, text, source, on, status). GET /v1/bank-lines/{line_id}/candidates lists candidates of kind return for a credit, and POST /v1/bank-lines/{line_id}/link with one reverses the payment by the line (difference_entry is the reversal; refused with bank_line_not_returnable). Undoing the match of a return or of the execution of a returned payment is refused (bank_line_return_undo). New candidate reasons return_of and returned_by_bank. Migration 0079 adds bank_line.returned. Operations: POST /v1/entities/{entity}/payment-status-reports, GET /v1/payments, GET /v1/bank-lines/{line_id}/candidates, POST /v1/bank-lines/{line_id}/link, POST /v1/bank-lines/{line_id}/unmatch, POST /v1/entities/{entity}/bank-statements.

2026-10-11: VAT return checks in German, French and Italian

Before: The errors and warnings on a VAT return (a line that fits no box, a missing account, a difference between the form and the VAT booked, an official VAT exchange rate not yet published, a missing Swiss UID, a period that does not match the VAT settings, and so on) stayed in English for people working in German, French or Italian; only the warning about earlier unfiled periods was translated. The official exchange rate table showed its series as codes such as ch-vat-monthly.

After: All twenty checks a VAT return or recapitulative statement makes are shown in the person's language, with amounts, dates and rates in their format and with what to do. The rate table names its series, such as "BAZG monthly average rate" or "ECB reference rate". A test keeps every check worded in all four languages.

For integrators: each VatIssue now carries its values in details (listed in erp.contracts.words.VAT_ISSUES). Two codes are split so each always names the same values: a missing VAT settlement account is missing_settlement_account (the rounding or net tax rate difference account stays missing_account), and a computational difference that includes official VAT exchange rate differences is computation_difference_fx. Operations: POST /v1/vat-returns, GET /v1/vat-returns/{return_id}.

2026-10-11: Foreign payments in transit are revalued at the period end

Before: A vendor payment in a foreign currency that was still in transit at a period end (booked on payments in transit, 1091 in the Swiss chart, waiting for the bank to execute it) stayed at its payment day's rate, while every other open foreign-currency item was remeasured at the closing rate.

After: The period-end FX revaluation remeasures what is in transit in each foreign currency at the closing ECB rate, with the rest: the difference goes to unrealised FX and reverses on the first day of the next period, so the bank line still moves the payment to the bank at its own rate. Exchange rates > Exposure shows it beside the bank balances as a negative amount (it lowers the cash in that currency) with the difference the run will book, and the close checklist asks for the revaluation when a foreign payment went through payments in transit. See ADR-0060 (section 9) and payments in transit.

For integrators: additive. POST /v1/periods/{period_id}/fx-revaluation returns an item for the payments-in-transit account per foreign currency (item is the account code), and GET /v1/entities/{entity}/fx/exposure and GET /v1/entities/{entity}/fx/exposure/items list it on the bank side (kind account). No migration.

2026-10-11: The product is now called ESoR Headless

Before: the app, the operator portal, the API and the documents it makes were named "vNext ERP".

After: they are named ESoR Headless, where ESoR stands for Enterprise System of Record. The new name shows in the browser tab and installed app, the header, the sign-in pages of the app and the operator portal, the API's name and documentation, the producer of invoice, QR-bill and VAT return PDFs, the export package and its attestation, and the documentation. The web address, API paths and integrations stay the same; an installed app picks up the new name on its next update.

2026-10-11: Vendor payments reach the bank account when the bank executes them

Before: A vendor payment was booked to the bank account as soon as it was created by the payment proposal or by Record payment, before its payment file was released and before the bank executed it. The ledger's bank balance ran ahead of the bank, so the statement balance check compared the bank with payments it had not made yet.

After: A payment that awaits the bank's execution is booked on a new account, payments in transit (Swiss chart 1091 "Zahlungen unterwegs (Geldtransfer)", German HGB 2490 "Geldtransit", 1090 "Payments in transit" in the IFRS, US, UK and Canadian charts), and the bank account moves only when the bank statement line that shows the execution (camt.054 or camt.053) is matched, on the bank's booking day, with the bank's exchange difference as before when it converted the payment. Undoing that match puts the payment back in transit; reversing a payment still in transit takes it off that account. A company that pays in e-banking, by card or in cash records the payment with The bank already executed it: it goes to the bank account at once and into no payment file. Receipts are unchanged. Vendor Payments says where each payment stands ("in transit on 1091 until the bank executes it", "executed by the bank on …", "paid, booked on 1020"), and the period close warns of payments still in transit more than 10 days after their date. The warning that a payment takes the bank account below zero counts the payments still in transit from it, and comes when the payment is made, not when the bank executes it. The bills' "paid" status and the four-eyes rules do not change. See ADR-0060 and payments in transit.

For integrators: additive, with a changed booking. POST /v1/payments and POST /v1/payments/{payment_id}/refund take executed (default false: an outgoing payment is booked on the account with the new required role payments_in_transit); PaymentResult.in_transit; PaymentRow.booked_on and PaymentRow.bank_status (in_transit, executed or null); a payment recorded as executed lists the problem payment_recorded_paid; linking a bank line to a payment in transit returns its entry as difference_entry even without a difference. The bank balance warning has the code overdrawn_in_transit (with in_transit) when payments in transit count towards it, and the payment proposal's bank_balances[].balance leaves out what is in transit. The close checklist has the check payments_in_transit (transit_none, transit_recent, transit_old). Every chart template has the new account; an entity created before adds it with POST /v1/entities/{entity}/role-accounts, and until then its payments go to the bank account at once, as before. Migration 0078 adds payment.transit_account_id. Operations: POST /v1/payments, POST /v1/payments/{payment_id}/refund, GET /v1/payments, POST /v1/bank-lines/{line_id}/link, GET /v1/periods/{period_id}/close-checklist, POST /v1/entities/{entity}/payment-proposals, POST /v1/entities/{entity}/payment-runs.

2026-10-11: Show QR-bill in the open items list is disabled in a test copy too

Fixed: in a test copy the open items list (Receivables) still offered Show QR-bill on each invoice, which opened a dialog only to say the QR-bill cannot be made. It is now offered disabled there, with the same reason as QR-bill (PDF) on the invoice card: a test copy never prints a payment request, so nobody pays a test invoice by mistake. Both read whether the organization is a test copy from the signed-in organization (masked or not; a demo organization still prints QR-bills).

2026-10-11: Change a subscription's plan within its term

Before: An active subscription could only be ended early or renewed. More or fewer seats, another product, a new price or discount meant ending it and starting a new one by hand, with the credits and revenue worked out outside the system.

After: Change plan in a subscription's dialog changes its lines from today, the next billing period or another day in the term. The preview shows what is credited of what was already billed, the charge for the rest of the period at the new lines and their price to the term's end; a second person approves it. On approval the credit notes are posted, the old revenue contract ends the day before, the new lines get a revenue contract of their own, and the prorated charge is invoiced at once when due. A subscription in EUR or another currency changes in that currency. Approvals now name an end, a renewal or a change of a subscription in the reader's language.

2026-10-11: Swiss input VAT on two accounts, VAT due dates on working days, reverse charge only for a business

Changed: input VAT of a Swiss company is split by the account a purchase books to, by a rule of the chart: in the KMU chart class 4 (materials, goods for resale, third-party services) is Ziffer 400 and booked to 1170, classes 1, 5 and 6 (fixed assets, personnel, rent, administration) are Ziffer 405 and booked to 1171. Before, all input VAT was booked to 1170 and 1171 stayed empty. A CHF 3,000 server on 1520 is now 405 and 1171. The account of 1171 has the new optional role vat_input_investments; charts of other countries book all input VAT to their one input VAT account as before. Changed: a Swiss VAT return due on a Saturday, a Sunday or a public holiday is due on the next working day (SIC calendar, as payment dates), e.g. the first quarter of 2026 on 1 June instead of 30 May; a German one now also moves off the TARGET holidays. A return whose earlier periods have no final return yet shows the warning earlier_period_unfiled naming them; it can still be filed. Changed: a sale to a customer in another country goes under the reverse charge only when the customer has a VAT number or is marked as a business; otherwise it is taxed as a sale to a private person (a service abroad: no Swiss VAT) and the document says so; a business without a VAT number keeps the reverse charge with a warning asking for the number. New customer asks for the customer type (business or consumer). API: an issue of a return has details, the values its message names; see API: VAT returns and ADR-0058.

2026-10-11: A bank account below zero and an open own close task warn

Before: a payment, a payment run or a loan's repayment could take a bank account below zero in the books without a word (only the loan payout dialog warned, from a balance in the loan's currency), and the Period Close screen said "No check fails: the period can be closed" while one of the company's own close tasks was not done.

After:

  • A bank account below zero warns, and is not refused. POST /v1/payments, POST /v1/entities/{entity}/payment-runs and the loan's drawdown, repayment and settlement answer warnings and warning_reasons (code bankBalance.warning.overdrawn: the account, the company, the book balance before and after in the account's own currency, the day). The payment dialog shows it (and stays open to show it), the payment proposal warns as bills are ticked (new bank_balances on the proposal, and bank_account in its criteria), the run's result shows it, and the loan dialog warns before the payout and shows the server's warning after it. The loan's lender_bank_balance is now in the account's currency, given by the new lender_bank_currency. A manual journal entry cannot post to a bank account, so it has none. See ledger.
  • Own close tasks not done warn on the checklist. New close check close_tasks: a warning naming the tasks not done, never a failure, so the period still closes. The summary over the checklist now says when checks warn: "No check fails: the period can be closed. 1 check warns: look at it before you close."
  • Fixed: the Period Close page no longer asks for a year-end close a year never had (a 404 on every load); a year without one is a plain empty state.
  • Fixed: the appropriation dialog of a loss says "Loss still to cover: -2,000.00 CHF" and "Covered" for what the destinations cover, instead of a positive "remainder".

Decision: ADR-0057.

2026-10-11: VAT returns can be marked as submitted and paid

Before: A final VAT return offered Amend and its files, and nothing more. Nothing recorded that it was actually submitted to the tax administration or that the VAT was paid, so the periods list called a period filed the moment its return was finalised, the reminders of its due date stopped although nothing had been sent, and paying the amount on the VAT settlement account (2202) was left to whoever remembered to match the bank debit.

After: A final return has a Submission and payment panel. Mark as submitted records the day it was submitted (not before the period ends, not in the future) and the tax administration's reference, and who recorded it; nothing is sent from the application, and in a test copy the dialog says the record is only a note. Record payment links the posted entry that paid the VAT (or received the refund) on the settlement account, picked from the entries since the settlement, usually the bank debit matched to 2202 on the bank worksheet; several entries can pay in parts, and a different amount is shown as the difference. The return reads Paid once the payments cover what it settled. A submission or a payment link recorded by mistake is undone with a reason. A period now counts as filed only once its return is marked as submitted, so the home page's to-do counts and the period close keep reminding of a return that is final but not submitted; the periods list badges each return Final, Submitted or Paid and flags not paid when the VAT is still unpaid after the due date. Recording a submission or a payment is a person's act: agents are refused, as they are for finalising.

2026-10-11: Bank reconciliation per account and statement date

New: Bank and currency > Bank reconciliation. Pick a bank account and a statement date to see the bank's closing balance next to the balance in the ledger, and what explains the difference: statement lines not yet booked (not matched, ignored, waiting for approval or booked later) and booked entries the bank does not show yet, such as a payment it has not executed. When nothing is left unexplained, mark the date reconciled; the record keeps who did it and when, and is never changed. A difference you cannot explain can still be marked reconciled, with a note saying why. For a CSV statement, which carries no closing balance, you type the bank's balance. API: GET /v1/entities/{entity}/bank-reconciliation, POST and GET /v1/entities/{entity}/bank-reconciliations. See ADR-0056.

2026-10-11: A whole bank receipt can be kept on a customer's account from the screen

New: on the bank statement worksheet, a line that pays no open document (an advance payment, or money from a customer who has nothing open yet) can now be kept on account in one step: "Keep 150.00 CHF on account for Kunde GmbH". The worksheet suggests the customer from the line's open documents or from the name the bank sent, and you can pick another. Money paid out goes to a vendor's account the same way. Before, the Match button needed a ticked document, so this was possible only through the API (POST /v1/bank-lines/{id}/match with no allocations). The amount is applied to the party's documents later, from their card.

2026-10-11: Import CSV bank files on the Bank statements page

Before: Only camt.053 and camt.054 files could be imported on the Bank statements page, and the format started on camt.054. A CSV export from the bank (for example with semicolons, a decimal comma and dates like 30.09.2026) could only be imported through the API, there was no way to choose the bank account, and a CSV statement was listed under a code such as "e9bdbb12be16b176".

After: The format starts on the bank statement (camt.053) and offers CSV files too, and you can choose the bank account: a camt file goes to the account of the IBAN it carries unless you choose another one, a CSV file goes to the company's bank account unless you choose another one. When you choose a CSV file, the page works out which column holds the date, the amount (one column, or separate credit and debit columns), the currency, the counterparty, the reference and the text, and how the file writes dates and decimals. It shows these choices, which you can change, and the transactions it reads: how many, from when to when, the money in and out, and how many were already imported and will be skipped. Nothing is imported until you press Import. If the file cannot be read with the chosen columns, the page says why in plain words, for example which column the file does not have. A CSV statement is now listed by its account and dates, such as "CSV 1020 01.09.2026-30.09.2026"; the same file imported twice is still refused.

API: POST /v1/entities/{entity}/bank-statements/preview reads a CSV file without storing anything and guesses the column mapping when none is sent; the mapping (csv_mapping) takes separate credit and debit columns; a column the file does not have is refused with csv_column_missing.

2026-10-11: A customer's account activity over a period

New: a customer's statement has a second tab, Activity (also on the customer card): what the customer owes us between two days and how it came about. Per currency it shows the opening balance, every invoice, credit note and payment received (with a payment reversed and a credit applied later) with the running balance, and the closing balance; then the balance in the company's currency and the items still open at the end. The period is the first of the year to today unless you choose another. The opening and closing tiles open the open items of that day, and each document number opens the document. Download PDF gives an A4 statement "Your account with us" in the customer's language (German, French, Italian or English, on the company's letterhead); Download CSV gives one row per movement with an opening and a closing row per currency. Nothing is posted or archived. API: GET /v1/entities/{entity}/customers/{party}/activity?start=&end=&format=json|csv|pdf&language=; MCP tool customer_activity. See Customer statements and dunning.

2026-10-11: An aging report for receivables and payables

New: Reports → Aging Report shows what each customer owes, or what you owe each vendor, on a day you choose, split by how long it is overdue: not yet due, 1–30, 31–60, 61–90 and over 90 days. There is a line per customer (or vendor) and currency, with totals per currency and a total in the company's currency, and you can switch all amounts to the company's currency. The report says how its total ties to the receivables or payables account in the ledger. Click a line to see that party's open documents, or an amount to see only those in that age band; each document opens its card, and the statement of account is one click away. The report can be exported to CSV, and the open items on the invoices and bills pages and the aging chart in Analytics link to it. API: GET /v1/entities/{entity}/reports/aging.

2026-10-11: The month-end close shows its standard steps, who does each and who did it

Before: a period's card on Period Close listed only the company's own close tasks. The standard steps every close has (recurring invoices, depreciation, FX revaluation, deferred tax, a ready checklist, the close memo, the close itself, the forecast roll) were only in the API, so overdue steps went unnoticed, and the step names in the close schedule stayed English in German, French and Italian.

After: the card shows Close steps above the close tasks: each step with the person who does it, its due day, whether it is done, open or overdue, who did it and when, and why, in your language. Each step can have its own person in Settings, close schedule (otherwise it is the close owner's), and the Role Center shows a Periods in close cue with the periods that have ended and are not closed yet. API: step_owners in the close schedule; owner, done_by, done_at and reason on each step of GET /v1/periods/{period_id}/close-status; periods_in_close in the overview. See the close tracker and ADR-0034.

2026-10-11: A credit note of part of an invoice line takes its part of the discount

Fixed: a credit note for part of an invoice line credited the full price, ignoring the line's discount and its share of the document discount, so it credited too much net and VAT. Now each credit note line remembers which invoice line it credits and takes that line's discount in proportion to the quantity credited; the credit note that credits the rest of a line takes what is left, so crediting a line in parts credits exactly its discounts. A credit note that credits invoice lines takes the invoice's document discount this way and states none of its own. API: credits_line on a credit note line. See ADR-0055.

2026-10-11: The purchase order form says when a vendor waits for activation

Before: In a new purchase order, typing the code or name of a vendor that waits for a second person to activate it showed an empty list, with no word why the vendor could not be found.

After: The vendor field then says that the vendor is waiting for a second person to activate it and that its bills and orders cannot go ahead until then, with links to the vendor and to Approvals, as the bill form does.

For integrators: no change.

2026-10-11: A refused purchase order says who refused it and why

Before: When a second person refused to approve a purchase order, it went back to draft and looked like any other draft: the card gave no sign it had been refused, and the comment given with the refusal was not shown. Its status history listed "Returned to draft" before "Approval refused".

After: The card of such a draft says who refused it, when, and the reason they gave (or that they gave none), and that it can be changed and submitted again. The notice goes once the order is submitted again. The history lists the decision first, then what it did to the order: "Approval refused", then "Returned to draft"; likewise "Approval granted", then "Approved".

For integrators: additive. OrderCard.refusal (decided_by, decided_at, comment) is set on a draft whose last approval request a person refused; it is null otherwise, also for a request withdrawn by cancelling the order. In the audit trail of a purchase order, approval.rejected and approval.approved now come before purchase_order.reject and purchase_order.approve of the same decision. Operations: GET /v1/purchase-orders/{order}, GET /v1/purchase-orders/{order}/history.

2026-10-11: Bills blocked for payment are marked, and the payment proposal says why it leaves one out

Before: A bill whose variance against its purchase order waited for a second person's approval looked like any posted bill in Documents and in the open items. The payment proposal left it out without a word, so a due bill went silently missing from the payment run.

After: Such a bill shows "Payment blocked" in the document list and in the open items, with why on hover. The payment proposal lists it among the bills it leaves out ("… is blocked for payment until a second person approves its variance against the purchase order"), with a link to the bill, where the variance is approved. Once it is approved, the marker goes and the proposal includes the bill again.

For integrators: additive. DocumentOut.payment_blocked and AgingItem.payment_blocked are true for a bill blocked for payment by a purchase order variance. The payment proposal now names such a bill in skipped with the code bill_payment_blocked; before, it was left out of both lines and skipped. Operations: GET /v1/documents, GET /v1/documents/{document_id}, GET /v1/entities/{entity}/reports/aging, POST /v1/entities/{entity}/payment-proposals.

2026-10-11: One receipt can pay invoices in several currencies, and a short payment stays open

New: Record payment settles invoices in several currencies at once, for example one CHF receipt for a CHF invoice and a EUR invoice. Each invoice in another currency shows its share of the amount received at the day's rate, which you can change.

Changed: a bank line in another currency pays an invoice in full only when it is within 1% of the open amount at the day's rate (it was 2%). Further off, it is a short payment, not an exchange difference: it settles what it buys at the day's rate and the rest of the invoice stays open. See ADR-0054.

2026-10-11: Italian buttons in the tu imperative, French colons spaced

Fixed: the Italian screens mixed the infinitive and the imperative on buttons, actions and dialog titles ("Registrare" and "Chiudere" next to "Registra" and "Chiudi", "Scaricare il PDF", "Inviare…"). They now all use the tu imperative ("Registra", "Chiudi", "Scarica il PDF", "Invia…"), and a question title asks "Vuoi stornare …?". In French a colon now always has its space before it, also where a screen puts a label before its value ("Montant ouvert : 0,00 EUR").

2026-10-11: QR-bill offered disabled in a test copy

Fixed: in a test copy the invoice card offered QR-bill (PDF), which ended in "This can't be done yet: Invoices in a test copy carry no QR-bill". A test copy never prints a payment request, so nobody pays a test invoice by mistake (GET /v1/invoices/{invoice}/qr-bill answers 409 test_copy_no_qr_bill); the button is now shown disabled there, with that reason and the hint to try the QR-bill in a demo organization.

Fixed:

  • Once ending a subscription early was approved, the list still showed the old term end and plain "active". It now shows the term up to its new last day and "ending early, ends on …".
  • An invoice or credit note a subscription made said "Source: Entered" with no way back. Its card now names the subscription and the billed period, linking to the subscription (whose dialog links its revenue contract); an invoice of a recurring invoice links to it.
  • After "Record receipt" on a document's card the dialog stayed open over the document, which still showed it open: recording now refreshes the screens behind and, from a document's card, closes the dialog.
  • Recurring invoices: "Submission" (posting each invoice through the approval policy) read "Senden" and "Invio" (sending); it now reads "Buchung", "Comptabilisation", "Registrazione", with the related texts.
  • The close checks "Revenue recognised", "Contract positions presented", "Contract accounts = contracts" and "Subscriptions billed", and the balance sheet lines "Contract liabilities" and "Contract assets", have their names in German, French and Italian.

2026-10-11: Pay back what a customer has on account

New: the Balances of a customer card has Refund next to Apply wherever money is kept on account: it pays back part or all of an overpayment to the customer (on a vendor card it records the vendor paying back an advance). POST /v1/payments/{payment_id}/refund does the same over the API: a payment the other way that relieves the receipt's amount on account itself, at the rate it was booked (the difference to the bank is realised FX), through the approval policy, and reversed like any payment.

Fixed: before, the only way was an outgoing payment kept on account. The receipt (-185.78) and that refund (+185.78) then stayed side by side as two open items of the customer, in the aging, the statement and the balances ("on account 185.78"), though the money had gone back; nothing could set them against each other. A refund now leaves only what is really still on account. The receipt is not reversed while a refund of it stands (409 payment_on_account_refunded); a payment that is itself a refund kept on account is applied to its credit note instead (422 refund_not_on_account).

2026-10-11: A contract's change and an amendment's status in your language

Fixed: a revenue contract's "Changes" showed a subscription's early end in English with an ISO date ("SUB-000003 ends on 2026-10-31: Customer closes Lyon office") in every language. The change now also comes by code (reason_words on the contract's modifications, worded as revenueContracts.change), so the screen words it in the person's language and date format; the reason the person gave stays as typed. An applied subscription amendment showed "verrechnet", "imputée" or "compensata" (billed, offset: the wording of a payment applied to an invoice); it now reads "in force" ("in Kraft", "en vigueur", "in vigore").

2026-10-11: Vendor payments show what the bank did and why a payment is not sent

Before: On Vendor Payments, the "Payment file" column was squeezed to a sliver at a usual screen width, so the reason a payment could not be sent and the download link could not be read. A reversed payment said it "goes into a payment file once it is approved", and in French and Italian its status was in the wrong gender ("extournée", "stornata"). After the bank statement (camt.054) was imported, nothing on the payment showed that the bank executed it, and Reverse stayed on a row that could not be reversed. In a test copy, "Export payment file" was offered and then refused, saying the vendors were real, which is not so in a masked copy. Releasing one's own payment was refused with a sentence about API keys. Recording a payment ended with "Payment recorded: posted".

After: The columns have room: the IBAN and payment type sit under the payee, Reverse sits with the status, and the payment-file column keeps a readable width. A reversed payment says it is reversed and not sent; one waiting for approval says so; payment statuses agree with "paiement" and "pagamento". A payment the bank executed shows "executed by the bank on" its booking day, and, beside it, instead of Reverse, says to undo the bank match first, with a link to Bank statements. In a test copy the button is off and says why: no payment from a test reaches a bank, and a masked copy has made-up vendors but your company's real bank accounts. Test copies still make no payment files. The refusal for releasing your own payment points to the approval policy, and recording a payment ends with a sentence ("The payment is recorded and posted."). In French, the vendors page subtitle and "Montant ouvert :" read correctly.

For integrators: additive. PaymentRow.bank_line (id, statement_id, booking_date, amount, currency) names the bank statement line that shows the payment was executed; Me.test_copy is masked, unmasked or null. A payment's problems now say payment_reversed or payment_waiting_approval where they said payment_not_approved for those two statuses (that code stays for any other status). The refusal of a payment file in a test copy keeps its code test_copy_no_payment_file; the refusal of releasing one's own payment moves the sentence about the system-issued API key to its hint. Operations: GET /v1/payments, GET /v1/me, POST /v1/payment-exports.

2026-10-11: Discounts on invoices, quotes and sales orders

Before: a price could only be lowered by typing a lower unit price; the customer never saw the list price or what was taken off.

After: every line of an invoice, credit note, bill, quote or sales order has a Discount field (a percent, or an amount with the button at its end), and under the lines a Document discount takes a percent or an amount off the whole document. VAT is charged on the discounted net; a document discount is split over the lines by what each is worth, so the VAT of each rate goes down by its share. Discounts go from a quote to its order and on to the order's invoices (an invoice of part of a line takes its part of an amount), and a credit note of the whole invoice repeats them. The PDF shows the price, the discount and the net of each line, then the subtotal, the document discount and the net total; the e-invoice (XRechnung, ZUGFeRD) states them as line and document allowances per VAT rate, and the QR-bill carries the discounted total. API: discount_percent or discount_amount on a line and on the document. See ADR-0055.

2026-10-11: Subscription approvals in your language, with their own reason

Fixed: in German, French and Italian the Approvals page showed a subscription's request, its early end and its renewal in English ("End SUB-000002 for Berlin Software GmbH on 2026-10-31", ISO dates), gave every one of them the reason of a subscription's start ("starts billing"), left their amount unlabelled, and titled the decision "Approve and post" although nothing is posted. The requests now carry what they are about and why they wait by code (summary.words, summary.why of GET /v1/approvals, worded as approvals.request and approvals.why), so the page words them in the person's language with dates in their format; an early end and a renewal have their own reason (also in reason); the amount says what it is (the term's amount, what the early end takes off the term, with the term's amount before and after and the credit, or the next term's amount with its days); terms are approved with "Approve request". Further: "1 months" reads "1 month"; a request one asked for or prepared offers no Approve or Reject (the server refuses them) and says another person decides; the Italian subtitle addresses the reader with "tu".

2026-10-11: Cash discount terms on the customer and vendor card

New: the Payment tab of a customer or vendor card shows the cash discount (Skonto) terms next to the payment terms, for example "2% within 10 days", and Edit changes them there: a percentage and a number of days, both or neither (clearing both removes the terms). Before, they could only be set over the API. A refusal is now worded: percentage and days go together (cash_discount_terms_pair), and the discount period cannot be longer than the payment terms (cash_discount_beyond_terms, with days and terms), on PATCH /v1/entities/{entity}/parties/{party}.

2026-10-11: Purchase order matching and VAT warnings in your language

Before: In German, French and Italian, the problems a bill's purchase order matching lists ("line 1: price 95 is above the order's 80: 75.0000 more"), the reason a controller reads under Approvals before approving a variance or a bill that quotes an unknown purchase order, and the bill form's warning about VAT a foreign vendor charged were in English, with four decimals. The matching lines showed how alike the descriptions are as a bare ratio ("1") and a quantity variance of zero as an empty cell.

After: They are worded in your language, with the amounts in the currency's decimals and your separators. The similarity shows as a percentage ("100 %"), and variances of zero show as 0.00. Matches made before this change keep their English problems until the bill is matched again (Match now).

For integrators: the change is additive. MatchOut.problem_reasons gives problems by code (purchaseMatch.problem) with the values they name; ApprovalOut.reason_words gives the reason of a purchase order variance or a held bill by code (approvals.why), null for a policy rule's own text; a vendor document's provenance.warning_reasons gives its VAT warnings by code (documents.warning), each with its English text. The English problems, reason and provenance.warnings stay, and the variance amounts in them are now rounded to the currency ("75.00", was "75.0000"). Operations: GET /v1/documents/{document_id}/match, GET /v1/approvals, POST /v1/documents and POST /v1/documents/preview.

2026-10-11: Document lines and the invoice list fit the screen

Fixed: on a new invoice, credit note or bill at a 1400 px window the line table was wider than its panel, so the Net and VAT columns were cut off at the right edge; the customer and vendor invoice lists cut off the Open amount column the same way. Both now fit their panel at that width (the description column takes the room left); on a narrower window the table still scrolls inside its panel.

2026-10-11: A credit note starts with the credited invoice's lines

New: Credit note on an invoice or bill now opens the form with every line of the credited document ("Credit: …", same product, account, quantity, price and VAT) and in its currency, as POST /v1/documents already did when a credit note was sent without lines. Each line credits its line of the invoice or bill and takes that line's discount and its share of the document discount in proportion to the quantity you leave on it. Change or delete what is not to be credited. Choosing another document to credit replaces the copied lines as long as you have not changed them; lines you entered yourself are never replaced.

2026-10-11: A subscription the billing run ended can still be renewed

Fixed: a subscription without automatic renewal whose renewal was due was ended by the routine billing run once its term was over, and Renew then answered 409 subscription_not_active: the renewal was lost and its dialog offered only Close. A subscription ended with its term (not ended early) now still shows its renewal as due, Renew stays in its dialog (POST /v1/subscriptions/{subscription_id}/amendments with kind renew), and the second person's approval makes it active again and starts the next term at once, the day after the old one ended. A term ended early is still not renewed. See subscriptions and ADR-0047 section 2; the database guards change with migration 0073.

2026-10-11: The bank worksheet says why a document fits in your language

Fixed: the "Why" column of the bank reconciliation worksheet and of the booked payments a line may execute showed the server's English ("reference contains INV-2026-000007, counterparty name matches", "booked 1887.60 CHF, converted by the bank") in German, French and Italian too. The reasons are now worded in the person's language, amounts with their separators.

The candidates of GET /v1/bank-lines/{line_id}/candidates carry reason_words: the same reasons by code (vocabulary bankMatch.reason) with the values they name, as checks and exceptions do. reasons keeps the English sentences for agents.

2026-10-11: Bank worksheet title and refusal amounts read cleanly

Fixed: the bank worksheet title of a line that names no counterparty or reference (interest, fees) ended in a dangling " ·" ("09/23/2026 · 3.15 CHF ·"); it now ends with the amount. Amounts in refusals worded on screen are shown with the person's separators and the currency's decimals, as checks and exceptions show them: "allocated 9,500.00, but the payment is 8,550.00", no longer "8550.0000". A rate or other decimal value is never rounded (0.9438 stays 0.9438), in checks and exceptions too.

2026-10-11: Customers can pay an invoice in another currency

Before: a receipt had to be in the invoice's currency. A CHF payment for a EUR invoice was refused (payment_currency_differs), and a CHF bank line never found the EUR invoice it paid.

After: a receipt or payment settles documents in any currency. You say what it settles on the invoice in the invoice's currency (usually the open amount), and the difference between what the invoice carried and what the bank received is booked as a realised exchange gain or loss. The same works when money kept on account, or a credit note, is applied to an invoice in another currency, and when a bank line is matched: a CHF line within 1% of a EUR invoice's open amount at the day's rate pays it in full (further off, it settles what it buys at the day's rate and the rest stays open), automatically when its reference names the invoice. An invoice's settlements show what was received in the other currency.

API: allocations of POST /v1/payments, POST /v1/bank-lines/{line_id}/match and POST /v1/applications take document_amount (in the document's currency; default the payment day's rate); bank line candidates give line_open; document allocations give source_amount and source_currency. payment_currency_differs is replaced by document_amount_differs; a payment that pays a bill in another currency is kept out of payment files (bills_other_currency). See ADR-0054.

2026-10-11: A product picked early is priced in the document's currency

Fixed: on a new invoice, credit note, quote or sales order, a product picked while the form was still loading its customer (a credit note opened from a EUR invoice, for example) could take the list price in the company's currency: the line showed "List price 80.00 CHF" on a document in EUR. The list price is now always asked for the document's current currency and date: an answer that arrives after they changed is asked again, a price in another currency is never put on the line, and a product picked before the currency is known is priced as soon as it is. A price you typed yourself is still kept.

2026-10-11: Ignored bank lines in the close checklist show their amount in your number format

Fixed: the period close check "Bank statements reconciled" named the worth of ignored statement lines as a fixed English amount ("12000.00 CHF") in every language. It now shows each currency's amount with your separators and the currency's decimals (for example "12'000.00 CHF"). API: in the check's details, worth is now a list of reasons bank_ignored_amount, one per currency, each with amount (a decimal string) and currency.

2026-10-11: The periods of a closed year no longer count as open in later months

Fixed: after a year-end close, the close checklist of every later month warned "Earlier periods closed: 12 earlier periods still open" when the year's periods had not been closed one by one, although the closed year accepts no posting and its periods reopen only with the close. The check now counts the periods of a year whose year-end close is posted (and those before a posted catch-up) as closed. The year-end close itself still leaves each period's status as it was; after the close is reopened, the warning counts them again. See year-end close.

2026-10-11: A receipt cannot be dated in the future

Fixed: a receipt (or a refund received from a vendor) dated after today was posted: the invoice's open amount dropped at once while the aging of today still showed it open, and the receipt could only be reversed by typing its future date. Money received is now recorded once it has arrived: POST /v1/payments refuses an incoming payment dated after today in the company's country (422 receipt_in_future, with date and today), and the receipt dialog offers no later day. An outgoing payment may still be dated later, as the day the bank is to execute it; reversing such a payment without a date now takes its own day instead of being refused (POST /v1/payments/{payment_id}/reverse).

2026-10-11: A realised FX difference is no longer shown as a purchase or sale outside VAT

Fixed: when a vendor payment or a customer receipt in a foreign currency booked a realised exchange gain or loss, the VAT report and the VAT return reconciliation listed it under "Purchases outside the scope of VAT (VAT group, Organschaft)" (or the sales equivalent), for example -9.00. An exchange difference is not a supply: it no longer appears among the reconciling items (reconciling_items), which now show only real purchases and sales outside VAT.

2026-10-11: The VAT return shows the official-rate difference on each VAT account

Fixed: foreign-currency VAT is on the Swiss return at the ESTV monthly rate and in the books at the ECB rate of the booking day (MWSTV Art. 45). For acquisition tax the two differences cancel in the amount payable, so Ziffer 383 and 405 (and 399, 479) differed from the VAT accounts by a few cents while the reconciliation said "On the return" equal to the ledger and a difference of 0.00. Now each VAT account in Reconciliation with the VAT accounts shows what the return takes of it at the official rate, and a new column Official VAT exchange rates shows the difference to the ledger on that account. The settlement still clears what each account holds. API: each entry of reconciliation.accounts has a new tax_fx_difference, and its reported is the amount on the return (ledger = reported - tax_fx_difference + unmapped + untagged); the totals are unchanged. A company that wants the return equal to the books to the cent books at the official rate (tax_fx_booking in the VAT settings). See API: VAT returns.

2026-10-11: Revenue is recognised only once it is earned

Fixed (accuracy): run before the period's last day, the revenue recognition run booked the whole month, dated its last day: on 11 October it recognised October's revenue, marked a contract ending on 31 October completed and then refused to end its subscription on 15 October (contract_recognised_after_end). The run now recognises the schedule lines whose day has passed, through yesterday, and leaves the period's later lines for a run after their day; a contract completes only once its last day is in the past. The result says how far it recognised and what it left (recognised_through, left_for_later on GET /v1/periods/{period_id}/revenue-recognition/preview and POST /v1/periods/{period_id}/revenue-recognition), the preview on the period card says so in words, and the close check revenue_recognised says the period's lines are still to come (revenue_not_yet_earned) instead of asking for a run that would book nothing. See revenue contracts and ADR-0027 section 3.

2026-10-11: Statement balances are compared with the ledger in the bank account's own currency

Fixed: once a payment in another currency left a CHF bank account (a EUR bill paid from the CHF account), the period close check "Statement balances equal the ledger" read the payment at its EUR amount (2,000.00) instead of what it booked in CHF (1,887.60), so a fully reconciled account failed the check. The check now reads the account's balance as the general ledger shows it: on an account in the company's currency the amounts booked in that currency, on a foreign-currency account its amounts in that currency. The warning before an intercompany loan is paid out ("this payout takes it below zero") reads the lender's bank balance the same way.

2026-10-11: Open items list amounts on account and credit notes as rows

Fixed: the customer and vendor open items (Customer Invoices and Vendor Bills, "Open items") listed only invoices or bills, so a receipt or payment kept on account (and an open credit note) was missing: "Total open" differed from the ledger control account line under it with no row explaining why. Every open item is now a row with its type, so the rows add up to the total and the total to the ledger. An amount on account opens its customer's or vendor's card, where it is applied; QR-bill and XRechnung are offered for invoices only.

2026-10-11: A document of another company opens with that company's accounts and currency

Fixed: an invoice, bill or credit note opened by its link while another company was selected (for example "Review and accept" on the Intercompany page, which opens the other company's mirror bill) showed the lines' accounts as raw ids and "Total in functional currency" in the selected company's currency (10,000.00 CHF for an EUR company). The card now reads the document's own company: its accounts, its functional currency, its country (QR-bill, XRechnung), and names the company. Its accounts and party are shown, not linked into the selected company. Editing such a draft uses that company's accounts and parties too, and a VAT return of another company no longer links its accounts into the selected one.

GET /v1/documents/{document_id} and the document lists now return entity_id, the company the document belongs to.

2026-10-11: A missing exchange rate on a report in another currency is said in words

Fixed: when the ECB rates of a month were incomplete, an income statement or balance sheet in another currency answered with a technical "not found" error. It now says which rate is missing, as everywhere else (409 fx_rate_missing).

2026-10-11: Checks and consolidation exceptions in your language

Before: The explanations in the period close checklist, the year-end close's list of what stands in the way, the group close checklist and the Exceptions tab of a consolidation run were always in English, whatever language you had chosen.

After: They are shown in German, French, Italian or English, like the rest of the screen. Amounts, dates and codes are filled in as before, and dates appear in your date format. Exceptions of consolidation runs made before this change keep their English text; make a new run to see them in your language.

For integrators: the change is additive. A period close check and a group close check now also carry code and details (the result by a stable code, and the values it names); a consolidation exception carries details; the year-end preview has blocker_reasons and warning_reasons next to blockers and warnings, in the same order. The English detail and text stay as they were. Operations: GET /v1/periods/{period_id}/close-checklist, GET /v1/consolidation-groups/{group}/checklist, GET /v1/consolidation-runs/{consolidation_run}/exceptions and GET /v1/entities/{entity}/fiscal-years/{year}/close-preview.

2026-10-11: Quotes and sales orders

Before: there was no way to offer a customer a price before invoicing, or to record what a customer ordered and invoice it in parts.

After: Sales > Quotes and Sales > Sales orders (and New quote on a customer's card). A quote (QT-2026-000001) is priced as the invoice will be, sent as a PDF in the customer's language or recorded as handed over, and accepted, declined or withdrawn; past its valid-until day it shows as expired. Accepting it makes the sales order (SO-2026-000001); an order can also be entered directly and confirmed. Create invoice on an order drafts an ordinary customer invoice of everything left or of chosen quantities; the order shows partially invoiced or invoiced, and no invoice can ever bill more than was ordered. An order is closed for the rest or, while nothing was invoiced, cancelled. Quotes and orders are found by number or customer reference in search. Nothing posts until an invoice is posted. See Quotes and sales orders.

2026-10-11: No more night-time alert emails after a deploy

Before: After a deploy at night, the "95% of reads under 500 ms" alert mailed and cleared every minute or two for about half an hour. The deploy's own smoke test made one slow first request to the new, still cold server, and at night that was often the only request of the hour, so it counted as 100% slow.

After: Requests the deploy's smoke test sends to the new server before it takes traffic are not counted in the speed targets, the same way they were already left out of the error targets. Real requests are measured as before.

2026-10-11: Reports in another currency

New: the income statement and the balance sheet can be shown in another currency than the company's own, for example a Swiss company's figures in euros. Pick the currency under "Show in" on Reports > Income statement or Balance sheet. Income and expenses are translated at each month's average ECB rate, assets and liabilities at the rate of the report date, equity at the rates of the days it was booked, and the difference this makes is shown as its own line, "Currency translation difference" (the method of IAS 21, the same as the group consolidation). A note under the report lists every rate used. If a rate is not published yet, the report says which one is missing instead of guessing.

API: currency on GET /v1/entities/{entity}/reports/profit-and-loss, .../period-profit-and-loss and .../balance-sheet, with the rates in translation (ADR-0052).

2026-10-11: The group close can be completed: investments, goodwill and minority interests are consolidated

Before: A consolidation run removed what the group companies owe, sell and pay each other, but the parent's investment in a subsidiary and the subsidiary's equity both stayed in the group statements, goodwill was not shown and minority shareholders had no share. Every run was marked incomplete, and the group close checklist could never be ready.

After:

  • A run now sets the parent's investment in each subsidiary against the subsidiary's equity at the acquisition date, at the exchange rate of that date. What the parent paid above its share is goodwill: it is kept in the subsidiary's currency and shown at the month-end rate, and under IFRS it is not written down over time. If the parent paid less than its share, the difference is a gain on a bargain purchase in the year of the acquisition, and the run asks you to check the figures again first.
  • Minority shareholders (non-controlling interests) get their share of the subsidiary's equity at the acquisition, of its profit since, of its currency translation differences and of its other equity changes, such as a dividend paid to them. The balance sheet shows their interest as its own line in equity; the profit and loss shows the group's profit and how much of it belongs to the parent's shareholders and how much to the minority.
  • Each subsidiary's entries show what they rest on: the price paid, the equity at acquisition, goodwill in both currencies, the minority share and its parts, the exchange rates, and the paragraphs of the standard they follow. You find them under Finance → Consolidation, on a run's Eliminations tab.
  • The group close checklist has two new checks, one for the investments and goodwill and one for the split of the profit. When every company's periods are closed and nothing blocks, the checklist is ready.
  • Add subsidiary asks for the subsidiary's net assets at the acquisition date, next to the price paid and the parent's investment account. All three are needed for the consolidation.

What the system does not guess, and says instead: the net assets at acquisition are taken at the value in the subsidiary's books, so a different figure on the subsidiary's record stops the run until it matches. A subsidiary that was bought before the date it is consolidated from, a change of the parent's share, an investment account that does not hold the price paid, or missing acquisition details each stop the run with a clear message. Goodwill is not tested for impairment by the system; the run reminds you. Groups under Swiss GAAP FER or HGB still cannot be run until those standards have been read and cited.

The API operations are the ones that were there: GET /v1/consolidation-runs/{consolidation_run}/eliminations now lists acquisitions, GET /v1/consolidation-runs/{consolidation_run}/profit-and-loss adds attributable_to_owners and attributable_to_nci, and the checklist has capital_consolidated and nci_attributed.

2026-10-11: FX rounding is no longer shown as purchases outside VAT

Fixed: when a bill or invoice in a foreign currency left a rounding cent after conversion, the VAT report and the VAT return reconciliation listed that cent under "Purchases outside the scope of VAT" (or the sales equivalent). The cent is not a purchase or a sale; it now has its own line, "FX rounding of invoices and bills in a foreign currency", and the outside-the-scope lines show only real purchases and sales outside VAT (fx_rounding in reconciling_items).

2026-10-11: Subscriptions bill in the customer's or the deal's currency

Before: every subscription billed in the company's currency. A deal won in EUR with a EUR customer became a CHF subscription with CHF invoices.

After: a subscription bills in the currency of the deal it came from, else in the customer's currency, else in the company's. The new subscription form shows the currency and lets you pick another one. The new revenue contract form does the same, starting from the customer's currency, else the company's. Its prices, invoices, credit notes, renewals and revenue contract are all in that currency, and amounts show their currency on the subscription, in its end and renewal dialogs and in Approvals.

Prices are never converted. Each product needs a list price and a stand-alone selling price in the subscription's currency; if one is missing, the subscription is refused and the message says which currencies the product does have a price in, such as "CHF, EUR" (subscription_currency_unpriced, or subscription_ssp_missing for the selling price). The link to record a missing selling price opens the product with the right currency.

Revenue is still booked in the company's currency. What a customer was invoiced in advance becomes revenue at the rate of the invoice; revenue earned before it is invoiced is booked at the rate of the month's last day. The revenue run now also books the exchange gain or loss when an invoice in arrears comes in at a different rate, and values revenue not yet invoiced (where only time stands before the invoice) at the month-end rate, reversed the next day. The run shows these as exchange_differences and remeasured, and each obligation's functional_amount and billed_part; the preview before the run shows them too, each obligation with its currency and its amount in the company's currency.

The month-end rate is only published on the month's last day. A run earlier in the month still recognises every other contract; a foreign contract that needs that rate waits, and the run says which one and for which day's rate (waiting). Run again after the month end to recognise it; the period cannot be closed before.

A second person still approves every subscription. Approval limits are compared in the company's currency. API: POST /v1/subscriptions and POST /v1/revenue-contracts take an optional currency. See ADR-0050.

2026-10-11: A bill that quotes an unknown order waits for approval, and foreign VAT is booked as cost

A bill that quotes a purchase order we do not have

Before: a vendor bill that named a purchase order number that does not exist (or that belongs to another vendor, or to another of your companies) was posted like any other bill and could be paid straight away.

After: such a bill is not posted on its own. When it is submitted, it goes to Approvals and waits for a second person, with the reason in plain words, for example "the bill quotes purchase order PO-2026-000123, which does not exist". An order number used in another of your companies counts only if it is the same vendor's order there. The approver can approve it (the bill is then posted and can be paid) or reject it. The bill's card says the same while it waits, and once posted it names the person who approved it (a bill posted before this change says it was posted without that approval). This applies to bills typed in by hand, read from a PDF with the bill wizard, and imported from an e-invoice (XRechnung or ZUGFeRD order reference). An agent never approves. The submit answer has a new reason field that says why a document waits for approval.

A bill that was already posted and blocked for payment because it quoted another vendor's order stays blocked when it is matched again, until a second person approves the variance; once approved, it stays approved when the bill is matched again or goods are received on the order.

VAT a foreign supplier charges that we cannot reclaim

Before: when a foreign supplier's e-invoice charged its own country's VAT (for example a German supplier charging German VAT to a Swiss company), the bill was booked without that VAT, so paying the bill paid the supplier less than its invoice total; a warning pointed out the difference.

After: the bill is booked at the supplier's invoice total, and paying it pays the supplier in full. The foreign VAT is part of the cost of the purchase: it is booked to the same expense account, not as input VAT, and it does not appear on the VAT return. Where your company owes the VAT itself (acquisition tax in Switzerland, § 13b reverse charge in Germany, for example a German company buying an Austrian consultant's service on which the consultant wrongly charged Austrian VAT), that tax is still self-assessed and reported exactly as before; only the foreign VAT is added as cost, and the bill's warning says so. Where the supply is taxed abroad or is an import, the line shows "Foreign VAT, not reclaimable". The VAT report lists the foreign VAT booked as cost as a separate reconciling item, and its labels are now translated.

VAT that may be your own is not booked as cost: when the supplier is registered for VAT in your country (its VAT number in the vendor record, or the seller's or tax representative's VAT number on the e-invoice), or the VAT is at one of your country's rates on a purchase taxed in your country, the bill is booked as before with a warning to check it. VAT of a purchase taxed abroad (a hotel abroad) is always cost, even at a rate your country also has. A Liechtenstein company's bill from a Swiss supplier is Swiss VAT it can reclaim (Liechtenstein and Switzerland are one VAT area). Invoices without foreign VAT (reverse charge) are booked as before, with the acquisition tax.

An e-invoice dated in a closed period

When an e-invoice is uploaded with Submit for posting and its date falls in a closed period, the refusal now says what to do: upload it without submitting, change the bill's date to a day in an open period, then submit it.

See ADR-0049.

2026-10-11: Every action now has a screen: approval policy rules, new companies from a chart, payment file history and more

Before: A few things could only be done through the API. The approval policy page only showed the rules; changing them, or going back to the built-in policy, needed an administrator's API call. A company could only be added with the setup questions, never from a chart of accounts of your own choice, and the operator portal could not set up a customer's first company. A revenue contract could not be made by hand. The period close ran the revenue recognition without showing first what it would post. An invoice line had no product, so its price had to be looked up and typed. Payment files could be downloaded only right after releasing them or from a payment that was in one. An asset's schedule per book was shown, but not read from the book itself.

After:

  • Settings → Approval policy has Change rules and, when the organization has its own rules, Back to built-in policy. Each rule is a small form: who, which actions, above which amount, from which source, the decision and why; rules can be added, removed and moved, because the first rule that fits decides. A condition the form has no field for is kept exactly as it is and shown as text. Only a person with the administrator permission sees the buttons, and the server still decides: a rule that would let AI agents do what only people may do is refused and named, a change someone else saved meanwhile is never overwritten (the page offers to load their version), and the history keeps every version.
  • Settings → Company has Add from a chart template: a further company whose chart of accounts you pick from the templates (the country's standard chart is marked and chosen), with its accounting framework and fiscal calendar (twelve months, 4-4-5 and the other week-based calendars, or thirteen four-week periods), opening its first fiscal year if you want. Add company with the setup questions stays as it was.
  • The operator portal's New customer wizard has a Books step. The administrator still sets up the books at the first sign-in by default; the operator can instead make the first company now, from a template and a chart of accounts picked from the list.
  • Sales → Revenue contracts has New contract: the customer, the term, the fixed price and each promise with its own price and how it was set, or a product whose price in force is taken. It is saved as a draft, and nothing becomes revenue until a second person approves its terms.
  • On a period's close card, Run revenue recognition first shows what the run would post: the revenue due per promise, the contract positions presented and what waits for later. Nothing is posted until you run it from there.
  • A sales invoice or credit note line can name a product: the line takes its VAT rate and revenue account, and its list price in force on the document date in the document's currency is proposed (or the line says there is none).
  • Vendor payments lists the released payment files, newest first, with who released each, the number of payments and the control sum, and each file to download again.
  • An asset's schedule in each book is read from that book, with its position on the chosen date.

The API operations behind these screens were there already: PUT and DELETE /v1/approval-policy, GET /v1/charts, GET /v1/platform/charts, POST /v1/entities, POST /v1/revenue-contracts, GET /v1/periods/{period_id}/revenue-recognition/preview, GET /v1/products/{product_id}/price, GET /v1/payment-exports and GET /v1/assets/{asset}/books/{book}/schedule. No operation is waiting for a screen any more.

2026-10-11: Ending a subscription early counts earlier credit notes and says up front what it waits for

Before: End early worked out each period's credit as if nothing had been credited on its invoice yet. When a credit note had already been made by hand on one of the subscription's invoices (a goodwill credit, say), the end was accepted when asked and then failed when the second person approved it, because it would have credited more than was left on the invoice. The same happened when a payment or a credit note on one of those invoices was still waiting for approval: the approval failed with a message about a credit note the approver could not see.

After: End early counts the credit notes already posted on the subscription's invoices. A period is credited only what is left to credit on it, and whatever an earlier credit note took beyond the unused part comes off the term's new price, so what was billed less every credit is the new price. When a credit note on one of the invoices, or a payment or application on an invoice the end would credit, is waiting for approval, the end is refused when you preview or ask for it, and the message names the invoice: decide that first, then ask again. The same applies to POST /v1/subscriptions/{id}/amendments and its preview (credit_settlement_waiting); see ADR-0047.

2026-10-10: Vendor statements and payment reminders

Before: A vendor's card had no statement. To check our books against the statement a vendor sent, you had to compare the payables list and the ledger by hand. There was no way to remind a customer of an overdue invoice: the invoice only pointed to the customer's statement, which lists what is open but does not ask for payment.

After: A vendor's card has Statement. It shows what we owe the vendor for the days you choose, in the vendor's currency: the opening balance, each bill, credit note and payment with the running balance, the closing balance, and the bills still open. Type the closing balance from the vendor's own statement, and the page says whether ours agrees or by how much it differs. You can download it as a PDF in the vendor's language, or as a CSV. Sales → Payment Reminders lists the customers with overdue invoices that are due a reminder, and at which level: payment reminder, second reminder or final reminder. Make letter creates the letter as a PDF in the customer's language (English, German, French or Italian) and keeps it. Letters are dated today, with today's amounts. Then send it by e-mail, or record that you sent it by post or another way within the last 7 days. The next level counts from the day the letter was handed over. A letter that waited too long or whose amounts changed meanwhile (the customer paid part, a credit was used) can no longer be sent: it shows as out of date, and Withdraw takes it back so a new letter can be made. A letter deducts what the customer has to its credit (credit notes not yet used, money on account), each credit on one letter only, and a customer whose credits cover its overdue invoices gets no letter. When one letter names invoices at different levels, it says which ones were reminded before. A blocked customer still gets reminders. An overdue invoice has Send reminder, for a letter about that invoice only. Letters charge no fee and no interest, and nothing is posted. Test copies never send e-mail. The API has the same: GET /v1/entities/{entity}/vendors/{party}/statement and the /v1/.../reminders operations.

2026-10-10: VAT returns show when they are due, and plans wait in Approvals

Before: VAT returns listed each period with its status but not when its return had to be filed and paid, so a late return showed nothing until someone remembered the deadline, and neither the home page nor the period close mentioned it. A budget or forecast version submitted for approval waited only on its own plan page: Approvals did not list it, so an approver had to know where to look.

After: Each period in VAT returns shows when its return is due to be filed and paid, by the company's country: Switzerland 60 days after the period, Germany the 10th of the next month (a month later with the permanent extension, Dauerfristverlängerung, now a switch in the German company's VAT settings; the recapitulative statement by the 25th day; a German deadline on a Saturday or Sunday moves to the Monday, public holidays are not known yet), the United Kingdom one month and seven days (two months after the year under the Annual Accounting Scheme). A return not yet final is flagged due soon within 14 days of that date and overdue to file after it; a country without a known rule says "No due date known" instead of guessing. The home page counts the VAT returns due soon or overdue of a VAT-registered company, from the period its books start in (a company that starts mid-quarter files that quarter), and the period close warns (without blocking) when a return due by the period's end is not final. A submitted plan version now appears in Approvals with its plan and version, the planned result (revenue less expenses) as its amount and a link that opens the plan on that version; approving it there approves the version, rejecting it sends it back to draft with the comment as the reason, and deciding it on the plan page clears it from Approvals.

2026-10-10: Search finds subscriptions, contracts, deals, products and budgets; an asset's page shows its history

Before: Search (Alt+Q) found invoices, bills, orders, entries, payments, customers and vendors, but not a subscription, a revenue contract, a deal, a product or a budget or forecast, even by its exact number. A fixed asset's page showed its terms and depreciation schedules, but not what had actually been booked to it: to see its acquisition, each month's depreciation or its disposal one had to search the journal.

After: Search also finds subscriptions, revenue contracts and deals by their number, title or customer (a deal also by its prospect's name), products by code or name, and budgets and forecasts by name, in every company, with the title and customer on each line; choosing one opens it (a deal at the top of the pipeline, a product in the product list). An asset's page has a History section listing, oldest first, its acquisition, each depreciation, any correction booked to it, its disposal and reversals, each with the date, the amount, the book value left and a link to the journal entry (and to the purchase bill the cost came from), followed by each tax book's depreciation of the years that have ended. Its Audit trail shows when it was registered and added to a tax book.

2026-10-10: Clearer close checklist, VAT lock and approvals

Before: The close checklist counted with "(s)": a passed depreciation check read "1 asset(s) due", and the same for approvals, drafts, statements, loans, contracts and subscriptions. Posting into a period of a final VAT return was refused with a general "integrity violation" and no way to the return. In Approvals, an entry waiting for a second person, such as "Year-end close 2026", did not say which company it was for.

After: The checklist counts in proper English: "1 asset due", "2 assets due". A posting into a VAT period that a final return locks is refused with its own message naming the return and its period, and the message has Open VAT return to amend it. In Approvals, an entry waiting for a second person shows its company before the date.

2026-10-10: Money can stay on account, and a credit note can be applied later or exceed what is still owed

Before: A receipt or payment had to add up exactly to the invoices or bills ticked: a customer who paid more, or paid before the invoice existed, could not be recorded, and a bank line for more than its documents could not be matched. A posted credit note could only offset the invoice it credited, at the moment it was posted, and could not be applied to another invoice of the same customer later. An invoice or bill already paid could not be credited at all ("exceeds the open amount"), and its page offered no New credit note for.

After: Record receipt and Record payment take the amount received or paid; what is not applied to a ticked document stays on account with the customer or vendor, and the dialog says so before recording. The amount on account shows in the party's balances (a new On account column, with the open amount net of it), on the customer's statement and its PDF, and in aging under "current"; while a receipt awaits approval, aging lists it as pending. Apply next to it applies it to open invoices or bills of the same currency later; the application is its own entry, goes through the same approval as a payment, and in a foreign currency books the exchange difference at the rate of the original receipt or payment. Matching a bank line can keep the rest on account, or the whole line when no document is ticked. A credit note can be issued up to what was invoiced, less earlier credit notes, also on a paid invoice or bill (New credit note for is offered on it): what is not needed for the open amount stays open on the credit note. A credit note with something open offers Apply to an invoice (or Apply to a bill) and Record refund. Refused with a clear message: applying more than is left, to another party or currency, to a document dated after the application, dated before the last payment, application or reversal of either side, or the same document twice. A credit note waits to be posted while a payment of the invoice or bill it credits is waiting for approval. A payment proposal no longer ticks the bills of a vendor that has money on account or an open credit note in the bill's currency: it shows how much and asks you to apply it first, so the vendor is not paid twice.

2026-10-10: A subscription can be ended early or renewed, and a finished revenue contract completes

Before: An active subscription could not be changed at all. Ending it before its term meant leaving it to bill every remaining period and fixing the invoices and revenue by hand, and its revenue contract could never be changed once active. A subscription could not renew: "renews itself" and a renewal price increase were refused, and every subscription simply ended after its term. A revenue contract whose revenue was all recognised stayed active for ever.

After: End early on an active subscription's dialog asks for the last day of service, whether the unused part of what was already billed is credited or kept as a non-refundable prepayment, and why. It shows the term's price before and after and the credit for each billed period before anything is sent, and a second person approves it. On approval, billing stops after the last day (a period cut short is billed by its days), the system posts a credit note per period billed past it in the same step (it never waits for an approval of its own, and what was already paid stays on the credit note to refund or apply), also for a blocked customer, and the revenue contract is changed from the first day not yet recognised: revenue already recognised stays and the rest of the schedule is rewritten to the new price. The subscription shows the day it ends and its amendments, and the billing run marks it ended after that day. Renewals: a new subscription can renew itself at the term end, with a notice period and a price increase in percent. The first billing run after the term starts the next term, as long as the first, with its own revenue contract and the raised prices, and bills it. A subscription that does not renew itself shows renewal due in the list from the notice day, and Renew in its dialog shows the next term's dates and price and asks a second person to approve it. Completion: the period-end revenue run marks a contract completed once its last day has passed and all of its revenue is recognised, and says which ones it completed; the contract's dialog says so and lists any change of its terms (from which day, the price and last day before and after, and why). A completed contract can still be invoiced for its last period.

2026-10-10: An intercompany loan payout warns when the lender's bank account would go below zero

Before: Paying out an intercompany loan never looked at the lender's bank account: EUR 50,000 was paid out from an account with nothing on it, without a word.

After: The Pay out dialog shows a warning, before you confirm, when the amount is more than the book balance of the lender's bank account, with that balance. The payout is still made (the money may be in the bank and not booked yet), and the API's answer carries the same warning in warnings. A loan also shows its lender's bank balance (lender_bank_balance).

2026-10-10: Customers and vendors can be blocked or set inactive

Before: A customer or vendor was either waiting for activation or active. A vendor in a dispute could still be paid by the next payment proposal, and a customer you no longer serve showed up in every list you pick from.

After: On a customer's or vendor's card, Block stops new invoices, bills, credit notes, orders and subscriptions for it, and nothing is paid to it: payment proposals leave its bills out and say why, and payments to it are refused. A blocked customer's payments still settle its open invoices, and a credit note for one of its own posted invoices or bills can still be issued. Set inactive retires a party with nothing open: it no longer shows when you make documents, orders or payments, and nothing is paid to it or received from it. A customer or vendor still waiting for activation is activated, not set inactive. Unblock and Make active again undo either. Each change asks for a reason and takes effect once a second person approves it; while a block waits, payments to the party are already held. The lists filter by the new statuses. In German, French and Italian too.

2026-10-10: Consolidation groups are set up by anyone who may prepare entries

Before: New group and Add subsidiary on the Consolidation page were shown to administrators only, although the service lets every person with the draft permission create a group and ask for a subsidiary. A subsidiary request closed its dialog without saying whether it was in force or waited for a second person, and the acquisition cost and investment account could be given only through the API.

After: Every person who may prepare entries sees New group and Add subsidiary (an agent does not: the group structure is reserved for people). After a group is created the page says what to do next. After a subsidiary is requested it says whether it is in the group or waits for a second person, with a link to Approvals. The subsidiary dialog also takes the optional acquisition cost (in the group's currency) and the parent's investment account, which the acquisition entries use.

2026-10-10: Closing a period names the close tasks that are not done

Before: A period could be closed while one of its close tasks was still open or overdue, without a word; afterwards Mark done was refused, and the task stayed "not done yet" with no hint why.

After: The close checks alone decide whether a period can be closed, as before: a close task is a reminder, not a gate. The Close period dialog now lists the tasks that are not done and says that, once the period is closed, they can be marked done only after it is reopened. On a closed period such a task reads "not done when the period closed; reopen the period to record it", and the period's Close tasks no longer offer Mark done or Reopen (which the server refuses there) but say to reopen the period first.

2026-10-10: A period-end run waits for its period to start

Before: Depreciation, revenue recognition, the FX revaluation and deferred tax could be run for a period that had not begun. Run on 10 October for November and December, they booked those months already: a CHF 12,000 contract ending on 31 December was recognised as revenue in full in October.

After: These runs, and the interest accrual of an intercompany loan, are refused for a period that starts after today, with a message naming the period and the day it starts. The current period and earlier ones are run as before. On the period's close page, a period that has not started shows when it starts instead of the Run buttons.

2026-10-10: A partly delivered purchase order can be closed for the rest

Before: A purchase order could only be cancelled while nothing had arrived and no bill was matched. Once part was delivered, an order whose rest would never come stayed open for good: more could still be received on it, and a later bill for the rest was matched as if it were expected.

After: Close the rest on an open, partly received or partly invoiced order asks for a reason and closes it. What was received or invoiced stays; the rest of each line shows as Closed and is no longer expected: nothing more can be received on it, and a bill for more than is left is blocked for payment until a second person approves the difference. Goods already invoiced but not yet received can still be received. The order then reads closed, with the reason and who closed it, and can't be reopened. In German, French and Italian too.

2026-10-10: A bank match can be undone

Before: Once a bank statement line was matched, it stayed matched. A receipt matched to the wrong invoice, or bank fees posted to the wrong account, could not be taken back, and the message that refused to reverse a bank posting pointed to an action in the bank reconciliation that did not exist.

After: Undo match on a matched line in Bank Statements asks for a date and a reason. A payment or posting the match made is reversed (or withdrawn while it still waits for approval), a payment the line was only linked to stays booked, and the line is unmatched again, ready to be matched by hand. When the reversal needs a second person, the dialog says so and links to Approvals, and the line shows "undoing waits for approval" until it is decided. The API has POST /v1/bank-lines/{line_id}/unmatch.

2026-10-10: A request to approve terms says what it is about

Before: The reason of a request to approve a subscription read "Intercompany relationships and agreements, the group structure, revenue contracts, recurring invoices and subscriptions are activated after a second person approves their terms.", the same long list for every one of these requests.

After: Each kind has its own sentence, for example "A subscription starts billing after a second person approves its terms." or "A revenue contract is activated after a second person approves its terms."; likewise for a recurring invoice, an intercompany relationship, an intercompany loan agreement and a company entering the group structure. The default approval policy has one rule per kind, so an organization can allow or deny each on its own; an organization that changed its own policy keeps its rules as they are.

2026-10-10: A product's stand-alone selling price can start on a past day, as a subscription needs

Before: The stand-alone selling price form on a product left its first day empty, which meant today, so a subscription that started earlier was still refused; the refusal and the product said only that one was needed "in its currency".

After: The form shows the first day, today unless the subscription's refusal opened it with its start date, and any day can be chosen, also in the past. The product says that a subscription bills in the company's currency (for example CHF) and needs a price in it valid on its start date; the refusal names the currency and that date and asks for a price valid from that day or earlier.

2026-10-10: A subscription's invoice shows its seats and their price

Before: An invoice a subscription made had one line per product at quantity 1, for example 1 x 950.00 for 25 seats at 38.00 a month.

After: The line shows the subscription line's quantity at the period's price per unit, discount included: 25 x 38.00, or 25 x 114.00 for a quarter. The line's total is the same as before to the cent, so the invoices still add up exactly to what the revenue contract allocated; only where no unit price of six decimals multiplies to that total exactly does the line keep 1 x the amount.

2026-10-10: A won deal stays in the pipeline until its subscription is made

Before: Moving a deal to Won took it off the pipeline's deals at once: the list read "No deals yet" even with won and lost deals, and New subscription for the won deal appeared only after choosing "Show won and lost too".

After: A won deal stays listed with the open ones, its status reading Won · no subscription yet, with New subscription at hand, until a subscription is made from it. When nothing is open or waiting, the list says so and points to "Show won and lost too"; "No deals yet" shows only when there are none at all.

2026-10-10: The receipt dialog no longer shows a vendor invoice number column

Before: Recording a receipt from a customer listed the customer's invoices with an empty Vendor invoice no. column.

After: The column shows only when the documents are a vendor's (a payment of bills, or a refund from a vendor); a receipt from a customer and a refund to a customer leave it out.

2026-10-10: Fixed assets are registered, put in tax books and compared with them from the screens

Before: A new fixed asset could be registered only through the API, as could adding an asset to a tax book, and the differences between the accounting book and the tax books were not shown anywhere. An asset with a residual value never read "fully depreciated", because that status waited for a carrying amount of zero.

After: Fixed assets has New asset (also offered while the register is empty) for those who may enter postings: code, name, acquisition date, cost, residual value, method, useful life and the accounts (the company's by default), with the cost coming from a purchase bill, posted with it against a credit account, or already on the books. The dialog says when the acquisition is posted or waits for approval, with a link to the approvals, and opens the new asset. An active asset's page has Add to tax book for each tax book of its company it is not in yet, from an asset class or a method. Below the depreciation books, Book-tax differences shows each asset's carrying amount against each tax book's tax base at the end of the period of the chosen date, with the deferred tax and the totals per book. An active asset reads fully depreciated once its carrying amount is down to its residual value.

2026-10-10: A posted payment or receipt can be reversed

Before: A payment or receipt booked by mistake (to the wrong invoice, twice, or returned by the bank) could not be undone. Reversing its journal entry was refused with "Reverse the payment itself", but there was no way to do that.

After: Reverse on a payment's row in Vendor Payments or Customer Receipts, and on the payments listed on an invoice or bill, asks for a date and a reason. An entry of its own undoes the payment to the cent, exchange difference and cash discount included, and the invoices and bills it settled are open again. It follows the approval policy as the payment did: when it waits for a second person (always for an agent, for a payment already sent to the bank in a payment file, and for a payment someone else recorded or approved) the dialog says so and links to Approvals. A payment matched to a bank statement line is undone there first. The API has POST /v1/payments/{payment_id}/reverse, and MCP reverse_payment.

2026-10-10: A credit note can be refunded from its own page

Before: A posted credit note with an open amount offered no action on its page: paying it back to the customer, or recording the money a vendor sent back, was possible only through the API. A refund paid to a customer then showed among the outgoing payments under the column Vendor, and without an IBAN it asked to add "the vendor's bank account".

After: The page of a credit note that is still open has Record refund. For a customer's credit note it records the money paid back to the customer, for a vendor's the money received back from the vendor; the dialog lists that party's open credit notes with this one ticked. On the outgoing payments the column reads Payee, and a refund to a customer says so under the customer's name; a missing IBAN asks to add the bank account on that customer's or vendor's card.

2026-10-10: A receipt or payment in euros goes to the euro bank account by default

Before: A receipt or payment recorded without choosing a bank account always went to the main bank account, so a EUR receipt was booked as a EUR line on the CHF account 1020 even when the company has a EUR account 1021.

After: Without a chosen bank account, a receipt or payment goes to the main bank account when it holds the payment's currency, otherwise to the active bank account in that currency, and only then to the main account. The dialog's choice reads Default (in the payment's currency); a payment run follows the same rule.

2026-10-10: Fiscal-period reports, the VAT report, opening balances, the ledger check, the close memo and questions about the books are on screen

Before: Several things the server could already do had no screen. The income statement and the trial balance took only dates, so a fiscal period (an adjustment period, or a 4-4-5 calendar) and the comparison with last year needed a spreadsheet. The VAT report from the ledger, a year's opening balances, the check that no posted entry was changed, the close memo and "ask the books" were reachable only through the API.

After: The Income Statement and the Trial Balance have a Fiscal period choice next to the dates: the income statement then shows the period, the year to date, and the same two of the previous year; the trial balance shows the balances after that period, an adjustment period included. Reports → VAT Report lists the VAT of the posted entries between two dates by side, country, treatment and rate, and sets it against the VAT accounts, saying when some VAT has no tax data behind it. The Year-end panel shows the opening balances the year starts with. Journal Entries has Verify ledger, which checks every posted entry and says in plain words whether the ledger is intact or which entry breaks it. An open period's close page has Prepare close memo, which writes the memo with the points to look at, without running adjustments or closing anything. Analytics has Ask the books, a question in plain words answered from this company's books, naming the entries it is based on. When no AI model is set up, or the model fails, these screens say so in plain words, without technical details.

2026-10-10: Add a company, rename the organization or a plan, export all data and open a first fiscal year from the screens

Before: Only the first company could be made, at the first sign-in; a further one, a new name for the organization or for a plan, and the complete export of the organization's data were possible only through the API (the export also through the operator). A company without any fiscal year showed an empty Period close with no way to open one, so nothing could be posted in it.

After: In Settings → Company, and at the foot of the company picker in the top bar, an administrator has Add company, which asks the setup wizard's questions (country, accounting standard, currency, fiscal year start, tax return period, tax number) plus a code such as CH02. The company is made exactly as the first one at setup: the chart of the chosen standard, the periods of the fiscal year that holds today and its tax settings. The page then switches to it, to complete its address and contact; a taken code is said at the field. The same page renames the Organization. A plan's page has Rename; a name another plan of the company has is refused with a plain message. Reports → Archive export lets an administrator start the export of all of the organization's data, follow it while it is built, then download the package or get a link with its SHA-256. Period close of a company without any fiscal year offers an administrator to open its first year, by default the one that holds today.

2026-10-10: A product's stand-alone selling price can be recorded, and a won deal leads to its subscription

Before: There was no way to record a product's stand-alone selling price, so a subscription for a new recurring product was refused with "no stand-alone selling price" and nowhere to fix it. A product's name, unit, revenue account and VAT could not be changed. A deal with a prospect could never be won, because its customer could not be picked after the prospect became one, and a won deal offered only "Note or task". A deal past its expected close looked like any other.

After: Details on a product shows and changes its name, unit, revenue account and VAT, lists its stand-alone selling prices and records a new one (currency, price, period, how it was established, valid from). When a subscription is refused for a missing one, the message links to that product's details. Edit on a deal changes its title, customer, amount, owner, next step and expected close, so a prospect gets its customer and can then be won. A won deal offers New subscription, which opens with the customer and the deal filled in, or links to the subscription made from it. An open deal past its expected close is marked late.

2026-10-10: Customer receipts are listed, and payments lead to their entry, party and approval

Before: Customer receipts were listed nowhere, and a receipt waiting for approval found by search opened the open invoices instead. A payment's number, vendor or customer led nowhere, a payment file could only be downloaded right after it was released, and after recording a payment, matching a bank line to documents or creating payments from a proposal nothing said where the booking was or that it waited for a second person. Refusing to reverse a payment's or a bank line's entry, the journal said to reverse the payment itself or to undo it in the bank reconciliation, neither of which can be done.

After: Vendor Payments has a Receipts tab listing customer receipts (also under Customer Receipts in Tell me), and search opens a receipt or payment waiting for approval there, at the top of the list. Each payment's number opens its journal entry once posted, its vendor or customer opens their card, one waiting for approval links Approvals, and a sent payment keeps offering its pain.001 file. Recording a payment, a bank line matched to documents (whose status is read live) and a payment proposal link the entry or the approvals. Open documents in the bank reconciliation and the assistant's proposals link their document and party, and the assistant shows the line's currency. A document's settlement table links each payment's entry. The journal now says that payments and reconciled bank lines can't be undone yet, and to record a correcting entry.

2026-10-10: A journal entry says where it came from and what it waits for

Before: A reversed entry still read posted. An entry made from an invoice, a VAT return or an asset disposal named only the type of that record, so the place its greyed-out Reverse pointed to had to be looked up. The note that a reversal waits for a second person disappeared on reload, and Reverse was offered again. An entry of another company, opened by its link, showed account ids instead of accounts. Year-end resolutions, unaccepted group dividends and a replaced VAT return did not link to their entries or to the correcting return.

After: A reversed entry reads reversed. Made from at the top links the invoice or bill, the VAT return or the asset (other sources are named). An entry that waits for approval, or whose reversal does, shows so on every visit with a link to Approvals, and an approver who did not ask can Approve or Reject it there; Reverse is not offered while a reversal waits. An entry of another company shows that company's name and its accounts, without links into the selected company. Year-end resolutions link their entry (or Approvals while one waits), and a reversal links the entry it reverses; "Waiting for … to accept" opens the shareholder's entry; an amended or superseded VAT return links the return that corrects it.

2026-10-10: A period's close tasks can be ticked off on its close page, and revenue recognition runs from there

Before: The company's own close tasks could be defined under the close schedule, but no page showed whether they were done for a month, and marking one done or taking it back was only possible through the API. A failing "Revenue recognised" check said to run revenue recognition, but the close page had no button for it, and "Subscriptions billed" pointed nowhere although the Subscriptions page can bill them. Opening the close page of another company's period (from a link or search) said the period was not found.

After: The close page of a period has Close tasks: each task with its due date, whether it is done, open or overdue, who did it, when, and the evidence. A person with the draft permission can Mark done (with optional evidence and a note) or Reopen a task with a reason; a task a close check decides is shown with that check's result and is not ticked by hand. A failing revenue check offers Run revenue recognition, like depreciation and FX revaluation, and a failing subscriptions check links to Subscriptions. Another company's period opens with its own dates and currency.

2026-10-10: Customer, vendor and purchase order pages lead to the next step

Before: A customer's or vendor's page listed its documents as "posted" whether paid, partly paid or long overdue, without what was still open, and its balances showed open and overdue counts that led nowhere. Recording a receipt or payment, or writing a new invoice or bill for the party, meant going to the list and picking the party again; the statement had no way to record the payment it asked for. A purchase order offered Receive even when none of its lines needed a receipt, said "partially received" once all goods had arrived, and gave no way to enter the vendor's bill for it.

After: The documents on a customer's or vendor's page show their status (paid, partially paid, overdue with the days late) and the open amount, and the open and overdue counts under balances open the invoice or bill list for that party and currency. An active customer has Record receipt and New invoice (a vendor Record payment and New bill), opening with the party chosen; the customer's Statement has Record receipt too. A purchase order still in progress has Enter bill for this order, which opens a new bill for its vendor with the order number as order reference; Receive shows only while a line still waits for goods, and an order whose goods have all arrived reads received until it is billed. These actions act in the selected company, and are not offered for a party or order of another one.

2026-10-10: Invoices, bills and credit notes say truthfully what settled them

Before: An invoice or bill settled only by a credit note read paid, and one whose receipt or payment was still waiting for approval read paid too, with no way to record another. A credit note read paid once used, and an unused one read overdue after its date, also in the list's Overdue tab. A paid invoice or bill still offered New credit note for, which then always failed.

After: The page reads credited (or partially credited) when credit notes settled it, and payment awaiting approval while a receipt or payment waits for a second person. A credit note reads applied or partially applied, and is never overdue. New credit note for is offered only while something is still owed.

2026-10-10: A fixed asset can be sold or scrapped from its own page

Before: No screen could dispose of a fixed asset: it stayed active until someone called the API. An asset whose code also existed in another company could not be opened from the register at all, the asset page showed amounts in the selected company's currency even for another company's asset, its accounts and purchase bill were plain text, and an asset with nothing left to depreciate still read "active".

After: An active asset of the selected company has Dispose on its page, for those who may enter postings: give the disposal date and, when it is sold, the proceeds and the bank account they came in on. The page then says whether the disposal is posted (with its entry number) or waits for approval, with a link to the approvals. The register opens each asset by itself, so a code used in two companies no longer gets in the way; amounts show in the asset's own company's currency; the cost, accumulated depreciation and expense accounts open the account when the asset is in the selected company, and the purchase bill opens the bill. An active asset with no carrying amount left reads fully depreciated.

2026-10-10: A request to approve opens the subscription, contract, loan or group it is about

Before: In Approvals, a request about a subscription, a revenue contract, an intercompany loan or a subsidiary in a consolidation group linked to a journal entry that does not exist, and the line under it showed an empty date and source. A subsidiary's request had no title at all. A subscription had no page of its own, and the customer in a revenue contract could not be opened.

After: Each request opens its own page: a subscription or a revenue contract opens in its list with its details shown, an intercompany loan opens Intercompany, and a subsidiary's version opens Consolidation at its group. The line under it says what the request carries: for a subscription the company, how it is billed and for how long from when; for a contract its term and how many performance obligations it has; for a loan its term; for a subsidiary the share owned, from when it is consolidated and on what basis control rests. A request of a kind the page does not know is shown without a link. Clicking a subscription's number shows its terms, its lines, the periods billed, its customer and the revenue contract it belongs to; a revenue contract's customer opens the customer card (when the record belongs to the selected company).

2026-10-10: A document in a closed period says to change its date

Before: Submitting an invoice, bill or credit note dated in a closed period answered 409 "period 2026-04 is closed", which did not say what to do.

After: The refusal (still 409 period_closed) also carries document_date and says to change the document date to a day in an open period. The screens say so in your language.

2026-10-10: Muster Handel is no longer called Demo AG

Before: In the browser tests' organization Muster Handel AG, the companies were still named Demo AG and Demo GmbH, and so were the holder of their bank accounts and the debtor of their payment files. Its exchange rates also stopped in mid-October, so the year end could not be tried there.

After: Its companies are Muster Handel AG and Muster Handel GmbH, which also hold the bank accounts and pay in the German share capital. The demo organization keeps its Demo AG and Demo GmbH. The browser tests' exchange rates run to 31 December 2026.

2026-10-10: Open amounts in refusals use the currency's decimals

Before: A credit note or a payment larger than what is still open was refused with the open amount as stored, with four decimals ("credit note total 2000.00 is more than the 0.0000 still open").

After: credit_more_than_open and more_than_open show the amounts, in the message and in total, amount and open_amount, in the document currency's decimals ("0.00").

2026-10-10: Owners can be chosen without admin rights

Before: The owner list of a new deal and of the close schedule was read from GET /v1/users, which only an admin may call. An accountant opening "New deal" got a 403 in the background and an empty owner list.

After: A new endpoint, GET /v1/people, lists the active people of the organization (id, email, name) to anyone who may read, and both screens choose owners from it. A close owner who has since left stays shown. Managing users, with their invitations and scopes, is still GET /v1/users for admins.

2026-10-10: A missing exchange rate says so plainly

Before: Booking on a day the reference rates do not cover yet (a bank line or payment after the last ECB fixing, a revaluation of a future period, a dividend appropriation dated in the next year) answered 404 not_found with "run datavnext ingest ecb-fx", an instruction for the operator. Adding a group member on such a day answered 409 rate_source_missing, as if the ECB did not publish the currency at all.

After: All of them answer 409 fx_rate_missing with base, quote and date (a day, or a month for an average or closing rate), and say that there is no exchange rate for that date yet: book once the rate is published, or choose a date that has one. The screens word it in your language. A group member whose currency the ECB does not publish is still rate_source_missing. The rate pages (/v1/entities/{entity}/fx/...) still answer 404 for a day without a fixing.

2026-10-10: A missing reason is refused in plain words

Before: Approving a purchase order variance (POST /v1/documents/{document_id}/match/approve) or ignoring a bank line (POST /v1/bank-lines/{line_id}/ignore) without a reason answered 422 with a raw validation list (reason: Field required).

After: Both answer 422 with a code and a plain message, variance_reason_missing or ignore_reason_missing, also for a reason of blanks, and the screens word them in your language. The refusals of linking a bank line to a booked payment, bank_line_payment_not_booked and bank_line_amount_differs, are worded on screen too.

2026-10-10: Approving your own request says that a second person must approve it

Before: A person without the approve scope who tried to approve their own request (a vendor they entered, a purchase order variance of a bill they entered) was refused with "this API key lacks the 'approve' scope", which hid the actual rule.

After: Approving or rejecting a request you made or requested yourself is refused first by the four-eyes principle, whatever your scopes, with the code four_eyes (detail action) and a message that a second person must approve; the screens word it in your language. A person who did not make the request and lacks the approve scope is still told about the scope. Applies to POST /v1/approvals/{approval_id}/approve, .../reject and POST /v1/documents/{document_id}/match/approve.

2026-10-10: Selling an asset without saying where the money goes is refused in plain words

Before: Disposing of an asset with sale proceeds but no account for them (POST /v1/assets/{asset}/dispose) was refused with "proceeds need a proceeds_account".

After: The refusal says what to do: name the account the sale proceeds (with their amount) are booked to, usually the bank account they were paid into. The error carries the amount in proceeds, and the screens word it in your language. Where proceeds are booked is unchanged.

2026-10-10: The payment proposal says which bills cannot go into a payment file

Before: The payment proposal showed nothing special for a bill that a payment file cannot carry, such as USD to a bank account outside Switzerland and the SEPA area (a cross-border payment, type X). The payment was created and booked, and only releasing it with POST /v1/payment-exports refused it with payment_cross_border.

After: Each proposal line lists in problems what would stop its payment going into a payment file, by the same rule the export applies (code and details as the export's refusal), and the screen marks such a line no payment file, saying why in your language. Pay these bills in the bank's e-banking.

2026-10-10: A bank line that names a bill already paid finds the payment that paid it

Before: When the bank did not echo the payment number of a payment file but its text named the bill (for example "BM-1001"), the line was not recognised: the bill was already settled by the posted payment, so it was no open document, and the payment was not offered either.

After: A booked payment is a candidate of a bank line whose reference text names a document it settled, by our number or the vendor's (reference contains BM-1001, settled by PAY-2026-000003), and the matcher links the line by itself when exactly one booked payment is named that way. A line that names nothing is linked by itself only when one booked payment alone fits its exact amount and the payer's or payee's IBAN or name is that payment's party; otherwise a person chooses under Already booked payments.

2026-10-10: A bank line of a payment already booked is linked to it, also in another currency

Before: A bank line for a payment the books already held (a payment run's, or a receipt recorded by hand from an invoice's page) was recognised only when it came from a payment file, in the payment's own currency and at its exact amount. A USD bill paid from the CHF account, debited by the bank in CHF at its own rate, or a customer's money for a receipt already recorded on the EUR account, found no candidate at all ("No open document fits this transaction"), and the only way left, posting the line to an account, booked the money a second time.

After: The candidates of a bank line (GET /v1/bank-lines/{line_id}/candidates) include the posted payments of its bank account and direction that no bank line settles yet (kind payment), and the reconciliation screen lists them under Already booked payments with Link, which settles the line without booking anything again (POST /v1/bank-lines/{line_id}/link). The matcher links a line by itself when its end-to-end id, or else its reference text, names exactly one such payment. A payment in the line's currency fits only at its exact amount. A payment in another currency fits within 2% of the amount it booked at the ECB rate of its day; the bank's difference (for example CHF 11.49 on a USD payment) is booked against the bank account as a realised exchange gain or loss through the approval policy, so the bank account follows the bank. The reconciliation assistant leaves such lines to a person, and the MCP tool lists the booked payments with the candidates.

2026-10-10: Signing out also clears the pages and company this browser tab remembered

Before: After signing out (or when a session expired) on a shared computer, the tab still remembered the last pages, list filters and search texts, and the company the previous person had chosen, so the next person could see a customer name in a filter.

After: Signing out, an expired session and a new sign-in all clear that memory.

2026-10-10: An overdue invoice or bill can be paid and followed up from its own page

Before: An invoice or bill still owed, overdue or not, offered no way to settle it from its own page: a receipt or payment had to be recorded from the list, picking the customer or vendor and the document again, and the page said "posted" even when the due date had long passed.

After: The page of an invoice or bill that is still owed has Record receipt (or Record payment for a bill), which opens with the customer or vendor chosen and the document ticked at its open amount, less any cash discount. A customer invoice also opens the customer's Statement of open items, to send as a reminder until dunning notices exist. Past its due date the status reads overdue and the due date says how many days.

2026-10-10: A failed model call no longer shows platform details on screen

Before: When the AI model could not be reached, the error shown to the person included the provider's own error text, which can name the platform's cloud project, endpoints and quota limits.

After: The screen shows "the model could not complete this request; try again". The full technical error is kept in the server log for the operators.

2026-10-10: The close checklist's integrity check stays fast on a long ledger

Before: The ledger integrity check behind the close page and the daily close job read the lines of every posted entry with a separate database query, so it slowed down in step with the length of the history.

After: The lines are read in batches, so the same full check needs a small fraction of the queries. What it checks is unchanged.

2026-10-10: The US depreciation regime example in the API documentation is correct

Before: The API documentation named the US depreciation regime "us.macrs", which does not exist, so copying the example failed.

After: The example names the real regime, "us.macrs.gds".

2026-10-10: A supplier invoice is recognised as a duplicate however its number is written

Before: "RE-2026/0451" and "RE-2026-0451" counted as two different supplier invoices, so a re-sent copy, or one typed in by hand, could be booked and paid a second time.

After: Invoice numbers are compared by their letters and digits only, so differences in dashes, slashes, spaces and capitals no longer hide a duplicate. Scanned bills, e-invoice imports, bills typed in by hand and edits to a draft are all checked, and a duplicate is refused in plain words.

2026-10-10: Clearer reason when several bills carry a payment reference

Before: a payment for several bills, one of which carries a payment reference, said that several bills carry a reference.

After: it says that a bill with a payment reference is paid on its own, in all four languages.

2026-10-10: Why a payment cannot be sent yet, in German, French and Italian

Before: on the Payments screen and on a vendor's card, the reasons a payment could not go into a payment file yet (no IBAN, an IBAN not confirmed, a vendor waiting for activation, a bill blocked by a purchase order difference, a currency the file cannot carry) were listed in English only.

After: each reason is shown in the person's language, with what to do next, and sending such a payment is refused with the same words.

2026-10-10: A double click or quick retry now does the action once, without an error

Before: If the same request arrived twice at the same moment (a double click, or a retry while the first was still running), the second one failed with an internal database error, even though the action itself was only done once.

After: The second request waits for the first and then simply gets the same answer, so you see the result once and no error.

2026-10-10: Search for any invoice, bill or customer from the header

Before: The search box in the header (Alt+Q) found only pages and actions. To find an invoice by its number you had to open the right list in the right company and filter it there.

After: The box, now called "Search or tell me what to do", also finds records in every company of the organization: sales invoices and credit notes, vendor bills and credit notes, purchase orders, journal entries, payments, customers and vendors. Type a number with or without its dashes (INV-2026-000012, 2026000012), a vendor's own invoice number, a payment or QR reference, a VAT number or part of a name. The results are grouped by type, best match first, with the company, date, amount and status; opening one in another company switches to that company. Agents and integrations get the same search as GET /v1/search (reference) and the MCP tool find_records.

2026-10-10: Spreadsheet and bank statement file refusals in German, French and Italian

Before: when an imported CSV or Excel file, or a bank statement file (camt.053, camt.054 or CSV), could not be read, the message was in English only and often named technical parts of the file.

After: each problem has its own code and plain words in English, German, French and Italian that say what is wrong with the file (an old .xls file, a column named twice, a row with an unreadable date, a statement in another format) and what to do next.

2026-10-10: Trying again after a dropped connection no longer repeats a payment

Before: If the connection dropped or the server failed just after you pressed a button such as Pay, and you pressed it again, the second try counted as a new request, so the payment or e-mail could be done twice.

After: A second try of exactly the same action, after no answer came back, is recognised as the same request and done only once. Pressing a button again on purpose after it worked still does a new one.

2026-10-10: Report, import, test copy and VAT number refusals in German, French and Italian

Before: refusals from the analytics figures, ledger filters, file imports, test copies (no e-mail, e-invoice, QR-bill or payment file), subscription invoices, invalid VAT numbers, missing role accounts, switching organizations and a repeated request were shown in English only, with a generic code.

After: each has its own code and plain words in English, German, French and Italian that say what blocks the action and what to do next, naming the screen where it helps.

2026-10-10: The renewal refusal is explained in your language

Before: Asking for an automatic renewal on a subscription was refused, but the message showed no translation, and the field and export descriptions still described renewal as working.

After: The refusal is worded in plain English, German, French and Italian, and the field and export descriptions say renewals are not supported yet.

2026-10-10: Subscriptions no longer promise automatic renewal

Before: A subscription could be set to renew automatically after its term, but nothing ever renewed it: it stayed open and was never billed again, with no notice to anyone.

After: The option is gone from the subscription form, and a subscription asking for renewal or a renewal price increase is refused with a clear message. A subscription ends when its term is billed; make a new one for the next term.

2026-10-10: Purchase order, chart of accounts, close task and operator refusals in German, French and Italian

Before: Refusals when matching a bill with its purchase order, recording goods received, choosing a report line or role for an account, editing a close task, a customer or vendor, a test copy's refresh schedule, and in the operator portal came back in English, in technical wording.

After: They read in plain words in the user's language (English, German, French or Italian) and say what to do next. Account types in these messages are named in the user's language too.

2026-10-10: No more "Run failed" e-mails from GitHub

Before: GitHub refused every automatic check and job because the Actions budget is used up, and e-mailed a "Run failed" notice for each one.

After: GitHub's automatic workflows are paused and run only when started by hand, so those e-mails stop. Deploys still run from Cloud Build. Each deploy removes deleted organizations; until a scheduled job replaces the hourly run, a deletion waits for the next deploy. See GitHub Actions paused.

2026-10-10: Error budget alerts only mail when a request really failed

Before: The "error budget burning" alert for the ERP API's availability could mail although no request had failed, because on low traffic a counting lag between two log metrics looked like a burst of errors.

After: An availability alert also needs at least one server error or "too many requests" answer that Cloud Run itself counted in the same hour or six hours. One real server error still mails. Each objective now has one alert for the 1-hour window and one for the 6-hour window (runbook).

2026-10-10: Bill capture, e-invoice, revaluation and bank statement refusals in German, French and Italian

Before: Refusals when capturing a bill or importing an e-invoice, when printing an XRechnung or ZUGFeRD invoice, when running the foreign currency revaluation, when importing a bank statement twice or when adding sample data came back in English, in technical wording.

After: They read in plain words in the user's language (English, German, French or Italian) and say what to do next, for example to open the bill that was already recorded from the same file.

2026-10-10: Group statements no longer claim to be complete too early

Before: A group with a subsidiary showed its balance sheet and income statement as complete although the investment, goodwill and minority share were not yet eliminated, so assets and equity were overstated.

After: While a subsidiary is in the group, the run is marked incomplete with a blocking note saying what is still missing, until those eliminations exist.

2026-10-10: Deploys no longer stop on a false alarm

Before: A deploy could stop at the smoke test saying a service sent no request id, although it did; the check misread its own output. A stopped deploy had also already replaced the rules bundle the next deploy compares with.

After: The smoke test reads each answer in full before checking it, and the comparison bundle is replaced only once the new release serves traffic.

2026-10-10: Account roles named in your language

Before: Approval cards for a change of an account's role, and refusals about account roles, showed the role's internal key, such as ar_control or vat_input.

After: They show the role's name in the user's language, such as "Debitoren" or "Vorsteuer" in German. A change of an account's report line on an approval card also shows the line's name. Countries in refusals are written out in the user's language too, instead of a code such as CH.

2026-10-10: Company setup, bank statement, payment run, API key and attachment refusals in German, French and Italian

Before: Refusals in the company setup wizard, on bank statement imports, in payment runs, when creating API keys and when attaching files to documents came back in English, in technical wording.

After: They read in plain words in the user's language (English, German, French or Italian) and name what to do next, for example adding a bank account in the right currency under Settings, Bank Accounts, or attaching a PDF instead of an unsupported file.

2026-10-10: Exchange rate, account and ledger import refusals in German, French and Italian

Before: Refusals on the exchange rates page, when changing an account's role or deactivating it in the chart of accounts, and when importing open items from the old books came back in English with technical wording.

After: They read in plain words in the user's language (English, German, French or Italian) and say what to do next, for example to remove a role from the account that holds it first, or to give an old invoice a different number.

Dates in these messages now appear in the user's own date format instead of as 2026-10-09.

2026-10-10: Deploys run in Google Cloud, not on GitHub's machines

Before: Each commit on main deployed through a GitHub Actions workflow on GitHub's machines, after CI passed there. When the GitHub Actions allowance ran out, nothing deployed.

After: Each commit on main deploys from Cloud Build in the project's own Google Cloud (trigger deploy-main), with the same canary, smoke tests and rollback. It does not wait for CI on main; pull requests are tested before they merge. The Deploy workflow on GitHub remains for a deploy by hand. See Continuous deployment.

2026-10-09: Sending, test copy and setup refusals in German, French and Italian

Before: when an invoice could not be sent (the customer has no e-mail address, an address is not the customer's), a hand-over date was refused, or a test copy could not be made, the message was in English only.

After: these refusals now read in your own language (English, German, French or Italian) and say plainly what to do next, for example where to add the customer's e-mail address or which test copy to remove under Settings, Test copies.

2026-10-09: Invoice output and product refusals in German, French and Italian

Before: when printing or sending an unposted invoice, reopening a document that was not rejected, or recording a product price that already exists, the message was in English only.

After: these refusals now read in your own language (English, German, French or Italian) and say plainly what blocks the action and what to do instead, for example to record a new product price from a later day.

2026-10-09: Pay a declared group dividend on screen

Before: A group dividend declared at the year-end close could only be paid through the API, so there was nothing to click.

After: Intercompany shows a "Group dividends" panel with what each dividend is, what is still to pay and a "Pay dividend" button once the shareholder has accepted its entry; both companies post together.

2026-10-09: Customer, vendor and bank account refusals in German, French and Italian

Before: when a customer, a vendor or a company bank account was refused, for example a mistyped IBAN, a customer code that is already taken or a bank change that already waits for a second person, the message was in English only.

After: these refusals now read in your own language (English, German, French or Italian), say plainly what blocks the action and point to the screen that fixes it, such as Settings > Bank Accounts or Approvals.

2026-10-09: Intercompany payments refuse amounts with too many decimals

Before: Paying interest or a dividend between companies with more decimals than the currency has (for example 0.001 euro) was quietly rounded, so the amount booked differed from what was asked.

After: Such an amount is refused with a clear message, as drawdowns and repayments already did.

2026-10-09: Cash discount, QR-bill and payment file refusals in German, French and Italian

Before: when a cash discount, a QR-bill or a payment file was refused, for example taking a discount after its deadline, making a QR-bill for an unposted invoice or listing the same payment twice in a file, the message was in English only, whatever language you use.

After: these refusals now read in your own language (English, German, French or Italian), say plainly what blocks the action and point to the screen that fixes it, such as Settings > Bank Accounts when no account in the invoice currency exists.

Refusals about a change that already waits for a second person (a reversal, an asset purchase, disposal or depreciation entry, a revenue recognition entry) no longer tell you to approve or reject it yourself, which the four-eyes rule does not allow; they now say it must be approved or rejected under Approvals first.

2026-10-09: Consolidation group refusals in German, French and Italian

Before: When setting up a consolidation group or one of its member companies was refused, the message stayed in English even in German, French or Italian.

After: These refusals are shown in the person's language and say what to do. Examples are a group date that is not a month end, a reporting currency other than the parent company's, a goodwill treatment the framework does not allow, consolidation starting before the acquisition, and a member change that already waits for a second person. The framework and the goodwill treatment are named as on the screen.

2026-10-09: Depreciation book and deferred tax refusals in German, French and Italian

Before: When a depreciation book, a tax book entry for an asset or a deferred tax run was refused, the message stayed in English even in German, French or Italian.

After: These refusals are shown in the person's language and say what to do. Examples are a book code that is already taken, an asset class from another country than its tax book, running deferred tax before the period's depreciation or before an earlier period, and a second book with deferred tax in the same company.

2026-10-09: VAT in euro on foreign-currency invoices from Germany

Before: A German company's invoice in dollars showed the VAT only in dollars, but German law requires the VAT amount in euro.

After: The printed invoice and the XRechnung/ZUGFeRD file also state the VAT in euro, with the exchange rate and date used.

2026-10-09: A stalled download no longer holds up a release

Before: When the package registry stopped answering during the image build, the build sat silent until the 30-minute job limit, which cost a full run and a manual restart (three times so far).

After: The download gives up after five minutes, cleans its cache and tries once more, and each fetch has its own timeout and retries.

2026-10-09: Sales pipeline and user refusals in German, French and Italian

Before: When a sales opportunity, a CRM task or a change to users was refused, the message stayed in English even in German, French or Italian.

After: These refusals are shown in the person's language and say what to do. Examples are winning a deal whose customer a second person has not activated yet, a close date in the future, a deal that is already won, inviting someone who is already a user, removing your own admin permission, and sending a password e-mail to someone who signs in with Google. The refusal for submitting a document that is not a draft now also says that only a draft is submitted.

2026-10-09: CSV bank statements without a currency column follow the account

Before: A CSV statement without a currency column was read as Swiss francs, so importing it into a euro, dollar or yen bank account was refused as a currency mismatch.

After: Such lines take the currency of the bank account the file is booked on. A file can still name its own currency, in a column or as default_currency.

2026-10-09: Account names in the language you read

Before: Account names showed in the language of the company's chart, whatever language you work in: a Swiss company's budgets, reports and pickers listed "3200 Handelserlöse" to someone who reads English.

After: Every account of the shipped charts (Swiss KMU, German HGB, IFRS, US, UK, Canada) has its name in English, German, French and Italian, and the screens show the one in the language you picked: account pickers, the chart of accounts, trial balance, general ledger, budgets and forecasts, year-end close, bank statements, Bank Accounts, VAT return details, exchange exposure, analytics and consolidation. The company's own name stays what archive exports, documents and filings print, and the chart of accounts shows it under the translation. When you add or edit an account you can give its name in the other languages too.

API: name follows Accept-Language (en, de, fr, it) where a translation exists; official_name and names are new on the account, and names on adding and changing one. See ledger.

2026-10-09: Pay intercompany loan interest on screen

Before: Interest accrued on an intercompany loan could only be paid through the API.

After: A loan with open interest has a "Pay interest" button on the Intercompany loans panel. The amount starts at what is open; the payment posts in both companies, and the server refuses more than is open.

2026-10-09: Budget, forecast and invoice refusals in German, French and Italian

Before: When a budget or forecast, or a sales invoice that bills a revenue contract or a bill with a QR reference, was refused, the message stayed in English even in German, French or Italian.

After: These refusals are shown in the person's language and say what to do. Examples are a plan name that is taken, a plan line for an account that is not revenue or expense, two amounts for the same account and period, an older plan version approved over the current one, a QR reference without a QR-IBAN, and a credit note line that does not name the contract obligation it credits.

2026-10-09: Intercompany loan amounts follow the currency's decimals

Before: A loan facility, pay-out or repayment through the API could carry more decimals than its currency has (for example JPY 1000.50), which would have posted fractional yen in both companies.

After: Such amounts are refused with a clear message, in the loan request, the pay-out and the repayment.

2026-10-09: Intercompany refusals in German, French and Italian

Before: When an intercompany relationship, loan, group dividend or mirrored invoice was refused, the message stayed in English even in German, French or Italian.

After: These refusals are shown in the person's language and say what to do. Examples are two companies without an active relationship, a loan payout over what is left of the facility, a repayment larger than the principal, interest that is already accrued, a dividend the shareholder has not accepted yet, a VAT group across two countries, and a request that already waits for a second person.

2026-10-09: Revenue contract and subscription refusals in German, French and Italian

Before: When a revenue contract, a subscription, a recurring invoice or a revenue recognition run was refused, the message stayed in English even in German, French or Italian.

After: These refusals are shown in the person's language and say what to do. Examples are a customer a second person has not activated yet, a product with no selling price in the contract's currency, a service period outside the contract, terms that changed after the approval was requested, an earlier period whose revenue is not recognised yet, a subscription line for a product that is not recurring, and a billing date in the future.

2026-10-09: Intercompany loans on the Intercompany page

Before: Loans between two companies of the group, their pay-outs, interest and repayments could only be handled through the API, so nobody could see a loan's principal and open interest on screen or move it forward.

After: The Intercompany page has an "Intercompany loans" panel listing each loan with its terms, facility, principal outstanding and open interest. Administrators request a loan (lender, borrower, currency, amount, fixed rate and why it is at arm's length, day count, dates, both bank accounts, and whether it is part of the net investment in a foreign operation); a second person approves the terms in Approvals. For an active loan, people who may post can pay out, accrue interest through a day, and repay; each step is posted in both companies at once. In German, French and Italian too.

2026-10-09: Year-end close and setup refusals in German, French and Italian

Before: When closing, reopening or appropriating a year, closing earlier years, adding a company, adding missing accounts or opening a fiscal year was refused, the message stayed in English even in German, French or Italian.

After: These refusals are shown in the person's language and say what to do. Examples are a year that is closed or being closed, a date before the close of earlier years, an appropriation larger than what is left of the result, a dividend account that doesn't fit, reopening a year whose result is already appropriated, a company code that is taken, and a fiscal year that doesn't fit its calendar. The unexplained difference on a VAT return now names its currency.

2026-10-09: Request, suspend and resume intercompany relationships on screen

Before: An intercompany relationship between two companies of the group could only be requested, suspended or resumed through the API, so the Intercompany page said to use the API.

After: Administrators request a relationship on the Intercompany page (the two companies, the transfer-pricing method, the mark-up for cost plus, the pricing basis and the date it was documented). A second person approves it in Approvals before trade between the two starts. Administrators can also suspend an active relationship with a reason, and ask a second person to approve resuming a suspended one. Mirror documents stay drafts, differences up to 1.00 count as agreed and no VAT group applies; other terms are still set through the API. Everyone else sees the relationships as before. In German, French and Italian too.

2026-10-09: Set up a consolidation group on screen

Before: A consolidation group, and each subsidiary in it, could only be created through the API, so the Consolidation page showed "No consolidation group yet" with instructions for developers.

After: Administrators create a group on the Consolidation page (name, parent company, reporting framework; the reporting currency is the parent's) and add each subsidiary with its ownership share, the reason the parent controls it and the acquisition and consolidation dates. A second person approves every subsidiary in Approvals before it counts. Everyone else sees the structure as before. In German, French and Italian too.

2026-10-09: VAT return refusals in German, French and Italian

Before: When computing, adjusting, finalising, amending or discarding a VAT return, or saving the VAT settings, was refused, the message stayed in English even in German, French or Italian.

After: These refusals are shown in the person's language and say what to do, for example a box that can't be adjusted, an adjustment without an offset account, a return that is no longer a draft, books that changed since the return was computed, a difference on the VAT accounts without an explanation, a settlement dated before the period ends, and a VAT method that doesn't fit the company's country.

2026-10-09: A Subscriptions page for recurring sales

Before: Subscriptions (what a customer signs up to for a term and is billed for each period) could only be created, approved, cancelled and billed through the API.

After: Sales > Subscriptions lists the company's subscriptions with their term, billing, term amount, next invoice and status. People who can draft create a subscription as a draft (customer, start, term as a whole number of billing periods, monthly, quarterly or yearly, in advance or in arrears, automatic renewal, and the recurring products with quantities), ask for its terms to be approved, cancel a draft or a request waiting for approval with a reason, and bill the periods that are due. A second person still has to approve the terms in Approvals before anything is billed, and each invoice stays a draft for a person to submit. Active subscriptions show the periods billed with a link to each invoice. In German, French and Italian too.

2026-10-09: Period close and chart of accounts refusals in German, French and Italian

Before: When closing or reopening a period, ticking off a close task, opening a fiscal year or adding an account was refused, the message stayed in English even in German, French or Italian.

After: Eighteen of these refusals are shown in the person's language and say what to do, for example a period whose close checklist still fails, a period that is already closed, reopening without a reason, a close task already done, a fiscal year that is already open, and an account number that is taken.

2026-10-09: Depreciation books on the Fixed assets page

Before: The accounting book and the tax books of a company could only be listed, added or changed through the API, so nobody could set up a tax book or change the tax rate used for deferred tax on screen.

After: Fixed assets has a "Depreciation books" panel listing the accounting book and every tax book with its tax rules and deferred-tax rate. Administrators add a tax book (Switzerland, Germany, United States or United Kingdom rules, optionally carrying deferred tax at a rate, every period or only at year end) and change a tax book's name or rate; everyone else sees the list. A tax book posts nothing itself. In German, French and Italian too.

2026-10-09: A Sales pipeline page for deals and follow-up tasks

Before: Deals, their stages and the follow-up tasks about them (the CRM) could only be used through the API, so nobody could see the pipeline or keep it up to date on screen.

After: Sales > Pipeline shows the open deals by stage and currency (amount and amount weighted by the chance of winning, never converted between currencies), the deals themselves and the open tasks with the late ones marked. People who can draft add a deal for a customer or a new prospect, move it to another stage (a lost deal needs a reason, a won deal needs a customer and is final), add a note, call, meeting, e-mail or task to it, and mark a task done. Nothing is posted to the books from this page. In German, French and Italian too.

2026-10-09: Fixed asset refusals in German, French and Italian

Before: When registering, depreciating or disposing of a fixed asset was refused, the message stayed in English even in German, French or Italian, and some named internal field names.

After: Fourteen of these refusals are shown in the person's language and say what to do, for example an asset number that is already taken, depreciation that has already run for the month, a sale without the account for its proceeds, an asset that is already disposed of, and a disposal that waits for a second person.

2026-10-09: Close schedule and own close tasks on the Period close page

Before: Who owns the month-end close, the due day of each step, what the daily close job may run and the company's own close tasks could only be set through the API.

After: The Period close page has a "Close schedule" panel: pick the owner, set the working day each step is due, and tick which steps the daily close job may run (it prepares and records, and never approves or closes anything). Below it, "Own close tasks" lists the company's own month-end steps; add one with a due day, or take it out of the close and put it back later. Administrators change them; everyone else sees them.

2026-10-09: Journal entry refusals in German, French and Italian

Before: When a journal entry or a reversal was refused, the message stayed in English even in German, French or Italian, and some named internal terms.

After: Sixteen of these refusals are shown in the person's language and say what to do, for example an entry that does not balance, a date with no open fiscal year, an inactive account, a control account booked by hand, an entry that was already reversed, and a reversal without a reason.

2026-10-09: Budget and forecast against what was posted

Before: Comparing a budget or forecast with the books was only possible through the API.

After: A plan with amounts shows "Plan against actual" under its amounts: plan, actual, the variance in money and percent, and in words whether it is better or worse than plan, per account or report line, with revenue, expenses and net income added up. Below it, the outlook for the year (actuals of closed periods plus the plan for the rest). Open periods are not final.

2026-10-08: Enter budget and forecast amounts on screen

Before: A new budget or forecast draft could only be filled through the API, so the screen showed an empty draft.

After: On a draft, Reports > Budgets and Forecasts shows a grid of revenue and expense accounts by period. You type the planned amounts, add or remove accounts, and save. Amounts keep the currency's decimals, and an amount that is not a number blocks saving with a plain message.

2026-10-08: Purchase order and bank refusals in German, French and Italian

Before: When a purchase order, goods receipt, bill match, bank statement import or bank line was refused, the message stayed in English even in German, French or Italian.

After: Fourteen of these refusals are shown in the person's language and say what to do, for example a purchase order that is no longer a draft, receiving more than was ordered, a bill with no order number, a statement imported twice or in another currency, and a bank line that was already matched.

2026-10-08: Budgets and forecasts pages

Before: Budgets and rolling forecasts could only be created, submitted and approved through the API, so nobody could see a plan or work through its approval on screen.

After: Reports > Budgets and Forecasts lists the plans and creates a budget (for a fiscal year) or a forecast (a number of periods ahead). Opening a plan shows its versions with revenue, expenses and net, and the lines of a version as accounts by period. People who can draft start a new draft (empty or copied from a version), roll a forecast forward from actuals, submit a draft and send a submitted version back with a reason. A person with the approve scope approves a submitted version; the person who prepared it cannot, and the server says so. Entering the lines and the plan-against-actual comparison are still through the API and come next.

2026-10-08: A Revenue contracts page

Before: Revenue contracts (what is promised to customers, what each promise is worth, when it becomes revenue) could only be read and acted on through the API.

After: Sales > Revenue contracts lists the contracts. Opening one shows its promises with their own price and share of the contract price, and the schedule of when each part becomes revenue and whether it is booked. People who can draft ask for approval of a draft contract (a second person approves it on the Approvals page), cancel a draft or pending one with a reason, and record that a point-in-time promise was delivered, with the day and proof. Creating a contract still goes through the API.

2026-10-08: A Products page for what you sell and its prices

Before: Products and their list prices could only be created and changed through the API, so a clerk could not see what the company sells or set a price.

After: Sales > Products lists the company's products with the price in force today in each currency. People who can draft add a product (with an optional first price), add a new price from a day, and stop or resume selling a product. A price is never changed: a new price from a later day takes over from that day, and the price history is shown when adding one. Standalone selling prices for revenue contracts are still set through the API.

2026-10-08: Invoice and payment refusals in German, French and Italian

Before: When a payment, credit note or invoice line was refused, the message stayed in English even in German, French or Italian, and it named the rule rather than what to do.

After: These refusals are shown in the person's language and say what to do: payment amounts that don't add up, an account that isn't a bank account, a document the payment can't settle or lists twice, a document in another currency, an amount above what is still open, a credit note for another customer or for a document not yet posted, a credit note above what is still open, an inactive product, a negative total, and a vendor invoice recorded twice.

2026-10-08: Add missing accounts from the chart of accounts

Before: A company created before a required account existed (for example the cash discount accounts) could only get it through the API or a command line.

After: Administrators see an "Add missing accounts" button on the chart of accounts. It adds any account the program relies on that the company lacks, using the company's chart, and says what it added, or that nothing was missing. If a code is already used for something else, it names the code instead of guessing.

2026-10-08: The approval policy in plain words

Before: Which actions need a second person, which are refused and what agents may never do was only readable through the API as a decision table.

After: Settings > Approval policy shows the policy in force as a list of rules (who, what, only when, decision, why), says whether it is the built-in one or the organization's own, and lists every change with who made it and when. Changing the policy stays an administrator's API call.

2026-10-08: Matching tolerances on the purchase orders page

Before: How far a bill may differ from its order and receipt (percent, most per line, most per bill) could only be changed through the API.

After: Purchasing > Purchase orders has a "Matching tolerances" panel under the list. Administrators change the three limits and save; others see them. Until they are saved, the panel says the built-in limits apply.

2026-10-08: API keys screen

Before: API keys for programs and AI agents could only be issued, listed and revoked through the API.

After: Settings > API Keys (administrators) lists the keys with their scopes, last use and status. "Issue API key" asks for a name, whether the key is for an AI agent or a person, and what it may do (an agent key cannot approve, export the archive or administer). The secret is shown once, with a Copy button. A key can be revoked after a confirmation.

2026-10-08: VAT settings on the VAT returns page

Before: How a company files VAT (method, return period, recapitulative statement, tax number, settlement account, rate for foreign-currency VAT) could only be changed through the API, and the page showed it read-only in the subtitle.

After: Finance > VAT returns has a "VAT settings" panel. Administrators change the settings and save; others see them. The panel offers only what the company's country has (for example the effective and net tax rate methods in Switzerland, the ECB daily rate option in Germany). Approved net tax rates stay as they are.

2026-10-08: Open the next fiscal year from Period close

Before: A fiscal year could only be opened through the API, and closing a year did not open the next one. A company that never called it could not book anything once the last period ended.

After: Finance > Period close shows an "Open 2027" button (for administrators) that opens the next year with the same calendar, and a warning appears from 60 days before the last period ends.

2026-10-08: Common refusals in German, French and Italian

Before: When the ERP refused an action, the screen showed the server's English sentence, even in German, French or Italian.

After: The refusals people meet most often are shown in their language and say what to do: a closed period, a journal entry or document that is no longer a draft, an amount with too many decimal places for its currency, a fiscal year not yet opened, and a file that is too large. Other refusals still read in English; more follow area by area.

2026-10-08: Rule messages say what to do, in plain words

Before: Submitting a bill of a vendor that still waited for activation showed a red "Something went wrong" box with the vendor's code and an API path, and an amber box below repeated the same fact. Other rule messages (closed years, missing accounts, intercompany links) also carried API paths.

After: A bill or invoice of a party waiting for activation says so once, by name, in your language, with links to the vendor (or customer) and to Approvals; Submit stays off until a second person activates it. A broken rule is headed by what it is ("This can't be done yet", "Please check this") instead of "Something went wrong", and no message shows an API path. Agents still get the call that resolves a refusal, in the error's new hint field.

2026-10-08: Exchange rates for a new company

Before: A company with nothing in a foreign currency yet saw an empty Exchange Rates table, and on a weekday before the ECB's afternoon fixing the page said the ECB had published no rates because of "a weekend or a holiday".

After: Such a company sees the main currencies (EUR, USD, GBP, CHF, JPY other than its own) until it uses others. The note says why a date has no fixing: not published yet, a weekend, an ECB holiday, or not loaded.

2026-10-08: Yen and dinar amounts print with their own decimals

Before: Invoices, e-mails and statements printed every amount with two decimals, so JPY 1000 read "1'000.00" and a KWD 1.235 was cut to "1.24". A statement with several currencies used the first currency's decimals for all.

After: Amounts print with the decimals of their currency (none for yen, three for dinars), each currency block of a statement in its own. The e-invoice XML is unchanged and still uses two decimals.

2026-10-08: A payment run pays each currency from its own bank account

Before: A payment run with bills in more than one currency paid them all from one bank account. When that account took only one currency (the CHF account of a company that also has a EUR account), the whole run failed with a posting error.

After: Unless the run names an account, each bill is paid from the main bank account when it takes the bill's currency, otherwise from the bank account in that currency. If there is none, the run says so in plain words before anything is paid.

2026-10-08: An invoice names the bank account in its own currency

Before: A company with a CHF and a EUR bank account could print a EUR invoice, or export it as an e-invoice, with the first account's IBAN, whatever the invoice currency.

After: The invoice PDF and the e-invoice name the bank account in the invoice's currency, and fall back to the main account only when there is none.

2026-10-08: Failed downloads and server errors are explained in plain words

Before: A download that failed (QR-bill PDF, original document, attachment, payment file, import template) saved or showed the server's error text as if it were the file. A server error or a lost connection showed "Bad Gateway" or "Failed to fetch". A document list that failed to load said "no documents".

After: A failed download shows the reason next to the link. Lost connections, sign-outs and server errors are explained in plain words, an unexpected server error carries a request id to quote, and a failed document list shows the error instead of looking empty.

2026-10-08: A deleted organization can be undone for an hour, then goes within the hour

Before: an organization deleted in the operator portal stayed until the next deploy, which could be hours or days, and the deletion could not be undone.

After: for one hour after Delete, its page offers Undo delete, which makes it (and the test copies deleted with it) reachable again. After the hour it is deleted for good by a job that runs every hour, so it is gone one to two hours after the click. A test copy its administrator removed goes the same way. API: POST /v1/platform/organizations/{org_id}/undo-delete (an operator signed in to the portal; 409 after the hour) and delete_after on organizations (ADR-0042).

2026-10-08: Disabling a user also revokes the API keys they issued

Before: Disabling a user ended their sessions, but API keys they had created (and keys those created) kept working.

After: Disabling a user also revokes those keys, and the change history records it.

2026-10-08: Company bank accounts are added and edited on screen, and a new IBAN needs a second person

Before: a company's IBAN and QR-IBAN could only be registered through the API, so an administrator whose invoices could get no QR-bill (no bank account in the invoice's currency) could not fix it in the app.

After: Settings > Bank Accounts lists the company's bank accounts with their IBAN, QR-IBAN, BIC and account holder. An administrator adds one (a bank account of the chart, or a new one with its number, name and currency, such as an EUR account for the QR-bills of EUR invoices) and edits its details; others see them. A new bank account after the company's first, or a new IBAN or QR-IBAN, applies only after a second person approves it in Approvals, which shows the old and the new details; until then the row says a change waits. The message of a QR-bill that cannot be made points there. API: PUT /v1/entities/{entity}/bank-accounts/{account} answers status pending_approval and an approval_id for such a change, and the list shows both for an account whose change waits; new policy action change_company_bank_details (ADR-0043).

2026-10-08: The operator deletes an organization in the portal

Before: deleting an organization (other than a test copy) took two commands in Cloud Shell.

After: an organization's page in the operator portal has Delete. The operator types its exact name and, when it has been used, ticks that its books go with it. Its test copies go with it. Nobody can reach it from then on, and the next deploy deletes it. API: POST /v1/platform/organizations/{org_id}/delete (an operator signed in to the portal, never a platform key) and remove_requested_at on organizations; the job command is now erp platform remove-organizations (ADR-0042).

2026-10-08: EUR invoices of Demo AG get a QR-bill, and a QR-bill that cannot be made is explained

Before: "Show QR-bill" on an EUR invoice of Demo AG refused, because its Swiss company had only a CHF bank account; the message named an API path, and "Download PDF" (in the dialog and on the invoice) still opened a page of raw error text.

After: Demo AG's Swiss company has an EUR bank account (1021 Bankguthaben EUR, with a test IBAN and QR-IBAN), so its EUR invoices get a QR-bill paid to that account; the demo seed creates it too. When no QR-bill can be made, the message says in plain words what is missing, the dialog offers no PDF, and the invoice's QR-bill PDF button shows the reason on the invoice instead of opening an error page.

2026-10-08: A bill that names a different bank account is flagged before payment

Before: a vendor bill whose payment account differed from the vendor's stored IBAN (an imported e-invoice, a scanned bill or a typed one) was paid to the account on the bill, and the payment proposal showed no warning.

After: the payment proposal line carries a warning naming both accounts ("the bill names ... not Vendor's IBAN on file ...; check the account before paying"), and an e-invoice import adds the same warning to its result. The payment still follows the bill's account; a person decides.

2026-10-08: A new customer sets up its own books at the first sign-in, and gets Production and Test

Before: the operator chose a new customer's country, currency and chart of accounts in the operator portal, and the customer had only Production until an administrator made a test copy.

After: the operator enters only the organization's name and its administrator. At the first sign-in the administrator answers a short setup wizard (country, state or province in the US and Canada, accounting standard, currency, fiscal year, tax number and return period, address), and Production and a Test copy of it are made from the answers. Supported: the EU, the United Kingdom, Switzerland and Liechtenstein, the United States and Canada, with new charts of accounts for US GAAP, UK FRS 102 and Canada (ASPE or IFRS). API: GET /v1/setup/options, GET /v1/setup, POST /v1/setup; entities is now optional in POST /v1/platform/organizations (ADR-0041).

2026-10-08: An asset code used in two companies is no longer guessed

Before: two companies of one organization could each register an asset with the same code (for example LAPTOP-01). Disposing of, reading or adding a tax book to "LAPTOP-01" through the API picked one of them at random, so the wrong company's asset could be written off.

After: when a code exists in more than one company, the request is refused with a clear message to use the asset's id instead. Asset ids always pick exactly one asset.

2026-10-08: "Unusual entries" looks at every entry of the period, not the first 150

Before: the review of risky entries (manual, created by an agent, round or large amount, weekend, reversal) only examined the 150 oldest entries of the period or range. In a busy month, a risky entry dated late in the month was never listed, and the close review and the insights said "nothing unusual".

After: every posted entry in the period or range is examined. The insights still show a limited list, with the full count beside it; the close review agent and the AI (MCP) tool get the 150 biggest flagged entries first.

2026-10-08: The close no longer says "reconciled" when bank lines were ignored or never compared

Before: a bank line set aside as "ignored" was not counted as open, so "Bank statements reconciled" passed while that payment was outside the ledger. A CSV statement has no closing balance, yet it counted as a passed "Statement balances equal the ledger" check.

After: both checks show a warning instead of a pass. The first names the ignored lines and their total, the second names the statements that had no closing balance to compare. A warning does not block the close; a mismatch or an unmatched line still does.

2026-10-08: The Approvals page shows the old and the new bank details

Before: a request to change a supplier's bank details appeared on the Approvals page as "Change bank details for ACME". The approver could approve it without seeing the new IBAN, which was only on the supplier's card.

After: the request in the list and in the approve dialog shows the old and the new IBAN side by side. Changes to other party details (name, VAT number, country, currency) show the same way.

2026-10-08: A new sign-in on the same browser tab no longer shows the previous person's data

Fixed (from the production-readiness review). When a session expired and someone else signed in on the same tab, lists and reports the first person had opened could appear for a moment before the new data arrived, and stayed if a request failed. The screen now forgets everything it had loaded when a session ends or a new person signs in (ERP and operator portal).

2026-10-08: CSV exports can no longer run a formula in Excel

Fixed (from the production-readiness review). A text cell in a CSV export that starts with =, +, - or @ (for example a supplier's booking text) now gets a leading quote, so a spreadsheet shows it as text instead of running it. Amounts keep their sign. The file also starts with a UTF-8 marker so Excel shows umlauts correctly, and the download is no longer cancelled early by some browsers.

2026-10-08: Payments refuse amounts with too many decimals

Fixed (breaking bug fix, from the production-readiness review). A payment, an allocation or a discount with more decimals than the currency allows (for example 100.004 CHF) is now refused with a 422. Before, the ledger booked the rounded amount while the invoice kept the raw one, so the open amount of the invoice drifted from the control account and could never be settled exactly.

2026-10-08: One unbookable bank line no longer stops a statement import

Fixed (from the production-readiness review). When a statement line could not be matched automatically, for example because its period is closed or there is no exchange rate for the day, the whole import failed. Now that line stays unmatched with the reason stored on it, and the rest of the file is imported and matched. A camt entry without a booking date uses its value date.

2026-10-08: Bad bank statement files are refused with a clear 422

Fixed (breaking bug fix, from the production-readiness review). A camt or CSV statement with an unreadable amount or date, NaN or an absurd amount, or an invalid csv_mapping now answers 422 naming the problem (CSV: the line). Before, these answered a generic 500. A camt entry with neither a booking date nor a value date is refused; with only a value date, that date is used. Before, it was booked on the day of the upload, which decided the period.

2026-10-07: One bank movement can no longer settle two documents

Fixed (from the production-readiness review). Matching, posting or auto-matching a bank line now locks the line: a second request on the same line waits and then answers 409 "already matched". Before, two requests at the same moment (a person and the close job) could each post a payment for one bank movement.

2026-10-07: Closing a period no longer races with postings

Fixed (from the production-readiness review). Closing a period now holds it while the close checklist runs, and posting an entry waits for a close in progress. Before, an entry dated in the period could commit just after the close, changing the figures of a closed period. Posting into a closed period now answers 409 period ... is closed from the application, as drafting already did.

2026-10-07: Business journeys tested end to end; a new test copy shows in the picker at once

Added

  • Feature map: features.md lists the main features in plain words, where each is described and which tests prove it.
  • Business journeys: browser tests (ui/e2e/journeys.spec.ts) clone a real organization into a masked test copy and, in it, sell and get paid through the bank, buy from a new vendor a second person approves and pay, check that the reports moved by exactly those amounts and that Production did not move, then refresh and remove the copy.

Changed (bug fix)

  • Test copies: a copy just made appeared in the environment picker only after up to a minute or a reload; it now appears at once, and a removed one leaves it at once. The message after making a copy points to the picker instead of saying to sign out.

2026-10-07: Manual journal entries refuse system fields

Changed (breaking bug fix, from the production-readiness review). A manual journal line that carries a VAT tax tag or a subledger_type/subledger_id link is refused with 422. Those fields are written by the sales, purchase and other documents; a forged one would have fed the VAT report or a subledger link.

2026-10-07: All documentation in one place

New. docs/README.md is the one place all documentation starts. It leads to features, a plain-language list of everything the ERP does with its rules, its guide and its API reference, and to new guides for test copies and environments and bill capture. CI fails when an API area has no row on the features page, a link there is broken, or a page under docs/ can't be reached from the home.

2026-10-07: Exact line amounts and exchange rates in the ledger

Changed (breaking bug fixes, from the production-readiness review).

  • Rates: a manual exchange rate is rounded once to ten places before it is stored and hashed. Before, a payment settling part of a foreign-currency invoice could store a rate that differed from the hashed one, and the integrity check then reported "content hash mismatch".
  • Amounts: a line amount with more decimals than its currency allows (100.005 in CHF) is refused with a message instead of being rounded silently; an amount of 10^14 or more is refused instead of failing in the database. A rate of zero or less is refused.
  • Rounding allowance: the FX rounding that is booked to the rounding account is five minor units of the functional currency (0.05 for CHF, 5 for JPY, 0.005 for KWD), so entries in a zero-decimal functional currency post.

2026-10-07: Changes show what changed

Changed (web app, additive in the API).

  • Changes panel: each entry in the Changes of a customer or vendor, and in the history of a journal entry, invoice, bill or purchase order, opens to show every changed field before and after, the starting values of a new record, who requested and who approved a change, and the reason or comment given. The row itself names the fields changed.
  • Audit data: customer.create and vendor.create events carry the party's starting values in data.new; a changed default account is recorded by its code (default_account), not its id. document.update carries the changed header fields and lines in data.previous and data.new. A masked test copy shows these values (masked) like the rest of history.

2026-10-07: The ERP at its own address

Added. The ERP can be reached at https://erp.grunte.com (Terraform erp_domain, through a load balancer with a Google-managed certificate) as well as at its run.app address. Failed sign-ins are still counted per real client address on both (ERP_LOAD_BALANCER_HOSTS).

2026-10-07: Typed amounts are never guessed

Changed (bug fixes, from the production-readiness review).

  • Screens: an amount typed with a decimal comma and too many places (0,125, 1,2345) was read as thousands (125, 12345). It is now refused. In a price or quantity, 1,234 could be 1.234 or 1234, so it is refused too; write 1.234 or 1234. In an amount, 1,234 still means 1234.
  • Currencies with other decimals: payments, bank matching and the General Journal now use the currency's own places (0 for JPY, 3 for KWD and BHD) instead of always two.
  • Files: a value such as 12.500 or 1.000 in an import is refused (a German spreadsheet means 12,500); write 12500.00, or 12.50 for twelve and a half.

2026-10-07: Safer bank import and matching

Changed (breaking bug fixes, from the production-readiness review).

  • Reference matching: a document number or a vendor's invoice number must stand as a whole word in the bank text (123 is no longer found in RE 2023-1234). A counterparty name matches only when it equals the party's name or starts it with at least six letters.
  • Control accounts: posting a bank line straight to any control account (receivables, payables, intercompany) is refused; settle documents with a match.
  • Right account: a statement that names an IBAN is refused when the account it would go to is registered with another IBAN, and when no account carries the IBAN and the entity has more than one bank account (register the IBAN or pass account).
  • Overlapping exports: a bank line without a bank reference (every CSV line) counts as already imported when the account holds as many identical lines (date, amount, counterparty, reference, text); an overlapping export imports only the new lines.
  • Control accounts: POST /v1/bank-lines/{id}/post refuses a control account (receivables, payables, fixed assets, ...) and the bank account itself; an account with another control role is no longer accepted as a bank account for payments, statements or an IBAN registration. An extra bank account is a control account without a role that has an IBAN registered.

2026-10-07: A vendor made from a bill is never made twice

Fixed (bill upload).

  • After a vendor is created from an uploaded bill, going back to it (for example to take the name and address the VAT register returned) changes that vendor: the form says it was created from this bill, keeps its number, and "Save vendor" saves the changes. Before, the form offered to create it again and answered that the vendor already exists.
  • When a vendor with the proposed number exists already (created elsewhere, or before the file was read again), the form names it and offers "Use this vendor" for the bill, instead of an error.

2026-10-07: Bill upload never looks stuck

Fixed (ADR-0035).

  • Reading an uploaded bill gives up on a model that does not answer within 45 seconds and asks the fallback model (before: 120 seconds), so a read ends within about 105 seconds.
  • The upload screen counts the seconds while a bill is read and says when it takes longer than usual. An answer that has not come two minutes after the file was sent ends the read as failed, with "Read the file again" offered, instead of an endless "Reading the bill…".

2026-10-07: Demo AG is the test environment's production organization

Changed (migration 0055). The sample company "Demo AG" is no longer a demo: it keeps all it holds, is shown as Production (no test banner), is never deleted by the demo purge, and its test copies are made from it as from any real organization. Sample data is for test organizations, so it is no longer offered there. The change is in the operator's audit as org.mark_customer.

Changed (operator portal). The Organizations list shows each customer and demo with its test copies under it ("Test copy of Gruntech"), and its tiles count organizations, not their copies: a copy is an environment of its organization, not one more demo. A copy is linked to the organization it was made from when it is made and the link never changes, so every copy is listed under the right organization; a demo made by the operator (not a copy) stays on its own line.

2026-10-07: Test copy follow-ups

Changed (ADR-0040, migration 0054).

  • A copy's link asks before it switches: "Open another environment?" names where it goes (a test copy or Production) and where the person is now; "Stay here" keeps them there.
  • Setting a copy's refresh schedule, even to the one it has, makes the person who sets it its scheduler: another administrator takes over a schedule its setter may no longer run.
  • A refresh records the access of a copy's people as invitations only when it is new to the copy (a new person, or other scopes), not again every night.
  • refresh_problem holds only the codes the API documents.

Added (ADR-0040 point 9).

  • Copy link (Settings, Test copies): each copy has its own link, the app's address with ?org=<copy id>. It opens the copy itself, signing in there or switching to it; only the copy's own people get in.
  • Testers: in a masked copy, an administrator invites people as in any organization (Settings, Users). Someone who is not one of the real organization's people reaches the copy only, never the real organization, and stays through every refresh. An unmasked copy has the real customers and bank accounts: inviting such a person into it answers 409.
  • A refresh keeps a person disabled in the copy disabled.

2026-10-07: Masked copies mask more

Changed (ADR-0040 point 7, migration 0053). A masked copy also masks a customer's buyer reference (it often names a person), a house number in a delivery address, and a partner's name in an entry's tags under any tag name. Only the system registers a copy, of the organization it is made in, and marks it masked or complete: the API's database role can no longer write the source, either flag or a copy's registry row itself.

2026-10-07: Refresh a test copy

Added (ADR-0040 point 8, migration 0053).

  • Refresh now (Settings, Test copies; POST /v1/test-copies/{copy_id}/refresh): replaces everything in a test copy with a fresh clone of the organization as it is now, after a warning that the work done in the copy is lost. The copy keeps its name and its place in the environment picker, a masked copy is masked again, and its books are checked against the real ones as when it is made. The list of copies shows when each was last cloned.
  • Refreshed by itself (Settings, Test copies; PUT /v1/test-copies/{copy_id}/schedule): each copy can be refreshed every night or every week (default: off). The refresh happens with the daily job at 05:00 UTC and is the same as "Refresh now": whatever was done in the copy since is lost. When a scheduled refresh could not be done (the books could not be copied, or whoever set the schedule is no longer an administrator), the list of copies says so under its date, with the reason, until the copy is refreshed again (refresh_problem and refresh_problem_at).

2026-10-07: A test copy is a clone of Production

Changed (ADR-0040, migration 0052).

  • A new test copy (Settings, Test copies; POST /v1/test-copies) now carries everything of the real organization, exactly as it is: its books (every journal entry, invoice, bill, payment, asset, bank statement and VAT return), contracts, subscriptions, plans, closes, stored files and audit trail, besides the setup and the people. The copy's books are checked before it is made: every hash chain verifies and every account of every company has the real balance in every currency; otherwise the answer is 409 and nothing is made. API keys, sessions, the security log and exports stay behind. Work goes on in the copy as in the real books: closed periods stay closed, approvals waiting there can be given in the copy.
  • Masked copies, for testing with people who should not see the real customers and vendors: tick "Mask customers and vendors" ("masked": true). Their names, e-mails, e-invoicing addresses, streets, IBANs and contacts, the prospects of the CRM and the counterparties of bank lines become mock values (Customer 001, contact-001@example.com) everywhere in the copy; notes about them, remittances and the descriptions of journal entries typed by hand (such as salaries) become "(masked)", and stored files placeholders. The audit trail and approvals keep only what happened (amounts, codes, dates, statuses), not about whom, and a change of a partner's bank details still waiting arrives rejected. Codes, references, currencies and actions stay as they are, whatever a partner is called. Amounts, your own setup (companies, bank accounts, chart of accounts, products, settings), tax data and the books stay exactly the same, and so does a partner's name that the setup itself holds (such as an account named after a customer, or a partner's code); the people keep their sign-in. The list of copies shows which are masked.
  • A copy being removed no longer counts towards the five, and each update now removes every copy asked for (no longer one per update), so that none piles up.

2026-10-06: Test copies

Added (ADR-0039, migration 0050).

  • An administrator makes a test copy of the organization (Settings, Test copies; POST /v1/test-copies, listed by GET /v1/test-copies): a new test organization with the companies, fiscal years and periods, chart of accounts, VAT and purchasing settings, bank accounts, customers, vendors, products and prices, approval policy and people of the real one, and empty books. Its people find it in the list of organizations when they sign in. At most five copies at a time; a test organization is not copied again; agents cannot make one. Each person copied is recorded as invited.
  • A test organization (a test copy or a demo) shows a banner on every page and is marked at sign-in (test_organization on GET /v1/me and on each organization to choose).
  • A test copy has the real customers, vendors and bank accounts, so nothing that could reach them or a bank leaves it: e-mailing an invoice, the QR-bill, ZUGFeRD and XRechnung, and payment files answer 409 (GET .../dispatches has sending_configured false and test_copy true), and every invoice and statement PDF is marked "test copy, not valid" on each page. A person disabled in the real organization can no longer reach its copies, and a copy is suspended while its source is.
  • The operator portal shows which organization a test copy was made from (copy_of); the operator removes copies with the existing demo purge.
  • In a test organization, an administrator adds sample data to the current company (Settings, Test copies, Add sample data; POST /v1/entities/{entity}/sample-data): sample customers and vendors, four invoices and two bills of this month, and two payments. A real organization answers 409.
  • An administrator removes a test copy (Settings, Test copies, Remove; DELETE /v1/test-copies/{copy_id}, migration 0051): nobody can sign in to it any more, it shows "Being removed", and the next update of the system deletes it with everything in it (one copy per update; since clones, every copy asked for). Until then it counts towards the five (no longer, since clones: a copy being removed does not count). Only test copies can be removed; GET /v1/test-copies has remove_requested_at.
  • An environment picker in the top bar (in the account menu on a phone) lists the real organization as Production and each of its test copies under the name it was given (for example Test or Dev); one click switches without signing out (GET /v1/auth/organizations, with copy_of; POST /v1/auth/switch). The new organization is checked as a sign-in with the same account would be; the new session ends when the previous one would have (a switch never extends a sign-in), and it is recorded as user.login with via: switch. It shows for a person in several organizations.

2026-10-06: Edit the chart of accounts on screen

Added (UI). Administrators add accounts on the Chart of Accounts page (New account) and rename, move to another report line or block an account from its card (Edit), through the existing POST and PATCH /v1/entities/{entity}/accounts endpoints with their guardrails.

Changed (POST and PATCH /v1/entities/{entity}/accounts, the accounts import). Each report line holds one type of account (the line vocabulary in seed/consolidation_frameworks.yaml), so a line that is unknown or holds another type is refused (422): a revenue account on "Other operating expenses" made the P&L section wrong, and before only bs. against pl. was checked. A new account also gets the rename's checks: a number of one word, a name of one line.

Changed (PATCH .../accounts/{code} with active: false). Deactivating is refused (409, naming why) for a control account; an account the books still carry a balance on (for a profit and loss account, a result in any fiscal year not closed to zero); an account that unfinished work names (an entry, invoice or bill not yet posted, an open purchase order, a draft payment, a recurring invoice that has not ended); and an account that a fixed asset, product, subscription, revenue contract, party, bank account, depreciation book, intercompany loan or the VAT settings post to. Before only a role was checked, so the year-end close, the depreciation run or a recurring invoice could fail on a blocked account.

2026-10-06: Invited people get their password e-mail

Changed (ADR-0037 amended).

  • Every invitation now has Google e-mail the person the link that sets their password, with no box to tick: a new customer's administrator and further people (POST /v1/platform/organizations), an administrator invited again from the operator portal (a fresh link), and a person invited inside an organization (POST /v1/users). Not sent when password sign-in is not configured, nor to an address of the deployment's Google Workspace domains. The answers say so (password_email_sent on each provisioned user, on AdminInvited and on the answer of POST /v1/users). The request field password_email is gone (it is ignored if sent).

2026-10-06: depreciation in currencies without minor units

Fixed (ledger). In a currency without cents, such as JPY, each year's scheduled depreciation was rounded on its own, so 1,000 JPY over three years booked 333 a year and left 1 yen on a fully depreciated asset. Each year now books what the schedule reaches by the year end, rounded to the currency, less what it reached at the start (333, 334 and 333), so the life books the full cost. Amounts in currencies with two or more decimals do not change.

  • Depreciation stops at cost less salvage rounded down to the currency's minor unit (a cost of 1,000.5 yen depreciates 1,000, not 1,001).
  • An asset that holds exactly cost less salvage no longer reads short of a year no run can book, which cleared a close blocker for a cost or salvage finer than the currency's minor unit (1,000.5 yen, or a salvage of 0.005 BHD).
  • Tax books in JPY count accumulated tax depreciation in whole yen (40,000, not 40,000.40).

2026-10-06: Subscriptions

New (subscriptions, ADR-0036 slice 4, controls SB1 to SB5).

  • Subscriptions (/v1/subscriptions): a committed term of recurring products, billed monthly, quarterly or yearly, in advance or in arrears, at the list price on the start day or a price of its own, less a discount. Created as drafts, an agent's too; never edited or deleted, cancelled while a draft.
  • Approval: POST /v1/subscriptions/{subscription_id}/activate always waits for a second person (new policy action approve_subscription, denied to agents), a rule the database holds too; approving activates the term's revenue contract, one monthly obligation per line. A subscription starts in an open period.
  • Billing run (POST /v1/subscriptions/run, a person's request): invoices each period due against the contract's obligations, once, as drafts or submitted with auto_submit; a period whose invoice date is in a closed period is invoiced on the first day of the next open one. A run's draft keeps its customer, lines, VAT and cash discount; a voided draft is made again. A failed period stops only its subscription and is retried by the next run, and a next period in a fiscal year not opened yet never stops a run. A fully billed term ends after its last day, once its invoices are posted, unless it renews.
  • Close: check subscriptions_billed (a warning): periods not billed, and run invoices not posted.
  • MCP: list_subscriptions.
  • Database: migration 0049 adds subscription, subscription_line and subscription_invoice.

2026-10-05: accuracy fixes in the ledger, VAT, depreciation and the close

Changed (breaking, bug fix) (ledger, year-end close, intercompany). Amounts were wrong; these answers change on /v1.

  • POST /v1/journal-entries/{entry_id}/reverse answers 422 when on is before the entry's date; a reversal waiting for approval with such a date fails when it is approved.
  • A trial balance or general ledger asked for without the year-end closing entries (closing=exclude) leaves earlier years closed: only the year asked about shows its income and expense before its close. Before, every earlier year's result reappeared.
  • A foreign-currency credit note of a document is booked at the rate the document is carried at (the rest of it when it credits all that is left), also where documents book at the official VAT rate, so nothing stays on the receivables or payables control account once the document is settled; its allocation's functional_amount changes with it. Its VAT still reads the official rate. A credit note ignores the party's cash-discount terms (it answered 500).
  • /v1/tax/determine and every document treat Switzerland and Liechtenstein as one VAT area (MWSTG Art. 3): a supply between them is domestic, not an export or import.
  • Depreciation is spread over the months of the fiscal year the asset is in its useful life, so it stops when the life ends (and after a disposal) rather than running at a twelfth of the year amount to December. Each run books what the year's schedule reaches by the period less what is posted in the year, so months booked under the old spread are caught up in the next run. A new close check, Depreciation matches the year's schedule, fails after the year's last run when an asset is short of its schedule or over it (run that period again: a top-up books the difference) or its disposal waits for approval; the year-end close is not proposed while it fails.
  • A disposal books the asset's depreciation up to the end of its period before derecognising it (or takes back what later runs booked after its date), and runs skip an asset whose disposal waits for approval, so the disposal month is no longer left on the accumulated depreciation of a disposed asset (or in the loss). A disposal answers 409 while a depreciation entry or an earlier disposal of the asset waits for approval, or its acquisition entry is not posted (runs skip such an asset too), and when depreciation of the asset is booked in a fiscal year after the disposal's. A difference from the schedule of less than the currency's minor unit (0.33 JPY) does not count.
  • Deferred tax answers 409 when an earlier period whose deferred tax is due has not run (at year end only, the previous year end), and its balances read only postings up to the period.
  • A repaid intercompany loan stays active until the interest from its last accrual to the repayment is accrued and settled.
  • The FX revaluation remeasures a receivables, payables or intercompany account with a currency once: per open document or loan, not again as an account. A line of no loan on an intercompany account (an opening balance) is remeasured with the account.
  • A cash discount under the net tax rate method corrects the VAT of the activity of the account it reduces, and a discount after a credit note corrects only the VAT still open.
  • The Swiss legal reserve is 5 % of the profit after the loss carried forward (OR 672).

2026-10-05: Billing and recognising revenue contracts

New (revenue contracts, ADR-0036 slice 3, second part, controls RC7 to RC10).

  • Billing a contract: a sales invoice or credit note line may name an obligation; it is booked to the contract liability and names the contract. Posting is refused when a contract would be billed more than its transaction price, or an obligation credited more than it was billed. A line that bills no obligation is never booked to a contract account, and a credit note of an invoice that bills obligations names them.
  • Revenue recognition run (POST /v1/periods/{period_id}/revenue-recognition, GET .../preview): recognises the schedule lines due by the period end and presents contracts that recognised more than they billed as a contract asset or an unbilled receivable, reversed the next period. Through the approval policy; may repeat while the period is open. Refused (409) while an earlier period is open with revenue due in it: run that period first.
  • Accounts: new optional control roles contract_liability, contract_asset and unbilled_receivable; the chart templates carry an account for each (KMU 2301, 1281, 1101; IFRS 2320, 1510, 1230; HGB 4290, 2130, 2101). Existing entities give accounts these roles before their first run.
  • Close: checks revenue_recognised, contract_position_presented, contract_control_reconciled; the year-end close waits for the last period's recognition.
  • MCP: preview_revenue_recognition; run_period_end takes the step revenue_recognition.
  • Database: migration 0048 adds revenue_recognition_run and trade_document_line.obligation_id.
  • Fixed: a credit note is checked again when it posts, not only when it is drafted: two credit notes drafted against one invoice could both post and credit it more than its open amount. The second is now refused when it is submitted or approved: 409 naming the open amount left, or, for an invoice that bills a revenue contract, 422 when its lines without an obligation would credit more than the invoice's other lines left.

2026-10-05: a way back from every record

Changed (UI only; UI).

  • Every record (a customer, an invoice, a journal entry, an asset…) has a Back arrow above its title, named after where it leads: the page it was opened from ("Back to C-HH Hamburg Handel GmbH" on an invoice opened from its customer), or its list when it was opened by its address. On a phone it reads "← Customers".
  • The breadcrumb names the list a record belongs to ("Sales › Customers › C-HH Hamburg Handel GmbH"), and the list is a link. Invoices and credit notes sit under Customer invoices, bills under Vendor bills.
  • Back to a list (by the arrow, the breadcrumb or the browser) shows it with the same filters, view and scroll position (a column sort is not kept). A record opened from further down a list now starts at its top.
  • Cancel on a new or edited document, purchase order or recurring invoice goes back to where it was opened from; saving an edit returns to the card, and Back from a newly saved record no longer reopens the empty form.
  • Long values on a record card (an email address, a web address) wrap at their punctuation instead of in the middle of a word, and move under their label when they do not fit beside it. An empty field shows a dash.

2026-10-05: depreciation follows the rate of each year and the German windows

Fixed (the accuracy audit's depreciation findings 4 and 16 to 18).

  • UK writing-down allowances use the rate of each chargeable period, with a hybrid rate for a period that straddles a change (CAA 2001 s.56(1)), instead of the rate in force at purchase for the asset's whole life. A main-pool asset bought in June 2025 with years to March now takes 14% from FY2027 (11,480 on 82,000, not 14,760); a calendar year 2026 takes 18% for 90 days and 14% for 275. A schedule that names its own rate_percent keeps it.
  • German degressive AfA (a declining balance in a de.afa book) is refused for an asset acquired outside the § 7 (2) EStG windows, and capped inside them at the window's multiple of the straight-line rate and its percent (2020 to 2022: 2.5x and 25%; April to December 2024: 2x and 20%; July 2025 to 2027: 3x and 30%). A car bought in October 2026 over 6 years takes 30%, not 33.33%. The window follows the acquisition date, which the ERP now sends as acquisition_date; a degressive rate needs a useful life. A UK pool or a CH class with land is not written off at the end of a life even without the switch to straight line.
  • Declining balance without the switch to straight line writes what is left off in the last year of the useful life, instead of running past it and leaving value on the books.
  • Units of production stops at the depreciable base when the units used pass total_units (it went negative in the last year), keeps a year without usage in its fiscal year instead of moving the later years up, and refuses negative units.
  • Schedules already stored keep their numbers; a schedule computed from now on (a new asset, a book added to an asset) follows these rules.

2026-10-05: Revenue contracts

New (revenue contracts, ADR-0036 slice 3, first part, controls RC1 to RC6).

  • Revenue contracts (/v1/revenue-contracts): IFRS 15 contracts with a customer, a fixed transaction price and performance obligations over time (straight_line or monthly) or at a point in time, each with its SSP and basis, given or taken from a product. Created as drafts, an agent's too; never edited or deleted, cancelled while a draft.
  • Activation (POST .../{contract_id}/activate): the policy action activate_revenue_contract, denied to agents, by default approved by a second person; it allocates the price by relative SSP and writes the recognition schedule (GET .../{contract_id}/schedule).
  • Satisfaction (POST /v1/revenue-obligations/{obligation_id}/satisfy): a person records when control of a point-in-time obligation transferred (satisfy_obligation, denied to agents).
  • Nothing posts yet: billing and the period-end recognition run follow (see above).
  • MCP: list_revenue_contracts.
  • Database: migration 0047 adds revenue_contract, performance_obligation and revenue_schedule_line.

2026-10-05: tax schedules end with the asset

Changed (breaking, bug fix) (ledger).

  • A reducing-balance tax schedule without a useful life of its own (CH ESTV classes) now ends in the last fiscal year of the asset's accounting schedule: the asset is taken as scrapped then, and what is left of its tax value is written off in that year. It ran for 30 years and never reached zero, so a laptop written off over 3 years kept a tax value and a negative temporary difference into the 2040s. This changes the schedule that POST /v1/assets (with books or tax_asset_class) and POST /v1/assets/{asset}/books return for such a class without periods. A UK pool keeps the asset in the pool, and a CH class with land keeps the land; both still show 30 years. periods still shows that many years with nothing written off.
  • The tax memo (tax_asset_class) no longer asks for 30 periods for every regime: a class with a longer life shows all its years (us.macrs.gds.39y: 40).
  • Stored schedules: migration 0046 computes the open-ended ESTV schedules already stored again, in tax books and tax memos, by the same rule. No closed year changes: when the year of the write-off, or a later one, has a closed period or a year-end close (pending or closed), the write-off moves to the first fiscal year after it. The years before it stay as they were, and a schedule whose earlier years would differ is left alone. Each change is an audit event (asset.book.reschedule, by system:migration) holding the schedule it replaced, and the migration reports each change and each schedule left alone on stderr. No posted entry changes; the next deferred-tax run posts the movement of the new tax base. Every open-ended schedule was stored with 30 periods, so a tax book whose user asked for exactly 30 periods of an ESTV class is computed again too; the audit event keeps what it had. Assets registered between the migration and the new release taking traffic keep the old schedule.
  • An asset whose accounting life runs past 100 fiscal years keeps the old reducing balance (the engine's limit on a schedule request).

2026-10-05: asset class names on the books, tables that fit their content

New and changed.

  • AssetBookOut has asset_class_name (optional, nullable): the name of the book's asset class in force on as_of, in the regime's own language, from data-vNext. It is null when the book has no class, data-vNext does not know the class or it is no longer in force, or data-vNext cannot be reached; the books still answer then.
  • The asset card's depreciation books show that name (the class id in its tooltip) and the accounting book's framework in words (IFRS, US GAAP, Local GAAP) instead of the codes.
  • Tables that scroll inside their panel size their columns to what they hold, so a long word no longer runs over the next column; other tables keep even columns and wrap a long word in its own.

2026-10-05: lists with status views and bulk actions, approvals waiting for you

New.

  • Overview has pending_for_me: the entity's pending approval requests the caller may decide under the four-eyes principle, that is not requested, created, prepared or submitted by the caller (whichever key or session); 0 for the key the system issued. GET /v1/approvals takes for_me=true for the same requests. Home asks an approver to act on those only.
  • Customer Invoices and Vendor Bills have status views with counts, days overdue on the row and bulk actions on picked rows (submit the drafts, download the PDFs of posted sales documents).

2026-10-05: the close job rolls the forecasts after a close

New (close automation, ADR-0034 slice F3, first part, control CA16).

  • automated_steps may name forecast_roll: once a period is closed, the close job makes a new draft of each forecast of the entity that has no version made since the close (the roll POST /v1/plans/{plan_id}/roll makes, with its defaults). A person reviews and approves the draft; the job never submits or approves one.
  • The close tracker has a new step, forecast_roll (day 6 by default), done when every forecast of the entity has a version made since the period closed; n/a without a forecast. The close status of every period therefore counts one more step (total).
  • Operators: while the release rolls out, the previous release's pods refuse forecast_roll in a schedule or step log they read, so close-schedule and close-status can answer 500 there until the rollout completes.

2026-10-05: the close job writes the close memo

New (close automation, ADR-0034 slice C3, second part, control CA15).

  • automated_steps may name close_review: the close job runs the close review agent once per period, when no check of the close checklist fails or from day 4 of the close calendar, and stores its close memo as a document (close_memo). In the job the agent runs no depreciation or FX revaluation and has the read scope only; it reads, records issues and writes the memo. It posts, approves and closes nothing. The stored memo (also one a person asks for) starts with a line saying a language model wrote it and whether the checklist was ready.
  • The close tracker has a new step, close_review (day 4 by default), done when the period has a close memo (a person's review counts too); step_days may set its day. An open period past that day without a memo shows the step as overdue only where the job writes the memo; without a memo it is n/a on a closed period and for other entities.
  • Changed (breaking, bug fix): POST /v1/agents/periods/{period_id}/close-review answers 502 (agent_failed, already a documented answer for a model failure) and stores nothing when the model answers with no memo. It answered 200 and stored the placeholder "(the agent returned no memo)" as the period's close memo, which marked the tracker's step done. The stored memo's header names the model, and its provider, that answered.
  • Operators: the erp-close job gets the API's model settings (ERP_LLM_*) with the next terraform apply, which should come before any organization names close_review; until then the job uses the default model in the default region. While the release rolls out, the previous release's pods refuse close_review in a schedule or step log they read, so close-schedule and close-status can answer 500 there until the rollout completes.

2026-10-05: a stricter check when an approval policy is saved

Changed (control S2).

  • PUT /v1/approval-policy now refuses a table that lets an agent take a human-only action at any amount a rule singles out (it tried 0, 1 and 1e15 before; it now also tries every amount a rule names and a cent either side), and one whose in or not_in has a string rather than a list (a substring test). A stored table is not checked again until it is saved.

2026-10-05: a bare VAT number names a vendor only when it is unambiguous

Changed (ADR-0038).

  • A VAT number printed without its country prefix, on an invoice or e-invoice whose country cannot be read, names a vendor only when all the vendors it matches are one registration: the same nine digits can be a German number and a Swiss UID, while one supplier kept as two vendors is still one. On an invoice whose country is known, a number printed without its prefix is read as that country's.

2026-10-05: the close job runs the period-end runs, and a person posts them

New (close automation, ADR-0034 slice C3, first part, controls CA11 to CA14).

  • Period-end runs in the close job: automated_steps may name depreciation, fx_revaluation and deferred_tax. The job makes each run once per period, from its working day in the close calendar, when the checklist says it is due and not done.
  • What the job's runs post waits for a person: the default approval policy's new rule close_job_runs_need_a_person sends a posting of the close job with the source fa, fx, deferred_tax or rev to /v1/approvals. An organization may allow depreciation (fa) in its own policy. A run whose entry a person rejects is made again the next day. Runs a person makes, and every other system process, post as before.
  • Bank matching in the close job: automated_steps may name bank_matching. Each morning the job matches the period's statement lines the import's rules match and logs how many are left for a person. A payment it books above 5,000 waits for a person (rule close_job_large_bank_matches).
  • Deferred tax has its own posting source, deferred_tax (it was fa), so a rule about depreciation no longer covers it. A rule of an organization's own that names fa for deferred tax needs deferred_tax added.
  • Approval policy: a fifth, optional input actor_name (a system actor's name, close for the job; else the actor id). A table without it is still accepted and decides as before.
  • Database: migration 0043. It lets the schedule and the step log name the period-end runs, and it gives every organization that has its own approval policy a new version with actor_name and the rules, by system:migration, in the audit trail (GET /v1/approval-policy/versions). A rule of the organization's that names the source fa names deferred_tax with it, since deferred tax posted as fa before.

2026-10-05: VAT numbers compared as the same registration, evidence for every invoice vendor

Changed (ADR-0038).

  • A vendor is matched, and a known vendor's bill warned about, by its VAT registration: the Swiss MWST/TVA/IVA suffix is ignored and a number printed without its country prefix gets the vendor's. CHE-123.456.788 MWST on an invoice no longer says "VAT number differs" for a vendor kept as CHE-123.456.788.
  • A vendor that bill capture or the e-invoice import creates records the file it came from and bank_source: "invoice" in its audit entry, with the register not_checked.
  • A number a register adapter refuses without asking (a wrong check digit, HMRC not configured) no longer counts against the 30 register calls a minute, and is answered even at the limit instead of 429. No HMRC request starts more than 12 seconds into a check, and each keeps the 8 second per-phase timeout or what is left, whichever is shorter.

2026-10-04: Products, list prices and standalone selling prices

New (products, ADR-0036 slice 2, controls PR1 to PR5).

  • Products (/v1/products): recurring (priced per month or year) or one-off (per unit), with the revenue account, supply type and VAT rate type of their invoice lines. Never deleted; deactivated.
  • List prices (POST /v1/products/{product_id}/prices) by currency from a day, never changed; the price in force on a day: GET /v1/products/{product_id}/price.
  • Standalone selling prices (POST /v1/products/{product_id}/ssps) with their basis, recorded by a person: the new policy action set_ssp, denied to agents. An organization's own approval policy that predates it decides set_ssp by the default rules; saving that policy again needs set_ssp in its action list.
  • Invoice lines may name a product (product); the line takes its account, supply and rate type unless it names its own and stores product_id. A recurring invoice stores them resolved in its approved terms, so a later change to the product does not change what it invoices.
  • MCP: list_products.
  • Database: migration 0042 adds product, product_price, product_ssp and trade_document_line.product_id.

2026-10-04: sign-in with an e-mail address and a password

New (ADR-0037, users and sign-in).

  • People without a Google account sign in with their e-mail address and a password that Google Identity Platform keeps; Google stays the identity provider and the ERP stores no password. The sign-in page shows the form, with Forgot password?, where the deployment has ERP_IDENTITY_PLATFORM_API_KEY.
  • API: POST /v1/auth/password takes Identity Platform's ID token; GET /v1/auth/config has password_sign_in; POST /v1/users takes password_email, and POST /v1/users/{user_id}/password-email has Google e-mail a user the link that sets (or resets) their password; the operator's POST /v1/platform/organizations/{org_id}/admins takes password_email too and answers password_email_sent.
  • Sign-in limit: refused sign-ins at POST /v1/auth/google and POST /v1/auth/password now count against the client address like failed keys (429 past ERP_AUTH_FAILURE_LIMIT).
  • Who may use a password: a password sign-in counts only for the user the ERP set the account up for. ERP_ALLOWED_EMAIL_DOMAINS still limits Google sign-in to your Workspace domains; their people and anyone who signs in with Google get no password (409), and every other invited address, a customer's, may use one.
  • Infrastructure: infra/terraform/identity.tf (Identity Platform with sign-up disabled, the browser key, the custom role erpPasswordAccounts for erp-api). Migration 0041_user_password_subject adds a column to app_user.

2026-10-04: rolling forecasts and the scheduled close job

New (plans, close automation, ADR-0034 slices F2 and C2, controls PL9 to PL11 and CA7 to CA10).

  • Rolling a forecast (POST /v1/plans/{plan_id}/roll): a new draft of a forecast in which the closed periods of the year are actuals from the ledger, the open periods planned before are carried over, and the periods new to the horizon are filled by run_rate (the average of the last closed periods) or prior_year (with growth). A person approves the draft as any version.
  • The comparison (GET /v1/plan-versions/{version_id}/comparison) gains each fiscal year's outlook (actuals of the closed periods plus the plan of the rest) on every row, and ?against_version= to compare a forecast with a budget or an earlier forecast (against in each cell, the variance is this version's less that one's). Additive: the existing fields keep their meaning.
  • The scheduled close job (erp-close, daily at 05:00 UTC): for each entity whose close calendar names steps (automated_steps on PUT /v1/entities/{entity}/close-schedule; none by default), it makes the recurring invoices due through the period end and evaluates the close checklist, once a day, as the organization's system actor. It never approves or closes anything. What it did is job_log in GET /v1/periods/{period_id}/close-status.
  • MCP: plan_comparison takes against_version_id.
  • Database: migration 0040 (close_schedule.automated_steps, close_step_log).
  • Operators: apply Terraform (infra/terraform/close-job.tf) to create the job and its schedule; until then deploys skip it. erp_close_job_paused pauses it for every organization.

2026-10-04: bills are read on upload, vendors from the invoice, VAT register checks

New (agents, UI, ADR-0038).

  • Reading without booking: POST /v1/agents/bill-capture/read reads a vendor bill (e-invoice or model) and answers the vendor it matches or a proposed new vendor, and whether a bill can be made from it. The model's reading is stored with the file; POST /v1/agents/bill-capture with the same file books that reading without asking the model again.
  • VAT register check: POST /v1/vat-registers/check (draft scope, 30 register calls a minute per organization) asks VIES, the Swiss UID register or HMRC and answers valid, invalid, unavailable or no_register with the registered name and address. Advice only. New settings ERP_VAT_REGISTERS, ERP_HMRC_CLIENT_ID, ERP_HMRC_CLIENT_SECRET, ERP_HMRC_BASE_URL (Terraform erp_hmrc_base_url; see deployment).
  • Vendor Bills: a file is read as soon as it is dropped, and a wizard leads from what was read through the vendor (matched, created from the invoice after the register check, or chosen) to the bill. Check only is gone: reading a file checks it.
  • Vendor evidence: PartyInput.source_sha256 names the bill a vendor was read from; its audit entry and activation request then record that the bank details are the invoice's and what the register said. A known vendor's bill warns when the invoice's IBAN is not one of its accounts or its VAT number differs.
  • Changed: VendorMatch has the vendor's status; ExtractedBill has the vendor's street, postal code and town.

2026-10-04: the country's standard chart, and renaming an account

Added and changed (ledger: chart templates).

  • A German standard chart: the template de_hgb, our own chart laid out on the HGB balance sheet and income statement, with every system role. A company that keeps DATEV's SKR03 or SKR04 imports it as before. Customers provisioned for Germany start from it.
  • Provisioning picks the country's chart: an entity provisioned without chart (POST /v1/platform/organizations, the operator portal's New customer wizard) starts from its country's standard chart, ch_kmu for CH and LI and de_hgb for DE, else ifrs_generic. Before, the chart came with the preset: ch_sme gave ch_kmu and every other preset ifrs_generic, whatever the country. So generic with CH now gets ch_kmu, and ch_sme with another country ifrs_generic. An explicit chart wins as before. POST /v1/entities is unchanged: without chart it still uses ifrs_generic. GET /v1/charts and the new GET /v1/platform/charts list countries per chart and, with ?country=, mark the standard one; the wizard preselects it.
  • Renaming an account: PATCH /v1/entities/{entity}/accounts/{code} takes name. A rename applies at once, also on an account with a role, and is audited with the previous name (account.update). On a bank account it may not drop or change the IBAN in the name, or take one another account's name carries, since bank imports find the account by it (409). The number, type and currency stay fixed.

2026-10-04: CRM, opportunities and activities

New (CRM, ADR-0036 slice 1, controls CR1 to CR6).

  • Opportunities (/v1/opportunities): deals with a customer or a prospect, numbered per entity (OPP-000001), with a kind, an expected amount and close, an owner, a source and a next step; stages lead, qualified, proposal, negotiation (each with a default probability), won (needs a customer; final) and lost (needs a reason; can be reopened). Never deleted.
  • Activities (/v1/activities): notes, calls, meetings, e-mails and tasks about a deal or a customer, never edited or deleted; a task is completed once. A stage change writes a stage_change activity.
  • Pipeline (GET /v1/opportunities/pipeline): open deals by stage and currency with the weighted amount, deals won and lost in a range, open and overdue tasks; never converted between currencies.
  • MCP: list_opportunities and opportunity_pipeline.
  • Design: ADR-0036 plans the rest: products and prices, revenue contracts (ADR-0027 slice 1), subscriptions billed by period, upsells, downsells and cancellations prorated by days with credit notes, renewals and MRR.
  • Database: migration 0039 adds crm_opportunity and crm_activity.

2026-10-04: plan approval and close tasks, follow-up

Changed (plans, close tracker, controls PL2 and CA4).

  • Approving a plan version: whoever prepared the version, not only its submitter, is now kept from approving it by default: its creator and everyone who set its lines too (rule plan_submitter_does_not_approve, compared by person; an agent run counts as the person it ran for). A one-person organization still allows approve_own_plan.
  • Close tasks: a deactivated task is no longer reactivated (PATCH ... {"active": true} answers 409; create a new task). Reactivating it would have put it back into the closes of periods that closed while it was out of the close.
  • Database: migration 0038 refuses reactivating a close task.

2026-10-04: budgets and the close tracker

New (plans, close tracker, ADR-0034 slices F1 and C1, controls PL1 to PL8 and CA1 to CA6).

  • Plans (/v1/plans, /v1/plan-versions): a budget for a fiscal year or a forecast with a horizon, versions with lines by profit and loss account and period, submitted, then approved by a second person (approve_plan, agents denied; the submitter only where the policy allows approve_own_plan), and GET .../comparison: plan against posted actuals by period, per account or report line. A plan is never posted.
  • Close tracker: the close calendar of an entity (/v1/entities/{entity}/close-schedule: owner and the working day of each step), its own month-end tasks (/v1/entities/{entity}/close-tasks, optionally decided by a close check), completions with evidence, and GET /v1/periods/{period_id}/close-status. Changing the calendar and recording a task are a person's acts (manage_close, agents denied). A closed period keeps the tasks of its close as it closed, and its completions change only after it is reopened.
  • Approval policy: three new actions, approve_plan, approve_own_plan and manage_close, and the rules agents_no_plan_or_close_controls and plan_submitter_does_not_approve. A stored policy without them falls back to the default for them.
  • MCP: list_plans, get_plan_version, plan_comparison, close_status_of_period.
  • Database: migrations 0036 (plan, plan_version, plan_line) and 0037 (close_schedule, close_task, close_task_completion), all with forced row-level security.
  • Not yet: the forecast roll, the scheduled close job, the screens.

2026-10-04: model fallback and the operator's choice of models

New. The built-in agents and the source watcher ask a chain of models: when the model fails or does not answer within two minutes, the next one answers (by default Gemini 3.7 Flash after Gemini 3.8 Flash). A refusal, or a request the provider rejects as invalid, is an answer and is not retried elsewhere; a tool conversation falls back only before its first answer; no further model is asked after ten minutes. Design: ADR-0035.

  • Operator portal: a Models page (/admin/models) shows the chain in force, the Gemini models Google lists for the project and region (with a warning for a chosen one it does not list), and the earlier choices; the operator checks a model and saves a new chain of 1 to 4. The deployment's chain follows a choice as the last resort.
  • REST: GET /v1/platform/models, PUT /v1/platform/models (a person signed in to the portal; a platform key is refused), POST /v1/platform/models/check. Both writes are in the platform audit (platform.choose_models, platform.check_model).
  • Configuration: ERP_LLM_FALLBACK_MODELS, DATAVNEXT_LLM_FALLBACK_MODELS (Terraform llm_fallback_models).
  • Database: migration 0035 adds platform_model_choice, append-only (a trigger refuses any change, deletion or truncation, for every role).

2026-10-04: Swiss withholding tax booked at the dividend's declaration

New (guide, backlog B-69, control YC10).

  • A dividend destination of an appropriation (dividend_payable or group_dividend) takes withholding_tax: deduct books 35 % of the gross as payable to the tax administration and the destination only the net; notification (a group dividend to a Swiss shareholder, Art. 26a VStV) withholds nothing. Not set, the entry is the gross dividend as before, with the warning. The answer adds withholding_tax_due (the resolution date plus 30 days).
  • A group shareholder's draft books the gross income, the net receivable and the tax as a claim on the tax administration; the settlement pays at most the net.
  • Close check withholding_tax_due (a warning): tax past its due date still unpaid at the period's end. Pay it by posting the bank line to the withholding tax account.
  • Chart templates: two optional roles, withholding_tax_payable and withholding_tax_receivable (ch_kmu 2206 and 1176 Verrechnungssteuer, the generic chart 2230 and 1410). An existing entity adds them when it first deducts (409 role_missing names them).
  • Not yet: a foreign shareholder's treaty rate and refund.

2026-10-04: intercompany, slice 2: loans, interest, group dividends

New (guide, ADR-0025 section 6, controls IC12 to IC15 and CN14).

  • Loans between the two entities of an active relationship: POST /v1/ic/loans asks for one (fixed rate with its arm's-length basis, day count, facility, term, bank accounts, net investment) and a second person approves its terms (approve_ic_agreement, human only). Drawdowns, repayments, interest accruals and settlements (POST /v1/ic/loans/{id}/drawdowns, .../repayments, .../interest, POST /v1/ic/settlements) post both entities' entries in one transaction, each through its own policy, and are recorded append-only (GET /v1/ic/movements). Interest is exact by segment and day count and rounded once.
  • Group dividends: an appropriation destination of kind group_dividend names the shareholder entity; its posting drafts the shareholder's entry, which a third person accepts. Such an appropriation is not reversed.
  • Reconciliation and close: GET /v1/ic/reconciliation has balances per loan, interest and dividend (new reason loan_movement_missing, a timing item); the close check ic_interest warns while a loan is not accrued through the period's end. The FX revaluation revalues the group loan and other balances.
  • Consolidation: a run now also eliminates loans (E2; a net-investment loan's differences go to the translation reserve, IAS 21.32) and dividends (E6). eliminations names E1, E2, E3, E6, and eliminations_pending E4, E5, E7. A run made before this release is unchanged; a run without loans or dividends has the same inputs digest as before.
  • Chart templates: seven optional roles (ic_loan_receivable, ic_loan_payable, ic_other_receivable, ic_other_payable, ic_interest_income, ic_interest_expense, ic_dividend_income; ch_kmu 1445, 2455, 1150, 2160, 6952, 6902, 6951) and four group report lines. An existing entity adds the accounts when it uses loans (409 role_missing names them).
  • MCP: list_intercompany_loans and list_intercompany_movements (read).
  • Database: migration 0034 (provisional number) adds ic_loan and ic_movement with row-level security and their triggers. Additive; the organization export holds both tables.
  • Not yet: the web UI of loans, variable rates, repayment schedules.

2026-10-04: consolidation, slice 2: intragroup eliminations

New (guide, ADR-0026 section 5, controls CN11 to CN13).

  • A run eliminates intragroup balances (E1: the group receivables and payables) and intragroup income and expense (E3) between its entities, read from what the ledgers say about each other through the intercompany view. An agreed E1 residual is an exchange difference that stays in profit or loss (IAS 21.45); an agreed E3 residual is a translation difference and goes to the translation reserve (IAS 21.41). Sides that disagree in the document currency show the difference on lines of their own (bs.assets.current.ic_unreconciled, pl.ic_unreconciled) with the exception ic_difference (blocking above the group's ic_tolerance_amount); an intragroup cost in an asset is the blocking ic_capitalised; agreed balances that differ beyond the tolerance are the warning ic_exchange_difference.
  • REST: GET /v1/consolidation-runs/{id}/eliminations (by rule and pair) and GET /v1/consolidation-groups/{group}/checklist?as_of= (the group close checklist). A run's eliminations_applied is now true and eliminations / eliminations_pending name the rules; the statements carry eliminations_applied too. A run made before this release is unchanged and says it has no elimination.
  • Not yet: loans and dividends (E2, E6) come with intercompany slice 2 (see above); the investment, goodwill and non-controlling interests (E4, E5, E7) with consolidation slice 3. The warning eliminations_pending says so.
  • Web UI: the Consolidation page has two new tabs on a run, Eliminations (each pair, the sides that disagree first) and Checklist (the group close checklist at the run's date); the statements say how far they are eliminated.
  • Database: migration 0033 (provisional number) adds consolidation_line.partner_entity_id (only on an elimination line, a check) and consolidation_run.eliminations (part of the header that never changes). Additive: the previous release keeps working. The organization export holds both columns.

2026-10-03: recurring invoices

New. A recurring invoice is the template of an invoice that repeats (monthly, quarterly or yearly): a second person approves its terms once (never someone who prepared them), then a person makes the invoices that are due on demand, as ordinary invoices, idempotently (a period is never made twice, also under simultaneous runs). Guide: recurring invoices; design: ADR-0027 section 5.

  • REST: POST and GET /v1/recurring-invoices, GET and PATCH .../{id}, POST .../{id}/activate (a human decision: agents are denied; a second person approves, bound to a digest of the terms), POST .../{id}/pause, GET .../{id}/runs and POST /v1/recurring-invoices/run ({through}; Idempotency-Key). Approval requests of object type recurring_invoice appear in GET /v1/approvals.
  • Role Center and close: recurring_due in GET /v1/entities/{entity}/overview (the Role Center cue, which opens the due list) and the close check recurring_generated (a warning for a missed run).
  • Web UI: Recurring Invoices (list, card and form) in the Finance menu, in four languages.
  • MCP: the read tools list_recurring_invoices, get_recurring_invoice, recurring_invoice_runs.
  • Database: migration 0030 (provisional number) adds recurring_invoice_template and recurring_invoice_run with row-level security, a trigger that keeps the approved terms of an active template unchanged and evidence triggers on the run record. The organization export holds both tables.
  • Controls RV1 to RV9 in the control matrix.

2026-10-03: customer statements

New (ADR-0028, slice 1; see the guide)

  • GET /v1/entities/{entity}/customers/{party}/statement?as_of= returns a customer's open items on a day as JSON, CSV (format=csv) or a PDF in the customer's language (format=pdf; German, French, Italian or English). It is the aging report restricted to one customer, by the same rules (allocations count from the day they took effect, a part payment lowers its invoice, a credit note is a negative item, a payment waiting for approval reserves its amount), with the balance and the age bands per currency, and a reconciliation that ties the total to that customer's own lines on the receivables control account: unexplained is zero, and a period-end revaluation on the account is named (unrealised_fx), not hidden.
  • POST .../statement/issue archives the PDF with its SHA-256 in the document archive (one file per customer, day and language, never rewritten; GET ...&format=pdf then returns the archived file). Only a person with the post scope issues; an agent key is refused (403), and so is a day that has not come (422). The audit event is statement.issue.
  • The MCP tool customer_statement reads it; there is no tool to issue one.
  • The web UI has a Statement action on the customer card and a statement view with tiles that open the items they count, PDF and CSV download and Issue statement; every text is in the four catalogs.
  • Migration 0029_customer_statement (provisional number): the archive's CHECK, trigger and unique index for the new document kind. No new table; additive (the previous release keeps working).
  • A statement posts nothing and is not a request for payment. Dunning levels, default interest, charges and sending are the next slices of the ADR.

2026-10-03: intercompany, slice 1

New (guide, ADR-0025, controls IC1 to IC11).

  • Relationships between two entities of one organization (/v1/ic/relationships): requested by a person, approved by a second, with the VAT group (Swiss group or German Organschaft, inside one country, from a date) and the transfer-pricing record (method, mark-up, basis, documentation date) bound to the approval by a digest; a change is a new approval, suspending is one person's decision. Approving creates a customer and a vendor that is the other entity in each entity (IC-<code>, active at once).
  • The mirror. Posting an invoice or credit note to a group entity links it (/v1/ic/links) and makes a draft purchase bill (credit note) in the counterpart; a person there accepts it with the normal submit, not the person who posted the origin (accept_own_ic_document; agents and a system key never). Goods are refused, an invoice that cannot be mirrored does not post, the mirror must equal its origin (database trigger), retry and reject exist. mirror_mode: post posts it at once as the system.
  • Group accounts. The optional control roles ic_ar_control and ic_ap_control (suggested in both chart templates: 1110 and 2010, generic 1210 and 2010); documents of a group party post to them, the aging and the control-account close checks include them. GET /v1/documents/{id} gains ic.
  • Reconciliation (GET /v1/ic/reconciliation): by link, in the document currency, with a closed set of reasons; the close check ic_reconciled passes, warns (timing items) or fails (unexplained).
  • UI: Finance → Intercompany; the Approvals page (with the mirror mode, VAT group, tolerance and pricing basis) and the close checklist know the new requests and check.
  • Edge cases: a credit note of an invoice whose mirror was rejected ends with the link status mirror_rejected; a rejected relationship request is asked again with any PATCH; the open-items import refuses a group party (opening balances go through the relationship or a journal).
  • Migration 0032_intercompany (provisional number): ic_relationship, ic_link, party.group_entity_id, the view ic_journal_line, six triggers, UNIQUE (id, org_id) on entity; the counterparty_type check also allows entity (for slice 2). Expand-only: the previous release keeps working.

Behaviour changes for API clients. PartyOut gains group_entity_id, DocumentOut gains ic, GET .../aging reconciliation gains group_control_account (the balance it ties to is that of both accounts), and the close checklist has one more check, ic_reconciled (n/a for an entity without a relationship). Four MCP read tools: list_intercompany_relationships, list_intercompany_links, intercompany_reconciliation.

2026-10-03: deploys no longer block Terraform

Fixed

  • Terraform could not change a Cloud Run service. The deploy tagged its candidate with run deploy --tag, which writes the revision name into the service template; a later Terraform change (for example an environment variable) resent that name and Cloud Run answered 409. The deploy now tags the candidate after deploying, so Cloud Run names every revision (.github/scripts/deploy-candidate.sh, docs/deployment.md, Continuous deployment). The monitoring dashboard is written the way Cloud Monitoring stores it, so terraform plan no longer shows it changing on every run.

2026-10-03: security fix, reversing posted entries is a controlled action

Security. POST /v1/journal-entries/{id}/reverse and the MCP tool reverse_journal_entry needed only the post scope and called the posting engine without asking the approval policy. Any key with post, agent keys included, could reverse any posted entry at once, for any amount, past the agent limits and the second-person rules (an opening entry that needs a second person to post included); a person could undo another person's approved entry alone; an entry of a subledger (invoice, bill, payment, depreciation, FX revaluation, VAT settlement) could be reversed while its document, payment or asset stayed posted, so the ledger and its subledger drifted apart; and the reason was optional. Reversing is now a policy action, reverse_entry, with the rules below. Entries already reversed this way stay as they are (the audit trail has each journal.reverse event and its actor); reconcile subledger documents against the control accounts with the close checklist (ar_control_reconciled, ap_control_reconciled) and the aging reconciliation.

Behaviour changes for API clients (breaking, bug fix).

  • Agents are refused, on REST and MCP, always: 403 policy_denied, rule_id agents_never_reverse, action reverse_entry. An agent drafts a correcting entry, which waits for approval like any agent entry, and a person reverses.

  • reason is required (1 to 500 characters, whitespace trimmed): 422 without it. It is kept on the reversal entry (in its description and in provenance.reason) and in the journal.reverse audit event.

  • Only manual entries and opening entries are reversed. Any other source answers 409 with a message naming where the correction belongs: ar and ap (invoices, credit notes, bills: void a draft with POST /v1/documents/{id}/void, correct a posted document with a credit note; payments: the payment itself, not its entry), bank (the bank reconciliation), fa (depreciation: its period run POST /v1/periods/{id}/depreciation), fx (the revaluation reverses itself and has its period run POST /v1/periods/{id}/fx-revaluation), ic and rev (their processes), close and appropriation (the year-end close), vat (amend the VAT return). The list follows posting.SUBLEDGER_SOURCES plus vat and appropriation; any other source is refused as well. The opening entry of a data import is the exception: it is the import's correction path, and by the default rule opening_reversal_needs_a_second_person its reversal always waits for a second person (202); once the reversal is approved, the import accepts the balances of the entity again. The service insists on the second person whatever an organization's policy says. The reversal leaves the open items and the opening balance clearing account in place, so the close check opening_clearing fails until a new opening import (or corrections of the open items) brings the clearing account back to zero. A reversal request that nobody decides blocks the close of the entry's period (the approvals check) until it is approved or rejected. (A stored organization policy without that rule still asks the post rule for the source opening, which sends it to a second person in the default table.)

  • A second person may be needed. A person reverses an entry they created, posted or approved at once (200 with the reversal entry, as before). If another person created, posted or approved it, or the approval policy would not let one person post an entry of that source and size (the policy is asked for reverse_entry and for post), the answer is 202 with the approval request (ApprovalOut, object_type journal_reversal, action reverse_entry, object_id the entry to reverse). A second person decides it with POST /v1/approvals/{id}/approve (or reject); nobody approves their own request, and the entry, the period of the reversal date and the policy are checked again at that moment (409 or 403 leave the request pending). Only one request per entry waits at a time (409). The MCP tool answers {"status": "posted", "reversal_id", "number"} or {"status": "pending_approval", "approval_id", "reason"}.

  • A reversal into a closed period is refused with 409 (the reversal date ... is in closed period ...), as the database always did; it is now checked before anything is queued.

  • Organization policies that were saved before this release lack the action reverse_entry in their action list: they keep working (the default table decides that action), but a new PUT /v1/approval-policy must list it and keep agents denied; GET /v1/approval-policy shows the default.

  • MCP errors say what was refused. A refusal by a tool (policy_denied, conflict, validation_failed, forbidden, not_found) now reaches the client as Error executing tool <name>: <code>: <message>; before, the SDK withheld the text of every tool error and the client saw only Error executing tool <name>.

Interface. The journal entry card asks for a reason, hides Reverse for agents, shows it disabled with a tooltip naming the correct path for entries that are not manual, and says "sent for approval" when the answer is 202. German, French, Italian and English.

Controls. New control L19 (controls); an architecture test pins that only posting.auto_reverse and journals.reverse_entry call the reversal engine, so no unchecked path to a reversal can appear. No migration.

2026-10-03: service levels

New

  • GET /v1/platform/health (platform key with platform:read): per service (erp-api, datavnext-api) every service level objective with its internal target, its compliance over the rolling 28 days and the share of the error budget left; the 24-hour p95 of the application's response time by request class (read, write, heavy); the version, revision and time of the last deploy; and the uptime checks. Cached for 60 seconds. A part that cannot be read is named in problems and the rest is still answered; where monitoring is not configured the status is unavailable. Counts and durations of requests only, never tenant data.
  • Every request line of both APIs carries request_class (read, report, write, heavy, agent, other; data-vNext: read or other). The latency objectives select by it. A 429 sent by one of our own limits (failed sign-ins, the hourly mail limit) also carries app_limit.
  • GET /health/db (public, {"status": "ok"} or 503): a SELECT 1 through the runtime database role with a short timeout; it reads no data and does not compare the schema revision, so a deploy does not fail it. The uptime check the SLA measures by calls it.
  • Service level objectives in Terraform (internal targets, rolling 28 days): availability 99.9% for both services; ERP reads 95% under 500 ms, reports (statements, aging, analytics, FX exposure) 95% under 2 s and writes 95% under 1.5 s; data-vNext reads 95% under 1 s; six burn-rate alerts (2% of the budget in 1 hour, 5% in 6 hours); error-budget, compliance, burn-rate, traffic and p95-by-class charts on the dashboard. See SLOs and error budgets.
  • A draft SLA for the founder and legal review, not in force: docs/legal/sla-draft.md.

Operators

  • terraform apply adds 23 resources (including a fourth uptime check, erp-db, on /health/db) and updates the dashboard; the runtime service account gains a custom role of six Monitoring read permissions (erpHealthReader) and roles/run.viewer on the two services. The log-based metrics count from the day they are created. The endpoint answers unavailable until the roles exist.
  • New setting ERP_GCP_REGION (default europe-west6).

2026-10-03: organization export

New

  • POST /v1/exports (scopes admin and audit; the operator's twin is POST /v1/platform/organizations/{org_id}/exports): the complete package of an organization's data for its own archive, built asynchronously from one consistent snapshot. Every table as CSV and JSON with a documented schema, the reports of every entity and fiscal year as PDF and CSV, every stored file checked against its SHA-256, the hash-chain verification, a manifest with a hash per file (sha256sum -c manifest.sha256), index.xml (Beschreibungsstandard) for German entities, a verification program and a README in English, German, French or Italian with the retention rules of the countries of the entities. Follow it with GET /v1/exports/{id}, then /download (streamed) or /link (a signed link, seven days at most). The package's SHA-256 is recorded in the platform's append-only audit record. Kept 30 days. See Organization export and ADR-0033.
  • The operator portal's Export data starts an export and its Exports tab follows it, downloads the package and gives a signed link; the operator's record (platform_audit) has org.export_start, org.export_download and org.export_link for what the operator did and org.export or org.export_failed for what the job attested.
  • An organization that is suspended may still ask for its own export (it changes none of the books).
  • erp export worker (the Cloud Run job erp-export) builds queued exports; erp platform attestations --org-id lists what was recorded of an organization's exports.
  • Migration 0028_org_export (the table of exports); Terraform: the exports bucket, the erp-export job and its account (to be applied by the operator, see the deployment guide's offboarding runbook).

Changed

  • The export job holds no owner password: it attests the package through two functions of the owner, and a job that is taken over cannot overwrite the attempt that replaced it.
  • The general ledger report is read as a stream (reports.iter_ledger_lines) and the VAT report in batches; their answers are unchanged.

2026-10-03: the operator portal

New

  • The operator portal at /admin/: a separate app for the platform operator, for the addresses in ERP_PLATFORM_OPERATORS only (Terraform erp_platform_operators, default the platform owner). It is English only and unmistakable (a dark-red header and a yellow banner). It lists every organization with tiles (active, suspended, demo, customers) that open the filtered list with the same count, shows an organization's entities, people, evidence of use and the operator's record, suspends and reactivates with a reason, marks a legacy demo, invites or shows again an administrator, and provisions a customer in a wizard over the ch_sme, de_sme and generic templates (the result says whether it was created or already existed). Service health on its start page is GET /v1/platform/health; Export data is disabled ("coming soon"). See the portal and ADR-0032.
  • Operator sessions: POST /v1/platform/auth/google (Google, the Workspace must vouch for the address) sets the cookie erp_platform_session (HttpOnly, SameSite=Strict, Secure, Path=/v1/platform; 8 hours, 30 idle minutes, revoked at POST /v1/platform/auth/logout). It authenticates /v1/platform and nothing else; a change by cookie needs X-ERP-UI: 1. GET /v1/platform/me says who the credential is.
  • Platform API: GET /v1/platform/organizations takes kind and usage=true and returns kind and, with usage, the counts (entities, people, journal entries, documents) and last activity; GET /v1/platform/organizations/{org_id} returns entities, people with role and last sign-in, and the evidence of use (it was only the status); POST .../{org_id}/admins invites an administrator or shows the invitation again; POST .../{org_id}/mark-demo marks a demo with confirm_name (a database function checks the name and that the organization shows no sign of real use, and cannot accept any; an operator session, not a key); GET /v1/platform/audit is the operator's record. A provisioning answer has replayed.
  • platform_audit records every sign-in, refusal and act of the operator, with the client address and the outcome (ok, denied, refused); the organization's own audit trail names operator:<address> for what the operator did inside it.
  • Migration 0027 adds platform_session (with the Google sub of the first sign-in), makes platform_audit.target_org_id and org_name nullable and adds its address and outcome columns, adds the function erp_mark_demo, and lets the runtime role execute it and add to and read platform_audit (it still has no UPDATE on the kind; ADR-0032). Purging a demo is still only the command, over the owner connection.
  • Breaking for the operator's console commands: erp platform purge-demo measures the evidence of use itself (a dry run too) and refuses an organization that shows any sign of real use, whatever its kind says, unless --accept-activity is given; the evidence now also counts users invited by a person or an API key and more than one user who has signed in. Refused sign-ins to the portal are limited per address (429), recorded once per address and interval, and an address cannot sign in with another Google account than its first.

Operations

  • Apply Terraform: expect one in-place change, google_cloud_run_v2_service.erp (a new environment variable ERP_PLATFORM_OPERATORS), and nothing else. The migration is applied by the erp-migrate job on deploy.

2026-10-03: year-end close

New (ADR-0023; see the guide)

  • Close a fiscal year: GET /v1/entities/{entity}/fiscal-years/{year}/close-preview shows the closing entry and what stands in the way; POST .../close (a person) proposes it. One entry of source close brings every profit and loss account to zero against the account with the role annual_result (2979 in the Swiss chart, 3199 in the generic one; both templates have it now). The year is locked from the proposal: nothing else is posted into it until the close is approved, cancelled or reopened (the application says why; a database trigger refuses it too). A second person approves the entry (policy rule close_entries_need_a_second_person); the approval refuses with 409 year_changed if the figures moved since the proposal. POST .../close/cancel withdraws a proposal; GET .../close shows where a year stands.
  • Reopen: POST .../reopen-close (a person with the approve scope, a reason; policy action reopen_period) reverses the closing entry and reopens the year; the year can be closed again.
  • Appropriate the result: POST /v1/entities/{entity}/appropriations books the general meeting's resolution on its own date (source appropriation) into retained earnings, the legal or a voluntary reserve and a dividend payable; the remainder of the result is tracked (GET /v1/entities/{entity}/appropriations, POST .../appropriations/{id}/reverse). For a Swiss entity the answer carries the 35 % withholding tax of a dividend and the legal reserve warning. Six months after the year end an unappropriated remainder is flagged: appropriation_overdue, the close checklist (appropriation_pending), GET .../fiscal-years?unappropriated=true and a Role Center cue (unappropriated_years on the overview).
  • Catch up: POST /v1/entities/{entity}/year-close/catch-up closes the years an entity booked before it closed any, in one entry on the first day of the first clean year.
  • GET .../fiscal-years/{year}/opening-balances: what a year opens with. GET .../fiscal-years carries each year's close. The close checklist has two checks, year_close and appropriation_pending (warnings). MCP read tools year_close_preview, year_close_status, opening_balances, list_appropriations and unappropriated_years.
  • Web UI: Finance → Period close has a Year-end section (preview, propose, approve, cancel, reopen, appropriate, reverse, catch-up) in all four languages; the Role Center has a cue for the years to appropriate.
  • A year is not proposed while a period-end run of its last regular period is due and not done (depreciation, FX revaluation, deferred tax): the preview and the 409 year_close_blocked name the run and its operation. A proposal that waits for approval fails the close check year_close on its period. Proposals are serialised per entity (a second gets a clean 409) and read the totals once, behind the postings in flight. A catch-up needs every earlier period closed and holds everything dated before it while it stands. A resolution dated in a closed year is refused when it is made. 409 role_missing says that creating the account takes one person and giving the role to an existing account needs a second; the preview shows the template's code (result_account_suggestion).
  • Migration 0026 adds the tables fiscal_year_close and appropriation and the triggers behind them (additive; safe for the previous release).

Behaviour that changes

  • Profit and loss, period profit and loss and the analytics leave out entries of source close, so a closed year keeps its income statement. GET .../reports/trial-balance takes closing=include|exclude and .../general-ledger takes include_close; a ledger line has closing. Nothing changes for an entity that has not closed a year.
  • A period of a year that was closed cannot be reopened with POST /v1/periods/{id}/reopen (409): the year is reopened with reopen-close.
  • An entity created before this release has no annual_result account: add one with POST /v1/entities/{entity}/accounts (role annual_result, an equity account) before proposing a close.

2026-10-03: analytics

New

  • Analytics (navigation bar, phone menu, Tell me): key figures, trends, breakdowns and what moved, for one company and a period (month, quarter, fiscal year to date, or custom dates) against the previous period or the same period last year. Every figure and every bar, point and row opens the report or list it adds up, with the same amount or count; each chart has a "Show as table" toggle. See Analytics and ADR-0030.
  • GET /v1/entities/{entity}/analytics/series: one metric per month, quarter or year over a range, from a closed set (revenue, expenses, net_income, cost_of_sales, operating_expenses, gross_margin_pct, cash, receivables, payables, working_capital, dso, dpo, receivables_aging, payables_aging), optionally split by report line or account. Monthly revenue adds up to the profit and loss for the range; month-end cash, receivables and payables are the trial balance's; an aging snapshot is the aging report as of that day.
  • GET /v1/entities/{entity}/analytics/kpis: ten figures with their change against a comparison range (compare=previous|year_ago) and a trend each. /analytics/breakdowns: top customers by revenue, top vendors by spend, revenue by currency, the expense mix. /analytics/insights: the flux and unusual entries rules for any range, with a plain sentence per flagged account made from its numbers. MCP tools analytics_series, analytics_kpis, analytics_breakdown, analytics_insights.
  • GET /v1/documents takes currency. Customer Invoices and Vendor Bills lists take party= and currency= in the URL and show how many documents they list; their open items can be as of a day (as_of=) and in one aging bucket (bucket=), with a total in the functional currency. The trial balance takes focus=receivables|payables; the income statement and balance sheet take line=.
  • GET /v1/entities/{entity}/reports/aging items have open_functional, and the report functional_currency and totals_functional (the open amount per bucket in the functional currency: the carrying amount the ledger holds, as the FX revaluation reads it; a payment waiting for approval reserves the open amount in the document currency but relieves nothing yet). A document that pending payments reserve in full stays in the list, flagged reserved with open zero, so the functional totals are complete; the report's reconciliation sets them against the control account's ledger balance (the revaluation postings on it, a memo of the payments awaiting approval, and what is unexplained). The open items and the aging chart show it as one line. Entry summaries of the analytics endpoints carry the entry id.
  • Migration 0024 adds a partial index on allocation (credit_note_id) (additive; safe for the previous release).

Behaviour that changes

  • The aging report is as of the day it names (breaking, bug fix). It used to subtract every payment and credit note ever made, so a report as of a past day showed payments made after it as already received. It now counts a settlement from the day of the payment or credit note. A report as of today is unchanged, except for a payment dated in the future, which no longer settles anything yet.

2026-10-03: groundwork for the depth series

New (ADR-0029, enabling work X1 to X7; no change to existing behaviour unless it says so)

  • Change an account: PATCH /v1/entities/{entity}/accounts/{code} (admin scope; policy manage_accounts) gives an account a role, takes it away, changes its statement line or deactivates it. A role must be one the catalogue knows and fit the account's type, a control role is refused on an account that already has postings from outside its subledger, and a change to or from a control or required role is a request for a second person (policy action change_required_account_role): the answer is 202 with an approval_id. The audit trail keeps the previous role (account.role.change). See account roles. Behaviour change: POST /v1/entities/{entity}/accounts (and the account import) now refuse a role the catalogue does not know or that does not fit the type of the account (422), and a control role marks the account as a control account.
  • Approval requests of a new kind: GET /v1/approvals can list requests with object_type account_role (the change above); their summary has account, current, proposed and reason.
  • Thirteen new approval policy actions and the sources ic, rev and appropriation (close existed): see API: approval policy. An organization's own policy that predates them gets the default's answer until it is saved again with them listed; a table saved now must list them (422 with problems otherwise). The new default rule close_entries_need_a_second_person holds the year-end entries for a second person.
  • A document line can be outside the scope of VAT: vat_scope (in_scope, the default, or outside) and vat_scope_reason (damages, vat_group, organschaft) on a document line, in the request and in the response (migration 0025, a defaulted column: the previous release keeps working on it). An outside line has no tax determination, rate 0, no VAT and no tax tag, so no VAT report or return sees it, and the invoice prints the remark of the reason in its language. See invoices and bills.
  • One-time catch-up difference at the next FX revaluation. The revaluation now also remeasures foreign-currency liability accounts (loans, accruals, deposits that carry a currency), which it skipped before although IAS 21.23 requires them. An entity that has such an account with a balance gets, at its next revaluation, one difference for the movement of the rate since the balance was booked (the entry reverses on the first day of the next period like the rest, so the difference does not accumulate again). The exposure view shows these accounts with the payables.
  • Internal, no API change: the close checklist is assembled from registered checks, one module per check (the same ids, titles and order as before); the FX revaluation is assembled from registered position providers (the ones the exposure view reads) and now also remeasures foreign-currency liability accounts; hooks for a posted document and for the reopening of a period exist for the capabilities to come.

2026-10-03: demo organizations can be removed

New (ADR-0022)

  • Every organization is a customer or a demo (tenant_registry.kind, migration 0023, default customer). Existing organizations stay customers. erp seed-demo creates demos; create-org and POST /v1/platform/organizations create customers. The API does not show the kind and the runtime role cannot change it.
  • erp platform orgs: every organization with its id, kind, state, creation time, entity codes and number of users, name in quotes.
  • erp platform mark-demo --org-id ... --confirm-name "..." --operator ... [--accept-activity]: turns an existing organization (a demo seeded before the kind existed) into a demo. Refused unless the name is exact. It prints the evidence of use first (users, entries and invoices and how many the demo seed did not create, invoices sent to customers, files archived as issued, bank statements, posting dates) and refuses an organization that looks like a customer's (a sent invoice, an archived output, a user who is not an administrator, entries by someone else than the seed) unless --accept-activity is given; the evidence and the flag are recorded.
  • erp platform purge-demo --org-id ... --confirm-name "..." --operator ... [--dry-run]: deletes a demo organization and every row of it (entities, ledger, documents, audit trail, users, sessions, keys) in one transaction, and nothing of any other organization. Refused for a customer and for a wrong name. --dry-run only reads: it reports the rows and takes no lock. A real purge locks the tenant tables for a few seconds (all at once, giving up after 5 seconds and changing nothing if a transaction is in the way): run it in a quiet window. The stored files of its documents stay in the bucket until their 11-year retention ends; their URIs are printed.
  • platform_audit records every mark and purge (operator, Cloud Run execution, time, organization, rows per table); nobody can change or delete it, and a purge does not remove it.
  • These commands run over the owner connection (ERP_MIGRATION_DATABASE_URL) as the erp-migrate job, never over the API; see the runbook.
  • No API change. Migration 0023 adds a defaulted column and a table: the previous release keeps working on it.

2026-10-03: exchange rates

New

  • Finance → Exchange Rates (/exchange-rates, also in Tell me): the ECB's rates against the company's currency on a date, the cross-rate matrix, a pair's history with a chart, a converter, the accounting rate beside the official VAT rate (BAZG for Switzerland, the BMF's monthly average from the Bundesbank for Germany), and the foreign-currency exposure with the unrealised result a revaluation would post. See the UI guide and the reference. English, German, French and Italian.
  • Seven read operations (scope read; reference data only, nothing changes the books), all under /v1/entities/{entity}/fx/: rates (every ECB currency against the functional currency, with the rate, its inverse, the fixing date used, whether the company uses the currency, and the source), matrix (cross rates, "1 row = x column"), history (a pair's daily rates, minimum, maximum, average, change and the IAS 21 average and closing rate of the latest completed month), convert, tax-rates (accounting rate against the official VAT rate per currency), exposure and exposure/items (open receivables, payables and foreign-currency balances by currency, and the positions behind each count, paginated).
  • Cross rates are triangulated through EUR: 1 A = (eur[B] / eur[A]) B from the ECB's fixing of the day, the division at full precision and the result rounded once to 10 decimals, half-even, exactly as data-vNext's /v1/fx/convert; the inverse is computed from the fixings, not as 1 over the rounded rate. A date without a fixing (a weekend, a holiday) uses the previous fixing, and the answer says so (fallback, fixing_date). Amounts convert half-even to the target currency's minor unit.
  • The exposure and the period-end FX revaluation read the same positions (modules/fx_positions.py): posted documents in a foreign currency that are still open at the date (payments and credit notes posted on or before it) and the balances of foreign-currency asset accounts, each valued at the date's ECB rate and rounded half up. At a period end totals.unrealised equals the balancing line of the revaluation entry. The revaluation itself is unchanged.
  • data-vNext: GET /v1/fx/history?base&quote&start&end (the daily ECB cross rate of a pair, at most 1830 days), and GET /v1/fx/rates and /v1/fx/convert now carry the ECB in meta.sources. Additive. The ERP and data-vNext are deployed from one image, so the history works from the same release.
  • No migration, no new table, no change to posting.

2026-10-02: document archive

New

  • Issued invoices are kept as issued (ADR-0020). The first GET /v1/documents/{id}/pdf (PDF or ZUGFeRD) and GET /v1/invoices/{id}/xrechnung of a posted invoice or credit note stores the exact bytes with their SHA-256; every later request for the same output (the same format, profile, language, qr_bill) returns them and renders nothing, so an auditor gets what the customer got, also after the invoice was paid or the customer's address was corrected. The mail send sends carries the same files. Another language, qr_bill or profile is another output, archived the same way. The content gate decides the first output; a stored one stays readable. Only a person with the post scope, in an organization that is not suspended, makes the first output: anyone else (a viewer, an auditor) gets 409 not_issued and nothing is stored.
  • GET /v1/documents/{id}/files, GET /v1/documents/{id}/files/{file_id}: every file of a document (original, archived outputs, attachments) with source, SHA-256, who stored it and when.
  • POST /v1/documents/{id}/files: attach a scanned or received original (PDF, PNG, JPEG, WebP, up to 20 MB, typed by its bytes) to any document: a hand-entered bill, an opening receivable or payable, a credit note. Append-only; a second file is another attachment with its own audit event.
  • POST /v1/documents/{id}/mark-sent (Mark as sent… on the document card): record that an invoice was handed over outside the system (your own mailbox, post, the customer's portal) in the sent log: channel, recipient, day and the SHA-256 of the archived file. GET .../dispatches items have channel and handed_over_on; subject and message_id are null for a recorded row, and format may be xrechnung.
  • GET /v1/archive/export?entity&start&end (Reports → Archive export): a streamed ZIP of every file of the entity's posted documents in the range with manifest.csv and manifest.json (expected_files, included_files; a ZIP without manifest.json is incomplete). New scope audit and role Auditor (read and audit); an administrator has it; an agent key cannot hold it. Migration 0022: the app_user scope check allows audit.
  • Several bills at once: Vendor Bills → Upload a bill takes several files, chosen or dropped, with one result row per file and a few uploads at a time; a file that fails does not stop the others.
  • Two files of one new vendor uploaded together no longer both create it: vendor creation from a document waits for any other in flight and then finds the vendor.

Behaviour that changes

  • A sales invoice's PDF is no longer re-rendered on each request: the first one of each output is kept (see above). Existing posted invoices are archived when they are first output after this release.
  • Stored files (document) are append-only in the database; bill capture stores what it read together with the file.
  • The documents bucket gets an 11-year retention policy (unlocked) and keeps versioning.

2026-10-02: chart templates

Behaviour that changes (new entities only, except where it says breaking; entities that exist keep their accounts and numbers)

  • The Swiss template (ch_kmu) follows the Schweizer Kontenrahmen KMU for the financial result: Finanzaufwand 6900, Finanzertrag 6950, bank charges 6940, FX losses 6949, FX gains 6999, unrealised differences 6948, rounding 6799. It had 6840, 6900, 6940, 6950, 6960 and 6990 (numbers KMU uses for depreciation and securities). The data-import clearing and the suspense accounts are 2298 and 2299, no longer 2998 and 2999 (in KMU 29 is equity). Names of 1170, 1171, 1509 and 1520 are the full KMU names; the generic template's 1700 and the Swiss 1170 had lost the part of their names after the first comma.
  • A gain and a loss on a disposal have their own accounts: the required roles asset_disposal_gain and asset_disposal_loss (Swiss 8514 and 8504, generic 7510 and 7520) replace asset_disposal (7900 and 7500). An entity that has only asset_disposal keeps booking both on it, and POST /v1/entities/{e}/role-accounts and the opening-balance import count it as both new roles and add no account.
  • Every class of mobile Sachanlagen has its own cost and Wertberichtigung in the Swiss template (1500/1509, 1510/1519, 1520/1529, 1530/1539).

Breaking (bug fix), all entities

  • POST /v1/assets now refuses (422) a cost_account without an accumulated_account and the other way round, and a pair that is not two different asset accounts. Before, the missing one silently defaulted to the entity's role account, so an asset on 1520 was paired with the accumulated depreciation of 1509. A client that names only one of the two must name both or neither.
  • The demo: the laptop is booked to 1520 and 1529, and Demo GmbH starts with its 25,000 EUR Stammkapital paid into the bank (it was overdrawn by 2,380 EUR).

2026-10-02: installable app

New

  • The web UI is a progressive web app (ADR-0021). Chrome and Edge offer Install vNext ERP (desktop and Android); on iOS use Share, Add to Home Screen. The installed app opens /app/ in a window of its own with the vNext icon and the header colour. How to install it: Install it as an app.
  • A service worker, scoped to /app/, caches the program files only (the built scripts, styles and icons, and the page shell). It never caches or answers an API call, a document or other download, or anything about sign-in: balances, entries and reports are fetched when you look at them. Page loads go to the network first; the cached shell is used only when there is no connection.
  • Offline banner. "You are offline" is shown above every page while the browser has no connection. What is on screen stays as loaded and may be out of date. A page opened offline says it needs a connection instead of showing the sign-in page.
  • Nothing is written offline and nothing is queued. Posting, approving and every other change is refused while offline with the message that nothing was sent, and is not sent later; try again when the connection is back.
  • "New version available". After a release, a window that stays open offers Reload or Later. The page never reloads by itself; if you typed on the page, Reload asks once more because reloading discards the input.

Behaviour that changes

  • GET /app/sw.js, /app/manifest.webmanifest and /app/index.html are served with Cache-Control: no-cache (they were cached for five minutes), with the content types text/javascript and application/manifest+json. A missing sw.js, manifest or built file (/app/assets/*.js, .css and the like) now answers 404 instead of the page shell, except for a page load: a path such as /app/assets/X.png (a fixed asset with that code) is a page of the UI and gets the shell.
  • The UI's Content Security Policy adds worker-src 'self' and manifest-src 'self'. Nothing else was widened.
  • Every /v1 and /mcp response now carries Cache-Control: no-store (errors included), so no browser, proxy or worker keeps a copy of the books. A response that sets its own header keeps it: file downloads still say private, no-store.
  • Sign-out is honest. Offline (or when the server cannot be reached) it is refused with the message that you are not signed out on the server, and you stay signed in; it no longer clears the screen while the session stays valid. After a sign-out the page is replaced, and a page the browser restores from its back/forward cache reloads, so the previous person's data is not shown again.

2026-10-02: setup and compliance

New

  • No invoice leaves without the content the law requires (ADR-0019). The PDF, send, ZUGFeRD and XRechnung answer 409 with the code invoice_content_incomplete and a missing list (party seller or buyer, field, basis, record_id) when the entity's or the customer's record lacks content the law requires: Switzerland (Art. 26 para. 2 MWSTG) the name and place of both parties and the seller's UID when it is VAT-registered; Germany (§ 14 para. 4 and § 14a UStG) full addresses, the seller's USt-IdNr or Steuernummer and, for a reverse charge or an intra-Community supply, the buyer's USt-IdNr; any other country both names and addresses.
  • missing_content on every sales document: the same list, so drafts, POST /v1/documents/preview and the document card warn instead of refusing.
  • Settings → Company Profile (/settings/company): the entity's legal name, VAT number or UID (format checked), address, contact and electronic address, with PATCH /v1/entities/{entity}. The document card lists what is missing with its provision and links to this page or to the customer.
  • Legal remarks in the document's language. The remark on an invoice (reverse charge, exempt supply, not subject to Swiss VAT) now comes from data-vNext with its legal basis in German, French, Italian and English (trace.legend of POST /v1/tax/indirect/determine). A Swiss export of services says it is not subject to Swiss VAT by Art. 8 para. 1 MWSTG; it no longer says "reverse charge" and no longer cites the OECD guidelines.
  • A VAT number of a Swiss or German entity, customer or vendor must have the format of a UID (CHE-123.456.788 MWST, separators optional) or USt-IdNr (DE and nine digits), with a correct check digit: 422 otherwise. A vendor read from a received document is recorded as printed; when its number fails the check, vat_unverified is true on the party (list, card and API, with a warning on the vendor card) until a valid vat_number is saved (migration 0021, a column with a default).
  • A VAT-registered Swiss seller must show its UID with the register designation (MWST, TVA or IVA), and a German invoice for a reverse charge to another member state or an intra-Community supply needs the seller's USt-IdNr as well as the buyer's (§ 14a para. 1 and 3 UStG); a Steuernummer meets § 14 para. 4 Nr. 2 only.
  • GET /v1/documents/{id}/dispatches is not gated: the sent log is readable whatever the state of the records (an empty log for a draft; it answered 409 for an unposted document before).
  • Finance → Period close in the web UI (/app/period-close): a controller closes a month without the API. The periods of a fiscal year with their status; a period's close view with the 13 checks (status and explanation, a link to the screen that fixes each failing one), buttons for the period-end runs (depreciation, FX revaluation, deferred tax) that say whether the entry was posted or awaits approval, Close period (enabled when no check fails; the server's refusal is shown) and Reopen with its mandatory reason. It uses the existing operations; there is no API change.

Behaviour that changes

  • Posted invoices whose records lack required content stop printing until they are completed.
  • The seller's address on the PDF and in the e-invoice is the entity's own; the bank account holder's address no longer stands in for it.
  • The seller's tax number on the invoice is named for the seller's country, not the document's: a Swiss seller's registered number is MWST-Nr. (de), No TVA (fr), N. IVA (it), VAT no. (en), a bare UID is UID / IDE / IDI, a German seller's is USt-IdNr., any other VAT-Nr. / N° TVA / N. IVA / VAT no. (it said USt-IdNr./UID on every invoice).
  • A German entity without a USt-IdNr prints and e-invoices the Steuernummer of its VAT settings (BT-32).
  • Documents posted earlier print their stored English legal notes as they are; a rule without a legend (the generic non-EU table) adds none to new documents.

2026-10-02: net tax rate split

Fixed

  • VAT return, net tax rate method. A tax amount spread over three or more activities was split from the already-reduced first part, so the second and later activities got the wrong share (100.00 over turnover shares 50/30/20 became 50/15/35 instead of 50/30/20). Totals were always right; the per-activity figures and the net tax computed from them were not. One or two activities were never affected. Returns prepared with three or more net-rate activities should be recomputed.

2026-10-02: customer onboarding

New

  • Platform API (ADR-0015). POST /v1/platform/organizations provisions an organization from templates (ch_sme, de_sme, generic: country, currency, chart, VAT method, fiscal calendar) with its role accounts and invites its first people by email, all or nothing and idempotent; GET /v1/platform/organizations (and /{org_id}) lists them; POST .../{org_id}/suspend makes one read-only and .../reactivate lifts it. It takes a platform key (erpp_..., scopes platform:read and platform:provision; erp platform-key create | list | revoke), which works only on /v1/platform: a tenant's key or session gets 401 there, and a platform key gets 401 everywhere else. A suspended organization's requests that change state answer 403 suspended.
  • Data import (ADR-0016). POST /v1/entities/{entity}/imports brings customers, vendors, accounts, opening balances and open receivables and payables in from CSV or Excel: a dry run lists every problem (line, column, message), a commit is all or nothing; GET .../imports/templates/{type} returns the template. Settings → Data Import in the UI. Opening balances are one entry per entity (source opening); open items are posted documents with their own numbers, booked against the new account role opening_balance_clearing, which is zero when the books agree.
  • bound_to on API keys and GET /v1/me: the person a key acts for, or null (ADR-0017, backlog B-36 closed).

Behaviour that changes

  • opening_balance_clearing is a required account role: both chart templates carry it (2998 Swiss KMU, 2980 generic) and new entities get it. For an existing entity the first import adds it (POST /v1/entities/{e}/role-accounts does the same on request).
  • The posting source opening may touch control accounts (the bank and fixed-asset balances of an opening trial balance); only the import creates it.
  • The default approval policy has a new rule, opening_balances_need_a_second_person: a person's opening entry (source opening) waits for a second person. A one-person organization allows post for the source opening in its own policy before it imports; the import's answer says so. Open items are unchanged. An existing organization keeps the policy it stored without the new rule until it resets it to the default (DELETE /v1/approval-policy).
  • Opening entries are no movement of any VAT return period: the VAT report and the return reconciliation exclude them, as they do settlements (the VAT of the old period stays with the old system). Swiss open invoices and bills are imported with a warning about agreed and collected fees (on collected fees the VAT and the input tax move to the payment date, Art. 40 para. 2 MWSTG).
  • The close checklist has a thirteenth check, opening_clearing: the opening balance clearing account is zero (not applicable without an import).
  • A suspended organization cannot post an entry or put a payment into a payment file even past the middleware: database triggers refuse it. The platform's routing decision uses the path the router matches (a root path, repeated slashes and percent-encoding cannot swap the sides).

Operations

  • Platform keys are issued and revoked with the owner connection (ERP_MIGRATION_DATABASE_URL); the runtime role has only SELECT and UPDATE (last_used_at) on platform_key (a schema test proves it).
  • Migration 0019_platform: platform_key and tenant_registry (both outside row-level security, listed in tests/erp/test_schema.py); existing organizations are backfilled as active. New dependency: openpyxl (MIT) for .xlsx.
  • Issue the first platform key with erp platform-key create "<name>" --key-secret projects/<project>/secrets/<secret> (the secret must exist; Terraform does not define one yet, see deployment).

2026-10-02: invoice output

New

  • Invoice PDF. GET /v1/documents/{id}/pdf prints a posted sales invoice or credit note on A4: company header, customer address and references (BT-10, BT-13), lines with VAT, VAT per rate, totals, payment terms with cash discount, notes. German, French, Italian or English by the customer's language. A Swiss entity's open invoice gets the QR-bill payment part. The PDF is a pure function of the document (its date is the creation date).
  • ZUGFeRD / Factur-X. ?format=zugferd (German entities) writes a hybrid PDF (PDF/A-3b by construction) with the EN 16931 or XRechnung XML embedded as factur-x.xml. Not yet verified for PDF/A-3: structure is tested, no validator runs yet, so zugferd is available on request and is not the default of a send, also for German entities (B-55 adds veraPDF to CI, then flips the default).
  • Sending. POST /v1/documents/{id}/send e-mails the PDF (and the XRechnung XML to a public customer with a Leitweg-ID); GET /v1/documents/{id}/dispatches is the sent log with what a send would do. Off until ERP_SMTP_HOST and ERP_MAIL_FROM are set: otherwise 409. Only a person sends: the new policy action send_document is denied to agents (a stored organization policy without it falls back to the default for this action). to may name only the customer's own addresses (e-invoice address, e-mail, contacts); another address is added to the customer first. message is at most 1,000 characters; an organization sends at most 60 messages an hour (429 rate_limited). A refused message answers 502 mail_failed and records nothing. A document created by the opening-balance import is never printed, sent or issued as XRechnung (its QR-bill shows the open amount only, with a reference in a namespace of its own).
  • Web UI. The document card has Download PDF, Download ZUGFeRD (German entities), a Send… dialog and a Sent tab; without a mail server the dialog offers the downloads.

Operations

  • Migration 0020_document_dispatch: the append-only document_dispatch table (forced row-level security; only a posted sales invoice or credit note).
  • New dependency reportlab (BSD-3-Clause) and, through it, pillow (MIT-CMU); the font is DejaVu Sans, bundled in src/erp/invoice_pdf/fonts/ with its licence and listed in THIRD-PARTY-NOTICES.md (Polish, Czech, Romanian names print as written). The image needs no system package.
  • New settings ERP_SMTP_HOST, ERP_SMTP_PORT, ERP_SMTP_SECURITY, ERP_SMTP_USERNAME, ERP_SMTP_PASSWORD, ERP_MAIL_FROM, ERP_SMTP_TIMEOUT_SECONDS (10 s: the mail server is called inside the request's transaction), ERP_MAIL_HOURLY_LIMIT (deployment).
  • The OpenAPI documents sort every enum array, so they no longer depend on import order (due_date_convention and similar changed order in docs/api and the UI client; no value changed).

2026-10-02: audit hardening

Behaviour that changes for users

  • Four eyes compare people (ADR-0011). Another key or the browser session of the requester is still the requester; a built-in agent counts as the person who ran it. Nobody approves a journal entry they drafted.
  • Segregation of duties. Who created a payment does not release it (policy action release_own_payments); who created or last computed a VAT return does not finalise it (finalize_own_vat_return). Both are denied by default; the refusal (403, code policy_denied) names the rule and the remedy.
  • The organization's first API key (issued by erp create-org, and every key created with it) no longer approves, releases payments or finalises VAT returns: it belongs to no known person. Use a signed-in user, or a key a user created.
  • One-person and demo organizations need a second user, or a policy that allows their own payments and VAT returns (deployment).

API

  • POST /v1/periods/{id}/reopen requires {"reason": "..."}. This is an intentional break of /v1, made on 2026-10-02 because reopening a closed period without a reason was a control gap; future breaking changes go to /v2.
  • 403 from the approval policy has code policy_denied with action and rule_id.
  • 429 with Retry-After after repeated failed authentications from one address.
  • Database errors no longer return the database's own message; they carry a request_id.
  • GET /v1/entities/{e}/chain-verification reports hash_versions.
  • Revoking another organization's API key answers 404 (it was a 204 that changed nothing).

Operations

  • Migration 0017_audit_hardening: journal_entry.hash_version (entries posted from now on are hashed over every column) and the security_event table.
  • On Cloud Run both APIs refuse to start with env other than cloud, ERP development sign-in, or data-vNext without API keys. The image no longer trusts X-Forwarded-For in uvicorn; the ERP reads the client address from the entry its own proxy appended (ERP_TRUSTED_PROXY_HOPS, default 1).
Stabiso, the Enterprise System of Record · generated from the code and its guides · release 6f03a5e846b3