{"serverId":"mcp-registry/ch.zeno-works/zeno","tools":[{"name":"create_ar_invoice","fixed":{"name":"create_ar_invoice","description":"Create an outgoing (sales) invoice as a **draft** — nothing is booked yet\n(accountant role).\n\nA draft changes no ledger balance, gets no invoice number and appears in no VAT\nreturn; it exists so it can be edited. Posting happens later, at\n``POST …/ar/invoices/{invoice_id}/issue``.\n\nBody: ``issue_date`` (required), ``counterparty_id`` (the customer — required\nbefore it can be issued), ``currency``/``language``, ``due_date`` (filled from the\ncustomer's payment terms when omitted), ``doc_type`` (``","inputSchema":{"type":"object","required":["company_id","issue_date"],"properties":{"lines":{"type":"array","items":{"type":"object","properties":{"quantity":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}]},"item_code":{"anyOf":[{"type":"string"},{"type":"null"}]},"line_type":{"enum":["product","service","text","section","discount"],"type":"string","default":"product"},"product_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"sort_order":{"anyOf":[{"type":"integer"},{"type":"null"}]},"unit_label":{"anyOf":[{"type":"string"},{"type":"null"}]},"unit_price":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}]},"description":{"anyOf":[{"type":"string","maxLength":8000},{"type":"null"}]},"group_label":{"anyOf":[{"type":"string"},{"type":"null"}]},"vat_code_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"account_code":{"anyOf":[{"type":"string"},{"type":"null"}]},"line_discount_kind":{"enum":["none","percent","amount"],"type":"string","default":"none"},"service_period_end":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}]},"line_discount_value":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"default":"0"},"service_period_start":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}]}}}},"notes":{"anyOf":[{"type":"string"},{"type":"null"}]},"currency":{"type":"string","default":"CHF","pattern":"^[A-Za-z]{3}$","maxLength":3,"minLength":3},"doc_type":{"enum":["invoice","credit_note"],"type":"string","default":"invoice"},"due_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}]},"language":{"anyOf":[{"enum":["de","en","fr","it"],"type":"string"},{"type":"null"}]},"company_id":{"type":"string"},"issue_date":{"type":"string","format":"date"},"legal_text":{"anyOf":[{"type":"string"},{"type":"null"}]},"footer_text":{"anyOf":[{"type":"string"},{"type":"null"}]},"counterparty_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"payment_terms_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"issuer_profile_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"service_period_end":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}]},"credited_invoice_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"service_period_start":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}]},"invoice_discount_kind":{"enum":["none","percent","amount"],"type":"string","default":"none"},"invoice_discount_value":{"anyOf":[{"type":"number","minimum":0},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"default":"0"}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":890,"tokensAfter":890},{"name":"create_gl_journal_entry","fixed":{"name":"create_gl_journal_entry","description":"Create a **draft** journal entry by hand — accountant. It carries NO source document\nunless you name one.\n\nOnly the pipeline paths stamp ``source_ref_type``/``source_ref_id`` (an ingested\ndocument is ``'item'``), so by default a booking made here cannot be traced back to\nwhat justified it. ``document_id`` is the retrofit: name an existing ``gl_document``\n(create one with ``POST …/gl/documents``) and it is attached as the entry's source\ndocument, so the entry is not evidence-less. The same attach","inputSchema":{"type":"object","required":["company_id","period_id","posting_date","lines"],"properties":{"lines":{"type":"array","items":{"type":"object","required":["account_id","line_role"],"properties":{"vat":{"anyOf":[{"type":"object","required":["code"],"properties":{"code":{"type":"string"},"amount":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}]}},"description":"VAT tag for a manual journal line (Gap B — manual-booking metadata completeness).\n\nTag **both** legs of a taxable movement with the same ``code`` (a company\n``gl_vat_code``): the taxable **base** line *and* its paired **VAT-control** line —\nposting groups the entry's lines by this tag to pair base with VAT line, and the\ncomposition guard refuses (409) any VAT-control line left untagged (ZET-158: a\nbase-line-only tag has no working path). One tagged control line per code.\n``amount`` (the tax, a magnitude) is read from the **base** line's tag and falls\nback to the paired VAT line's amount — when several base lines share one VAT line,\ngive each base line its own ``amount``, or every pair books the full VAT-line\namount. Exception: a catalog-linked **zero-rated output** code (0 % rate) books no\nVAT-control line at all — tag only the base line(s); each line tagged with such a\ncode declares its turnover once. On post the entry gains ``gl_vat_posting`` rows so\nthe lines reach the MWST-Abrechnung (``doc/pipeline.md §\"VAT\"``)."},{"type":"null"}]},"currency":{"anyOf":[{"type":"string","maxLength":3},{"type":"null"}]},"line_role":{"enum":["bank","ar","ap","revenue","expense","vat","payroll_liability","salary_expense","fixed_asset","depreciation","inventory","cogs","clearing","rounding","fx_gain_loss","suspense"],"type":"string"},"open_item":{"anyOf":[{"type":"object","properties":{"create":{"type":"boolean","default":false},"due_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}]}},"description":"Open a new AR/AP subledger item from a manual control line (Gap B).\n\nSet ``create=true`` on the line posting to the AR/AP control account; ``due_date`` seeds\naging. Direction is derived from the account's ``control_type``. To settle/adjust an\n*existing* item instead, pass ``open_item_id`` on the line (its balance is rebuilt on post)."},{"type":"null"}]},"account_id":{"type":"string"},"amount_base":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}]},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"open_item_id":{"anyOf":[{"type":"string","maxLength":21},{"type":"null"}]},"amount_currency":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}]},"counterparty_id":{"anyOf":[{"type":"string"},{"type":"null"}]}}},"minItems":1},"currency":{"anyOf":[{"type":"string","maxLength":3},{"type":"null"}]},"period_id":{"type":"string"},"company_id":{"type":"string"},"entry_type":{"enum":["manual","invoice","payment","bank","vat","payroll","accrual","deferral","depreciation","inventory","closing","adjustment","reversal"],"type":"string","default":"manual"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"document_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"posting_date":{"type":"string","format":"date"},"auto_reverse_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}]}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":1103,"tokensAfter":1103},{"name":"describe_operations","fixed":{"name":"describe_operations","description":"Get the full argument schema for operations found via ``find_operations``.\n\nReturns each operation's description and JSON-Schema arguments — everything\na listed tool would have shown you.\n\n``include_output_schema`` is the ONLY way to obtain an operation's result\nshape: ``tools/list`` publishes no output schema for any operation, listed or\nnot (they are 67 % of the payload, and a schema describing a *result* is not\nwhat you need to *choose* a tool). Ask for it when you must know the shape in\nadva","inputSchema":{"type":"object","required":["names"],"properties":{"names":{"type":"array","items":{"type":"string"}},"include_output_schema":{"type":"boolean","default":false}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":212,"tokensAfter":212},{"name":"find_operations","fixed":{"name":"find_operations","description":"Search EVERY operation this server offers, including the ones not in your tool list.\n\nThe tool list you can see holds only the common operations. Several hundred\nmore — payroll, VAT returns, year-end close, fixed assets, expenses, cash\nbook, settings, integrations, onboarding — are available but unlisted, and\nthis is how you find them. **Search here before concluding that Zeno cannot\ndo something.**\n\nCall with no arguments to browse the catalogue of areas (tags) with their\noperation counts; with","inputSchema":{"type":"object","properties":{"tag":{"type":"string","default":""},"limit":{"type":"integer","default":25},"query":{"type":"string","default":""},"offset":{"type":"integer","default":0}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":218,"tokensAfter":218},{"name":"get_ar_invoice","fixed":{"name":"get_ar_invoice","description":"One authored outgoing invoice with its lines (CompanyMember read).\n\nCarries the header (customer, dates, currency, discounts), every line with its\ncomputed ``net_amount``/``vat_amount``/``gross_amount``, the document totals, and\nthe lifecycle: ``status`` (``draft`` → ``issued`` → ``cancelled``), ``document_no``\n(assigned at issue — ``null`` on a draft), ``commercial_document_id`` /\n``snapshot_document_id`` (the booked document and the frozen PDF), plus the derived\n``payment_status`` and ``open_a","inputSchema":{"type":"object","required":["company_id","invoice_id"],"properties":{"company_id":{"type":"string"},"invoice_id":{"type":"string"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":194,"tokensAfter":194},{"name":"get_bank_entry","fixed":{"name":"get_bank_entry","description":"Get Bank Entry","inputSchema":{"type":"object","required":["entry_id"],"properties":{"entry_id":{"type":"string"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":44,"tokensAfter":44},{"name":"get_bank_status","fixed":{"name":"get_bank_status","description":"Composed bank snapshot — \"is my banking up to date?\" (CompanyMember). \"Reconciled\" means\nentries matched, NOT ledger = statement balance (the close check\n``BANK_BALANCE_DOES_NOT_TIE_OUT`` compares that).\n\nOne call for what otherwise takes ``GET …/bank-accounts`` + one\n``GET /bank-accounts/{id}/coverage`` per account + ``GET …/reconciliation/summary``\n— and which those three still do not answer: per-account entry-status counts, the\n``in_suspense`` counts, each account's latest closing balance, an","inputSchema":{"type":"object","required":["company_id"],"properties":{"company_id":{"type":"string"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":183,"tokensAfter":183},{"name":"get_company","fixed":{"name":"get_company","description":"Get one company (jwt, scoped). 403 for a non-member non-admin, 404 if absent.","inputSchema":{"type":"object","required":["company_id"],"properties":{"company_id":{"type":"string"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":62,"tokensAfter":62},{"name":"get_financial_statements","fixed":{"name":"get_financial_statements","description":"Computed balance sheet + P&L for a fiscal year, from the journal (CompanyMember).\n\nAggregates every posted GL journal line whose **posting date** falls in the fiscal\nyear's window, classifies each account against the chart of accounts, and reports\nproblems (unbalanced trial balance, unclassified accounts, year-not-opened, mixed\ncurrency). **Read-only and distinct** from the uploaded balance-sheet snapshots\n(``GET .../balance-sheets``): nothing here reads a snapshot. ``fiscal_year`` optional\n→ th","inputSchema":{"type":"object","required":["company_id"],"properties":{"month":{"anyOf":[{"type":"string","pattern":"^\\d{4}-(0[1-9]|1[0-2])$"},{"type":"null"}]},"company_id":{"type":"string"},"fiscal_year":{"anyOf":[{"type":"integer"},{"type":"null"}]}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":227,"tokensAfter":227},{"name":"get_gl_account_ledger","fixed":{"name":"get_gl_account_ledger","description":"One account's ledger (Kontoauszug) — every booking that touched it, in date\norder, with Soll/Haben and the running balance after each line (CompanyMember).\n\nNo fiscal-year parameter: the chart is FY-owned, so ``account_id`` already\nfixes the year. Posted **and** reversed entries are included (a reversal is a\nsecond posted entry, so both legs must show or the balance is wrong); drafts\nare not. The running balance is carried across pages by the server —\n``page_opening_balance`` enters this page's ","inputSchema":{"type":"object","required":["company_id","account_id"],"properties":{"limit":{"type":"integer","default":100,"maximum":200,"minimum":1},"offset":{"type":"integer","default":0,"minimum":0},"date_to":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}]},"date_from":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}]},"account_id":{"type":"string"},"company_id":{"type":"string"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":270,"tokensAfter":270},{"name":"get_gl_journal_entry","fixed":{"name":"get_gl_journal_entry","description":"One entry + lines, plus the subledger rows it produced/touched (Gap B detail\nsurfacing): its ``gl_vat_posting`` rows, its ``gl_vat_zero_posting`` turnover-only\ndeclarations (zero-rated/exempt output — R2-13b: these have no VAT line to pair, so\nwithout them a correctly coded 0 % line is indistinguishable from an untagged one)\nand referenced ``gl_open_item`` rows. A declared base line also carries the resolved\ncode on ``lines[].vat_code``. Drafts carry empty lists. ``documents`` carries the\naccoun","inputSchema":{"type":"object","required":["company_id","entry_id"],"properties":{"entry_id":{"type":"string"},"company_id":{"type":"string"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":194,"tokensAfter":194},{"name":"get_intake","fixed":{"name":"get_intake","description":"Get one intake row by id (scoped to its company: 404/403 split).","inputSchema":{"type":"object","required":["intake_id"],"properties":{"intake_id":{"type":"string"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":57,"tokensAfter":57},{"name":"get_item","fixed":{"name":"get_item","description":"Item metadata (any source).","inputSchema":{"type":"object","required":["item_id"],"properties":{"item_id":{"type":"string"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":45,"tokensAfter":45},{"name":"get_kpi_report","fixed":{"name":"get_kpi_report","description":"Snapshot KPIs + 13-week liquidity forecast, from the GL (CompanyMember).\n\nCash/liability balances aggregate the FY window of posted/reversed journal lines;\naging + forecast read ``gl_open_item``, pending salary batches and unpaid VAT\nperiods. Read-only. Explicit non-member ``company_id`` → 403.\n\n``fiscal_year`` optional → latest FY with journal data; FY figures as of that\nyear's end once past. Aging and forecast anchor to today.","inputSchema":{"type":"object","required":["company_id"],"properties":{"company_id":{"type":"string"},"fiscal_year":{"anyOf":[{"type":"integer","maximum":2200,"minimum":1900},{"type":"null"}]}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":188,"tokensAfter":188},{"name":"get_work_list","fixed":{"name":"get_work_list","description":"Everything waiting for this user in this company, most severe first.","inputSchema":{"type":"object","required":["company_id"],"properties":{"company_id":{"type":"string"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":60,"tokensAfter":60},{"name":"global_search","fixed":{"name":"global_search","description":"Full-text search across emails + items, ranked by ``ts_rank_cd``, scoped (A1).\n\n``archive_status`` constrains item results only (the Documents → Archive page\npasses ``archived`` so its search box never surfaces pending/triage-parked items).","inputSchema":{"type":"object","required":["q"],"properties":{"q":{"type":"string","minLength":1},"type":{"anyOf":[{"type":"string","pattern":"^(email|item)$"},{"type":"null"}]},"limit":{"type":"integer","default":20,"maximum":200,"minimum":1},"offset":{"type":"integer","default":0,"minimum":0},"source":{"anyOf":[{"type":"string","pattern":"^(email|epost|upload|bexio|abacus)$"},{"type":"null"}]},"direction":{"anyOf":[{"type":"string","pattern":"^(payable|receivable)$"},{"type":"null"}]},"company_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"archive_status":{"anyOf":[{"enum":["pending","archiving","archived","failed"],"type":"string"},{"type":"null"}]}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":265,"tokensAfter":265},{"name":"list_ar_invoices","fixed":{"name":"list_ar_invoices","description":"List authored AR invoices, newest first.\n\nOptional ``status`` / ``doc_type`` filter by lifecycle + document kind.\nOptional ``period`` (``YYYY-MM``) filters by the invoice's issue date (its\nmonth); a malformed value returns 422 ``{\"msg\": \"period must be YYYY-MM\"}``.","inputSchema":{"type":"object","required":["company_id"],"properties":{"limit":{"type":"integer","default":50,"maximum":200,"minimum":1},"offset":{"type":"integer","default":0,"minimum":0},"period":{"anyOf":[{"type":"string"},{"type":"null"}]},"status":{"anyOf":[{"enum":["draft","issued","cancelled"],"type":"string","description":"Editorial lifecycle owned by ``ar_invoice`` (paid/overdue are derived on\nread from the linked ``gl_open_item``, not stored here)."},{"type":"null"}]},"doc_type":{"anyOf":[{"enum":["invoice","credit_note"],"type":"string"},{"type":"null"}]},"company_id":{"type":"string"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":257,"tokensAfter":257},{"name":"list_autopost_decisions","fixed":{"name":"list_autopost_decisions","description":"Recent AI-accountant autonomy decisions (CompanyMember) — the activity feed.\n\nShows what the autonomy policy posted (``posted``), would have posted in\n``assist`` shadow mode (``shadow``), or held for human review (``held``).\n``ai_no_match`` shadow rows (one per AI-consulted bank entry with no verdict)\nare feed noise and hidden by default; ``include_no_match=true`` surfaces them\nfor audit. ``outcome`` / ``source_ref_id`` narrow server-side (ZET-75), so the\nheld-review surfaces can see held rows b","inputSchema":{"type":"object","required":["company_id"],"properties":{"cause":{"anyOf":[{"enum":["not_a_decision","expense_receipt","autonomy_off","post_failed","period_closed","amount_cap","credit_note","duplicate_suspicion","self_counterparty","card_statement","payroll_liability_review","vat_uncertainty","fx_untranslated","risk_flags","open_questions","proposal_rejected","missing_original","settlement_mismatch","recognition_schedule","pre_llm_guard","low_confidence","fy_predates_books","fy_gap","unknown_vat_code","currency_mismatch","bank_history","bank_suspense","awaiting_review","unclassified"],"type":"string","description":"Why a decision is sitting in the held tier."},{"type":"null"}]},"limit":{"type":"integer","default":50,"maximum":200,"minimum":1},"offset":{"type":"integer","default":0,"minimum":0},"outcome":{"anyOf":[{"enum":["posted","held","shadow","dismissed"],"type":"string","description":"Outcome recorded for one autonomy decision (``gl_autopost_decision``).\n\n``posted`` — ``auto`` mode, policy cleared, entry was posted (AI).\n``held`` — policy did not clear; nothing posted, falls back to human review.\n``shadow`` — ``assist`` mode, policy *would* have posted but did not (shadow).\n``dismissed`` — held, then closed unposted: the document was decided another way."},{"type":"null"}]},"company_id":{"type":"string"},"source_ref_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"include_no_match":{"type":"boolean","default":false}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":564,"tokensAfter":564},{"name":"list_bank_accounts","fixed":{"name":"list_bank_accounts","description":"The company's bank accounts — the ``bank_account_id`` every statement import,\nbank-entry list and reconciliation call needs (CompanyMember read).\n\nOldest first, no filter and no paging. Each row carries the ``iban``, ``currency``,\n``label``/``bank_name`` and ``ledger_account_number`` — the chart code this account\nposts through; **without one a bank movement cannot be coded**, and it is set with\n``PUT /bank-accounts/{account_id}``.\n\n``auto_created`` marks an account inferred from an imported stat","inputSchema":{"type":"object","required":["company_id"],"properties":{"company_id":{"type":"string"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":183,"tokensAfter":183},{"name":"list_bank_entries","fixed":{"name":"list_bank_entries","description":"List Bank Entries","inputSchema":{"type":"object","required":[],"properties":{"q":{"anyOf":[{"type":"string"},{"type":"null"}]},"to":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}]},"flag":{"anyOf":[{"type":"string"},{"type":"null"}]},"from":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}]},"limit":{"type":"integer","default":20,"maximum":200,"minimum":1},"offset":{"type":"integer","default":0,"minimum":0},"company_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"credit_debit":{"anyOf":[{"enum":["credit","debit"],"type":"string"},{"type":"null"}]},"match_quality":{"anyOf":[{"enum":["full","strong","partial","weak","manual","imported"],"type":"string","description":"Graded bank-entry ↔ invoice match quality (match-quality feature, 2026-06-09).\n\nAuto bands (from how many matchable fields line up, see ``services.reconciliation``):\n``full`` > ``strong`` > ``partial`` > ``weak``. ``manual`` (user-created/confirmed)\nand ``imported`` (asserted by a source export) are provenance values that win over\nany auto band."},{"type":"null"}]},"bank_account_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"counterparty_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"bank_statement_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"settlement_status":{"anyOf":[{"enum":["unsettled","settled","partially_settled","suspense"],"type":"string","description":"Clean-bank settlement state of a bank entry (distinct from ``reconciliation_status``,\nwhich the matcher owns). Set by ``settle_bank_entry``."},{"type":"null"}]},"reconciliation_status":{"anyOf":[{"enum":["unmatched","partially_matched","matched","ignored"],"type":"string"},{"type":"null"}]}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":481,"tokensAfter":481},{"name":"list_companies","fixed":{"name":"list_companies","description":"List companies (jwt, scoped). Non-admins see only their member companies.","inputSchema":{"type":"object","required":[],"properties":{}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":50,"tokensAfter":50},{"name":"list_counterparties","fixed":{"name":"list_counterparties","description":"List a company's counterparties (filter by ``kind``/``q``, paginated).\n\n``kind`` repeats to filter on a set (``?kind=customer&kind=both``) — a\nrole-shaped picker asks for every kind that plays the role. A single\n``?kind=supplier`` keeps its original meaning.","inputSchema":{"type":"object","required":["company_id"],"properties":{"q":{"anyOf":[{"type":"string"},{"type":"null"}]},"kind":{"anyOf":[{"type":"array","items":{"enum":["supplier","customer","both","employee"],"type":"string","description":"Master-data axis of a counterparty (what the party primarily is).\n\n``employee`` (payroll SP1, brief frozen decision 6) is set **only** on\ncounterparties auto-created by ``payroll.master_data\n.ensure_employee_counterparty`` and is terminal — matching/merge flows never\nwiden it to ``both``. Subledger participation rides the richer\n``gl_counterparty_role`` table; payroll logic never branches on ``kind``."}},{"type":"null"}]},"limit":{"type":"integer","default":20,"maximum":200,"minimum":1},"offset":{"type":"integer","default":0,"minimum":0},"company_id":{"type":"string"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":316,"tokensAfter":316},{"name":"list_gl_accounts","fixed":{"name":"list_gl_accounts","description":"List the postable chart (CompanyMember). ``fiscal_year`` filters to one year's chart\n(the chart is FY-scoped, so codes repeat across years).","inputSchema":{"type":"object","required":["company_id"],"properties":{"limit":{"type":"integer","default":50,"maximum":200,"minimum":1},"offset":{"type":"integer","default":0,"minimum":0},"company_id":{"type":"string"},"fiscal_year":{"anyOf":[{"type":"integer"},{"type":"null"}]}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":130,"tokensAfter":130},{"name":"list_gl_journal_entries","fixed":{"name":"list_gl_journal_entries","description":"List journal entries.\n\n``period_ids`` narrows to specific fiscal periods, and is how a caller asks for\n**one fiscal year** — the year's period ids (ZET-285). Without it this route\nlisted, and counted, every fiscal year the company has: the Journal page cut the\nyear out of the fetched page *client-side* while ``total`` stayed company-wide,\nso a year whose entries sat past the first page rendered as \"no entries match\"\nwith no way to reach them. The service applies the filter before the count, so\n`","inputSchema":{"type":"object","required":["company_id"],"properties":{"q":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"sort":{"enum":["entry_no","posting_date","total"],"type":"string","default":"posting_date"},"limit":{"type":"integer","default":50,"maximum":200,"minimum":1},"order":{"enum":["asc","desc"],"type":"string","default":"desc"},"offset":{"type":"integer","default":0,"minimum":0},"origin":{"anyOf":[{"enum":["human","system","ai","api","mcp","chat"],"type":"string"},{"type":"null"}]},"status":{"anyOf":[{"type":"string"},{"type":"null"}]},"company_id":{"type":"string"},"period_ids":{"anyOf":[{"type":"array","items":{"type":"string"},"maxItems":200},{"type":"null"}]},"untagged_subledger_control":{"anyOf":[{"type":"boolean"},{"type":"null"}]}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":373,"tokensAfter":373},{"name":"list_gl_open_items","fixed":{"name":"list_gl_open_items","description":"One page of the AR/AP subledger (member read).\n\n``direction`` filters server-side (ZCT-577) — omit it for both. The screen used to\nfilter the fetched page in the browser, which made the AR chip drop rows and the\ntotal count both directions.\n\n``as_of`` (ZCT-577) reconstructs each item's open amount from posted journal lines\ndated on or before that date, instead of reading today's cached amount. Items are\nconsidered **regardless of their current status** — one settled since that date was\nopen on i","inputSchema":{"type":"object","required":["company_id"],"properties":{"as_of":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}]},"limit":{"type":"integer","default":50,"maximum":200,"minimum":1},"offset":{"type":"integer","default":0,"minimum":0},"status":{"anyOf":[{"type":"string"},{"type":"null"}]},"direction":{"anyOf":[{"type":"string","pattern":"^(receivable|payable)$"},{"type":"null"}]},"company_id":{"type":"string"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":277,"tokensAfter":277},{"name":"list_gl_periods","fixed":{"name":"list_gl_periods","description":"The company's **accounting periods** — the months a journal entry may post into\n(CompanyMember read).\n\nA period belongs to one fiscal year and carries ``period_no`` (1..12 within the\nyear), ``date_from``/``date_to`` and a ``status``: ``open`` accepts postings,\n``soft_closed`` still accepts them, and ``closed``/``year_closed`` refuse them — a\nposting date inside a closed period fails until an accountant explicitly reopens\nthe period (``POST …/gl/periods/{period_id}/reopen``, reason required, reco","inputSchema":{"type":"object","required":["company_id"],"properties":{"company_id":{"type":"string"},"fiscal_year_id":{"anyOf":[{"type":"string"},{"type":"null"}]}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":200,"tokensAfter":200},{"name":"list_gl_vat_periods","fixed":{"name":"list_gl_vat_periods","description":"List Gl Vat Periods","inputSchema":{"type":"object","required":["company_id"],"properties":{"company_id":{"type":"string"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":48,"tokensAfter":48},{"name":"list_items","fixed":{"name":"list_items","description":"List/filter items, scoped to the caller's member companies (A1).\n\nOptional ``period`` (``YYYY-MM``) filters by the item's effective date\n(invoice date, falling back to the Europe/Zurich-local ingestion date); a\nmalformed value returns 422 ``{\"msg\": \"period must be YYYY-MM\"}``.\n``exclude_triage_status`` drops items carrying that triage verdict (NULL-safe;\nfor ``unrelated`` not a booked one, ZCT-954). Payables/Receivables pass\n``unrelated``, so parked items show only in the Inbox.\n\nOptional ``q`` ","inputSchema":{"type":"object","required":[],"properties":{"q":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"paid":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"limit":{"type":"integer","default":20,"maximum":200,"minimum":1},"offset":{"type":"integer","default":0,"minimum":0},"period":{"anyOf":[{"type":"string"},{"type":"null"}]},"posted":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"source":{"anyOf":[{"enum":["email","epost","upload","bexio","abacus","filesystem","clouddrive"],"type":"string"},{"type":"null"}]},"direction":{"anyOf":[{"enum":["payable","receivable","unknown"],"type":"string","description":"Query-filter vocabulary for ``Item.direction`` — NEVER a column type.\n\n``Item.direction`` is tri-state (``payable``/``receivable``/NULL = unknown);\nthe PG ``direction`` enum stays binary. This enum exists so list endpoints\ncan accept ``direction=unknown`` (→ ``direction IS NULL``) alongside the two\nreal values without widening the DB enum."},{"type":"null"}]},"duplicate":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"company_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"credit_note":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"triage_status":{"anyOf":[{"enum":["irrelevant","needs_review","unrelated"],"type":"string","description":"AI ingest-gate verdict parking an item in the Inbox (pipeline §\"Ingest triage\",\n§\"Relevance gate\").\n\nNULL = not triaged / normal flow. ``irrelevant``/``needs_review`` are the\nmetadata-triage verdicts on a held-unusable item (a human override flips\nbetween them; re-analysis resets the columns to NULL). ``unrelated`` is the\nrelevance-gate verdict on a *cleanly extracted* invoice that doculyse's\nentity resolver matched to neither party of the owning company — the item is\nheld ``pending`` (never archived, no counterparty, never coded) until a human\nrestores it (clears the verdict) or deletes it."},{"type":"null"}]},"archive_status":{"anyOf":[{"enum":["pending","archiving","archived","failed"],"type":"string"},{"type":"null"}]},"counterparty_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"exclude_triage_status":{"anyOf":[{"enum":["irrelevant","needs_review","unrelated"],"type":"string","description":"AI ingest-gate verdict parking an item in the Inbox (pipeline §\"Ingest triage\",\n§\"Relevance gate\").\n\nNULL = not triaged / normal flow. ``irrelevant``/``needs_review`` are the\nmetadata-triage verdicts on a held-unusable item (a human override flips\nbetween them; re-analysis resets the columns to NULL). ``unrelated`` is the\nrelevance-gate verdict on a *cleanly extracted* invoice that doculyse's\nentity resolver matched to neither party of the owning company — the item is\nheld ``pending`` (never archived, no counterparty, never coded) until a human\nrestores it (clears the verdict) or deletes it."},{"type":"null"}]}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":945,"tokensAfter":945},{"name":"list_payroll_employees","fixed":{"name":"list_payroll_employees","description":"Every employee on the payroll master data, with today's contract (accountant\nrole — this payload is personal data, so it is not a plain member read).\n\nOne row per employee, active and left alike (filter on ``status`` /\n``employment_end`` yourself — no server-side filter, no paging): master data\n(``employee_no``, ``display_name``, address, ``ahv_number``, ``birthdate``), the\nQuellensteuer election (``qst_liable`` + canton/tariff), the salary ``iban``, and\n``current_contract`` — the contract valid","inputSchema":{"type":"object","required":["company_id"],"properties":{"company_id":{"type":"string"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":184,"tokensAfter":184},{"name":"list_payroll_runs","fixed":{"name":"list_payroll_runs","description":"The company's payroll runs — one per wage period — newest period first\n(CompanyMember read; the money is aggregate, no per-employee PII).\n\nA run walks ``draft`` → ``calculated`` → ``approved`` → ``posted`` → ``paid`` →\n``locked`` (plus ``reversed``); ``run_kind`` separates a ``regular`` run from a\ncorrection/reversal, which are always **separate runs**, never in-place edits, and\nare linked by ``original_run_id`` / ``reversed_by_run_id``. ``journal_entry_id`` is\nthe wage entry it posted (``null``","inputSchema":{"type":"object","required":["company_id"],"properties":{"year":{"anyOf":[{"type":"integer"},{"type":"null"}]},"company_id":{"type":"string"},"status_filter":{"anyOf":[{"enum":["draft","calculated","approved","posted","paid","locked","reversed"],"type":"string","description":"Run lifecycle (payroll SP0; enforced from SP6 via ``invoiage_core.payroll.states``)."},{"type":"null"}]}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":264,"tokensAfter":264},{"name":"post_gl_journal_entry","fixed":{"name":"post_gl_journal_entry","description":"Post a draft entry into the books — accountant. One-way.\n\nA posted entry can no longer be replaced or discarded (both refuse with 409); the only\ngeneral correction is ``…/reverse``, which leaves both the original and the reversal\nvisible. The one exception is ``PUT …/guessed-account``, which re-points a\n``coding_guessed`` entry's expense account in place — and it works *only* on a posted\nentry. Check the draft before posting rather than after.","inputSchema":{"type":"object","required":["company_id","entry_id"],"properties":{"body":{"anyOf":[{"type":"object","properties":{"not_vat":{"type":"boolean","default":false,"description":"Acknowledge that code-less lines on VAT accounts (1170/2200...) are not VAT: a reclass within one side, undoing a netting, a payment with a fee line, a payment or settlement on an account used for both directions, or opening balances in a hand-built opening entry (ZET-159). Recorded on the audit row. A VAT payment or settlement on one-direction VAT accounts (only cleared: debit output, credit input; against bank/cash, the settlement account or each other) needs no flag. Off by default: an untagged VAT movement must fail, not miss the MWST-Abrechnung."},"allow_untagged_control":{"type":"boolean","default":false,"description":"Post an AR/AP control line with no open item (recorded on the entry). Not for VAT-account lines: see not_vat."},"confirm_bank_duplicate":{"type":"boolean","default":false}},"description":"Optional body for posting a manual entry.\n\n``allow_untagged_control``: an AR/AP control line with no open item (ZET-150).\n``not_vat``: code-less lines on VAT accounts that are not VAT (F2431). Both are\nrecorded; both off by default so the common mistake fails loudly."},{"type":"null"}]},"entry_id":{"type":"string"},"company_id":{"type":"string"}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":519,"tokensAfter":519},{"name":"settle_entry","fixed":{"name":"settle_entry","description":"Post this reconciled bank entry into the GL (clean-bank settlement).\n\nMatched → Dr Bank / Cr AR; unmatched → Dr Bank / Cr Suspense; a previously-suspensed\nentry now fully matched → Dr Suspense / Cr AR. Idempotent, and a no-op (``skipped``) when\nthe GL chart is not configured for native settlement. The matcher still owns\n``reconciliation_status``. See doc/architecture.md §\"Posting general ledger\".","inputSchema":{"type":"object","required":["entry_id"],"properties":{"entry_id":{"type":"string"}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":152,"tokensAfter":152},{"name":"start_signup","fixed":{"name":"start_signup","description":"Create a Zeno account for somebody who does not have one yet.\n\nUse `request_api_key` first if they might already have an account — this tool\ncannot tell you, and deliberately never will.\n\nWe email `email` a verification link. The user opens it, sees that you asked\n(by the name in `client_name`, shown as an unverified claim), chooses their own\npassword, and decides separately whether to connect you. **You never see the\nlink**, and you must not ask for it: it is what proves they own the mailbox,\na","inputSchema":{"type":"object","required":["email"],"properties":{"email":{"type":"string"},"client_name":{"type":"string","default":""}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":200,"tokensAfter":200},{"name":"upload_document","fixed":{"name":"upload_document","description":"Upload an INVOICE or RECEIPT (PDF, image, or .zip of them) for analysis.\n``file_base64`` is the file's raw bytes, base64-encoded. ``direction`` is\n``payable`` (incoming supplier invoice) or ``receivable`` (outgoing).\nCreates Item(s) and queues extraction.\n\nThis door is invoices-only: it never routes a file onward by type, so a\nbank statement, a journal export or a balance sheet sent here is analysed\nand then just sits as a document. For anything that is not an invoice — or\nwhen you are not sure ","inputSchema":{"type":"object","required":["company_id","filename","file_base64"],"properties":{"filename":{"type":"string"},"direction":{"type":"string","default":"payable"},"company_id":{"type":"string"},"file_base64":{"type":"string"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":228,"tokensAfter":228},{"name":"upload_intake","fixed":{"name":"upload_intake","description":"Upload ANY file and let the system decide what it is (the unified intake\nqueue — the same door as \"Documents → Upload\" in the web app).\n``file_base64`` is the file's raw bytes, base64-encoded.\n\nPrefer this whenever the file is not plainly an invoice, and always for:\nbank statements (CAMT.053 XML, CSV, PDF or Excel exports) — these become\nBankStatement + BankEntry rows, matched to the registered bank account by\nIBAN; journal/accounting exports; and balance sheets, P&L statements and\nVAT filings. ","inputSchema":{"type":"object","required":["company_id","filename","file_base64"],"properties":{"filename":{"type":"string"},"company_id":{"type":"string"},"file_base64":{"type":"string"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":214,"tokensAfter":214},{"name":"zeno_info","fixed":{"name":"zeno_info","description":"What this server is, which MCP revision it speaks, and how to get a credential.\n\n**Needs no credential.** Call it first if a tool has just refused you: the\n`authentication` block names the tools that can get this session connected.","inputSchema":{"type":"object","properties":{},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":97,"tokensAfter":97}],"toolsChanged":36,"tokensSaved":0,"todo":[{"tool":"accept_autopost_decision","params":["body","company_id","decision_id"]},{"tool":"call_operation","params":["name","arguments"]},{"tool":"collect_api_key","params":["claim_token"]},{"tool":"create_ar_invoice","params":["lines","notes","currency","doc_type","due_date","language","company_id","issue_date","legal_text","footer_text","counterparty_id","payment_terms_id","issuer_profile_id","service_period_end","credited_invoice_id","service_period_start","invoice_discount_kind","invoice_discount_value"]},{"tool":"create_gl_journal_entry","params":["lines","currency","period_id","company_id","entry_type","description","document_id","posting_date","auto_reverse_date"]},{"tool":"customer_statement","params":["as_of","company_id","counterparty_id"]},{"tool":"describe_operations","params":["names","include_output_schema"]},{"tool":"entry_candidates","params":["entry_id"]},{"tool":"find_operations","params":["tag","limit","query","offset"]},{"tool":"get_ar_invoice","params":["company_id","invoice_id"]},{"tool":"get_bank_entry","params":["entry_id"]},{"tool":"get_bank_status","params":["company_id"]},{"tool":"get_company","params":["company_id"]},{"tool":"get_financial_statements","params":["month","company_id","fiscal_year"]},{"tool":"get_gl_account_ledger","params":["limit","offset","date_to","date_from","account_id","company_id"]},{"tool":"get_gl_journal_entry","params":["entry_id","company_id"]},{"tool":"get_intake","params":["intake_id"]},{"tool":"get_item","params":["item_id"]},{"tool":"get_kpi_report","params":["company_id","fiscal_year"]},{"tool":"get_work_list","params":["company_id"]},{"tool":"gl_balance_sheet","params":["period_id","company_id","fiscal_year"]},{"tool":"gl_profit_and_loss","params":["period_id","company_id","fiscal_year"]},{"tool":"gl_trial_balance","params":["period_id","company_id","fiscal_year"]},{"tool":"global_search","params":["q","type","limit","offset","source","direction","company_id","archive_status"]},{"tool":"issue_ar_invoice","params":["company_id","invoice_id"]},{"tool":"list_ar_invoices","params":["limit","offset","period","status","doc_type","company_id"]},{"tool":"list_autopost_decisions","params":["cause","limit","offset","outcome","company_id","source_ref_id","include_no_match"]},{"tool":"list_bank_accounts","params":["company_id"]},{"tool":"list_bank_entries","params":["q","to","flag","from","limit","offset","company_id","credit_debit","match_quality","bank_account_id","counterparty_id","bank_statement_id","settlement_status","reconciliation_status"]},{"tool":"list_counterparties","params":["q","kind","limit","offset","company_id"]},{"tool":"list_gl_accounts","params":["limit","offset","company_id","fiscal_year"]},{"tool":"list_gl_journal_entries","params":["q","sort","limit","order","offset","origin","status","company_id","period_ids","untagged_subledger_control"]},{"tool":"list_gl_open_items","params":["as_of","limit","offset","status","direction","company_id"]},{"tool":"list_gl_periods","params":["company_id","fiscal_year_id"]},{"tool":"list_gl_vat_periods","params":["company_id"]},{"tool":"list_items","params":["q","paid","limit","offset","period","posted","source","direction","duplicate","company_id","credit_note","triage_status","archive_status","counterparty_id","exclude_triage_status"]},{"tool":"list_payroll_employees","params":["company_id"]},{"tool":"list_payroll_runs","params":["year","company_id","status_filter"]},{"tool":"post_gl_journal_entry","params":["body","entry_id","company_id"]},{"tool":"reconciliation_summary","params":["to","from","company_id"]},{"tool":"request_api_key","params":["email","client_name"]},{"tool":"reverse_gl_journal_entry","params":["body","entry_id","company_id"]},{"tool":"settle_entry","params":["entry_id"]},{"tool":"start_signup","params":["email","client_name"]},{"tool":"upload_document","params":["filename","direction","company_id","file_base64"]},{"tool":"upload_intake","params":["filename","company_id","file_base64"]}],"note":"Deterministic fixes. Changes marked verify need a human look before shipping (renames, inferred annotations, inferred types, removed text). Parameter descriptions in `todo` need to be written by someone who knows the API."}