TILL

io.github.tillbooksPublished by GitHub account tillbooksv1.0.0-beta.2Updated Nov 6, 2026

Agent-first open-source ERP for Swiss SMEs: 844 MCP tools, one local file.

Overview

AI-generated overview

TILL is a local, open-source ERP for Swiss SMEs that lets an assistant run sales, purchasing, banking, stock, projects and double-entry bookkeeping in one…

What you can do
  • Set up and configure a workspace: company and fiscal profile, chart of accounts, cost centres, VAT method and Swiss tax codes
  • Post, reverse, draft and read journal entries, and inspect numbering and the audit trail
  • Close, reopen and lock periods and fiscal years, and read period locks
  • Prepare Swiss VAT: configuration, Saldosteuersatz history, eligibility and declaration basis
  • Run the wider ERP: sales, purchasing, payments, bank import, contacts, products and stock, projects and reports
When to use it
Useful for a Swiss small business or its accountant that wants an AI assistant to keep the books and run day-to-day ERP work on a machine they control, with approvals before anything is booked. Worth a parallel run against the existing system first, since it is a public beta.
Requirements
Runs as a local stdio process via npx or a global npm install; needs Node.js 22 or newer on macOS or Linux. No account or API key. Books live in a SQLite file at ~/.till/till.db unless TILL_DB_PATH is set; TILL_SUPPORT_DIR holds diagnostics and backups. The Studio listens on 127.0.0.1 only.
Before you install
It writes real accounting data: postings, payments files, invoices and period or year closures, and posted entries are append-only, corrected only by reversing entries. Some areas can be set to act without asking, so check the approval settings. Books and anything the agent reads become part of the conversation with the AI provider. It is a beta, not tax advice, and filing stays with you.

Installation

In SourceWeft

  1. Open TILL in the dashboard and add it to a workspace.
  2. Enable the server for the chats that should use its tools.

Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.

Other MCP clients

Follow the launch instructions in the repository.

README

TILL

[npm (beta)] [MCP Registry] [Glama score] [License: MIT]

Trusted Independent Ledger Library. An agent-first ERP for Swiss SMEs.

TILL is a free, open-source (MIT) ERP for Swiss small businesses: sales, purchasing, payments, stock, projects, books and MWST. Every function is exposed as an MCP tool, 844 of them, so your AI agent runs it and the Studio (the app you work in) shows you what it did. It works with Claude Desktop, Cursor and other MCP clients that can start a local stdio server. TILL runs on your own computer, or on a server you host yourself. Your books are a single SQLite file on that machine, and no server of ours holds a copy.

By default, the agent waits for your OK before it books anything, moves your money, grants access or deletes your data for good: it drafts, and you approve. For each of those areas you decide whether it may act on its own. Much of what you can change back, such as drafts and contact details, runs without asking, and every call the agent makes is recorded. The dial covers agents that connect to TILL. An agent that can drive your browser or run commands on your computer can act as you: give your agent TILL, not your browser or your shell.

Public beta, version 1.0.0-beta.2. Nomadik GmbH, the company behind TILL, keeps its own books in it. If you move your books to TILL, keep your old system running until the figures match, and read what the beta does not do yet first.

Why

TILL is MIT licensed and costs nothing. You can run it, change it and keep it, with no limit on users, companies or bookings, and your books stay on a computer you control. It is built for what makes Swiss books Swiss:

  • QR-bill (Swiss QR-Rechnung) with the structured-address standard
  • MWST at 8.1%, 3.8% and 2.6%, with the effective and the Saldosteuersatz method, and the MWST-Abrechnung drawn straight from the ledger
  • Kontenrahmen KMU seeded out of the box
  • ISO 20022 banking: camt.053 statements in, pain.001 payment files out
  • de-CH and en

What TILL covers

  • Accounting: double-entry ledger, period locks and year close, foreign currencies and revaluation, accruals, fixed assets, and several companies in one install.
  • MWST: the MWST-Abrechnung per period, reconciled to the ledger, and the eCH-0217 file you upload to the ESTV ePortal.
  • Sales: quotes, orders, delivery notes, invoices with the QR-bill, credit notes, recurring invoices, reminder letters and the open-items list.
  • Purchasing: supplier bills with input VAT, purchase orders, goods receipt, expense claims, and pain.001 payment files that you release in your eBanking. TILL never sends a payment.
  • Bank: import the camt.053 statements from your eBanking. The bank inbox proposes a booking for each line it can place, and you confirm it.
  • Contacts and CRM, products and stock (warehouses, lots and serial numbers, stock valuation), projects and time tracking, documents, reports.
  • Moving in: import from bexio's CSV and Excel exports (there is no bexio API connection), a trial company to try the import on, and a parallel run that checks TILL's figures against your old system. Fixed assets, stock and payroll history do not come over, and last year's journal lands in a read-only archive beside the live books.

The ledger is not negotiable

Posted entries are append-only and immutable. A correction is a reversing entry, never a destructive edit, exactly as real accounting works. Posting is idempotent: a double-post does not double-count. These are asserted in the test suite, not merely promised in a README.

Install and run

You need Node.js 22 or newer on macOS or Linux. The quickest way in is to let your agent do it: tillbooks.ch/en/start has a setup prompt for Claude Code (terminal or desktop app), Cursor or Windsurf: it installs TILL, starts the Studio, asks you to open the demo company and connects itself. By hand, it is two commands:

bash
npm install -g tillbooks@betatill up

Keep the @beta. Until the beta ends, a plain tillbooks on npm is the old 0.1.0, which has no Studio.

till up starts the Studio at http://127.0.0.1:8788 and opens it in your browser. It listens on your own machine only. The first screen offers three ways in: create a new ledger, restore a backup, or restart TILL on an existing database file. Beneath them, Try the demo sets up a demo workspace with sample postings. Your books go to ~/.till/till.db; set TILL_DB_PATH to put them somewhere else.

To connect an MCP client such as Claude Desktop or Cursor, point it at the stdio server. No account and no API key are needed:

json
{  "mcpServers": {    "tillbooks": {      "command": "npx",      "args": ["-y", "tillbooks@beta", "mcp"]    }  }}

In Claude Code, the same server is one line:

bash
claude mcp add tillbooks -- npx -y tillbooks@beta mcp

Or install it as a plugin, which pins the server to the current beta: in Claude Code, /plugin install tillbooks --marketplace tillbooks/tillbooks. The same plugin folder works in Cursor.

The agent and the Studio work on the same file, so what the agent does shows up in the Studio.

To work on TILL itself, build it from a checkout:

bash
npm run setup            # installs the core and the Studio dependenciesnpm run build:all        # builds the core into dist/ and the Studio into app/dist/node bin/till.mjs up

CONTRIBUTING.md has the full dev setup, and the quickstart walks through the first run.

Your data

  • Your books are one SQLite file. You can copy it, back it up and open it with standard tools.
  • No account, no sign-up, no telemetry. TILL does not report on you or on how you use it. Error details are recorded only if you switch that on, they stay on your machine, and they leave it only in a feedback mail you send yourself.
  • You can export a whole company at any time in an open, documented format, and a backup restores into a fresh workspace without touching the ones you have.

Privacy Policy

TILL is software you run yourself. Nomadik GmbH, which publishes it, does not run a server for it and receives nothing from it. This covers the tillbooks package, its MCP server, the Studio and the plugin in plugins/tillbooks.

  • Data collection. TILL collects no data about you or how you use it: no account, no sign-up, no telemetry, no analytics. Error details are recorded only if you switch that on.
  • Usage and storage. What you enter (your company, contacts, documents and postings) is stored in one SQLite file on your machine, ~/.till/till.db or the path in TILL_DB_PATH. Diagnostics, backups and feedback reports you create stay in ~/.till or TILL_SUPPORT_DIR. TILL itself opens no outbound connection: the MCP server talks over stdio and the Studio listens on 127.0.0.1 only.
  • Third-party sharing. TILL shares nothing with anyone. Two things leave your machine because other software sends them: npm or npx downloads the package from the npm registry, under npm's terms, and what your agent reads through TILL's tools becomes part of your conversation with your AI provider, under that provider's terms. Files TILL writes for you (an eCH-0217 return, a pain.001 payment file, an invoice PDF, a feedback report) leave only when you upload or send them yourself.
  • Data retention. Your data stays on your disk until you delete it. Posted entries are append-only, as Swiss bookkeeping requires; a correction is a reversing entry. Deleting the database file and ~/.till removes everything TILL stored. Keeping your books for the statutory period (OR Art. 958f) is your responsibility.
  • Contact. Nomadik GmbH, Männedorf, Switzerland, [email protected]. The privacy statement for the tillbooks.ch website is at tillbooks.ch/en/legal.

What the beta does not do yet

  • No payroll. TILL exports your employee data for your payroll provider and posts the wage journal you bring back. Pay slips, AHV and Swissdec stay with the provider.
  • No MWST filing. TILL prepares the MWST-Abrechnung and writes the eCH-0217 file. You upload it and file in the ESTV ePortal yourself.
  • No MWST on received consideration (vereinnahmte Entgelte). TILL computes the Abrechnung on agreed consideration (vereinbarte Entgelte) and refuses a period set up the other way.
  • No email sending yet. TILL makes the PDF of an invoice, quote or reminder, and you send it from your own mail program. Sending through your own mail account is planned for a later beta release.
  • German (de-CH) and English only. No French or Italian yet.
  • No mobile app, and no Windows app. We do not test on Windows; the documented route there is running TILL inside an agent.
  • No direct bank connection in this release. The EBICS client is built and has been tested with ZKB's test platform, but the module that connects it to the bank is not published yet (it will be, under MIT). Until then you download camt.053 statements from your eBanking and import them.
  • On your own computer, backups run when you start them (Data & Backup, "Back up now"), not on a schedule. Keep a copy of the backup file somewhere other than the computer the books are on. (The self-hosting recipe replicates the book continuously with Litestream.)
  • Several people in one book only on a server you host yourself. The local app is for one computer.
  • No eBill sending, e-signature or online customer portal.

Documentation

Before you use it

TILL keeps books, so DISCLAIMER.md is worth two minutes: TILL is not tax advice, you stay responsible for your own filings, and a beta deserves a parallel run before you rely on it.

Help is free and best effort during the beta, and paid help with setup and with moving from bexio is available. SUPPORT.md has the details.

Contributing

License

MIT, Copyright (c) 2026 Nomadik GmbH. See LICENSE.

TILL is an independent project. It is not affiliated with, endorsed by, or derived from bexio AG or any other accounting vendor, and it copies no code, UI or trade dress from any of them.

Source: README.md at commit 15cf0d3

Tools

200

create_workspace

Mint a new workspace (tenant) with its KMU chart of accounts. A new workspace starts with a 30-day default Zahlungsfrist (defaultPaymentTermsDays, 0 to 365; null for none).

ParametersnamerequiredlegalFormbaseCurrencyidempotencyKeyfiscalYearStartdefaultPaymentTermsDays

bootstrap_workspace

One-call agent setup: parse a short description into a workspace and VAT config.

ParametersnamedescriptionrequiredidempotencyKeyrequired

set_fiscal_config

Set legal form, base currency, or fiscal year start (currency/year lock once the ledger is non-empty).

ParameterslegalFormworkspaceIdrequiredbaseCurrencyfiscalYearStart

set_vat_method

Set the VAT method (effektiv/saldo) and accounting timing (soll/ist). CONSEQUENCE: Changes how MWST is worked out for your returns: effective or net tax rate method, agreed or received payments. You can set it back, but the earlier setting is not kept.

ParametersvatMethodrequiredworkspaceIdrequiredvatAccountingrequired

set_creditor_profile

Set the QR-bill creditor: IBAN (a QR-IBAN gives a QRR reference, a plain IBAN gives SCOR), optional name (defaults to the company name) and optional structured address. The IBAN saves without the address; the address is needed before the first QR-bill renders (buildQrBill answers needs_creditor_address until then). A partial address is refused (needs_structured_address). CONSEQUENCE: Changes the account every QR-bill is paid into, including the QR-bills of invoices already open when they are downloaded, sent again or dunned. A customer who already paid is not affected.

ParametersibanqrIbanaddressworkspaceIdrequiredcreditorName

get_company_profile

Read the workspace company & fiscal profile.

ParametersworkspaceIdrequired

update_company_profile

Update company identity fields (name, legal form, UID, MWST number) and the workspace default Zahlungsfrist (defaultPaymentTermsDays, 0 to 365 days; null clears it). A document whose customer or supplier has no terms of its own falls due that many days after its date. CONSEQUENCE: Changing the MWST number also changes it on the QR-bills of open invoices when they are downloaded or sent again. You can change it back; what customers already received keeps the old number.

ParametersuidnamemwstNolegalFormworkspaceIdrequireddefaultPaymentTermsDays

create_account

Create a ledger account.

ParametersnamerequiredtyperequirednumberrequiredworkspaceIdrequiredidempotencyKeyvatCodeDefaultcostCenterAllowed

update_account

Rename an account or change its VAT default, its cost-centre flag, or statementIsVoucher ("Beleg = Kontoauszug": the bank statement is the voucher, so a bank line booked to such accounts alone is not counted as missing a Beleg; on by default for 6900 Finanzaufwand). Number and type are frozen.

ParametersnameaccountIdrequiredworkspaceIdrequiredvatCodeDefaultcostCenterAllowedstatementIsVoucher

archive_account

Archive an account (soft; the account keeps its postings).

ParametersaccountIdrequiredworkspaceIdrequired

unarchive_account

Reactivate a soft-archived account (idempotent).

ParametersaccountIdrequiredworkspaceIdrequired

delete_account

Hard-delete an account that never carried a posting. CONSEQUENCE: Deletes the account from your chart for good; only an account that never carried a booking can go. You can create it again, but not restore it.

ParametersaccountIdrequiredworkspaceIdrequiredidempotencyKey

list_accounts

List accounts, optionally filtered by search text. savedViewId applies a saved view (G00): its stored filters are merged underneath any filter named explicitly here.

ParameterssearchsavedViewIdworkspaceIdrequiredincludeArchived

create_cost_center

Create a cost centre (Kostenstelle).

ParameterscoderequirednamerequiredworkspaceIdrequiredidempotencyKey

archive_cost_center

Archive a cost centre (soft).

ParametersworkspaceIdrequiredcostCenterIdrequired

unarchive_cost_center

Reactivate a soft-archived cost centre (idempotent).

ParametersworkspaceIdrequiredcostCenterIdrequired

delete_cost_center

Hard-delete a cost centre that no posted line references. CONSEQUENCE: Deletes the cost centre for good; only one that no booking uses can go. You can create it again, but not restore it.

ParametersworkspaceIdrequiredcostCenterIdrequired

list_cost_centers

List cost centres. savedViewId applies a saved view (G00): its stored filters are merged underneath any filter named explicitly here.

ParameterssavedViewIdworkspaceIdrequiredincludeArchived

post_entry

Post a balanced double-entry journal entry (business sources only; reversal/close are separate tools). Instead of lines, template { standardId, asks } books a Vorlage (a Buchungsregel of scope manual, list_bank_standards): asks { legIndex: amountMinor } fills its asked legs (gross on a taxed leg), a same leg copies the asked leg it names, the rest leg balances the entry on its own side, and the lines are composed and judged exactly as compose_entry with that template answers them (the entry text defaults to the Vorlage name). Template refusals: rule_not_found, rule_invalid (an account or code the Vorlage names is no longer valid: archived, now a Bankkonto ledger, a code gone; invalidReason says which), wrong_scope (a bank rule), ask_missing, rest_is_zero, rest_changes_side; lines and template together are invalid_input. CONSEQUENCE: Books this in your accounts. If it is wrong, you correct it with a new correcting entry; both stay visible.

ParametersrefdaterequiredlinessourcerequiredentryIdtemplatedescriptionworkspaceIdrequiredidempotencyKeyrequired

reverse_entry

Reverse a posted entry by posting its faithful mirror (OR 957a). Refuses `owned_by` on an entry a capability minted with a row of its own (an MWST-Saldierung, an Abgrenzung, a Rückstellung, a payment, a bank-line booking, a vendor bill, an issued invoice or credit note, a depreciation run, an inventory valuation run, and their mirrors): reverse those through the owning verb the refusal names (`vat_settlement_reverse`, `accrual_reverse`, `provision_reverse`, `provision_release_reverse`, `reverse_payment`, `reopen_bank_line`, `void_vendor_bill`, `transition_document` to cancelled, `asset_depreciation_run_reverse`, `inventory_valuation_reverse`) so the row moves with the entry. An entry a bank line holds (booked or matched on it) always refuses `owned_by reopen_bank_line` with that line's `bankTxnId`. CONSEQUENCE: Books a correcting entry that mirrors a posted one; the original stays visible beside it. If the correction was wrong, you correct it the same way.

ParametersdateentryIdrequireddescriptionworkspaceIdrequiredidempotencyKeyrequired

save_draft

Create or update a draft journal entry (no money effect until posted).

ParametersrefdaterequiredlinesrequiredentryIddescriptionworkspaceIdrequiredidempotencyKeyrequired

delete_draft

Delete a draft journal entry (never a posted one). CONSEQUENCE: Deletes the unposted draft entry for good; nothing was booked. To book it after all, you enter it again.

ParametersentryIdrequiredworkspaceIdrequiredidempotencyKeyrequired

get_entry

Read a journal entry and its lines. The entry reports its Belegnummer (number, null on a draft). Each line reports the transaction amounts (debit, credit) under its currency, and the amounts the books hold (baseDebit, baseCredit) under baseCurrency, which is the workspace base currency.

ParametersentryIdrequiredworkspaceIdrequired

list_journal

List journal entries, filtered by date range, account, source, status, or Belegnummer (number: an exact match on the number as shown, or for a plain integer every series' entry with that number). Each entry reports its Belegnummer (number, null on a draft), the currency its total is in (null when the entry has no lines), plus the base-currency total, rate and base currency for a foreign-currency entry, all derived from its posted rows. savedViewId applies a saved view (G00): its stored filters are merged underneath any filter named explicitly here.

ParameterstofromnumbersourcestatusaccountsavedViewIdworkspaceIdrequired

compose_entry

Compose a journal entry without booking it. mode "expand" turns lines into the exact post_entry lines: an intent line {account, side, amount, gross, taxCode?, supplyDate?, costCenter?} is expanded with its MWST legs (Vorsteuer, Umsatzsteuer, Bezugsteuer, Einfuhrsteuer, the saldo fold, a refund mirrored), a plain line {account, debit|credit, taxCode?, taxBase?, taxAmount?} passes through, and the answer carries the lines, a caption per taxed line and the verdict post_entry would give (vat_trace_unreconciled, period_locked, unbalanced, ...). mode "decompose" reads a stored entry (entryId) or stored lines back into intent and says whether it roundTrips. A bank line instead: mode "expand" with bankTxnId and target (a book_bank_line target of kind accounts, viaAccountId included, or rule with its asks; a computed leg needs no ask) answers exactly the lines book_bank_line would post for it, the rule expanded on the line (target: the accounts target it expands to, computed amounts filled in), and its refusal as the verdict (rule_not_found, ask_missing, rest_is_zero, legs_do_not_sum, line_not_open, ...). D178: EVERY book_bank_line target previews this way (open_item with documentId or allocations, payment_batch, existing_entry, contact, expense_claims, accounts with fx legs), and a bank line's answer carries booking { targetKind, bookable, blockedBy (the refusal code book_bank_line would give now, else null), verdict, deviationMinor (line minus legs, allocations or claims), remainder, remainderOptions (what remainder may be now), documents [{ documentId, kind, contactId, openMinor, allocatedMinor, writeOffMinor, openAfterMinor }], contact { contactId, name, counterpartyKind, mode, settingMode, overridden, accountNumber }, claims [{ claimId, title, employeeId, status, totalMinor }], fx { currency, foreignMinor, baseMinor, rate, conversionLines }, entries [{ currency, lines }] (every entry the booking would post, with accountNumber and CHF per line), warnings, consequences [{

ParametersdatemoderequiredlinestargetentryIdtemplatebankTxnIdworkspaceIdrequired

journal_numbering_plan

Inspect the Belegnummer run of the book without writing anything: the numbering settings in force (ledger.belegnummer_mode, _start, _prefix, _split_voucher), how many posted entries are numbered, unnumbered and adopted, whether a catch-up is owed, every series with its next number, the migrated entries (source import) classified by their ref (plain integers and bexio refs, the highest migrated number, the split vouchers, the duplicates: claims that do not read their old number because different vouchers claim it, with the entry that keeps it, and groups of entries with identical content flagged as a possible double import for a person to check, and the non-numeric refs), and the numbers the next catch-up would assign to the posted entries still without one. Lists are capped at 200 rows, each with its total.

ParametersworkspaceIdrequired

close_month

Soft-close a month (reversible guardrail). CONSEQUENCE: Closes the month, so nothing more can be booked into it. You can reopen it unless it has been hard-locked since.

ParametersperiodrequiredworkspaceIdrequiredidempotencyKeyrequired

reopen_month

Reopen a soft-closed month (a hard-sealed month refuses). CONSEQUENCE: Reopens a closed month, so bookings into it are accepted again. You can close it again at any time.

ParametersperiodrequiredworkspaceIdrequiredidempotencyKeyrequired

close_year

Hard-close a fiscal year: sweep the P&L into equity and seal the year. Refuses year_already_closed on a sealed year. While an open year_close checklist run exists for the year, a call WITHOUT runId refuses year_close_in_progress (naming yearRunId): the run seals the year from its seal row, which calls close_year WITH its runId. A call with runId runs only for an open year_close run of this workspace for exactly this year whose year_sealed row is unblocked (not_found, run_abandoned, run_not_year_close, run_year_mismatch, prerequisite_open otherwise). CONSEQUENCE: Closes the fiscal year for good: the profit or loss moves into equity and nothing more can be booked into that year. There is no reopening; a mistake found later is corrected in the following year.

ParametersyearrequiredrunIdworkspaceIdrequiredidempotencyKeyrequired

lock_period

Lock a month or year, soft or hard, with an optional reason. The seal reasons (vat_filed, year_close) are reserved and refused with reserved_lock_reason: a filed MWST period is sealed by vat_mark_filed and a fiscal year by close_year, never by a lock. CONSEQUENCE: Locks the period, so nothing more can be booked into it. A soft lock can be lifted at any time; a hard lock only by someone with the right to unlock.

ParameterskindrequiredperiodrequiredreasonworkspaceIdrequiredidempotencyKeyrequired

unlock_period

Unlock a period (a filing/year seal refuses). CONSEQUENCE: Lifts the period lock, so bookings into it are accepted again; a filed MWST period or a closed year stays sealed. You can lock it again at any time.

ParametersperiodrequiredworkspaceIdrequiredidempotencyKeyrequired

get_audit_log

Read the tamper-evident audit trail and its chain-verification status.

ParameterstofromentityKindworkspaceIdrequired

list_period_locks

List all period locks for the workspace.

ParametersworkspaceIdrequired

vat_seed_defaults

Seed the default Swiss tax-code set (idempotent).

ParametersworkspaceIdrequired

vat_configure

Configure VAT: method, timing, registration, VAT number, and the ESTV Bewilligung (asOf picks the statutory rate era). A filer holding several Saldosteuersätze maps each Tätigkeit to its Ertragskonten with saldoActivities, which is how MWSTV Art. 84 Abs. 3 books the Erträge separately per rate. Changing an approval that already governs posted turnover must say which it is: saldoGrant for a new ESTV Bewilligung from a given day, saldoCorrection to rewrite the open one. A change of method or timing is dated with methodChange: { validFrom }, a 01.01, the start of a Steuerperiode (MWSTG Art. 37 Abs. 4, Art. 39 Abs. 3). The one mid-year exception is MWSTV Art. 81 Abs. 2: Saldo to effektiv (timing may change too, Abs. 5) on the day the ESTV adjusted a Saldosteuersatz of this book, stated with basis: mwstv_81_2 on a day a saldoGrant took effect. Any other mid-year day refuses vat_method_change_not_period_start, naming what is unmet. CONSEQUENCE: Sets your MWST registration, method, timing, VAT number and ESTV approval. Earlier approvals stay on record; one already used for a filed period is not rewritten, you record a new one.

ParametersasOfmethodrequiredtimingrequiredvatNumberregisteredrequiredsaldoGrantsaldoRatesworkspaceIdrequiredmethodChangeidempotencyKeyrequiredsaldoActivitiessaldoCorrection

vat_config

Read the current VAT configuration, including the Saldosteuersätze in force today, each Tätigkeit with the Ertragskonten mapped to it, and the elected MWSTV Art. 88 Abs. 6 declaration basis.

ParametersworkspaceIdrequired

vat_saldo_generations

The Bewilligungsverlauf: every ESTV Saldosteuersatz approval this workspace has recorded, oldest first, with the days each governed and the Tätigkeiten and Ertragskonten it carried. Under Saldo no rate is stamped on a journal line, so this history is the only evidence of what a filed period was computed with.

ParametersworkspaceIdrequired

vat_saldo_eligibility

The MWSTG Art. 37 Abs. 1 Saldo eligibility limits in force today (both halves of the cumulative test, era-scoped, boundary 1.1.2024) beside the measured steuerbarer Umsatz of one calendar year (Ziffer 299, computed by the same path as the Abrechnung). Never a verdict: eligibility turns on EXPECTED turnover (ESTV practice, MWST-Info 12) and on a tax-due half that needs a rate the ESTV has not granted yet, so this read compares and stops.

ParametersyearworkspaceIdrequired

vat_saldo_declaration_basis

Elect, or withdraw, the MWSTV Art. 88 Abs. 6 simplification for one Steuerperiode: `highest_rate` declares the whole taxable turnover at the highest approved Saldosteuersatz on one Ziffer, `per_activity` restores the Abs. 1 split per Tätigkeit. It is voluntary and usually raises the tax due, so the engine never applies it on its own. CONSEQUENCE: Chooses whether this tax year's net-rate turnover is declared at the highest approved rate or split per activity; the first usually raises the tax. You can withdraw it until a period of the year is filed.

ParametersbasisrequiredtaxPeriodrequiredworkspaceIdrequiredidempotencyKeyrequired

vat_codes

List tax codes (active only by default).

ParametersworkspaceIdrequiredincludeArchived

vat_code_upsert

Add or edit a tax code at the single tax enumeration point.

ParameterscoderequiredkindrequiredlabelrateBprequiredformLinerequiredvalidFromworkspaceIdrequiredidempotencyKey

vat_code_deactivate

Archive a tax code (never deletes; a posted line always resolves its code).

ParameterscoderequiredworkspaceIdrequired

vat_code_reactivate

Reactivate an archived tax code, so it appears again on new documents. The exact mirror of vat_code_deactivate; posted lines never depended on the flag.

ParameterscoderequiredworkspaceIdrequired

account_set_tax_default

Set (or clear) an account's default tax code.

ParameterstaxCodeaccountIdrequiredworkspaceIdrequired

vat_preview

Preview a line VAT (net/tax/gross, ESTV form line, kind, deductibility) without posting.

ParameterstaxCodesupplyDateamountMinorrequiredworkspaceIdrequiredamountIsGross

vat_return

Compute the MWST-Abrechnung for a period: ESTV form lines with base/tax, the payable or credit, and the drill-down entry ids.

ParametersperiodEndrequiredperiodStartrequiredworkspaceIdrequired

vat_export_ech0217

Export the MWST-Abrechnung for a period as an eCH-0217 v2.0.0 XML file for upload to the ESTV ePortal. Produces a file and transmits nothing: there is no ESTV submission API.

ParametersperiodEndrequiredperiodStartrequiredworkspaceIdrequiredtypeOfSubmission

vat_periods

List the statutory reporting periods of a year (quarterly under effektiv, semi-annual under Saldo) and whether each is filed. In a year the method switched under MWSTV Art. 81 Abs. 2 each method lists its periods inside its own era, and the part of a period the switch cut is listed under stubs (computed by dates, not sealed).

ParametersyearworkspaceIdrequired

vat_mark_filed

Record that a VAT period was filed with the ESTV, applying the A03 hard lock (reason vat_filed) to its months in one transaction: a month under a soft or an ordinary hard lock is upgraded to the seal, every month is read back, and the filing refuses filing_seal_not_held (and rolls back) unless each month holds the seal. Only a filing period as vat_periods lists it for that year can be marked: any other label refuses not_a_filing_period, and a label touching a switch-year stub refuses switch_year_stub_not_modelled, sealing nothing. Transmits nothing. CONSEQUENCE: Records that you filed this MWST period with the ESTV and seals its months for good. TILL sends nothing; later corrections go to the ESTV as a correction return.

ParametersperiodrequiredworkspaceIdrequiredidempotencyKeyrequired

create_contact

Create a customer/vendor contact. paymentTermsDays is its own Zahlungsfrist in days: omit it and its documents fall due after the workspace default (update_company_profile), 0 means due immediately.

ParametersnameemailaddresspartyRolerequiredvatNumberdescriptionworkspaceIdrequiredidempotencyKeydefaultCurrencypaymentTermsDays

update_contact

Update a contact from a patch. patch.paymentTermsDays null clears its own Zahlungsfrist back to the workspace default; 0 means due immediately.

ParameterspatchrequiredcontactIdrequiredworkspaceIdrequired

archive_contact

Archive a contact (soft).

ParameterscontactIdrequiredworkspaceIdrequired

unarchive_contact

Reactivate a soft-archived contact (idempotent).

ParameterscontactIdrequiredworkspaceIdrequired

get_contact

Read a contact.

ParameterscontactIdrequiredworkspaceIdrequired

list_contacts

List contacts, filtered by query or party role. savedViewId applies a saved view (G00): its stored filters are merged underneath any filter named explicitly here.

ParametersquerypartyRolesavedViewIdworkspaceIdrequiredincludeArchived

create_item

Create an invoicing item (a reusable line default).

ParametersnamerequiredunitcurrencyworkspaceIdrequireddefaultTaxCodeidempotencyKeyrevenueAccountIddefaultUnitPriceMinorrequired

update_item

Update an item from a patch.

ParameterspatchrequireditemIdrequiredworkspaceIdrequired

archive_item

Archive an item (soft).

ParametersitemIdrequiredworkspaceIdrequired

unarchive_item

Reactivate a soft-archived item (idempotent).

ParametersitemIdrequiredworkspaceIdrequired

get_item

Read an item.

ParametersitemIdrequiredworkspaceIdrequired

list_items

List items, filtered by query. savedViewId applies a saved view (G00): its stored filters are merged underneath any filter named explicitly here.

ParametersquerysavedViewIdworkspaceIdrequiredincludeArchived

create_document

Create a document draft (quote, order, invoice, or credit note) with positions.

ParameterstyperequiredlinesnotesdueDatecurrencycontactIdsupplyDateworkspaceIdrequiredidempotencyKey

update_document

Patch a draft document (draft-only; an issued document is immutable and corrected by reversal).

ParameterspatchrequireddocumentIdrequiredworkspaceIdrequiredidempotencyKey

transition_document

Advance a document (issue, send, accept, decline, confirm, cancel); issuing an invoice posts via the delegate. Accepting a quote is quotes_accept: an expired quote refuses quote_expired, the acceptance is stamped, and the customer link is spent. CONSEQUENCE: Moves the document to its next status: issuing numbers it and books an invoice or credit note, accepting makes a quote binding. A wrong issued invoice is cancelled with a correcting entry or corrected by a credit note.

ParameterstorequireddocumentIdrequiredworkspaceIdrequiredidempotencyKey

convert_document

Convert an accepted quote to an order or invoice (or a confirmed order to an invoice), linking the source.

ParameterstoTyperequireddocumentIdrequiredworkspaceIdrequiredidempotencyKey

get_document

Read a document with its positions and status trail. For an invoice, include 'qr' and/or 'pdf' to attach the Swiss QR-bill payload and the rendered PDF (A11, D14).

ParametersincludedocumentIdrequiredworkspaceIdrequired

list_documents

List documents, filtered by type, status, contact, credited invoice (A13: the credit notes of invoice X), or date range. savedViewId applies a saved view (G00): its stored filters are merged underneath any filter named explicitly here, so an explicit one always wins.

ParameterstofromtypestatuscontactIdsavedViewIdworkspaceIdrequiredcreditedDocumentId

issue_invoice

Issue a draft invoice: assign the gap-free number, post the balanced VAT entry (A10 onIssue delegate), and attach the QR-bill summary. CONSEQUENCE: Issues the document with its number and books it; from then on it can't be edited. You correct it with a credit note or a cancellation, and the original stays on record.

ParametersinvoiceIdrequiredworkspaceIdrequiredidempotencyKey

send_invoice

Send an issued invoice by email (renders + attaches the PDF, transitions to sent). Outbound step is P8-gated: pass confirmed=true or enable the workspace dial. CONSEQUENCE: Sends it to the recipient. Once it is out, it can't be called back; a correction goes out separately.

ParametersemailconfirmedinvoiceIdrequiredworkspaceIdrequiredidempotencyKey

create_credit_note

Draft a Gutschrift against an issued invoice: full, selected positions (reduced quantities), or a net amount apportioned over the remaining line nets. Always a draft; issuing is a separate, confirmed step (P8).

ParametersmodelinesreasonamountMinorworkspaceIdrequiredfromInvoiceIdrequiredidempotencyKeyrequired

issue_credit_note

Issue a drafted Gutschrift: assign the gap-free G-number and post the mirror entry (debit revenue and output VAT, credit 1100 Debitoren) dated today, closing against the invoice per rate class (D67/D71). CONSEQUENCE: Issues the credit note with its number and books it against the invoice; from then on it can't be edited. If it is wrong, you cancel it, which books a correcting entry.

ParametersworkspaceIdrequiredcreditNoteIdrequiredidempotencyKeyrequired

get_ebill_config

Read the workspace eBill configuration (the eBill-Biller-ID the owner received from a certified network partner at enrollment), or null when none is set. Enrollment happens with the partner, not in TILL.

ParametersworkspaceIdrequired

set_ebill_config

Record the eBill-Biller-ID after enrolling with a certified network partner (a commercial step outside the product). Validates the SWP billerPid shape (41 followed by 15 digits) and upserts the one config row per workspace; a malformed id returns invalid_biller_pid and persists nothing. Naturally idempotent (asserts an absolute state), so it takes no idempotency key. Posts nothing.

ParametersbillerPidrequiredworkspaceIdrequired

ebill_prepare

Turn an issued invoice (issued, sent, or partially_paid) into an eBill delivery payload on disk, carrying A11's QR reference unchanged. Files the A11 PDF as an E00 document (entity_kind ebill_delivery, OR 958f retention) and records an ebill_deliveries row (status prepared) with the payload's conformance facts (PDF/A profile, eBill addressing, byte length) recorded, never claimed. Refuses a draft/cancelled/settled invoice (invalid_state) and an invoice with no payment part (needs_qr_bill), writing nothing. Idempotent by outcome: at most one active (non-failed) delivery per invoice, so a re-prepare returns the existing row and its stored artifact regardless of key. Posts nothing.

ParametersinvoiceIdrequiredworkspaceIdrequiredidempotencyKeyrequired

ebill_transmit

Transmit a prepared delivery through the owner-gated cloud connector (P8: outbound, draft-by-default; pass confirmed=true or enable the workspace dial). Guard order: no connector returns the honest {transmitted:false, reason:'cloud_tier'} and the artifact stays downloadable; then needs_biller_pid; then needs_confirmation; then payload_not_conformant if the payload is not PDF/A-3b, not eBill-addressed, or over 10 MB (nothing non-conformant is ever transmitted). On acknowledgement records the business case and drives the invoice issued->sent (A10) only from issued. Idempotent: a transmitted delivery never resubmits; transmission is at-least-once via a stored correlation id. Posts nothing. CONSEQUENCE: Transmits the invoice as an eBill into the customer's e-banking through the connected eBill service. Once it is out, it can't be called back; a correction goes out separately.

ParametersconfirmeddeliveryIdrequiredworkspaceIdrequiredidempotencyKeyrequired

ebill_delivery_status

Track eBill deliveries: one by deliveryId (with its mirrored partner-status event trail), or a filtered list by invoiceId, local status (prepared|submitting|transmitted|failed), mirrored partner status (NWP_PENDING|OPEN|APPROVED|REJECTED|COMPLETED), or created-at range, newest first. Read-only.

ParameterstofromstatusinvoiceIddeliveryIdsavedViewIdworkspaceIdrequiredpartnerStatus

bank_channel_connect

Open or advance a bank channel, EBICS 3.0 (H005) by default or the managed bLink rail with channelKind='managed_blink' (A37). Human only: pass confirm=true (P8); never fired by an automation rule. EBICS, keyed to the bank contract (host, partnerId, userId), never an account: the routed accounts are checked first (camt054_lines_refused; a sandbox or demo workspace is trial_workspace_refused; one live connection per subscriber in the database, live_connection_exists), then HEV (a host without H005 is unsupported_ebics_version), then three RSA 2048 key pairs with self-signed X.509 certificates are generated locally, the electronic-signature private key is dropped at once, the connection and the INI letter (the certificate hashes to sign and post) are filed, and INI and HIA are sent as two legs. The answer is pending_bank_activation, ini_sent (a lost answer: call again with connectionId to resend only the unconfirmed leg), or a refusal in words: init_refused (wrong partner or user ID, H005 not enabled, or already initialised), certificate_refused, key_exchange_refused. With connectionId: resends unconfirmed legs, runs HPB, and without confirmBankKeys returns the bank's key and certificate hashes for the owner to compare with ZKB's published sheet; with confirmBankKeys=true and expectedBankKeyHashes set to exactly that reviewed set it pins them and activates, only if a fresh HPB equals them (else bank_keys_mismatch, nothing pinned). The same reviewed confirm resumes a bank_keys_changed or locally blocked channel. Under till up TILL checks once a day by itself whether the bank has activated the access (bank_channel_status activation.bankReady, D143); the comparison and the confirm stay the human's. Never returns key material; posts nothing. CONSEQUENCE: Opens a channel to your bank: TILL makes the keys and sends the bank its setup, then statements (on the managed rail also payments) flow over it. What the bank got can't be recalled; you can retire or block the channel.

ParametershostscopesbankRefconfirmkeyLengthchannelKindworkspaceIdrequiredconnectionIdidempotencyKeyrequiredconfirmBankKeysrouteBankAccountIdsexpectedBankKeyHashes

bank_sync

Fetch the new day-end camt.053 statements (version 08) over an active EBICS channel, on demand (the Studio's Kontoauszüge abrufen, or when the owner asks) and, under till up, once a day by the scheduler (D142, set_bank_sync_schedule); never by an automation rule. Persists the bank's container and every new statement as documents first, then imports each one through import_camt by its account IBAN, then sends the bank's receipt, so nothing fetched is lost and nothing counts twice. On an EBICS-fed account a statement is imported only as the successor of the newest statement in the books or as the filler of a hole, by number and balances; a copy of one already there is already_imported; others wait (gap_refused, before_feed_start, not_day_end_statement, unsupported_multipage_statement, unmatched_account) and are retried from the stored bytes at every sync, or are refused for good (overlap_refused). A statement whose number skips some but whose balances chain imports with notice statement_number_jump and jump {after, statement, skipped} naming the missing numbers, which the owner imports by hand if they exist. Each call needs a fresh idempotencyKey; dateRange is refused (unsupported_date_range). Answers download_refused (not_granted, busy, too_large, other, with the bank's raw code), untrusted_response, bank_keys_changed or channel_unreachable in words. One fetch per connection at a time: a second call while one runs answers sync_in_progress. With no transport wired it reports needs_bank_transport. Posts nothing. CONSEQUENCE: Fetches new day-end statements from your bank and imports them; each new bank line waits in the bank inbox and nothing is booked. The bank gets its receipt, and an import cannot be taken back.

ParametersdateRangeworkspaceIdrequiredconnectionIdbankAccountIdidempotencyKeyrequired

payment_batch_transmit

Hand a generated A18 pain.001 payment batch to a bank channel that uploads payments. The EBICS channel downloads statements only: a batch whose debtor account is routed over EBICS is refused upload_disabled before anything is sent (upload the pain.001 in eBanking and release it there). A batch whose account is routed over the managed bLink rail (A37) is submitted there, P8-gated (confirm=true). With no channel it returns needs_bank_channel and the file path stands. Never marks anything paid (paid comes from the camt debit). Posts nothing. CONSEQUENCE: Submits the payment batch to your bank over the connected channel; TILL can't call it back. It marks nothing as paid: that comes from booking the bank debit or confirming the batch as paid.

ParametersbatchIdrequiredconfirmworkspaceIdrequiredidempotencyKeyrequired

bank_channel_disconnect

Retire or block an EBICS bank channel (P8-gated: pass confirm=true or enable the approval dial). retire is the local, terminal close: the channel flips to retired, its order history stays readable, and its keys are destroyed. block is local only: TILL stops using the channel at once and sends nothing to the bank (a block at ZKB is the owner's phone call); the reviewed bank-key confirm through bank_channel_connect is the way back. Both idempotent. Posts nothing. CONSEQUENCE: Retiring ends this bank channel and destroys its keys for good; blocking only stops TILL using it. Its order history stays readable, and a retired channel cannot reopen: connecting again makes new keys.

ParametersmoderequiredconfirmworkspaceIdrequiredconnectionIdrequiredidempotencyKeyrequired

bank_channel_status

Read EBICS channel health: per channel the connection state, the INI and HIA legs, the keystore state, the pending INI letter, per routed account the newest statement in the books (number, year, date, closing balance) and lastNumberJump (the numbers skipped right before the newest statement the channel imported, null once they are in the books or the next statement followed normally), every downloaded statement still waiting with the reason it waits (a gap, before the feed start, an unrouted IBAN, a split statement) and the facts it names, the last sync, whether a fetch is running right now (syncInProgress, syncStartedAt: one fetch per connection at a time, D142), the daily automatic fetch (autoSync: mode, atTime, nextAt, the last automatic attempt, attemptsToday), the short name of the bank (bankName), the daily activation check while ZKB has not activated the access yet (activation: checking, lastCheckAt (the last HPB the bank answered), lastAttemptAt, nextCheckAt, lastOutcome, bankReady; D143), and the recent order log. When activation.bankReady is true the bank has answered and the keys must be compared: tell the human to open the bank accounts screen (/bank-accounts) and compare the bank key hashes with the sheet the bank published, then confirm (bank_channel_connect with confirmBankKeys). That comparison is an act of the human; never confirm the keys yourself. Optionally scoped to one bank account, or the order log filtered by status (a saved view applies its stored status filter; an explicit status wins). Read-only.

ParametersstatussavedViewIdworkspaceIdrequiredbankAccountId

bank_channel_directory

Look up a Swiss bank in TILL's static, in-package EBICS directory for the connect wizard (A36): substring or BIC match over the curated set, returning each bank's names, BIC, the marketing name of its EBICS channel, the segments it offers EBICS to, where to post the signed INI letter, known protocol quirks, and an EXPLICITLY UNVERIFIED fee note (verified:false always). The per-contract host URL and Host ID are null in v1 (the bank issues them on your signed EBICS contract; enter them in the manual fields). Reads static data only: opens NO socket, needs no idempotency key, is never a gate on the ceremony. An empty query returns the whole set; no match returns an empty array, not an error.

ParametersqueryworkspaceIdrequired

set_bank_sync_schedule

Set the daily automatic statement fetch of an EBICS connection (D142): mode 'daily' or 'off' and atTime as a Swiss local time HH:MM (default 07:00; an active connection that never set a mode is fetched daily). Under till up the scheduler then fetches once a day at that time, catches up at start when TILL was closed, and retries a bank that did not answer or was busy hourly, at most three attempts a day; till serve has no scheduler. No automation rule fetches statements: passing a ruleId is refused sync_schedule_parked, and the call also clears a rule pointer an older build stored. Idempotent; banking-write. Posts nothing. CONSEQUENCE: Sets when TILL fetches your bank statements each day, or turns that off. The fetch runs in the name of whoever set it, or the owner's once that person loses the right.

ParametersmodeatTimeruleIdworkspaceIdrequiredconnectionIdrequiredidempotencyKeyrequired

confirm_statement_gap

Confirm that nothing is missing in a statement gap of a Bankkonto (A36 §4.7, D150): the day-end statements afterStatementId and beforeStatementId are neighbours in the books, their numbers skip, and the later one opens exactly on the earlier one's closing balance (difference 0), so the skipped numbers are days without bookings or bookings that cancel out. Human only: a person at the button passes confirmed=true and a reason; an agent seat is refused human_only (propose it to the person instead). Refuses gap_has_waiting_statement when a downloaded statement inside the gap does not chain (it proves money the books do not explain), and gap_not_chained when the balances do not chain (the missing statements carry money that is not in the books: import them, such a gap can never be confirmed) and gap_not_found when the gap was filled or the two statements are not neighbours. Appends one confirmation row and its audit entry; changes no statement and posts nothing. A confirmed gap leaves bank_channel_status statementGaps and the attention hub; a statement found later still imports. Idempotent per key; a gap already confirmed answers alreadyConfirmed. CONSEQUENCE: Records that the missing bank statements between two statements hold no money, so the gap stops waiting for you. You can withdraw this, and a statement found later still imports.

ParametersreasonrequiredconfirmedworkspaceIdrequiredbankAccountIdrequiredidempotencyKeyrequiredafterStatementIdrequiredbeforeStatementIdrequired

revoke_statement_gap_confirmation

Withdraw the confirmation that a statement gap holds nothing (A36 §4.7, D150), so the gap is an attention item again. Human only (confirmed=true, a reason; an agent seat is refused human_only). Appends a withdrawal row beside the confirmation, which stays; a gap that is not confirmed answers notConfirmed without a row, and a gap filled since answers gap_not_found. Changes no statement, posts nothing. Idempotent per key. CONSEQUENCE: Withdraws the confirmation that a statement gap holds nothing, so the gap waits for a person again. The confirmation stays on record beside the withdrawal.

ParametersreasonrequiredconfirmedworkspaceIdrequiredbankAccountIdrequiredidempotencyKeyrequiredafterStatementIdrequiredbeforeStatementIdrequired

record_exchange_rate

Record a foreign-currency rate (§H-FX). `rate` is a DECIMAL STRING and is the price of one unit of `baseCurrency` in the workspace base currency, so EUR/CHF 0.9412 means 1 EUR = 0.9412 CHF. `asOf` is the date the rate is valid FOR. `method` names the admissible MWSTV Art. 45 basis (daily | monthly_avg | bank | group). Recording the same rate twice is one row; a different rate under the same date and source is refused, because it may already have priced a posted entry. CONSEQUENCE: Records an exchange rate for its date; foreign-currency entries on that date then use it. The rate stays on record unchanged; an entry it priced wrongly is fixed with a correcting entry.

ParametersasOfrequiredraterequiredmethodsourceprovenanceworkspaceIdrequiredbaseCurrencyrequiredquoteCurrencyidempotencyKeyrequired

list_exchange_rates

List recorded foreign-currency rates, newest validity date first, optionally filtered by currency pair and date range.

ParameterstofromworkspaceIdrequiredbaseCurrencyquoteCurrency

get_exchange_rate

Resolve which rate WOULD convert a currency into the workspace base currency on a date, and report the validity date, source and method of the rate that governs. Answers `needs_fx_rate` when none is admissible, rather than inventing one.

ParametersdatecurrencyrequiredworkspaceIdrequired

set_fx_method

Elect the MWSTV Art. 45 conversion basis for a Steuerperiode: `daily` (Tageskurs, Devisenkurs Verkauf), `monthly_avg` (Monatsmittelkurs) or `group` (Konzernumrechnungskurs, group members only). `taxPeriod` is a calendar year (MWSTG Art. 34 Abs. 2) and defaults to the current one. The election carries forward until it is changed. Art. 45 Abs. 5 binds it for at least one Steuerperiode, so it may only be written while that period, and every period after it, still holds no posted foreign-currency entry. `bank` (Abs. 3bis) is not electable: it is the mandated fallback for currencies the ESTV publishes no rate for and stays admissible under every election. CONSEQUENCE: Chooses the exchange-rate basis MWST uses for foreign-currency amounts from this tax year on. You can change it only while that year and later ones hold no posted foreign-currency entry.

ParametersmethodrequiredtaxPeriodworkspaceIdrequired

get_fx_method

Report which MWSTV Art. 45 conversion basis governs a Steuerperiode, whether that period is already settled by a posted foreign-currency entry, the earliest period whose basis may still be chosen, and the full election history. Ask by `taxPeriod` (YYYY) or by any `date` inside it.

ParametersdatetaxPeriodworkspaceIdrequired

describe_rate_feed

Report which published rate series MWSTV Art. 45 admits, the endpoint each one lives at, how its validity dates and per-unit quotations work, and the pages that establish it. Call this first: the engine performs NO network I/O, so a caller fetches the endpoint itself and hands the response body to `import_exchange_rates`.

ParametersworkspaceIdrequired

import_exchange_rates

Import a published BAZG rate payload into the rate store as `source='rate_api'`. `payload` is the response body from the endpoint `describe_rate_feed` names, verbatim. The payload identifies its own series; pass `series` to have a mis-paired payload refused rather than mis-dated. Daily rates are stored on each date in the payload's validity window (not on the determination date), monthly averages once on the first of their month, and per-unit quotations (100, 1000, 10000) are scaled. A published rate TILL cannot hold exactly at twelve decimal places is reported rather than rounded; the whole BAZG daily series is within that (its deepest is nine places). CONSEQUENCE: Imports the published BAZG exchange rates into your rate list, each on the dates it is valid for. Imported rates stay unchanged; an entry one priced wrongly is fixed with a correcting entry.

ParametersseriespayloadrequiredcurrenciesworkspaceIdrequiredidempotencyKeyrequired

fx_revaluation

Revalue every open foreign-currency monetary position (A19 FC bank balances, A16/A17 open FC debtor/creditor items) at the closing rate for `periodEnd` (OR Art. 960a: Bilanzstichtagskurs). Returns each position with its foreign-currency amount, book CHF, closing rate, revalued CHF and the UNREALISED diff, the per-currency subtotals, the net total, and any currency that still needs a rate. A read model: nothing is posted. Negative total is a net unrealised loss.

ParametersperiodEndrequiredworkspaceIdrequired

post_fx_revaluation

Post the period-end UNREALISED currency gain/loss as a balanced entry (source `fx`) plus its next-period reversal, atomically. The unrealised difference books to account 6949 against each revalued position, and the entry auto-reverses on the first day of the next period so the REALISED figure at settlement (A14/A18) is never double-counted. Idempotent per `periodEnd`: a re-post with the same key replays, a different key returns `already_posted`, a zero-diff period posts nothing. Refuses `needs_rate` when any open FC position lacks a closing rate, and `period_locked` for a locked target period (A03). CONSEQUENCE: Books the period-end currency gain or loss, with its automatic reversal on the first day of the next period. If it is wrong, you revert the run with correcting entries; the originals stay visible.

ParametersperiodEndrequiredworkspaceIdrequiredidempotencyKeyrequired

fx_revaluation_reverse

Revert a posted FX revaluation run (D129 Q2): books the mirror of the revaluation entry dated the period end (`source` `fx`) plus its own next-day reversal, atomically, so every position and account 6949 net to zero on both dates and nothing is edited. `runId` is the run `post_fx_revaluation` returned. Refuses `not_posted` for a run that posted nothing, `already_reversed`, `later_run_exists` naming the later period end whose run still stands (revert newest first), and `period_locked` for a locked period. Idempotent on `idempotencyKey`. CONSEQUENCE: Reverts the period-end currency revaluation with mirror entries, so every account nets to zero. The original entries stay visible beside them; this revert can't be reverted again.

ParametersrunIdrequiredworkspaceIdrequiredidempotencyKeyrequired

accrual_create

Describe an Abgrenzung (OR Art. 958b) and save it as a DRAFT: `kind` is one of prepaid_expense (Aufwand vorausbezahlt, Dr 1300), accrued_income (Ertrag noch nicht fakturiert, Dr 1300), accrued_expense (Aufwand noch nicht fakturiert, Cr 2300) or deferred_income (Ertrag vorausbezahlt erhalten, Cr 2300); `contraAccount` is the P&L account (number or id, income for the income kinds, expense for the expense kinds); `amountMinor` is integer Rappen; `periodEnd` is the balance-sheet date. Returns the draft with the exact lines the post will write and the reversal lines dated the day after. Nothing is posted.

ParameterskindrequiredcurrencyperiodEndrequiredsourceRefamountMinorrequireddescriptionrequiredworkspaceIdrequiredcostCenterIdcontraAccountrequiredidempotencyKeyrequired

accrual_post

Post a drafted Abgrenzung as an atomic PAIR: the accrual entry dated `periodEnd` (source `accrual`) and its automatic Rückbuchung dated the first day after (a real reversal, linked). A locked reversal date rolls the whole pair back, so the books never carry an accrual without the reversal that backs it out. Idempotent per accrual: the same key replays, a different key on a posted accrual is `already_posted`, a discarded draft is `draft_discarded`. CONSEQUENCE: Books the accrual and its automatic reversal in the next period as one pair. If it is wrong, you revert it with a mirror pair; all four entries stay visible.

ParametersaccrualIdrequiredworkspaceIdrequiredidempotencyKeyrequired

accrual_reverse

Revert a posted Abgrenzung without editing history: posts the MIRROR pair (the Storno dated `periodEnd`, its own reversal dated the day after), so all four entries net to zero on every account in both periods. Refuses `already_reversed` a second time and `not_posted` on a draft. CONSEQUENCE: Books the mirror pair against a posted accrual, so every account nets to zero; the original pair stays visible. If the reversal was wrong, you create and post the accrual again.

ParametersreasonaccrualIdrequiredworkspaceIdrequiredidempotencyKeyrequired

accrual_discard

Retire a drafted Abgrenzung before it posts. The row stays on record as `discarded` with its reason; a posted accrual cannot be discarded (`already_posted`), reverse it instead.

ParametersreasonaccrualIdrequiredworkspaceIdrequiredidempotencyKeyrequired

accrual_get

Read one Abgrenzung: the row, its lines and reversal lines (from the same function the post uses), and the journal entries it has produced (the accrual, its reversal, the Storno pair).

ParametersaccrualIdrequiredworkspaceIdrequired

accrual_list

List Abgrenzungen, newest period first, filtered by periodEnd, status (draft | posted | reversed | discarded) or kind. `totalMinor` sums the drafts and the posted ones. savedViewId applies a saved view (G00).

ParameterskindstatusperiodEndsavedViewIdworkspaceIdrequired

provision_create

Describe a Rückstellung (OR Art. 960e) and save it as a DRAFT: `reason` is one of the eight statutory ids (Garantie, Ferien und Überzeit, Prozess, Grossreparatur, Sanierung, Restrukturierung, Steuern, Sonstige; `invalid_reason` names the exact ids, and `sonstige` needs a description of at least 10 characters); `provisionAccount` is 2330, 2600 or a liability numbered 23xx/26xx; `expenseAccount` is the P&L account the formation is charged to. Returns the draft with its lines (Dr expense / Cr provision). Nothing is posted.

ParametersreasonrequiredperiodEndrequiredamountMinorrequireddescriptionrequiredworkspaceIdrequiredexpenseAccountrequiredidempotencyKeyrequiredprovisionAccountrequired

provision_post

Post a drafted Rückstellung as ONE entry (Dr expense / Cr provision, source `provision`) dated `periodEnd`. No automatic reversal: OR 960e Abs. 4 does not release a provision by the calendar. Idempotent per provision; a different key on a posted one is `already_posted`. CONSEQUENCE: Books the provision as one entry; it is not released automatically. You release it later, or correct it with a correcting entry; both stay visible.

ParametersprovisionIdrequiredworkspaceIdrequiredidempotencyKeyrequired

provision_release

Release part or all of a posted Rückstellung: posts Dr provision / Cr `targetAccount` (the original expense account, or an income account) for `amountMinor` on `date`. Refuses `release_exceeds_balance` above the open balance; the provision reads `released` at zero. Idempotent on `idempotencyKey` for as long as the release that key booked still stands; a key whose release was undone refuses `already_reversed_key` (release again under a new key). A release is undone only with `provision_release_reverse`; the raw `reverse_entry` refuses its entry with `owned_by`. CONSEQUENCE: Books the release of part or all of the provision to the target account. If it is wrong, you undo the release with a correcting entry; both stay visible.

ParametersdaterequiredamountMinorrequiredprovisionIdrequiredworkspaceIdrequiredtargetAccountrequiredidempotencyKeyrequired

provision_reverse

Reverse the formation entry of a posted Rückstellung (a real reversing entry, dated `periodEnd` unless `date` names another open day). Refuses `release_blocked` while any release stands unreversed (undo the releases first through `provision_release_reverse`, newest first) and `already_reversed` a second time. This is the ONLY way to reverse the formation: the raw `reverse_entry` refuses it with `owned_by`. CONSEQUENCE: Corrects the provision's original booking with a correcting entry; any releases have to be undone first. The original stays visible; if this was wrong, you create and post the provision again.

ParametersdatereasonprovisionIdrequiredworkspaceIdrequiredidempotencyKeyrequired

provision_release_reverse

Undo one release of a Rückstellung with a real reversing entry (dated the release date unless `date` names another open day), so the open balance and the `posted` / `released` reading follow from the journal again. This is the ONLY way to reverse a release entry: the raw `reverse_entry` refuses it with `owned_by`, and so it refuses the undo itself. Refuses `not_found` and `already_reversed`. Idempotent on `idempotencyKey`. CONSEQUENCE: Undoes one release of the provision with a correcting entry and restores its open balance. The release stays visible; to release again, you book a new release.

ParametersdatereasonreleaseIdrequiredworkspaceIdrequiredidempotencyKeyrequired

provision_discard

Retire a drafted Rückstellung before it posts. The row stays on record as `discarded` with its reason; a posted provision cannot be discarded (`already_posted`), reverse or release it instead.

ParametersreasonprovisionIdrequiredworkspaceIdrequiredidempotencyKeyrequired

provision_get

Read one Rückstellung: the row, its formation lines, every release with its entry (and the reversal of that entry, when one exists) and the open balance, derived as the formation amount minus the live releases.

ParametersprovisionIdrequiredworkspaceIdrequired

provision_list

List Rückstellungen, newest period first, each with its open balance, filtered by periodEnd, status (draft | posted | released | reversed | discarded) or reason. `openTotalMinor` sums the open balances. savedViewId applies a saved view (G00).

ParametersreasonstatusperiodEndsavedViewIdworkspaceIdrequired

tax_provision_preview

Propose the Steuerrückstellung for the fiscal year to `periodEnd` the way Kanton Zürich ZStB 27/1 computes it: profit before tax times s/(1+s) at `rateBp` (default 2000 = 20 %, the rate is cantonal and yours to set), minus the instalments already charged to 8900, never below zero. Returns the figures and a ready `provision_create` draft (reason steuern, Dr 8900 / Cr 2330). On an Einzelfirma answers applicable:false. A read: nothing is posted.

ParametersrateBpperiodEndrequiredworkspaceIdrequired

vat_settlement_preview

Preview the MWST-Saldierung of one filed period (`period` is the A07 label, `2026-Q3` or `2026-H1`): the booked movement on 2200 (Umsatzsteuer), 1170 and 1171 (Vorsteuer) over the period beside the return's Ziffer 399 and 400 + 405 with the differences, and the exact lines the post would book (Dr 2200 / Cr 2201, Dr 2201 / Cr 1170, Dr 2201 / Cr 1171; under the Saldosteuersatz the flat-rate tax due against 3809). Settlement entries and their reversals are excluded from the read, so a settled period previews as settled, never twice. A read: nothing is posted.

ParametersperiodrequiredworkspaceIdrequired

vat_settlement_post

Book the MWST-Saldierung the preview showed: transfer the filed period's balances on 2200, 1170 and 1171 to 2201 (MWST-Abrechnungskonto), dated the period end, `source='vat_settlement'`, so the three tax accounts read zero and 2201 carries what the ESTV is owed. Admitted inside the filed, hard-locked period under three enforced conditions (no VAT trace, only the tax accounts, never into a year-close seal); the filed return and the Abstimmung are unchanged by it. Refuses `period_not_filed` before the filing, `nothing_to_settle` on an empty period, `already_posted` while a settlement of the period stands, `period_locked` with reason `year_close` on a sealed year. Idempotent on `idempotencyKey` for as long as the settlement that key booked still stands; a key whose settlement was reversed refuses `already_reversed_key` (post again under a new key, which books a new settlement). The correction is `vat_settlement_reverse`. CONSEQUENCE: Moves the filed period's MWST balances from 2200, 1170 and 1171 to 2201; the filed return does not change. If it is wrong, you reverse the settlement and book it again.

ParametersperiodrequiredworkspaceIdrequiredidempotencyKeyrequired

vat_settlement_reverse

Reverse a posted MWST-Saldierung with a reversing entry dated the period end, so 2200, 1170, 1171 and 2201 net to zero again inside the settled period; the settlement row reads `reversed` and keeps its history, and a later `vat_settlement_post` for the period books a new settlement. This is the ONLY way to reverse a settlement entry: the generic `reverse_entry` refuses it with `owned_by`, because the settlement row must move with the mirror. Refuses `not_found` and `already_reversed`. Idempotent on `idempotencyKey`. CONSEQUENCE: Reverses the MWST settlement with a correcting entry dated the period end; the settlement stays on record as reversed. You can then book a new settlement for the period.

ParametersworkspaceIdrequiredsettlementIdrequiredidempotencyKeyrequired

vat_settlement_list

List the MWST-Saldierungen on record, newest period first, posted and reversed, with the moved figures and the entry ids. `year` (YYYY) narrows to one Steuerperiode. A read.

ParametersyearworkspaceIdrequired

vat_annual_reconciliation

The two annual MWST reconciliations of a calendar year as figures (Art. 128 Abs. 2 and 3 MWSTV, the ESTV's Umsatz- and Vorsteuerabstimmung): the class-3 revenue per the books adjusted for the accruals and disposal proceeds the ledger can name against the sum of Ziffer 200 of the year's returns, and the booked Vorsteuer on 1170 + 1171 against the sum of Ziffer 400 + 405, each with its difference and a status (`match`, `warn`, or `unavailable` naming the periods not yet filed). Under the Saldosteuersatz the Vorsteuer half is not applicable; a workspace with no MWST method answers `applicable: false`. A read.

ParametersyearrequiredworkspaceIdrequired

preview_payment

Preview exactly what a payment would post, without writing anything: the remainder still to allocate, each document's resulting open amount and status, the Skonto VAT split, the Ist-timing paid-portion VAT, the currency conversion with its realised difference, and the journal legs with account numbers and labels. Call this before record_payment so the figures a human sees and the figures the ledger books are the same figures. It takes no intent and can never post.

ParametersdaterequiredfxRatesourcecurrencydirectionrequiredreferenceallocationsamountMinorrequiredworkspaceIdrequiredbankAccountIdrequiredcounterpartyIdonAccountMinor+1 more

suggest_payment_matches

Rank the open items a payment might settle, with the reason in words: reference matches, amount and customer match, or amount close. A QR or Creditor Reference whose check digit fails is reported as a typo and never ranked as a match, and a candidate that fits no tier carries no reason at all. This is the same read model the Studio's candidate list renders.

ParameterscurrencydirectionreferenceamountMinorworkspaceIdrequiredcounterpartyId

record_payment

Record an incoming or outgoing payment and post its balanced entry (bucht eine Zahlung). Allocates across one or several open items with partial amounts, Skonto, and an Ausbuchung of a small residual; any remainder is parked as a Guthaben for its counterparty. Requires intent='post_payment', because money never moves as a side effect. CONSEQUENCE: Records the payment and marks what it settles as paid. If it is wrong, you reverse the payment, which reopens them; both stay visible.

ParametersdaterequiredfxRateintentrequiredsourcecurrencydirectionrequiredreferenceallocationsamountMinorrequiredworkspaceIdrequiredbankAccountIdrequiredcounterpartyId+3 more

allocate_payment

Allocate a parked Guthaben to open items. The money was already booked when the payment arrived, so this posts no entry: it records which open items that money settles. Requires intent='allocate_payment'. A wrong allocation is corrected by reversing the whole payment and recording it again. CONSEQUENCE: Applies a payment already on the books to open items and marks them paid; nothing new is booked. A wrong allocation is corrected by reversing the whole payment and recording it again.

ParametersintentrequiredpaymentIdrequiredallocationsrequiredworkspaceIdrequiredidempotencyKeyrequired

reverse_payment

Reverse a payment (storniert eine Zahlung): posts a reversing entry and re-opens every document it settled. It never deletes, the original entry stays untouched, and a payment is always reversed as a whole. Requires intent='reverse_payment'. CONSEQUENCE: Reverses the payment with a correcting entry and reopens everything it settled. The original stays visible; if the payment was right after all, you record it again.

ParametersdateintentrequiredpaymentIdrequiredworkspaceIdrequiredidempotencyKeyrequired

get_payment

Read one payment: its allocations by document number, its Guthaben, its journal entry, and its reversal when it has one.

ParameterspaymentIdrequiredworkspaceIdrequired

list_payments

List payments, filtered by direction, status, date range, or the document they settled. savedViewId applies a saved view (G00): its stored filters are merged underneath any filter named explicitly here, so an explicit one always wins.

ParameterstofromstatusdirectiondocumentIdsavedViewIdworkspaceIdrequired

set_write_off_threshold

Set the residual below which the Studio may offer a one-click Ausbuchung, in Rappen (default CHF 1.00). It governs the offer only: a larger write-off stays recordable when it is stated deliberately. This is a product setting and not a rounding rule. CONSEQUENCE: Sets the leftover amount below which the Studio offers a one-click write-off of an open item. Larger write-offs stay possible when stated deliberately; you can change the amount again.

ParametersworkspaceIdrequiredidempotencyKeyrequiredthresholdMinorrequired

list_open_items

The OP-Liste: every unpaid or partly-paid invoice with its open amount, due date, days overdue and aging bucket, plus every parked customer payment. A parked payment carries its direction: incoming is a Guthaben the customer holds and reads NEGATIVE, outgoing is a refund not yet matched and reads POSITIVE. Reports bucket subtotals in the invoice currency (bucketTotals) and in base currency (baseBucketTotals, the only one that is an amount when several currencies are open), the grand total both ways, and whether the base total reconciles to account 1100 Debitoren as of the same date. Pass asOf to see the receivables exactly as they stood on a past cut-off (payments after it are ignored). Filter by customerId or currency to narrow the table without changing what reconciled means. Reads only.

ParametersasOfcurrencycustomerIdworkspaceIdrequired

customer_balance

What one customer owes: their total open amount, the split across aging buckets in both the invoice currency and base currency, how many days their oldest overdue item has been outstanding, and any parked payment attached to them. onAccountMinor is reported positive for a Guthaben, so it comes out negative when what is parked is an unmatched refund: read the direction on the row rather than the sign of the total. An unknown customer reads as an empty balance rather than an error. Reads only.

ParametersasOfcustomerIdrequiredworkspaceIdrequired

aging_report

The receivables summary behind the aging bar: the same open items list_open_items itemises, totalled per bucket and per customer, largest debtor first. byBucket is the invoice-currency sum and baseByBucket the base-currency one, which is the figure that ties to baseTotalOpenMinor in a workspace holding more than one currency. Use it to answer "how much is over 90 days overdue and who owes it" in one call. Carries the same reconciliation flag against 1100 Debitoren. Reads only.

ParametersasOfworkspaceIdrequired

get_aging_bucket_config

The day thresholds the aging buckets cut at, and whether this workspace chose them or is on the shipped default of 30/60/90 days. Also returns the bucket keys those boundaries produce. Reads only.

ParametersworkspaceIdrequired

set_aging_bucket_config

Redefine where the aging buckets cut, as a strictly increasing list of positive day counts (the default [30, 60, 90] gives 0-30, 31-60, 61-90 and 90+). This changes the VIEW only: the buckets re-partition the same receivables total and can never change it, and the reconciliation to 1100 Debitoren holds for any boundary set. It is a DACH reporting convention, not a statutory figure, and it is not a dunning deadline (A15 owns those). A repeat call under the same idempotencyKey writes nothing.

ParametersworkspaceIdrequiredboundariesDaysrequiredidempotencyKeyrequired

create_bank_account

Register a Bankkonto: validates the IBAN (ISO 13616 mod-97), derives whether it is a QR-IBAN (QR-IID 30000 to 31999, receive-only), and links it to an asset account in the chart (ledgerAccountId, e.g. 1020 Bankkonto). Does NOT set an opening balance: that is set_bank_opening_balance. CONSEQUENCE: Registers a bank account with its IBAN and ledger account, so payment runs and bank imports can use it; nothing is booked. You can fix its details until an opening balance is posted, or archive it.

ParametersibanrequirednamerequiredcurrencyworkspaceIdrequiredidempotencyKeyledgerAccountIdrequired

update_bank_account

Rename a Bankkonto or correct its details. The IBAN, currency and verknüpftes Konto freeze once the account is referenced by a posted opening balance (account_in_use); the name stays editable for good. CONSEQUENCE: Changes this bank account's IBAN, currency or ledger account; later payment runs and bank bookings use the new details. You can change them back until an opening balance is posted on it.

ParametersibannamecurrencyworkspaceIdrequiredbankAccountIdrequiredidempotencyKeyledgerAccountId

set_bank_opening_balance

Post a Bankkonto's opening balance as a real balanced journal entry (debit the bank account, credit 9100 Eröffnungsbilanz), in integer Rappen. Explicitly confirmed and idempotent: re-sending the same idempotencyKey returns the original entry and never posts twice. A correction to a posted opening balance is a reversing entry, never a second opening. CONSEQUENCE: Books the bank account's opening balance against 9100 Eröffnungsbilanz, once. A correction is a correcting entry, never a second opening; both stay visible.

ParametersdaterequiredfxRatecurrencyamountMinorrequiredworkspaceIdrequiredbankAccountIdrequiredidempotencyKeyrequired

preview_bank_opening_balance

Show what set_bank_opening_balance WOULD post for a Bankkonto, in the workspace base currency, without posting anything: the base amount, the rate it converted on, and the two legs (the Bankkonto's verknüpftes Konto against 9100 Eröffnungsbilanz). It runs the posting's own arithmetic, so the FIGURE cannot disagree, and it answers the same refusals the posting answers for the state it reads (needs_account for a missing or archived 9100, currency_mismatch, opening_balance_already_set, needs_fx_rate, period_locked). It is not a promise about the posting. It checks no capability, so a caller without the post right gets the full figure here, and at set_bank_opening_balance that caller is refused permission_denied at every amount, including the zero one that books no journal entry; that refusal is not always the FIRST one, because an unknown Bankkonto, an opening balance already set, or (above zero only) a missing 9100 is answered before the capability is consulted, and an idempotencyKey that replays an already-completed call still replays. It also holds no state, so a period locked, a 9100 archived or another actor posting in between still refuses there. Takes no idempotency key and can never post: use it before Buchen, because a posted opening balance is immutable and its only correction is a reversing entry.

ParametersdaterequiredfxRatecurrencyamountMinorrequiredworkspaceIdrequiredbankAccountIdrequired

archive_bank_account

Archivieren: hide a closed Bankkonto from pickers while keeping it referenceable by historical statements and entries. Never deletes the account.

ParametersworkspaceIdrequiredbankAccountIdrequiredidempotencyKey

unarchive_bank_account

Wiederherstellen: put an archived Bankkonto back in the pickers. The inverse of archive_bank_account, and a Bankkonto that is already active stays active (no rejection). Nothing about the account changes: this only clears the archived flag.

ParametersworkspaceIdrequiredbankAccountIdrequiredidempotencyKey

list_bank_accounts

List this workspace's Bankkonten with their IBAN, currency, verknüpftes Konto, QR-IBAN flag and opening balance. Archived accounts are excluded unless includeArchived is true. savedViewId applies a saved view (G00): its stored filters are merged underneath any filter named explicitly here.

ParameterssavedViewIdworkspaceIdrequiredincludeArchived

get_bank_account

Read one Bankkonto, including whether its IBAN is a QR-IBAN and therefore receive-only (a QR-IBAN may only be credited, never used as a debit account).

ParametersworkspaceIdrequiredbankAccountIdrequired

trial_balance

Saldenbilanz (trial balance): every account's opening balance, its debit and credit movement in the period, and its closing balance, in the workspace base currency and integer Rappen. Figures are debit-positive, exactly as the ledger holds them, and drafts are excluded. Pass compareTo={periodStart,periodEnd} for the prior-period column and its delta. reconciles reports three checks by name (debit equals credit, the closing column ties to an independently queried ledger balance, and every account the ledger moved was rendered); it is evidence about coverage and the period boundaries, not proof that an account sits in the right section. groupBy accepts 'kmu' today and refuses anything else, because account-level custom fields are a G00 capability that does not exist yet.

ParametersgroupBycompareToperiodEndrequiredperiodStartrequiredworkspaceIdrequired

balance_sheet

Bilanz (balance sheet) as of a date, grouped into the FIRST LEVEL of OR Art. 959a: Umlaufvermögen and Anlagevermögen on the Aktiven, kurzfristiges Fremdkapital, langfristiges Fremdkapital and Eigenkapital on the Passiven, each section carrying its statutory heading in de/fr/it/en, plus one residual section per side (the "weitere Positionen" of OR Art. 959a Abs. 3) so no account can be dropped. Under each section the accounts are listed individually, ordered by account number. Every line is positive on its own side. The running result IS carried as an Eigenkapital position (OR Art. 959a Abs. 2 Ziff. 3 lit. f and lit. g), which is what makes the statement foot before a year-end close has run. WHAT IT IS NOT: the statutory minimum structure. Twenty-four individual sub-positions are prescribed "einzeln und in der vorgegebenen Reihenfolge" (flüssige Mittel, Forderungen aus Lieferungen und Leistungen, aktive Rechnungsabgrenzungen, and so on) and are not modelled: only the seven groupings above are, with raw account lines under them. The two Absätze that prescribe those 24 are OR Art. 959a Abs. 1 and OR Art. 959a Abs. 2, and A08 reaches the grouping level of each and stops there. On the shipped Kontenrahmen KMU the account numbers happen to ascend in the statutory order, so the output looks right by coincidence; on a renamed or renumbered chart it names none of the required positions and may order them arbitrarily, and no reconciliation flag can see the difference because the statement still foots either way. So do not report this output as OR-conformant, as the minimum structure, or as ready to file. Pass compareTo={asOf:'YYYY-MM-DD'} for the prior-date column. This is the filed statement, so it accepts no alternate grouping.

ParametersasOfrequiredcompareToworkspaceIdrequired

income_statement

Erfolgsrechnung (income statement) for a period, in the OR Art. 959b Abs. 2 Gesamtkostenverfahren layout: the ten account-backed positions in their prescribed order, each under its statutory wording, plus reingewinnMinor, which is position 11, Jahresgewinn oder Jahresverlust. Unlike the Bilanz this Absatz is a flat list with no sub-level, so all eleven positions are present and in the prescribed order. Two things to say about the eleventh rather than claim: it is the COMPUTED result and not a section of its own, so no account falls into it, and the exported Erfolgsrechnung heads it with the ENACTED wording resolved by sign, Jahresgewinn above zero and Jahresverlust below it, keeping the full Jahresgewinn oder Jahresverlust verbatim at exactly zero. Each single word is the enacted wording with the branch the book did not take dropped, because the oder enumerates the two outcomes a result can have rather than naming one heading. The conventional Treuhand wording Reingewinn oder Reinverlust is not printed. The Bilanz carries the same figure under the same enacted wording as an Eigenkapital position, so the two statements of one Jahresrechnung give one number one name. A RESIDUAL section, `uebrige_positionen`, follows the ten and holds anything that fits none of them, so no account is dropped. It is the "weitere Positionen" of OR Art. 959b Abs. 5, which is a different Absatz and not one of the eleven: it is neither position 11 nor a twelfth prescribed position. Every position is signed as its CONTRIBUTION TO PROFIT (revenue positive, expense negative, and the three positions the statute writes as Aufwand AND Ertrag shown net), so a loss is a negative figure and never an absolute value. This is the verb that answers "wie war der Gewinn in Q2?". Pass compareTo={periodStart,periodEnd} for the prior-period column. The Absatzerfolgsrechnung of OR Art. 959b Abs. 3 is not built, and this is the filed statement, so it accepts no alternate grouping.

ParameterscompareToperiodEndrequiredperiodStartrequiredworkspaceIdrequired

general_ledger

Kontoblatt (general ledger) for ONE account: the opening carry, every posted line in the period in date order with its running balance, and the closing balance. Each line carries the entryId to drill into with get_entry and the entry's Belegnummer (number). Debit-positive like the Saldenbilanz, with naturalSide naming which way the account is expected to lean. Drafts never appear. groupBy accepts 'kmu' today and refuses anything else.

ParametersgroupByaccountIdrequiredperiodEndrequiredperiodStartrequiredworkspaceIdrequired

export_statement

Render any of the four statements as a LOCAL file and hand back its bytes, base64-encoded: kind one of 'trial', 'balance', 'income', 'ledger', format one of 'csv', 'pdf', plus that statement's own parameters. The CSV is locale-neutral for re-import (integer Rappen, ISO dates, a record_type column carrying the totals) and the PDF is a print artifact with no PDF/A conformance claimed. The model is computed by the same verb the screen calls, so the file and the screen cannot disagree. Nothing is transmitted: e-filing or publishing the artifact is a cloud-tier concern and is not part of this tool.

ParametersasOfkindrequiredformatrequiredgroupByaccountIdcompareToperiodEndperiodStartworkspaceIdrequired

set_opening_balances

Seed the workspace's opening position as ONE balanced, posted, immutable opening entry (Eröffnungsbilanz), each account carrying exactly one side in integer Rappen. Accounts are named by id or by chart number. If the set does not tie out it is REFUSED with the signed Rappen difference and nothing is written: pass differenceAccount (KMU 9100 Eröffnungsbilanz is the usual one) to book the delta explicitly, because a difference is never plugged silently. asOf defaults to the fiscal-year start. Once the prior year is closed the position is carried from that close and this verb answers carried_forward. An account that already carries a balance on that date is refused with account_already_has_balance, naming what it holds: seeding on top of it would double-count, and a bank opening balance from set_bank_opening_balance is the usual reason. A posted position has no edit path: correct it with a reversing entry (reverse_entry) and a fresh call under a new key, which this verb accepts once the storno has unwound the old position. reference records the Inventar or Beleg the position is traced to, and get_opening_balances reads it back. CONSEQUENCE: Books your opening position as one balanced opening entry for the year. If it is wrong, you correct it with a correcting entry and enter the position again; both stay visible.

ParametersasOflinesrequiredreferencedescriptionworkspaceIdrequiredidempotencyKeyrequireddifferenceAccount

preview_opening_import

Dry-run already-parsed migration rows into an opening position without writing anything: normalises a signed balance column or a debit/credit pair into integer Rappen, resolves each account number against the chart, and reports the rows the chart does not know (unmapped) alongside the balance check. Takes rows as an array of column-value objects, NOT a CSV string: parsing the export into rows is the caller's step. It refuses the same things import_opening_balances refuses, including an account named twice, so a preview that comes back clean is not followed by a surprise rejection. This reports what the FILE says and whether its own two sides agree; it cannot tell you the file is the right one, so compare the per-account lines against the Beleg. It takes no idempotency key and can never post.

ParametersasOfrowsrequiredformatmappingreferenceworkspaceIdrequireddifferenceAccount

import_opening_balances

Import already-parsed migration rows as the opening position, posting exactly what preview_opening_import showed. Takes rows as an array of column-value objects, NOT a CSV string: this verb parses no file, and turning an export into rows is the caller's step. Refuses with unmapped_account when any row names an account the chart does not have, because importing the rest would post a position that balances only because a row was dropped. A row with no account at all (what a subtotal or Total line looks like) is refused as invalid_row naming that row, rather than blamed on the mapping. format accepts csv or bexio, which select default column names; mapping overrides them field by field for an export with different headers. Retrying the same idempotencyKey returns the existing entry and never a second opening position. CONSEQUENCE: Books the imported opening position as one balanced entry. If it is wrong, you correct it with a correcting entry; both stay visible.

ParametersasOfrowsrequireddryRunformatmappingreferenceworkspaceIdrequiredidempotencyKeyrequireddifferenceAccount

get_opening_balances

Read the opening position for a fiscal year: the seeded entry when there is one, or the balance sheet carried from the prior year's close (source=carried_forward, editable=false), or none. Returns each account with its number, name, type and side in Rappen, plus the totals and the reference (the Inventar or Beleg the position is traced to, null when none was given). A position that was reversed reads as source=none, because the ledger no longer holds it. A carried position is derived from the close and is not re-keyable: A03 owns the year-end result posting. If a carried position does not tie out it is REFUSED with carried_position_unbalanced rather than handed on, naming the signed difference and unclosedYears: an unclosed prior year leaves its result on Erfolgsrechnung accounts that a balance sheet does not carry, and every report above this one would be built on the gap.

ParametersyearworkspaceIdrequired

preview_feedback

Render exactly what a feedback report would contain, writing nothing. Use it to show a person what would be sent before it is.

ParameterskindlocalemessagerequiredsubjectrequiredclientErrorworkspaceIdrequiredincludeDiagnostics

prepare_feedback

Write a feedback report to ~/.till/feedback and return a mailto link for it. TILL cannot send: the report is handed to the operator to send from their own mail client. CONSEQUENCE: Writes your feedback report, with diagnostics if you include them, to a file on this machine and returns a mail link for it. TILL sends nothing; once you send it, it cannot be called back.

ParameterskindlocalemessagerequiredsubjectrequiredclientErrorworkspaceIdrequiredidempotencyKeyrequiredincludeDiagnostics

list_feedback

List the feedback reports written on this computer. Every one reads "prepared": TILL hands a report to a mail client and cannot observe whether it was sent.

ParametersworkspaceIdrequired

get_diagnostics

Read the error-recording preference and every recorded entry: the whole of what TILL has kept about this person, readable at any time with no request to make.

ParametersworkspaceIdrequired

set_diagnostics

Turn error recording on or off for this computer. Off is the default, and turning it off also deletes what was recorded. Set this only when the person asks for it.

ParameterscapturerequiredworkspaceIdrequired

clear_diagnostics

Delete every recorded error detail. Reports already written are kept.

ParametersworkspaceIdrequired

whoami

Report who this session is in a workspace: the actor, the role it holds, and the exact capabilities that role resolves to. The ONE source a client may use to pre-disable a write control. For the governed agent seat it also carries agentDial, the effective approval-dial level per capability (auto executes, ask drafts), so an agent can predict whether a write will post or wait for approval without holding the owner-only get_agent_dial.

ParametersworkspaceIdrequired

list_members

List everyone bound to this workspace, pending invites included and labelled as pending (a pending member holds no capability at all).

ParametersworkspaceIdrequired

list_roles

List every assignable role with its resolved capability bundle and its member count, plus the capability registry itself so a client can render the whole matrix.

ParametersworkspaceIdrequired

invite_member

Invite someone to this workspace with a role. Writes a pending membership and PREPARES the invite; TILL has no transport and never claims a send, so the token is handed back for the operator to deliver. kind (human, the default, or agent) says what the invitee is: an agent member is the governed seat, its writes draft under the approval dial and its calls land in the trace, on every door including a served one. CONSEQUENCE: Gives a person or an agent access with the chosen role. Whoever redeems the invitation sees the books until you revoke the access; what they saw stays seen.

ParameterskindrolerequiredemailrequireddisplayNameworkspaceIdrequiredidempotencyKeyrequired

accept_invite

Redeem an invite token: bind this session actor to the invited identity and activate the membership. Pre-workspace, because the accepter is not a member yet.

Parameterstokenrequired

set_role

Change a member's role. It takes effect on their next call: nothing caches a capability resolution. Demoting the only remaining owner is refused (last_owner). CONSEQUENCE: Changes what this member may see and do, from their next step on. You can change it again at any time.

ParametersrolerequiredmemberIdrequiredworkspaceIdrequired

revoke_member

Remove a member's access to this workspace. The person's identity survives, so re-inviting them restores their history. Revoking the only remaining owner is refused (last_owner). CONSEQUENCE: Removes this member's access from their next step on. What they did stays on record; you can invite them again.

ParametersmemberIdrequiredworkspaceIdrequired

define_role

Create a custom role, or reshape one of the three editable built-ins, as a named subset of the capability registry. A name outside the registry is refused (unknown_capability): a role can only ever recombine capabilities that already gate a real verb. CONSEQUENCE: Changes what this role may do, for everyone who holds it. The earlier set of rights is not kept.

ParametersnameroleIdworkspaceIdrequiredcapabilitiesrequiredidempotencyKeyrequired

archive_role

Soft-flag a custom role so it stops being offered. Never a delete, so historical attributions still resolve, and refused while any member still holds it (role_in_use). CONSEQUENCE: Stops offering this custom role; it is refused while anyone still holds it, and past actions keep their role. You can define a role with the same rights again.

ParametersroleIdrequiredworkspaceIdrequiredidempotencyKeyrequired

define_field

Define a typed custom field on any registered entity, or patch an existing one by its key. The key may not shadow a column the entity already has, and once any value is stored the type is frozen (changing it would silently corrupt what is already there). A field defined by the agent actor lands as a draft and is invisible until confirm_field releases it. CONSEQUENCE: Adds or changes a custom field on every record of the chosen kind, for everyone in the workspace. You can archive it later; values already stored are kept.

ParameterskeyrequiredsorttyperequiredoptionsrequiredlabelI18nrequiredentityKindrequiredworkspaceIdrequireddefaultValueidempotencyKey

confirm_field

Release a draft custom field so every screen and every caller can see it: the human half of draft-by-default. Confirming a field that is already live is a successful no-op.

ParametersfieldDefIdrequiredworkspaceIdrequiredidempotencyKey

archive_field

Archive a custom field so it stops being offered as an input. Never a delete: every value already stored survives and stays readable through list_field_values. Returns how many saved views still name the field, so the caller can warn rather than be surprised later.

ParametersfieldDefIdrequiredworkspaceIdrequiredidempotencyKey

list_field_defs

List the custom fields defined on an entity kind, in display order. Archived fields and unconfirmed drafts are excluded unless asked for.

ParametersentityKindrequiredworkspaceIdrequiredincludeDraftsincludeArchived

set_field_value

Set one custom field value on one record, validated against the field type (a money value is an integer Rappen count, a date is ISO-8601, a select value is one of its options). Passing null clears the value. Requires whatever capability editing that entity itself requires, never a G00 capability: a custom field is not a side door.

ParametersvalueentityIdrequiredfieldKeyrequiredentityKindrequiredworkspaceIdrequiredidempotencyKey

list_field_values

Read every custom field value stored on one record, archived fields included and labelled as archived. This is how a retired field's data stays reachable after it stops rendering as an input.

ParametersentityIdrequiredentityKindrequiredworkspaceIdrequired

create_saved_view

Save a named filter, sort, column set and layout over an entity list. The view belongs to the calling session unless shared is true, which publishes it to everyone in the workspace and requires manage_saved_views.

ParametersnamerequiredsortlayoutsharedcolumnsfiltersisDefaultentityKindrequiredworkspaceIdrequiredidempotencyKey

update_saved_view

Patch a saved view. A personal view may only be changed by the session that owns it; a workspace-shared one, and publishing a personal one, require manage_saved_views.

ParameterspatchrequiredviewIdrequiredworkspaceIdrequiredidempotencyKey

delete_saved_view

Delete a saved view outright. A true delete rather than an archive, because a view mints no state and holds no history; deleting a shared one requires manage_saved_views.

ParametersviewIdrequiredworkspaceIdrequiredidempotencyKey

list_saved_views

List the saved views available for an entity kind: this session's own first, then the workspace-shared ones. Another session's personal views are never returned.

ParametersentityKindrequiredworkspaceIdrequired

create_document_template

Create a document template (invoice, credit_note, quote or dunning_run): footer text per locale, render language (fixed or per contact), an ordered set of OPTIONAL non-legal line-item columns, and an E00 logo file linked via entityKind document_template. Always saved as a NON-default draft: going live is a separate set_default_document_template call a human confirms. Presentation only: the Swiss QR-bill payload, VAT figures and legal content are produced by the document itself and pass through byte-identical under every template.

ParametersnamerequiredfooterI18nfixedLocaleworkspaceIdrequireddocumentKindrequiredlanguageModeidempotencyKeylogoDocumentIdlineItemColumns

update_document_template

Update a template (name, footer, language, optional columns, logo). NEVER changes an already-issued document: issue froze the template content onto the document, so a mailed copy and a later reprint always match; only documents issued after the edit pick it up. CONSEQUENCE: Changes the template for documents issued from now on; documents already issued keep the look they were issued with. You can change it again.

ParameterspatchrequiredtemplateIdrequiredworkspaceIdrequiredidempotencyKey

set_default_document_template

Make a template THE default for its document kind: the prior default clears in the same atomic write (at most one default per kind), and every document of that kind issued afterwards freezes to it. Wide-effect and reviewable: this changes the workspace's outward face. CONSEQUENCE: Makes this template the default for its kind of document, for everything issued from now on; issued documents keep theirs. You can choose another default at any time.

ParameterstemplateIdrequiredworkspaceIdrequireddocumentKindrequiredidempotencyKey

archive_document_template

Soft-archive a template (and clear it as default). NOT destructive: a template frozen onto an already-issued document keeps rendering that document forever; archiving only removes it from the active list and from new defaults.

ParameterstemplateIdrequiredworkspaceIdrequiredidempotencyKey

preview_document_template

Render a template preview as PDF bytes, mutating nothing: against sampleDocumentId, else the most recent real document of the template's kind, else synthetic MUSTER-watermarked sample data that is never a payable document (no fabricated QR code; a missing QR-IBAN surfaces A11's own needs_qr_iban cause).

ParameterstemplateIdrequiredworkspaceIdrequiredsampleDocumentId

list_document_templates

List document templates, newest defaults first per kind: id, kind, name, default/archived flags, footer locales, language mode, and the linked logo file id. documentKind narrows to one kind; includeArchived includes archived rows; savedViewId applies a saved view (G00).

ParameterssavedViewIdworkspaceIdrequireddocumentKindincludeArchived

get_document_template

One template with its full configuration: footer text per locale, language mode, fixed locale, optional column order, default/archived flags, and the linked logo file id.

ParameterstemplateIdrequiredworkspaceIdrequired

dispatch_text_upsert

Speichere den Textbaustein (subject + body) for one document kind (invoice, quote or dunning_run) and locale: the workspace's own outbound message text, with {{variable}} interpolation validated against the kind's registered variable set AT SAVE TIME (an unknown variable is refused with unknown_variable naming the valid set, never a broken mail later). Naturally idempotent: it asserts the absolute state of one (documentKind, locale) slot, and a value-identical re-save writes nothing at all. Sends NOTHING and changes no send behaviour; a missing slot falls back to the built-in default text at preview. CONSEQUENCE: Saves the email text for this kind of document and language; messages sent from now on use it. You can edit it again or reset it to the built-in text.

ParametersbodyrequiredlocalerequiredsubjectrequiredworkspaceIdrequireddocumentKindrequired

dispatch_text_reset

Return one Textbaustein slot (documentKind invoice, quote or dunning_run, and locale) to the built-in default text by deleting its saved subject + body; the slot then resolves exactly as one never saved (preview defaulted:true). Naturally idempotent: resetting a slot with no saved text answers ok with unchanged:true and touches nothing. Sends NOTHING, changes no send behaviour and never edits the send log. CONSEQUENCE: Deletes the saved email text for this kind of document and language; messages from now on use the built-in text. To get your text back, it has to be saved again.

ParameterslocalerequiredworkspaceIdrequireddocumentKindrequired

dispatch_preview

Render the concrete outbound message(s) as a pure read: nothing is sent, nothing is written. documentId (invoice/quote) yields exactly one resolved message; runId (dunning_run) yields one per debtor (narrowable via contactId), each with the recipient, locale, filled variables and attachment list; with neither, the saved or built-in text renders against MUSTER sample values (the editor's preview; locale picks the slot). A recipient without an email carries the owning send verb's own flag (needs_customer_email per A11, needs_email_transport per A15).

ParametersrunIdlocalecontactIddocumentIdworkspaceIdrequireddocumentKindrequired

list_dispatches

Das Versand-Protokoll: the cross-document send log, newest first, one row per recipient per send attempt (send_invoice, send_dunning_run, quotes_send), each carrying kind, document/run link, contact, recipient, channel (smtp|cloud_relay|artifact_only), locale, the resolved subject and body that actually left, outcome (sent|degraded|failed|artifact_created) and a degrade reason. Filter by kind, contact, outcome or date range; savedViewId applies a saved view (G00). Also returns texts, the saved Textbausteine slots, so the editor reads its state in the same call. Append-only: no verb can edit or delete a row.

ParameterstofromoutcomecontactIdsavedViewIdworkspaceIdrequireddocumentKind

create_automation_rule

Define an automation: when this event happens, and this condition holds, call this write verb with this input. The action must name a registered write verb, the trigger must name a registered event, and a rule whose action is the very verb that emits its own trigger is refused outright. A rule created by the agent actor lands DISABLED and a human must enable it. CONSEQUENCE: Creates the automation rule; once enabled, it runs its action in its author's name on every matching event, unattended. You can disable or archive it; what it already did stays done.

ParametersnamerequiredactionrequiredenabledtriggerrequiredconditionworkspaceIdrequiredidempotencyKey

update_automation_rule

Patch a rule: its name, trigger, condition or action. The patched shape is validated exactly as a new rule is, so an edit can never leave a rule the engine would have refused to create. The change takes effect on the next event; an archived rule refuses. CONSEQUENCE: Changes what the rule reacts to or does, from the next event on; an enabled rule runs the change unattended. You can change it again, but the earlier version is not kept.

ParameterspatchrequiredruleIdrequiredworkspaceIdrequiredidempotencyKey

enable_automation_rule

Let a rule fire again. Requires manage_automations, which is the asymmetry that makes disabling safe to leave open: anyone may stop a rule, only an administrator may start one. An archived rule refuses. CONSEQUENCE: Lets the rule fire again: every matching event then runs its action in its author's name, unattended. You can disable it at any time; what it already did stays done.

ParametersruleIdrequiredworkspaceIdrequired

disable_automation_rule

Stop a rule firing, immediately. Deliberately requires NO capability at all: a stop button that needs a permission is not a stop button, and disabling only ever prevents a write, never causes one.

ParametersruleIdrequiredworkspaceIdrequired

archive_automation_rule

Retire a rule: it stops matching, disappears from the default list, and stops being enable-able. Never a delete, and its run history is untouched, because a firing that happened stays true after the rule that caused it is gone.

ParametersruleIdrequiredworkspaceIdrequired

run_due_automations

The tick for schedule triggers: fire every enabled rule whose cadence has come due at asOf, at most ONCE each however far behind it had fallen. Requires manage_automations, because the tick is what makes a schedule rule write unattended. asOf may name a past instant but never a future one: the injected clock decides what is due, not the caller. Safe to call as often as you like: a repeated tick for the same asOf computes the same occurrence key and does nothing. CONSEQUENCE: Runs every enabled automation whose schedule is due, each in its author's name and unattended. What they do can't be undone by TILL; you stop a rule by disabling it.

ParametersasOfworkspaceIdrequired

retry_automation_run

Finish a run that is stuck in `running`, which is what a process death between the claim and the settle leaves behind. Re-sends the SAME stored input, including the derived idempotency key, so a lost invocation that had in fact committed cannot happen twice. Runs as the actor the firing ran as, so a since-demoted author is refused now exactly as it would be on a fresh firing. Requires manage_automations. A settled run refuses with run_not_stuck. CONSEQUENCE: Finishes an automation run that was cut off, sending its stored step again in the name it ran under. What it does stays done; the same step never runs twice.

ParametersrunIdrequiredworkspaceIdrequiredidempotencyKey

list_automation_rules

List this workspace automation rules, newest first, archived ones excluded unless asked for. Also returns the CATALOGUE a rule can be built from: every registered trigger event, every write verb that is a legal action, and every condition operator, all read off the live registries so a picker cannot offer something the engine would refuse. savedViewId applies a saved view (G00): its stored filters are merged underneath any filter named explicitly here.

ParameterseventenabledsavedViewIdworkspaceIdrequiredincludeArchived

get_automation_rule

Read one automation rule: its trigger, its condition, the verb it calls and the input template it calls it with, who authored it (and therefore whose capabilities its firings carry), and when it last fired.

ParametersruleIdrequiredworkspaceIdrequired

list_automation_runs

The run log, newest first: what fired, when, why, what it actually sent, and what came back. A failed run carries the target verb's own rejection code verbatim, so the reason reads the same way it would on that verb's own screen.

ParameterslimitruleIdstatusworkspaceIdrequired

get_automation_run

Read one firing in full, including the resolved action input that was really sent rather than the template it came from.

ParametersrunIdrequiredworkspaceIdrequired

delete_item

Hard-delete an item that is referenced nowhere (no document line, price-list row, variant, or stock movement). A referenced item is refused with item_referenced and its reference kinds; archive it instead so posted history stays resolvable. CONSEQUENCE: Deletes the item for good; only an item nothing refers to can go. You can create it again, but not restore it.

ParametersitemIdrequiredworkspaceIdrequiredidempotencyKey

item_categories_upsert

Create or edit an item category (two-level tree: the parent of a child must itself be a root). Pass categoryId to edit, omit it to create.

ParametersnamesortparentIdcategoryIdworkspaceIdrequiredidempotencyKey

item_categories_delete

Delete an item category. Refused (category_in_use) while any item or child category still points at it; reassign or empty it first. CONSEQUENCE: Deletes the item category for good; only one that no item or subcategory points at can go. You can create it again.

ParameterscategoryIdrequiredworkspaceIdrequiredidempotencyKey

item_categories_list

List the item categories in the workspace, ordered by sort then name.

ParametersworkspaceIdrequired

price_lists_upsert

Create or edit a price list, scoped to exactly one contact OR one segment (scope_ambiguous otherwise). One scope holds at most one list: a second list for a contact or segment that already has one is refused with scope_taken naming the existing list, so no price is ever resolved by insertion order. Pass priceListId to edit, omit it to create.

ParametersnamesegmentcontactIdpriceListIdworkspaceIdrequiredidempotencyKey

price_lists_set_price

Set a list price for an item from a validFrom date (ISO YYYY-MM-DD; anything else is refused with invalid_input, never coerced). Price history is append-only: this adds a row rather than editing a past one, so a resolve at any date reads a stored integer Rappen amount.

ParametersitemIdrequiredcurrencyvalidFromrequiredpriceMinorrequiredpriceListIdrequiredworkspaceIdrequiredidempotencyKey

price_lists_list

List the workspace price lists with their scope (contact or segment).

ParametersworkspaceIdrequired

price_lists_get

Read one price list and its price rows (the validFrom history per item).

ParameterspriceListIdrequiredworkspaceIdrequired

Version history

2
  1. v1.0.0-beta.2LatestNov 5, 2026
  2. v0.1.0Sep 16, 2026

Score

90% of the applicable checks met

How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.

  • Reached in the last 30 days25 pts
  • Auth declared10 pts

    Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.

  • Tools listedRequired15 pts
  • DescriptionRequired15 pts
  • Logo10 pts
  • Documentation15 pts
  • License10 pts