Simpler.Asia

asia.simplerPublished by simpler.asiaOfficial publisherv1.0.0Updated Oct 27, 2026

Free back office for small businesses in Singapore and Southeast Asia: invoices, expenses, stock.

VerifiedStreamable HTTPWeb executableProductivity & WorkflowFinanceBusiness & Commerce

Overview

AI-generated overview

Remote MCP server for a small-business back office in Singapore and Southeast Asia: invoices, quotes, expenses, stock, contacts and forecasts.

What you can do
  • Discover services and business outcomes, then describe or invoke any operation through the catalog tools
  • Create and convert quotes, invoices and receipts, record payments, credit notes and adjustments
  • Log expenses, submit and approve claims, and track receivables, reminders and client statements
  • Manage contacts, interactions, stock adjustments, team assignments and workflows
  • Generate LHDN e-invoices and record portal submission UUIDs
  • Run forecasts, check AI wallet budget and cost estimates, generate images, extract documents and create spreadsheets
When to use it
Useful when an assistant should handle day-to-day bookkeeping and sales paperwork for a small business: drafting and converting quotes, issuing invoices, chasing receivables, logging expenses and stock, or running cash-flow forecasts. The discovery tools let an assistant explore the catalog before acting.
Requirements
Remote streamable HTTP endpoint at simpler.asia; no package install. Most read-only discovery tools need no auth, but executing operations, wallet and budget data require authentication. Some features depend on account setup, such as MyInvois settings before e-invoices can be generated.
Before you install
Many tools write or change business records: invoices, payments, expenses, stock, contacts, workflows and cross-tenant messages. Some actions are gated by approval and may return an approval that must be checked rather than retried. Paid AI lanes bill a prepaid wallet, so check budget and cost estimates first. Cross-tenant messaging and settlement proposals reach other businesses and require consent grants and KYB approval.

Installation

In SourceWeft

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

Web executable via Streamable HTTP. Remote servers run from the web runtime once configured in a workspace.

Use with an agent

Paste this prompt into Claude Code, Codex, Cursor or another agent — or into SourceWeft chat. The agent reads this server's install guide, shows you its publisher, endpoint and sign-in, and adds it to your client once you agree. Keys never go into a command.

Read https://sourceweft.com/mcp/asia.simpler%2Fsimpler-asia/install.md and connect the MCP server it describes to my client. Before changing any config, show me its publisher, endpoint and sign-in method and wait for my OK. Never put an API key in a command or in a file I would commit; ask me where it should come from.

Read the install guide the agent follows

Other MCP clients

Add this to your client's mcpServers config.

{
  "mcpServers": {
    "simpler-asia": {
      "type": "http",
      "url": "https://simpler.asia/mcp"
    }
  }
}

Tools

200

simpler.directory

DISCOVERY TIER 1 of 3 — the service directory: one token-dense line per service (s:name st:status cost:class ops:N — purpose). START HERE instead of reading every tool. No auth required. NEXT: simpler.describe_service with a service name from the listing.

Takes no parameters.

simpler.describe_service

DISCOVERY TIER 2 of 3 — one service's operations and entity shapes from the live catalog, token-dense (op lines name the underlying tool). No auth required. NEXT: simpler.invoke {service, operation, args} — or call the named tool directly.

Parametersservicerequired

simpler.invoke

DISCOVERY TIER 3 of 3 — execute one operation: dispatches to the underlying generated tool (same gates, same envelope, same idempotency as calling it directly). Requires auth. args = the underlying tool's arguments object.

Parametersargsservicerequiredoperationrequired

simpler.outcomes

DISCOVERY TIER 0 — the OUTCOME MENU: one line per common business outcome, each mapped to ONE tool with one-call notes (composites: ONE call creates the whole document). START HERE for a business task. No auth required. NEXT: call the named tool directly, or simpler.directory for the full catalog.

Takes no parameters.

simpler.approval_status

Check a parked approval by approval_id (returned in an APPROVAL_REQUIRED refusal). States: pending (the account owner has not decided — tell the user, do NOT retry the original call), executed (done — the result rides this response; never re-run the call; an approved invoice_share or quote_share keeps no link or document here, so read the current link with that tool and action 'get' as result.link_not_stored.next_call says), rejected / expired / failed (terminal — re-compose only if the user asks again). Scoped to the granting user's approvals.

Parametersapproval_idrequired

agent_note_send_cross_tenant

Send a cross-tenant AI-to-AI message into another tenant's agent_note inbox — the door that turns a cross_tenant_comm consent grant into an actual reach. Both tenants must be KYB-approved and the recipient must hold an active grant (claims can narrow it further); every recipient-side failure refuses identically so the network can't be probed for who exists — only your OWN unapproved KYB answers distinctly, since that discloses nothing about the target. Your sender identity is injected server-side into from_tenant_id and cannot be spoofed. NEXT: agent_note_list on the recipient side confirms delivery.

Parametersbodystatussubjectto_tenant_idrequiredfrom_tenant_idconsent_grant_idrelated_entity_idrelated_entity_type

contact_upsert

Create-or-merge a contact keyed on email-then-phone identity — the sync door the future desktop harness and ingest lanes write through. Matching probes email first, then phone; a hit MERGES only the fields you send (omitted fields keep their stored values, so a partial sync payload can never wipe a curated field), a miss creates. Status moves still obey the transition machine — an upsert cannot jump straight to vip. Accepts the same first-note/first-interaction composite children as contact_create.

Parametersnamenotetagsemailnotesphonesourcestatusaddresscompanyinteractionlast_contact_at+1 more

expense_create

Logs a new expense — amount_cents must be integer CENTS (a decimal string via `amount` also works, and `category` resolves to a category_id by name). A same-amount, same-UTC-day duplicate for the same user is refused as CONFLICT unless you resend identically with force: true. Status is honor-or-refuse: admin/owner creators land 'approved' by default (or may request it explicitly), everyone else always starts 'draft'. NEXT: expense_submit sends a draft into the approval queue.

Parameterscostdateforcenotestotalamountstatusvendorcategorycurrencyocr_jsontax_type+17 more

expense_submit

Sends your own draft expense into the approval queue — draft only; an already-submitted or resolved expense refuses CONFLICT, and only the expense's own creator may submit it. Nobody is notified automatically — approvers see it appear in the Team Claims view. NEXT: expense_approve or expense_reject (admin/owner).

Parametersidrequirednotesstatusvendorcurrencyocr_jsontax_typetags_jsonapproved_atapproved_bycategory_idreceipt_key+10 more

expense_approve

Approves a submitted expense claim — requires the admin or owner role, and only a 'pending' expense qualifies (CONFLICT otherwise). Stamps approved_by and approved_at with the caller and the current time; once approved, the row's content freezes for everyone except the approver themselves. NEXT: expense_reimburse (owner only) to pay it out.

Parametersidrequirednotesstatusvendorcurrencyocr_jsontax_typetags_jsonapproved_atapproved_bycategory_idreceipt_key+10 more

interaction_create

Log an interaction — call, meeting, email, message, visit, payment, or other — against a contact. The ONE activity-logging door every lane funnels through, so the contact's last_contact_at freshness bumps no matter which surface logged the touch; the bump never moves backward, so importing older history can never regress a fresher touch. Accepts contact_name, contact_email or contact_phone in place of contact_id. NEXT: contact_read to see the bumped last_contact_at.

Parameterssummarycontact_idsource_refoccurred_atcontact_namecontact_emailcontact_phoneinteraction_type

invoice_create

ONE call creates the whole document: claims the next invoice number, then writes the header plus every line in `items` ([{description, quantity, unit_price_cents}], integer CENTS). client_name finds a client by name or creates one. Birth status 'paid' needs items in the same call. tax_scheme sg_gst or my_sst taxes each line by its tax_code (refusals: see tax_scheme). When present, the second content block is the invoice as copy-ready text, shown with a Copy button where the host draws MCP Apps; if the user says they cannot see the invoice, give them that block word for word; share:true adds a client link, only when the user asks for one. NEXT: invoice_payment_record to record money-in.

Parametersforceitemsnotessharestatuspaid_atsent_atcurrencydue_datecash_saleclient_idsource_id+13 more

invoice_adjustment_record

[REQUIRES APPROVAL] Records a credit note, write-off or post-issue discount on an issued invoice. A credit_note is numbered from the credit note series, holds its tax at the invoice's rate and takes reason_code and credit_outcome: reduce_balance (default) lowers what is owed; refunded_outside records a refund the business made outside Simpler, moves no money and is how a 'paid' invoice gets one. write_off and discount_post_issue need a 'sent' invoice. Money is integer CENTS (amount_cents) or a decimal STRING (amount); caps: see preconditions. Refuses a draft, a cancelled invoice, an amount over its cap and dispute_settlement; each refusal names the fix. An agent's call parks for approval. NEXT: invoice_read.

Parametersamountreasonpartialcurrencyinvoice_idoccurred_atreason_codeamount_centscredit_outcomeinvoice_numberadjustment_type

invoice_payment_record

Records a money-in fact row against an invoice (by invoice_id or its invoice_number) in ONE call: money is integer CENTS via amount_cents, or a decimal STRING via amount — never multiply or divide yourself. A draft invoice auto-advances to 'sent'; a cancelled invoice refuses; an amount above the remaining balance refuses unless allow_overpayment is true. status 'paid' derives from the ledger this writes, so this is how an invoice becomes paid. A same invoice/amount/day repeat refuses as a likely duplicate unless resent with force:true. NEXT: invoice_read to read it back.

Parametersnoteforceamountmethodcurrencyinvoice_idoccurred_atamount_centsinvoice_numberallow_overpayment

myinvois_invoice_generate

Mint a sealed LHDN e-invoice in ONE call: invoice_type, line_items (unit_price_cents in integer CENTS, tax_rate_bps in basis points), and a buyer as customer_id or an inline buyer object. Refuses until myinvois_settings_save is done; invoice_type 02/03/04 also needs original_invoice_id naming a type-01 invoice with a recorded LHDN UUID, checked BEFORE the number is claimed, so a rejected note never burns a number. The result is immutable and carries portal_field_sheet; Simpler never submits it to LHDN. NEXT: the owner keys the field sheet into the MyInvois portal form, then myinvois_invoice_record_submission with the UUID the portal assigns.

Parametersbuyernotesstatuscurrencybuyer_tinlhdn_uuidtax_centsbuyer_nameissue_dateline_itemsrequiredcustomer_idtotal_cents+10 more

myinvois_invoice_record_submission

Record the UUID the LHDN MyInvois portal assigned after the owner keyed this e-invoice into the portal (its form or LHDN's Excel batch template). Simpler never submits to LHDN and never calls the MyInvois API; this only stores the UUID the owner copies back. Only a type-01 original accepts a UUID: credit, debit and refund notes are refused (they link through BillingReference), so record the UUID on the original instead. Moves status generated to submitted; calling it again on a submitted invoice corrects a mistyped UUID. NEXT: myinvois_invoice_read to see the UUID attached.

Parametersidrequiredstatuscurrencybuyer_tinlhdn_uuidtax_centsbuyer_nameissue_datecustomer_idtotal_centsinvoice_datainvoice_type+5 more

myinvois_invoice_read

Read one sealed e-invoice by id, with its portal_field_sheet: the document's values laid out to key into the LHDN MyInvois portal form (e-invoice details, supplier, buyer, each line, totals), coded values with their meaning. Simpler never submits to LHDN; portal_field_sheet.portal_routes names the two portal routes (the online form and LHDN's Excel batch template). Refuses an id outside your tenant with NOT_FOUND: call myinvois_invoice_list for valid ids. NEXT: after the portal accepts it, myinvois_invoice_record_submission with the UUID it assigns.

Parametersidrequired

quote_create

ONE call creates the whole document: claims the next atomic quote number, then writes the header plus every line in `items` ([{description, quantity, unit_price_cents}], integer CENTS) together. client_name resolves an existing client by name or creates one. tax_scheme sg_gst or my_sst taxes each line by its tax_code (refusals: see tax_scheme). When present, the second content block is the quote as copy-ready text, shown with a Copy button where the host draws MCP Apps; if the user says they cannot see the quote, give them that block word for word; make a client link with quote_share only when the user asks for one. NEXT: quote_convert to turn the quote into its invoice.

Parametersforceitemsnotesstatuscurrencyclient_idissue_dateline_itemstax_schemeclient_namevalid_untilquote_number+3 more

quote_convert

Converts a quote into its invoice in ONE call: claims the quote's 'converted' status first (so a lost race never creates an orphan invoice), copies every live line item across, and records converted_invoice_id — nothing else to do afterward. Refuses if the quote is already converted (the error carries converted_invoice_id) or declined (revive it with quote_update first), and refuses with nothing converted when the quote or its lines changed while it was converting (for example a quote_item call running at the same time): read the quote again with quote_read, then convert. NEXT: invoice_read on the new invoice.

Parametersidrequirednotesstatuscurrencyquote_idclient_idissue_datetax_schemevalid_untilquote_numbertax_rate_bpsprices_include_tax+1 more

settlement_proposal_propose_cross_tenant

Record a payment obligation against a KYB-verified partner tenant's settlement_proposals — the record-don't-move on-ramp: this writes the row only, no money moves until a later settlement-rails door executes it. amount_cents is a nonzero integer CENTS (positive = you owe them, negative = a refund/reversal record) and currency must be a 3-letter code. Requires the payee's cross_tenant_settlement consent grant and both sides KYB-approved; payee-side failures refuse identically to block probing, but your OWN unapproved KYB answers distinctly. Your tenant is injected as payer_tenant_id and cannot be spoofed. NEXT: settlement_proposal_accept moves it to accepted on the payee side.

Parametersstatuscurrencyaccepted_atdescriptionamount_centsto_tenant_idrequiredpayer_tenant_idconsent_grant_idrelated_entity_idrelated_entity_type

sticky_note_create

Create a sticky_note.

Parameterscolorwidthheightcontentrequiredz_indexposition_xposition_ytriggers_at

stock_item_adjust

Move stock in or out by id or item name, in one atomic write with its movements-ledger row: stock-out clamps at zero (never negative) and applied_quantity reports what actually moved. An unmatched name CREATES the item at quantity 0 first so the delivery still lands as an audited ledger row; a name matching multiple active items refuses with the candidate ids. Concurrent adjusts on the same item retry instead of losing a write. NEXT: stock_item_list to see the item's updated balance in the full inventory.

Parametersidnametyperequiredunitnotesreasoncategoryquantityrequiredis_activeitem_nameexpiry_dateoccurred_at+3 more

team_assignment_create

[REQUIRES APPROVAL] Delegates a task to a teammate (or yourself) as a new team_assignment; status is workflow-owned and always starts 'pending' — passing status yourself fails validation instead of being silently overridden. Accepts natural language for the common fields: task_name/task for title, due_date/due for due_at ('YYYY-MM-DD', epoch millis, 'today', 'tomorrow', or 'next <weekday>'), and 'self'/'me'/'yourself' for assigned_to. assigned_to must already be a member of your tenant. NEXT: team_assignment_accept, then _start and _complete, drive the lifecycle from there.

Parametersduetasktitledue_atstatusdue_dateprioritytask_namestarted_ataccepted_atartifact_idassigned_to+13 more

workflow_create

Create a workflow — an AI operator's job: validates definition_json (a { nodes, edges, schedule? } graph) against the platform's node vocabulary, refusing unknown node types and any comms/message-shaped step that isn't a prepare_output node, since the platform prepares outputs but never sends them. trigger_type 'schedule' requires a schedule block; next_run_at is then computed server-side from it, never from a caller value. is_active defaults to 0 (draft) — going live is a separate explicit choice. NEXT: workflow_update to fix a rejected definition or flip is_active.

Parametersnameis_activerole_namedescriptionnext_run_attrigger_typeimported_fromdefinition_json

workflow_prepared_outputs

List this tenant's APPROVED prepared outputs: drafts a workflow's prepare_output nodes parked for review, which a human then approved in the platform inbox. Pending and rejected drafts never appear here — approval happens only in the inbox, never through this call. Optional limit 1-100 (default 50), newest-decided first. This is the platform-prepares-never-sends handoff: the desktop harness pulls approved drafts here and sends them locally.

Parametersnamelimitis_activerole_namedescriptionnext_run_attrigger_typeimported_fromdefinition_json

ai.budget

Your prepaid wallet, period spend, caps, and runway as a compact line DSL (~8 lines). Requires auth (your balance is closed data). Read this BEFORE committing paid actions; pair with ai.estimate_cost to price a specific call first.

Takes no parameters.

ai.estimate_cost

Price an AI task BEFORE running it: provider passthrough at wholesale cost, no markup — the per-token platform fee is retired, so text/reasoning passes through at cost. Anonymous — open catalog includes open pricing. Returns integer micro-USD plus a USD total. NEXT: run the action, then ai.budget to watch the runway.

Parameterstaskrequiredtokens_intokens_out

sales_receivables

Who owes you, by client: each client's unpaid balance aged by days past its due date — current (not yet due), 1-30, 31-60, 61-90 and over 90 days — counted in the business's own days (the sales_settings time zone), so an invoice due today is still current. Only issued invoices with a balance left count: drafts, cancelled, paid, written-off and fully credited invoices never do. Money is integer CENTS in each invoice's own currency: one row per client per currency, and totals has one row per currency, never converted or added across currencies; read the figures as given, never multiply or divide them yourself. Clients come oldest past-due first. Optional client_id ages one client; an id that is not one of your clients is refused NOT_FOUND (find it with invoice_client_search or invoice_client_list). A limit outside 1-200 is refused VALIDATION_ERROR (leave it out for 50; totals always cover every client). NEXT: invoice_list with state 'overdue' lists the past-due invoices themselves.

Parameterslimitclient_id

sales_document_show

Show one invoice, quote or receipt as the document itself. When the result has a second content block, it is the document as copy-ready plain text (title, business, billed to, dates, one line per item, the totals as printed, notes, payment terms and details, or on a receipt the day paid and the method) to paste into an email or a chat, and hosts that draw MCP Apps show the page with a Copy button. A paid invoice shows as its receipt, and each payment has its own numbered receipt (the receipt_number invoice_payment_record answers, for example RCT-2026-001), shown by that number or the payment id. Pass id or number (exactly one, from invoice_list, quote_list or invoice_payment_list); kind 'invoice', 'quote' or 'receipt' is optional (else invoice first, then quote, then receipt). It sends nothing and makes no link. Refusals: unknown id or number NOT_FOUND (a removed payment's receipt: reason_code SIMPLER_SALES_RECEIPT_VOID); a missing or doubled id/number or another argument VALIDATION_ERROR; kind 'receipt' before receipts are set up SERVICE_UNAVAILABLE SIMPLER_SALES_RECEIPT_UNMIGRATED; stylesheet unreadable SERVICE_UNAVAILABLE SIMPLER_SALES_DOCUMENT_CHAT_ASSETS_UNAVAILABLE (retry_after); not read within 3 seconds SERVICE_UNAVAILABLE SIMPLER_SALES_DOCUMENT_SHOW_SLOW (call again after retry_after). Read-only tokens and viewers may call it. Free.

Parametersidkindnumber

invoice_message_prepare

Prepare what a business sends a client about one issued invoice: purpose 'invoice', 'reminder' (overdue), 'receipt' (paid in full) or 'promise_follow_up' (promised date passed); left out, the one that fits. language 'en', 'ms' or 'zh'; left out, the client's (invoice_client_update message_language), else English. Returns to, subject, body, share_text, a mailto and link_on; share_url only for a member or above with a live link (never made here). NOTHING IS SENT. Refusals (CONFLICT): draft SIMPLER_SALES_MESSAGE_NOT_ISSUED (issue it with invoice_update status 'sent'); cancelled; written off; SIMPLER_SALES_MESSAGE_PURPOSE_UNFIT (send its next_call). Other arguments: VALIDATION_ERROR. Free.

Parametersidrequiredpurposelanguage

invoice_reminders_due

Today's collection list, in the business's days (its sales_settings time zone). reminders: each issued invoice still owed that is 3 days before its due date, on it, or 7 or 14 days after, as its client's reminder_cadence allows (standard, from_due, overdue_only or off; set it with invoice_client_update), with the amount owed in CENTS, the step, the language and next_call (the invoice_message_prepare call). A step shows only on its own day. issues: drafts the recurring run made, each with the invoice_update call that issues it; recurring_held: due templates it could not make, and why. NOTHING IS PREPARED OR SENT. Any argument is refused VALIDATION_ERROR (it takes none). Free; changes nothing.

Takes no parameters.

statement_prepare

A client's statement of account over a period of the business's own days (the sales_settings time zone): the balance they owed before it (opening), each invoice issued, payment received, credit and write-off in it with a running balance, the balance at its end (closing), and what was still owed at its end aged current / 1-30 / 31-60 / 61-90 / over 90 days past due, counted as sales_receivables counts. Only issued invoices count: drafts and cancelled invoices never do. Money is integer CENTS in each invoice's own currency, one section per currency, never converted or added across currencies; read the figures as given. It prepares nothing and sends nothing: returns the figures, document_path (the print-ready page, while signed in) and pdf_next_call (the statement_pdf call that makes the PDF). A statement with more than 150 rows lists its first rows, whole business days only, and lines_cut says which and gives the next_call that lists the rest; the balances, totals and ageing always cover the whole period. When the result has a second content block, it is the statement as copy-ready text (a very long one shortened), and hosts that draw MCP Apps show the statement with a Copy button, as plain text when it is too long to draw as a document. Refusals: a client_id that is not one of your clients is NOT_FOUND (find it with invoice_client_search or invoice_client_list); a date not written YYYY-MM-DD, a to after today, a from after to, or a period longer than 366 days is VALIDATION_ERROR naming the field; a client with more than 2000 issued invoices is PAYLOAD_TOO_LARGE SIMPLER_SALES_STATEMENT_TOO_LARGE (sales_receivables still ages them). Free.

Parameterstofromclient_idrequired

invoice_handoff

Get one issued invoice ready to give the client, in one call: the document, the client link (made if it has none), the PDF (printed or the stored one) and the prepared message (invoice_message_prepare's words, with the link). ready lists the parts made; missing gives each other part's reason_code, reason and next_call. A turned-off link is never re-issued: invoice_share action 'rotate' waits for the owner's approval. NOTHING IS SENT. Refusals (CONFLICT): draft SIMPLER_SALES_MESSAGE_NOT_ISSUED (issue it with invoice_update status 'sent'); cancelled; written off; SIMPLER_SALES_MESSAGE_PURPOSE_UNFIT (send its next_call). Read-only token or viewer: FORBIDDEN. Free.

Parametersidrequiredpurposelanguage

forecast.run

WHAT-IF / scenario lane: runs monthly data YOU supply through a seeded 5,000-path simulation of monthly revenue and costs; bands are simulation outputs, not calibrated probabilities. The paths are clustered into 3-7 outcome bands for the next 6 months, with per-month revenue and cost percentiles and deathRisk, the share of paths the engine marks closed (a distress signal, not a cash-out date). Amounts are in currency (major units, default USD): 1500 or "1500.00" - never cents. currency is one of 16 codes (USD, EUR, GBP, JPY, CNY, AUD, HKD, INR, SGD, MYR, IDR, THB, PHP, VND, BND, MMK); leave it out and USD is assumed, and the output says currency_assumed: true. The engine runs in USD; every output figure converts back into currency at fx_rate_to_usd (USD per 1 unit), named in the top-level currency field. Send cash_balance (the bank balance, negative for an overdraft) with cash_as_of (its date, YYYY-MM-DD, within 45 days) and the cash block adds runway, cash-out share, lowest balance and health from that balance plus each path's revenue minus costs (balance_source 'request'); without them it carries net-flow figures only and its next_step says what to send. Send declared_totals {revenue, costs}, the totals your own source shows for the months sent, and the sums are checked on the server; totals_check echoes them. history_months, shallow (true under 5 months) and blend {yours_pct, peers_pct} (null when shallow) say how much of the result is your own history. Earlier releases removed the per-band cash range and the per-month cash percentiles (they started from an invented balance) and interpreted_money.unit (the top-level currency field replaces it). Refuses with VALIDATION_ERROR and a fix per field: fewer than 2 or more than 120 rows, a date that is not YYYY-MM, revenue or costs sent in cents or as text that is not a number (an exponent such as 1e5, letters other than one currency code or symbol, or a comma that does not group thousands), an amount whose code or symbo

Parameterscash_ondriverscurrencyrecurringcash_as_ofmonthlyDatarequiredcash_balancepaid_historycash_currencyopen_payablesdeclared_totalsopen_receivables

forecast.run_stored

Run the tenant's REAL forecast from the books — cash receipts (recorded invoice payments by payment date, plus paid invoices with no recorded payment at their paid date, flagged) and expenses by expense date, monthly in the tenant's base currency, up to the last complete month (the current month is left out; months are the business's calendar months in its own time zone, set in sales settings, Asia/Singapore when unset, and the books block names it in bucket_timezone) and at most 36 months — through the one import pipeline. The result is STORED, replacing the forecast every member reads, so it is owner or admin only: any other role is refused with FORBIDDEN SIMPLER_FORECAST_IMPORT_ROLE_REFUSED before anything is read, saved, run or charged (ask an owner or admin to run it; forecast.read reads the forecast already stored). No arguments are needed. Optional cash_balance (the bank balance in cash_currency, negative for an overdraft), cash_currency (one of 16 codes) and cash_as_of (its date, YYYY-MM-DD) go together: they are saved as the workspace's declared bank balance, replacing the balance entered before, and then the forecast runs and starts its cash block from that balance. When more than one bank account is saved on the Forecast page, it refuses with CONFLICT SIMPLER_FORECAST_CASH_MULTI_ACCOUNT_REFUSED before anything is saved or run, because one balance would replace them all: ask the owner or an admin to update the balances on the Forecast page, or call without the three cash fields to run from the saved balances. It refuses with SERVICE_UNAVAILABLE SIMPLER_FORECAST_SETTINGS_READ_FAIL when the saved balances cannot be read (retry in a minute). The cash fields refuse like forecast.run's (SIMPLER_FORECAST_CASH_WITHOUT_DATE, SIMPLER_FORECAST_CASH_AS_OF_INVALID, VALIDATION_ERROR naming cash_currency when it is missing or outside the list, or cash_balance when its code or symbol names another currency than cash_currency, before anything is saved) and with SERVICE_UN

Parameterscash_as_ofcash_balancecash_currency

forecast.read

Read the tenant's STORED forecast — the verify-read for this rail. view picks the answer, one of summary, monthly, variance, scenarios, weekly (default summary). summary: the outcome bands (label, growth_direction, probability_pct, final_month_revenue_low and _high, distress_risk_pct, path_count), the cash block, blend {yours_pct, peers_pct} (the share of the result from this business's own history versus businesses like it; null when shallow). monthly: per month revenue, costs and net at p10, p25, p50, p75 and p90 with distress_risk_pct, plus balance_p10 to balance_p90 and below_zero_cum_pct when a balance anchors the cash block, and the cash block. This release moved monthlyForecast out of the default answer into view 'monthly'. Every view carries view, dataMonths, history_months, shallow (true under 5 months of history) and CFO narration status, money formatted in the business's currency, named in the top-level currency field: the engine runs in USD and figures convert back at the rate pinned on the stored result (fx_rate_to_usd); before this release they were USD. Free. Refuses with VALIDATION_ERROR naming view when it is not in the list. The cash block is computed from a real bank balance plus each simulated path's revenue minus costs: with basis 'anchored' it carries provenance (the balance grade), balance_source ('statement': the latest balance on bank statements imported with banking.import_statement, used while under 31 days old in place of a typed balance for the same account; a typed account whose label ends in other last 4 digits, or the same digits in another currency, and dated on or after the statement, is added to it and provenance is then declared (an older one is left out: save it again with today's date); 'settings': entered on the Forecast page or saved by forecast.run_stored; 'upload_row': the cash row of an uploaded sheet), balance_as_of, runway_months_p10 and _p50 (null = no cash-out within the horizon), cash_out_pct, lowest_balance_p10, burn_

Parametersviewcash_ondriversrecurringpaid_historyopen_payablesopen_receivables

image.generate

[paid: $0.042/image max] Generate 1-3 images from a text prompt on the first lane that delivers, tried in order: gemini-2.5-flash-image via the Cloudflare AI Gateway, square requests only ($0.0406/image), then @cf/black-forest-labs/flux-2-dev via Cloudflare Workers AI ($0.00/image), then google/nano-banana-2-lite via fal ($0.042/image). Each lane settles its own price per delivered image, so an image never costs more than the fal price, the credit a call must have available before it runs; price.charged_micro_usd in the result is the actual charge. Each image is stored in this workspace's image library and returned as JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; never a provider link. A result with no download_url says why in download_url_unavailable. Refuses: VALIDATION_ERROR on an empty prompt (describe the image), INSUFFICIENT_CREDITS (add credits), SIMPLER_IMAGE_PROMPT_REFUSED names the label (sexual, minor, real_person or other_unsafe): rewrite the prompt without it, for example no real person's name or likeness; SIMPLER_IMAGE_PROMPT_CHECK_UNAVAILABLE (the safety check is down, nothing charged: retry later); SIMPLER_IMAGE_OUTPUT_FLAGGED (the output failed the content check and was refunded: change the prompt); SIMPLER_IMAGE_OUTPUT_SCAN_UNAVAILABLE (outputs cannot be checked right now, nothing charged: retry later). A partial result lists refunded outputs in output.withheld; SIMPLER_IMAGE_RESULT_UNDELIVERED means nothing was charged: retry. For canvas-sized generation inside an edit session use image.generate_for_edit. NEXT: pass images[0].asset_key as image_asset_key to image.variation or image.upscale.

Parameterscountwidthheightpromptrequiredrequest_refpreview_format

document.extract

Fetch a document from a URL, classify its type (personal/medical/business/unknown) with keyword + PII-pattern heuristics (documents containing actual PII data are NEVER business), and return extracted text capped at 100K chars. Free. STREAMING SUPPORTED: send Accept: text/event-stream for started/fetched/classifying/completed phases.

Parametersdocument_urlrequired

spreadsheet.create_with_content

Create a spreadsheet in the user's library from CSV content in ONE call: the platform stores the bytes (honest text/csv) and registers the file row. Free — storage costs nothing; only AI lanes bill. Returns the file row including its id. Refuses: VALIDATION_ERROR when title or csv_content is empty (send both), VALIDATION_ERROR when csv_content is over 10 MB (split it into smaller files), NOT_FOUND when folder_id names no folder in this workspace (call spreadsheet_folder_list, or leave folder_id out), FORBIDDEN when your role is viewer or your token is read-only (ask the owner for the member role, or connect with the 'mcp' scope). Nothing is stored when it refuses. NEXT: spreadsheet_file_list to see the library.

Parameterstitlerequiredfolder_idcsv_contentrequired

agent_note_mark_read

Confirm receipt of a note in your cross-tenant agent_note inbox — the ONLY edit a recipient holds, moving status unread to read under the registry's transition guard. Idempotent: re-confirming an already-read note is a no-op, and the subject/body stay fixed forever so a recipient can never rewrite what the sender said. NEXT: agent_note_list to see the rest of the inbox.

Parametersidrequiredbodystatussubjectfrom_tenant_idconsent_grant_idrelated_entity_idrelated_entity_type

agent_note_list

List your agent_note rows (newest first).

Parameterslimitcursor

agent_note_read

Read one agent_note by id.

Parametersidrequired

brand_assets_read

Read one brand_assets by id.

Parametersidrequired

brand_assets_list

List your brand_assets rows (newest first).

Parameterslimitcursor

contact_create

Create a contact and, in the same call, log its first note and/or first interaction — the composite first-touch door. Children validate before the contact write; if the contact commits but a child then fails, the error names the contact's id plus the exact call to finish it, never a silent orphan. A contact with the same email or phone already in this tenant refuses with CONFLICT. NEXT: contact_upsert merges into an existing contact instead of creating a duplicate.

Parametersnamenotetagsemailnotesphonesourcestatusaddresscompanyinteractionlast_contact_at+1 more

contact_update

Update a contact you own (optionally compare-and-set via expected_updated_at).

Parametersidrequirednametagsemailphonesourcestatusaddresscompanylast_contact_atexpected_updated_at

contact_delete

Delete a contact you own.

Parametersidrequired

contact_read

Read one contact by id.

Parametersidrequired

contact_list

List your contact rows (newest first).

Parameterslimitcursor

contact_note_create

Add a note to a contact — the same door as the granular tool, extended only to accept contact_name, contact_email or contact_phone in place of contact_id. A name match must be unique: more than one hit refuses, listing the candidate ids; zero matches on every alias refuses with the runnable two-step call to create the contact first, then retry. NEXT: contact_upsert when no contact exists yet.

Parameterscontentcontact_idcontact_namecontact_emailcontact_phone

contact_note_list

List your contact_note rows (newest first).

Parameterslimitcursor

contact_note_read

Read one contact_note by id.

Parametersidrequired

contact_note_delete

Delete a contact_note you own.

Parametersidrequired

desk_surface_create

Pins one widget or a saved artifact onto the operator's home canvas. kind='widget' requires widget_key from the shipped enum; kind='artifact' requires content_ref (an assistant artifact id) — the door enforces whichever pair the kind demands, since the schema alone cannot express it. Rows are private to the creating user, even though teammates share the same tenant. NEXT: desk_surface_list reads the current layout.

Parametershwxykindz_indexwidget_keycontent_ref

desk_surface_update

Moves, resizes, or restacks a surface already on the desk canvas — x/y/w/h/z_index are real column writes, not a layout cache. The surface's identity is frozen here: kind, widget_key, and content_ref never change through this door, only via delete then re-create. NEXT: desk_surface_list to find the id to move.

Parametershwxyidrequiredkindz_indexwidget_keycontent_refexpected_updated_at

desk_surface_read

Read one desk_surface by id.

Parametersidrequired

desk_surface_list

List your desk_surface rows (newest first).

Parameterslimitcursor

desk_surface_delete

Delete a desk_surface you own.

Parametersidrequired

edit_project_create

Create a canvas project from a Fabric.js toJSON serialization (canvas_json, required): every embedded data:image src is pulled out, hashed, and written to a tenant-scoped, content-addressed R2 key before the row is saved, so canvas_json never carries image bytes and identical images dedupe across projects. SVG, an unrecognized image type, an oversized embedded image, or a canvas still too large after externalization refuse the whole create — nothing is silently dropped. An optional thumbnail_data_url is externalized the same way into thumbnail_key; thumbnail_key and version are server-set and refuse if you pass them. NEXT: edit_project_update to autosave the same project as it changes.

Parametersnamewidthheightversioncanvas_jsonthumbnail_keythumbnail_data_url

edit_project_update

Save changes to an existing canvas project by id — the autosave door: send only the fields that changed (name, canvas_json, width, height, thumbnail_data_url), everything else on the row is left alone. A canvas_json you send goes through the same image-externalization and hygiene refusal as edit_project_create (embedded images become R2 keys; SVG, unrecognized types, oversized images, or a still-too-large canvas refuse the whole save). version is bumped by the door itself on every save and thumbnail_key is server-minted from thumbnail_data_url — both refuse if you pass them directly. NEXT: edit_project_read to confirm the saved version.

Parametersidrequirednamewidthheightversioncanvas_jsonthumbnail_keythumbnail_data_urlexpected_updated_at

edit_project_read

Read one edit_project by id.

Parametersidrequired

edit_project_list

List your edit_project rows (newest first).

Parameterslimitcursor

edit_project_delete

Delete a edit_project you own.

Parametersidrequired

expense_update

Edits an expense's own fields only — status and approval stamps refuse with VALIDATION_ERROR here since those are transitions (expense_submit, expense_approve, expense_reject, expense_reimburse). amount_cents, if changed, must stay integer CENTS. Only the expense's own creator may edit it, and content freezes once the row is 'pending', reimbursed, or approved by someone else — draft, rejected, and your own self-approved rows stay editable.

Parametersidrequirednotesstatusvendorcurrencyocr_jsontax_typetags_jsonapproved_atapproved_bycategory_idreceipt_key+11 more

expense_delete

Deletes an expense you created — draft and rejected rows only. Pending, approved, and reimbursed rows are approval-trail records and refuse with CONFLICT (approval_locked: true), and the lock is pinned into the delete itself so a status change racing your read still refuses instead of silently deleting. NEXT: expense_reject it first (admin+) if it must leave the books.

Parametersidrequirednotesstatusvendorcurrencyocr_jsontax_typetags_jsonapproved_atapproved_bycategory_idreceipt_key+10 more

expense_reject

Rejects a submitted expense claim — requires the admin or owner role, and only a 'pending' expense qualifies (CONFLICT otherwise). rejection_reason (up to 500 chars) is optional but records why; rejected is terminal and never counts as spend, so a corrected claim is a fresh expense_create, never a resubmit.

Parametersidrequirednotesstatusvendorcurrencyocr_jsontax_typetags_jsonapproved_atapproved_bycategory_idreceipt_key+10 more

expense_reimburse

Marks an approved expense as paid out — restricted to the tenant owner because it moves real money, and only an 'approved' expense qualifies (CONFLICT otherwise). Stamps reimbursed_at, after which the row's content freezes unconditionally since the amount approved is the amount recorded as paid. Records the payout only — no funds move automatically.

Parametersidrequirednotesstatusvendorcurrencyocr_jsontax_typetags_jsonapproved_atapproved_bycategory_idreceipt_key+10 more

expense_read

Read one expense by id.

Parametersidrequired

expense_list

List your expense rows (newest first).

Parameterslimitcursor

expense_aggregate

Aggregate (count/sum) over your expense rows.

Parametersfunctionrequiredproperty

expense_budget_create

Create a expense_budget.

Parameterscurrencyis_activecategory_idalert_at_pctmonthly_limit_centsrequired

expense_budget_update

Update a expense_budget you own (optionally compare-and-set via expected_updated_at).

Parametersidrequiredcurrencyis_activecategory_idalert_at_pctexpected_updated_atmonthly_limit_cents

expense_budget_delete

Delete a expense_budget you own.

Parametersidrequired

expense_budget_read

Read one expense_budget by id.

Parametersidrequired

expense_budget_list

List your expense_budget rows (newest first).

Parameterslimitcursor

expense_category_create

Adds a custom category to your tenant's category table — slug must be 1-50 chars of lowercase a-z, 0-9, '-' or '_' and unique per tenant (CONFLICT if taken). is_default is server-owned: supplying it at all refuses with VALIDATION_ERROR, and this door always creates with is_default 0 — the 9 seeded defaults exist only via expense_category_ensure_defaults. sort_order defaults to the next open slot when omitted.

Parametersiconnameslugcoloris_defaultsort_order

expense_category_update

Edits a category's name, icon, color, or sort_order — slug and is_default are the category's fixed identity, and any attempt to change either refuses with VALIDATION_ERROR. color, when given, must be a #rrggbb hex value. Create a new category instead of trying to rename an existing slug.

Parametersidrequirediconnameslugcoloris_defaultsort_orderexpected_updated_at

expense_category_delete

Deletes a custom category — categories are tenant-shared, so any tenant member can remove one regardless of who created it; the 9 seeded defaults are undeletable and refuse with CONFLICT (default_category: true). Expenses already carrying the deleted category keep their category_id untouched and simply render as Uncategorized; nothing is reassigned or blocked.

Parametersidrequirediconnameslugcoloris_defaultsort_order

expense_category_ensure_defaults

Seeds the 9 canonical default categories (Food & Dining, Transport, Utilities, Shopping, Entertainment, Healthcare, Travel, Office & Supplies, Other) into your tenant, skipping any slug that already exists. Takes no arguments and is safe to call repeatedly or concurrently — it converges rather than duplicating. NEXT: expense_category_list to see the result.

Parametersiconnameslugcoloris_defaultsort_order

expense_category_read

Read one expense_category by id.

Parametersidrequired

expense_category_list

List your expense_category rows (newest first).

Parameterslimitcursor

expense_recurrence_create

Create a expense_recurrence.

Parametersnotesvendorrequiredcurrencynext_duerequiredfrequencyrequiredis_activecategory_idday_of_weekamount_centsrequiredday_of_monthtotal_generatedlast_generated_at

expense_recurrence_update

Update a expense_recurrence you own (optionally compare-and-set via expected_updated_at).

Parametersidrequirednotesvendorcurrencynext_duefrequencyis_activecategory_idday_of_weekamount_centsday_of_monthtotal_generated+2 more

expense_recurrence_delete

Delete a expense_recurrence you own.

Parametersidrequired

expense_recurrence_read

Read one expense_recurrence by id.

Parametersidrequired

expense_recurrence_list

List your expense_recurrence rows (newest first).

Parameterslimitcursor

interaction_upsert

Create-or-update a interaction keyed on source_ref.

Parameterssummaryrequiredcontact_idrequiredsource_refrequiredoccurred_atinteraction_typerequired

interaction_list

List your interaction rows (newest first).

Parameterslimitcursor

interaction_read

Read one interaction by id.

Parametersidrequired

invoice_update

Edits an invoice's header fields and status. status 'paid' cannot be set here — it derives from the payment ledger, so call invoice_payment_record instead. Status moves are machine-checked (draft to sent/cancelled, sent to cancelled); cancelled is terminal and a paid invoice never reopens by hand. A move to 'sent' stamps sent_at automatically when omitted, and currency is ledger-locked once any live payment exists. An agent's change to notes or payment_terms on an issued invoice waits for the owner's approval; on a draft it does not. tax_scheme and prices_include_tax are fixed at creation, as is the tax rate. NEXT: invoice_payment_record to settle money, invoice_read to verify.

Parametersidrequirednotesstatuspaid_atsent_atcurrencydue_datecash_saleclient_idsource_idissue_datetax_scheme+10 more

invoice_share

Returns share_url, the client's view link for this invoice, and the full document (parties, lines, totals in integer CENTS) in one call. action: 'get' (default) returns the live link; 'rotate' replaces it and the old link stops working; 'revoke' turns it off. A link the owner turned off stays off for agents: 'get' never re-issues it, and an agent's 'rotate' waits for the owner's approval. Anyone with the link can open the page until the invoice is cancelled or 90 days after payment (see link_serving). There is no PDF file yet; the link opens a print-ready page the client can print or save as PDF. Nothing is sent: give the link to the owner, who decides whether it goes to the client.

Parametersidrequiredactioninvoice_idfresh_after

invoice_payer_confirmation

Acts on a payer's report, sent from the invoice's client page, that they have paid. action 'accept' records the payment with the same writer as invoice_payment_record, graded counterparty_confirmed: the amount is amount_cents (or amount) when given, else the balance still owed; occurred_at defaults to when the payer reported. An agent's accept waits for the owner's approval. action 'dismiss' closes a report whose money did not arrive. Find reports in invoice_read's payer_confirmations. Refuses a report already accepted or dismissed with CONFLICT (invoice_read shows it), and an invoice with nothing owed unless amount_cents comes with allow_overpayment. Nothing is sent to the payer.

Parametersidnoteforceactionrequiredamountmethodinvoice_idoccurred_atamount_centsallow_overpayment

invoice_debit_note

[REQUIRES APPROVAL] Issues a numbered debit note that raises what an issued ('sent' or 'paid') invoice is owed: an extra charge, or a price or tax correction. It is numbered from the debit note series and adds amount_cents (or a decimal STRING amount), tax at the invoice's rate inside, in its currency; a 'paid' invoice goes back to 'sent' until the rest is paid. Refuses a draft (change its lines instead), a cancelled invoice, an invoice with no lines, another currency, and a paid invoice with no payment recorded (record it first with invoice_payment_record); every refusal names the call to send. Never deleted: undo one with a credit_note. An agent's call parks for approval. Nothing is sent. NEXT: invoice_read.

Parametersamountreasoncurrencydebit_kindinvoice_idoccurred_atamount_centsinvoice_number

invoice_reissue

Voids a 'sent' invoice with no money on it and reissues it under the next number: 'sent', dated today, same client, currency, notes, terms and lines, tax on today's rules, the old days to pay unless due_date is sent. A foreign-currency sg_gst invoice issued before today needs fx_rate_to_sgd and fx_rate_date for today unless its rate is dated today. The old one is cancelled, its client link stops working, both trails link them. Paid, part-paid, credited or written-off invoices refuse: correct them with a credit note (invoice_adjustment_record). When present, the second content block is the new invoice as copy-ready text. Nothing is sent. NEXT: invoice_share only if the user asks for a link.

Parametersidreasondue_datefx_rate_datefx_rate_to_sgdinvoice_numberfx_rate_to_sgd_ppmexpected_updated_at

invoice_promise_set

Records the date a client promised to pay an issued invoice that still has money owed: promised_payment_date 'YYYY-MM-DD', the business's own date, today or later. clear true removes it. invoice_read then shows promise_status: pending until that business day ends, kept when paid in full by then, broken after it. A broken promise makes promise_follow_up the default purpose of invoice_message_prepare. Each change is kept in the invoice's activity as promise_set. Refuses a draft, a cancelled invoice and one with nothing owed (CONFLICT), and a past or malformed date (VALIDATION_ERROR). Nothing is sent. NEXT: invoice_read.

Parametersidnoteclearinvoice_numberexpected_updated_atpromised_payment_date

invoice_read

Returns one invoice with its money and state worked out on read, in integer CENTS: subtotal_cents, tax_cents, total_cents, paid_cents, credited_cents, adjusted_cents, remaining_cents (copy remaining_cents, never compute it). state: draft, sent, partially_paid, overdue, written_off, paid or cancelled. Overdue means issued, still owed and due before today in the business time zone (sales_settings.timezone, unset = Asia/Singapore); days_overdue counts days there. activity lists the newest 50 events; viewed_count and last_viewed_at count every view. payer_confirmations lists the payer's "I've paid" reports. An unknown id refuses with NOT_FOUND: find it with invoice_list or invoice_search.

Parametersidrequired

invoice_list

Lists your invoices, newest first (200 at most; pass limit and cursor to page). Every row carries the same worked-out money and state as invoice_read: total_cents, remaining_cents, state, is_overdue, days_overdue (integer CENTS, business days). To find what to chase, pass state 'overdue': only invoices issued, still owed and due before today in the business time zone come back. payer_confirmations_pending counts the payer's "I've paid" reports waiting on each row. Refuses an unlisted state, a limit outside 1-200 or a malformed cursor with VALIDATION_ERROR: resend with a listed state, without limit, or with next_cursor exactly as returned.

Parameterslimitstatecursor

invoice_adjustment_read

Read one invoice_adjustment by id.

Parametersidrequired

invoice_adjustment_list

List your invoice_adjustment rows (newest first).

Parameterslimitcursor

invoice_adjustment_delete

[REQUIRES APPROVAL] Retires a dilution fact row — the only correction lane (adjustments are append-only; amounts are never edited). If the retired row was a credit that settled the invoice, the status rolls from 'paid' back to 'sent' and paid_at clears; the remaining balance and the derived 'written_off' state re-derive from the live rows immediately. An agent's call parks here for human approval. NEXT: invoice_adjustment_record to re-record the correct amount.

Parametersidrequiredreasoncurrencyinvoice_idoccurred_atreason_codeamount_centsadjustment_idcredit_outcomeadjustment_type

invoice_client_create

Create a invoice_client.

Parametersnamerequiredemailnotesphoneaddresscompanycontact_idmessage_languagereminder_cadence

invoice_client_upsert

Create-or-update a invoice_client keyed on email.

Parametersnamerequiredemailrequirednotesphoneaddresscompanycontact_idmessage_languagereminder_cadence

invoice_client_update

Update a invoice_client you own (optionally compare-and-set via expected_updated_at).

Parametersidrequirednameemailnotesphoneaddresscompanycontact_idmessage_languagereminder_cadenceexpected_updated_at

invoice_client_delete

Delete a invoice_client you own.

Parametersidrequired

invoice_client_read

Read one invoice_client by id.

Parametersidrequired

invoice_client_list

List your invoice_client rows (newest first).

Parameterslimitcursor

invoice_item_create

Adds one line item to an invoice. unit_price_cents is integer CENTS, 0 or more ($15.00 → 1500); quantity is 0.001 to 1,000,000 with up to 3 decimals (1.5 hours → 1.5); unit is an optional label (hr, kg); discount_bps is an optional line discount (10% → 1000). quantity × unit_price_cents is at most 10,000,000,000. Refuses a negative price (send discount_bps instead), a 'paid' parent (retire the payment first) and a 'cancelled' parent (issue a new invoice). Re-derives the invoice's paid/sent status. tax_code is the line's code under the document's tax_scheme, refused on a document without a scheme. NEXT: invoice_read to see the updated total.

Parametersunitpositionquantitytax_codeinvoice_iddescriptiondiscount_bpsunit_price_cents

invoice_item_update

Edits a line item's description, quantity, unit, price or discount, or re-parents it to another invoice. expected_updated_at is required: the line's updated_at from your read; without it nothing is written and the refusal names the current value; a line changed since then answers CONFLICT. Bounds: unit_price_cents is integer CENTS, 0 or more; quantity 0.001 to 1,000,000 (3 decimals); discount_bps 0 to 10000; quantity × unit_price_cents at most 10,000,000,000. unit "" clears the unit. Refuses when the current or target invoice is paid or cancelled. tax_code is the line's code under the document's tax_scheme, refused on a document without a scheme. NEXT: invoice_read to see the updated total.

Parametersidrequiredunitpositionquantitytax_codeinvoice_iddescriptiondiscount_bpsunit_price_centsexpected_updated_atrequired

invoice_item_delete

Soft-deletes a line item — it leaves the invoice and its derived total immediately, but the row is retired for audit history, never removed. expected_updated_at is required: the line's updated_at from your read (invoice_item_read); without it the removal is refused and the refusal names the current value. Refuses when the parent invoice is 'paid' (ledger-locked: retire the payment first) or 'cancelled' (terminal: its lines stay as issued), and with CONFLICT when the line changed since that read (read it again). If retiring the line settles the remaining balance on a partially-paid invoice, the parent re-derives to 'paid' by itself. NEXT: invoice_read to see the updated total.

Parametersidrequiredunitpositionquantitytax_codeinvoice_iddescriptiondiscount_bpsunit_price_centsexpected_updated_atrequired

invoice_item_read

Read one invoice_item by id.

Parametersidrequired

invoice_item_list

List your invoice_item rows (newest first).

Parameterslimitcursor

invoice_payment_read

Read one invoice_payment by id.

Parametersidrequired

invoice_payment_list

List your invoice_payment rows (newest first).

Parameterslimitcursor

invoice_payment_delete

[REQUIRES APPROVAL] Retires a payment fact row — the only correction lane (payments are append-only; amounts are never edited). If retiring it un-settles the invoice, the reversal itself rolls the status from 'paid' back to 'sent' and clears paid_at. An agent's call parks here for a human to approve in the Simpler UI; a human's call is its own approval. NEXT: invoice_payment_record to re-record the correct amount.

Parametersidrequirednotemethodcurrencyinvoice_idpayment_idoccurred_atamount_cents

invoice_recurring_create

Create a invoice_recurring.

Parametersnotestitlerequiredends_oncurrencynext_duerequiredclient_idfrequencyrequiredis_activeitems_jsonrequiredtax_schemeday_of_weekday_of_month+4 more

invoice_recurring_update

Update a invoice_recurring you own (optionally compare-and-set via expected_updated_at).

Parametersidrequirednotestitleends_oncurrencynext_dueclient_idfrequencyis_activeitems_jsontax_schemeday_of_week+6 more

invoice_recurring_delete

Delete a invoice_recurring you own.

Parametersidrequired

invoice_recurring_read

Read one invoice_recurring by id.

Parametersidrequired

invoice_recurring_list

List your invoice_recurring rows (newest first).

Parameterslimitcursor

invoice_recurring_skip

Skips a recurring template's next invoice: next_due moves one period on (the template's own weekly, monthly or yearly day) and no invoice is made. A skip does not count toward max_invoices. Refuses NOT_FOUND for an id outside your tenant (call invoice_recurring_list) and CONFLICT when the template has ended (raise ends_on or max_invoices with invoice_recurring_update) or changed since expected_updated_at (read it again). Each call skips one run: to skip the run after, send expected_updated_at from this answer. Nothing is sent. NEXT: invoice_recurring_read.

Parametersidrequiredexpected_updated_at

market_signal_create

Post to one of the tenant's 8 market-signal wish-bar slots — a buy, sell, swap, capacity-offer or capacity-request visible ONLY to platform-matched partners, never the wider network. The door counts your OPEN signals server-side before writing: the 9th is refused with slots_used/slots_max in the error rather than silently queued. NEXT: market_signal_update to close or fulfil a signal and free a slot.

Parametersnotesstatusproductcurrencydeadlinelocationquantityprice_centssignal_type

market_signal_update

Update a market_signal you own (optionally compare-and-set via expected_updated_at).

Parametersidrequirednotesstatusproductcurrencydeadlinelocationquantityprice_centssignal_typeexpected_updated_at

market_signal_list

List your market_signal rows (newest first).

Parameterslimitcursor

market_signal_read

Read one market_signal by id.

Parametersidrequired

myinvois_customer_create

Register a buyer in the LHDN buyer registry so it attaches to future e-invoices with one pick (customer_id) instead of typing the party every time. Validates the TIN format, and validates state_code against the LHDN state table when you send one — a buyer with no real TIN uses the LHDN generic TIN (EI00000000010 Malaysian public, EI00000000020 foreign) instead. NEXT: myinvois_invoice_generate with customer_id.

Parametersbrntincitynameemailnotesaddresssst_numberstate_codepostal_codecountry_codecontact_number

myinvois_customer_update

Edit a registered buyer's LHDN details — send only the fields you are changing. When tin is included it is re-checked against the same format gate create runs, and when state_code is included it is re-checked against the LHDN state table, so an edited buyer stays valid to generate against. Never touches e-invoices already generated for this buyer; each one sealed its own copy of the buyer party. NEXT: myinvois_customer_list to find the id.

Parametersidrequiredbrntincitynameemailnotesaddresssst_numberstate_codepostal_codecountry_code+2 more

myinvois_customer_delete

Delete a registered buyer. E-invoices already generated for this buyer keep their sealed copy of the buyer party and are never touched. Refuses an id outside your tenant with NOT_FOUND: call myinvois_customer_list for valid ids.

Parametersidrequired

myinvois_customer_read

Read one registered buyer by id: name, TIN, BRN, SST number, address and contact. myinvois_invoice_generate seals the name, TIN, BRN, address and contact; the buyer's SST number stays on this record and is not sealed on the e-invoice. Refuses an id outside your tenant with NOT_FOUND: call myinvois_customer_list or myinvois_customer_search for valid ids. NEXT: myinvois_invoice_generate with this id as customer_id.

Parametersidrequired

myinvois_customer_list

List registered buyers, newest first, to pick a customer_id for myinvois_invoice_generate. An empty list means none are registered yet: call myinvois_customer_create, or send an inline buyer {name, tin} to myinvois_invoice_generate instead.

Parameterslimitcursor

myinvois_invoice_list

List your myinvois_invoice rows (newest first).

Parameterslimitcursor

myinvois_settings_save

[REQUIRES APPROVAL] Save the one-time LHDN supplier profile — the door checks for an existing row on your tenant and creates or updates in place, so this is the only write you ever call. Validates the TIN format, checks msic_code is a 5-digit MSIC code, and checks state_code against the LHDN state table before the row moves. NEXT: myinvois_invoice_generate reads this profile as the supplier party of every e-invoice.

Parametersbrntincityemailscopemsic_codesst_numberstate_codepostal_codecompany_namecountry_codeaddress_line1+7 more

myinvois_settings_read

Read the supplier profile by id: the party printed on every e-invoice (company, TIN, BRN, SST and tourism tax numbers, MSIC code, address). Refuses an id outside your tenant with NOT_FOUND: call myinvois_settings_list, which returns the one profile row. NEXT: myinvois_settings_save to change it.

Parametersidrequired

myinvois_settings_list

List the supplier profile: at most one row per tenant. An empty list means setup is not done, and myinvois_invoice_generate refuses until it is: call myinvois_settings_save with the company, TIN, BRN, MSIC code and address. NEXT: myinvois_invoice_generate.

Parameterslimitcursor

myinvois_template_create

Save a reusable line-item preset so a repeat invoice's items do not get re-typed. line_items_json is parsed and each item validated by the SAME checker myinvois_invoice_generate uses (description, quantity, unit_price_cents in integer CENTS, tax_rate_bps in basis points, tax_type/classification_code/unit_code against the LHDN code tables) and re-serialized canonically, so a saved template's items are always generate-ready. NEXT: myinvois_template_list to pull the items back out for a generate call.

Parametersnamenotesdescriptionpayment_termsline_items_json

myinvois_template_update

Edit a saved line-item preset. When line_items_json is included it is re-parsed and every item re-validated exactly as create does, so an edited template cannot drift into a shape myinvois_invoice_generate would reject. NEXT: myinvois_template_list to find the id.

Parametersidrequirednamenotesdescriptionpayment_termsline_items_jsonexpected_updated_at

myinvois_template_delete

Delete a saved line-item preset. E-invoices generated from it are never touched: each one sealed its own lines. Refuses an id outside your tenant with NOT_FOUND: call myinvois_template_list for valid ids.

Parametersidrequired

myinvois_template_read

Read one line-item preset by id. Its line_items_json holds generate-ready items: pass them as line_items to myinvois_invoice_generate. Refuses an id outside your tenant with NOT_FOUND: call myinvois_template_list for valid ids.

Parametersidrequired

myinvois_template_list

List saved line-item presets, newest first. Each line_items_json is generate-ready: pass its items as line_items to myinvois_invoice_generate. An empty list means none are saved yet: call myinvois_template_create.

Parameterslimitcursor

quote_update

Edits a quote's header fields and bookkeeping status (draft, sent, accepted, declined move freely). status 'converted' and converted_invoice_id are convert-flow-owned — hand-setting either refuses, and a converted quote is frozen: no status move and no header edit (it could convert again and double-bill; its invoice is the live document, so edit that). tax_scheme and prices_include_tax are fixed at creation, as is the tax rate. NEXT: quote_convert to create the invoice and record the link atomically.

Parametersidrequirednotesstatuscurrencyclient_idissue_datetax_schemevalid_untilquote_numbertax_rate_bpsprices_include_taxexpected_updated_at+1 more

quote_share

Returns share_url, the client's view link for this quote, and the full document (parties, lines, totals in integer CENTS) in one call. action: 'get' (default) returns the live link; 'rotate' replaces it and the old link stops working; 'revoke' turns it off. A link the owner turned off stays off for agents: 'get' never re-issues it, and an agent's 'rotate' waits for the owner's approval. Anyone with the link can open the page until the quote expires, is declined or converted (see link_serving). There is no PDF file yet; the link opens a print-ready page the client can print or save as PDF. Nothing is sent: give the link to the owner, who decides whether it goes to the client.

Parametersidrequiredactionquote_idfresh_after

quote_read

Returns one quote with subtotal_cents, tax_cents and total_cents worked out from its live lines (integer CENTS) and effective_status, which reads 'expired' once a sent quote's valid_until has passed (the stored status never says so). A quote with a tax_scheme also returns tax_summary (each line's tax code, net, tax and gross, and the per-code totals). Refuses an unknown id with NOT_FOUND: find the id with quote_list or quote_search.

Parametersidrequired

quote_list

Lists your quotes, newest first (200 at most; pass limit and cursor to page). Every row carries total_cents (integer CENTS) and effective_status, so an expired quote reads 'expired'. Refuses a limit outside 1-200 or a malformed cursor with VALIDATION_ERROR: resend without them, or with next_cursor exactly as returned.

Parameterslimitcursor

quote_item_create

Adds one line item to a quote. unit_price_cents is integer CENTS, 0 or more ($15.00 → 1500); quantity is 0.001 to 1,000,000 with up to 3 decimals (1.5 hours → 1.5); unit is an optional label (hr, kg); discount_bps is an optional line discount (10% → 1000). quantity × unit_price_cents is at most 10,000,000,000. Refuses a negative price (send discount_bps instead), a line above the ceiling (split it) and any line on a converted quote (CONFLICT: change its invoice instead). tax_code is the line's code under the document's tax_scheme, refused on a document without a scheme. NEXT: quote_read to see the quote.

Parametersunitpositionquantityquote_idtax_codedescriptiondiscount_bpsunit_price_cents

quote_item_update

Edits a quote line's description, quantity, unit, price or discount, or moves it to another quote. expected_updated_at is required: the line's updated_at from your read; without it nothing is written and the refusal names the current value; a line changed since then answers CONFLICT. Refuses a line of a converted quote or a move onto one (CONFLICT: change its invoice instead). Bounds: unit_price_cents is integer CENTS, 0 or more; quantity 0.001 to 1,000,000 (3 decimals); discount_bps 0 to 10000; quantity × unit_price_cents at most 10,000,000,000. unit "" clears the unit. tax_code is the line's code under the document's tax_scheme, refused on a document without a scheme. NEXT: quote_read.

Parametersidrequiredunitpositionquantityquote_idtax_codedescriptiondiscount_bpsunit_price_centsexpected_updated_atrequired

quote_item_delete

Soft-deletes a quote line — it leaves the quote and its totals immediately, but the row is retired for audit history, never removed. expected_updated_at is required: the line's updated_at from your read (quote_item_read); without it the removal is refused and the refusal names the current value. Refuses with CONFLICT when the line changed since that read (read it again and decide on the current line) and on a converted quote (its lines are frozen: change its invoice instead). NEXT: quote_read to see the quote.

Parametersidrequiredunitpositionquantityquote_idtax_codedescriptiondiscount_bpsunit_price_centsexpected_updated_atrequired

quote_item_read

Read one quote_item by id.

Parametersidrequired

quote_item_list

List your quote_item rows (newest first).

Parameterslimitcursor

sales_settings_upsert

[REQUIRES APPROVAL] Saves your business identity and document defaults (one row per business; scope is 'default'). timezone is the business clock: an invoice turns overdue at midnight in that zone after its due day, and days_overdue counts days there; unset means Asia/Singapore. Left out, timezone and the text fields keep their saved value, but defaulted fields (default_currency, default_tax_rate_bps, payment_terms_days, invoice_prefix, quote_prefix, template_style) reset, so resend them from sales_settings_read. An agent call waits for the owner's approval. Refuses an unlisted timezone or currency, or a tax rate outside 0-10000, with VALIDATION_ERROR before it waits for approval: send a listed value.

Parametersscoperequiredtax_idtimezonebank_nametax_schemebank_accountquote_prefixbusiness_namebusiness_emailbusiness_phoneinvoice_prefixtemplate_style+7 more

sales_settings_numbering_set

[REQUIRES APPROVAL] Sets where a document type's numbers continue, for example after moving from another invoicing tool. doc_type: invoice, quote, credit_note, debit_note or receipt. next_number is the sequence number the next document gets (42 prints as INV-2026-042). Numbering only moves forward: a number at or below the last one used or skipped is refused with VALIDATION_ERROR naming that number, and nothing changes. prefix sets the credit_note, debit_note or receipt prefix; invoice and quote prefixes are invoice_prefix and quote_prefix on sales_settings_upsert. An agent call waits for the owner's approval. Read the current numbers with sales_settings_numbering_read.

Parametersprefixdoc_typerequirednext_number

sales_settings_list

List your sales_settings rows (newest first).

Parameterslimitcursor

sales_settings_read

Read one sales_settings by id.

Parametersidrequired

settlement_proposal_accept

Accept a settlement obligation recorded against your tenant as payee — moves status proposed to accepted and stamps accepted_at under the registry's transition guard. Calling it again on an already-accepted row is idempotent (accepted_at simply re-stamps); once a proposal reaches settled the guard refuses any further move, since settled is terminal. Recording agreement only: no money moves here, settlement execution is a later rail's door. NEXT: settlement_proposal_read to verify the accepted row.

Parametersidrequiredstatuscurrencyaccepted_atdescriptionamount_centspayer_tenant_idconsent_grant_idrelated_entity_idrelated_entity_type

settlement_proposal_list

List your settlement_proposal rows (newest first).

Parameterslimitcursor

settlement_proposal_read

Read one settlement_proposal by id.

Parametersidrequired

spreadsheet_file_create

Register a spreadsheet file row in the library for bytes that already sit in R2: r2_key must be a spreadsheets/<your tenant>/ key minted by the upload route or spreadsheet.create_with_content — a caller-invented, foreign-tenant, or wrong-domain key is refused. sheet_count, row_count, and column_count are unverified client-reported hints, since nothing server-side parses the binary. NEXT: spreadsheet.create_with_content stores the bytes and registers the row together.

Parameterstagstitler2_keysourceai_promptfile_sizefolder_idrow_countdeleted_atsheet_countsheet_namescolumn_count+3 more

spreadsheet_file_update

Edit a spreadsheet file's metadata — title, tags, folder_id, or the AI-analysis fields — without touching the underlying R2 object. r2_key and source are birth facts: including either field in the call at all, even set to its current value, is refused — re-upload as a new file to change custody or provenance. Moving folder_id re-verifies the target folder belongs to your tenant. NEXT: spreadsheet_file_read to confirm the change landed.

Parametersidrequiredtagstitler2_keysourceai_promptfile_sizefolder_idrow_countdeleted_atsheet_countsheet_names+5 more

spreadsheet_file_delete

Soft-delete a spreadsheet file: stamps deleted_at on the row so it drops out of the library and folder listings, while the row and its R2 object both survive untouched — there is no purge or restore lane yet, so this cannot be reversed through any current door. Requires the file id; an empty id is refused before anything is touched. NEXT: spreadsheet_file_read to confirm deleted_at is now set — the generic list tool still includes soft-deleted rows.

Parametersidrequiredtagstitler2_keysourceai_promptfile_sizefolder_idrow_countdeleted_atsheet_countsheet_names+4 more

spreadsheet_file_read

Read one spreadsheet_file by id.

Parametersidrequired

spreadsheet_file_list

List your spreadsheet_file rows (newest first).

Parameterslimitcursor

spreadsheet_folder_create

Create a spreadsheet_folder.

Parametersnamerequiredcolor

spreadsheet_folder_update

Update a spreadsheet_folder you own (optionally compare-and-set via expected_updated_at).

Parametersidrequirednamecolorexpected_updated_at

spreadsheet_folder_delete

Delete a spreadsheet_folder you own.

Parametersidrequired

spreadsheet_folder_read

Read one spreadsheet_folder by id.

Parametersidrequired

spreadsheet_folder_list

List your spreadsheet_folder rows (newest first).

Parameterslimitcursor

sticky_note_list

List your sticky_note rows (newest first).

Parameterslimitcursor

sticky_note_update

Update a sticky_note you own (optionally compare-and-set via expected_updated_at).

Parametersidrequiredcolorwidthheightcontentz_indexposition_xposition_ytriggers_atexpected_updated_at

sticky_note_delete

Delete a sticky_note you own.

Parametersidrequired

sticky_note_due

Read this tenant's sticky notes whose reminder time (triggers_at) has arrived — the poll the desktop host runs to know what to fire as a local notification, since the web itself has no send channel. Read-only, no arguments: now is always the server's clock, never a caller-supplied value, and only notes with triggers_at set and <= now come back, soonest-due first, capped at 200. NEXT: sticky_note_dismiss clears triggers_at once you've notified, so the same note stops showing up here.

Parameterscolorwidthheightcontentz_indexposition_xposition_ytriggers_at

sticky_note_dismiss

Clear a sticky note's reminder (triggers_at back to NULL) so it drops out of the sticky_note_due poll — call this after the host fires the local notification for it. Runs through the same tenant-scoped write path as every entity update, but via a clear-properties op: ordinary updates ignore null values, so this is the only way to null the column. Idempotent — dismissing an already-cleared note just re-nulls a NULL and succeeds. NEXT: sticky_note_due to see what's still outstanding.

Parametersidrequiredcolorwidthheightcontentz_indexposition_xposition_ytriggers_at

stock_item_create

Create a stock_item.

Parametersnamerequiredunitnotescategoryquantityis_activeexpiry_datesell_price_centscost_per_unit_centslow_stock_threshold

stock_item_update

Update a stock_item you own (optionally compare-and-set via expected_updated_at).

Parametersidrequirednameunitnotescategoryquantityis_activeexpiry_datesell_price_centscost_per_unit_centsexpected_updated_atlow_stock_threshold

stock_item_delete

Soft-delete a stock item by id: sets is_active to 0 while its movements-ledger history stays intact. stock_item_adjust then refuses the id as NOT_FOUND and stops resolving it by name, so a later delivery under the same name creates a fresh item instead of reviving this one. stock_item_list and stock_item_read still return it with is_active: 0 — only the dashboard's stats header excludes it. Restore by running a normal update that sets is_active back to 1; there is no separate restore door.

Parametersidrequirednameunitnotescategoryquantityis_activeexpiry_datesell_price_centscost_per_unit_centslow_stock_threshold

stock_item_read

Read one stock_item by id.

Parametersidrequired

stock_item_list

List your stock_item rows (newest first).

Parameterslimitcursor

studio_character_create

Create a studio_character.

Parametersnamerequiredcolor_tagdescriptionrequiredprompt_suffixreference_image_key

studio_character_read

Read one studio_character by id.

Parametersidrequired

studio_character_list

List your studio_character rows (newest first).

Parameterslimitcursor

studio_character_update

Update a studio_character you own (optionally compare-and-set via expected_updated_at).

Parametersidrequirednamecolor_tagdescriptionprompt_suffixexpected_updated_atreference_image_key

studio_character_delete

Delete a studio_character you own.

Parametersidrequired

studio_environment_create

Create a studio_environment.

Parametersnamerequiredcolor_tagdescriptionrequiredprompt_suffixreference_image_key

studio_environment_read

Read one studio_environment by id.

Parametersidrequired

studio_environment_list

List your studio_environment rows (newest first).

Parameterslimitcursor

studio_environment_update

Update a studio_environment you own (optionally compare-and-set via expected_updated_at).

Parametersidrequirednamecolor_tagdescriptionprompt_suffixexpected_updated_atreference_image_key

studio_environment_delete

Delete a studio_environment you own.

Parametersidrequired

studio_project_create

Create a video studio project — the composition document (scenes, tracks, clips, captions) starts empty and is built up afterward through the studio.* director tools. thumbnail_key is server-owned: send thumbnail_data_url instead, and this door externalizes it into a content-addressed R2 key through the media hygiene kit (magic bytes decide the type; SVG refuses). Free. NEXT: studio_project_read to verify.

Parametersnamestatusthumbnail_keythumbnail_data_url

studio_project_read

Read one studio_project by id.

Parametersidrequired

studio_project_list

List your studio_project rows (newest first).

Parameterslimitcursor

studio_project_update

Rename a project, change its status, or replace its thumbnail on a project you own — send thumbnail_data_url, never thumbnail_key, which is server-owned and computed by this door the same way studio_project_create does. status here is UI-summary-only by design: it never gates render or generation, which are tracked separately by the export job row and the composition document's own scene status. Free. NOT_FOUND when the id is outside your scope — list first with studio_project_list.

Parametersidrequirednamestatusthumbnail_keythumbnail_data_urlexpected_updated_at

Version history

1
  1. v1.0.0LatestOct 23, 2026

Score

83% 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 daysRequired25 pts
  • Auth declaredRequired10 pts
  • Tools listedRequired15 pts
  • DescriptionRequired15 pts
  • Logo10 pts
  • Documentation15 pts

    Declare a GitHub repository with a README, or serve resource_documentation in the OAuth protected-resource metadata.

  • LicenseNot applicable

    A hosted service is used under its provider's terms, so no license is expected here. This check is left out of the percentage.