DeepLedger

DeepLedger MCP connects QuickBooks Online with Claude, ChatGPT, Grok, Gemini, Cursor and other AI agents.

От сообщества: Добавлен пользователем или импортирован; проверьте владельца перед подключениемРаботаетБез входаГлобальныйБесплатноМожет изменять данные

Что умеет

  • QbCompanyProfile: Read the active QuickBooks company's identity and settings, list the companies you can work in, or switch the active company. OPERATIONS: operation="profile" (default, no parameters
  • QbMasterData: Retrieve, create, or update master data. MODES: RETRIEVE (default): pass entityTypes array (account, vendor, item, class, customer, taxRate, taxCode, paymentMethod, term) for IDs, or det
  • QbBill: Create/Update Bill (unpaid vendor liability). CREATE: vendorId, txnDate, lines (amount + accountId|itemId) required. DATES: txnDate and dueDate must be REAL calendar dates — QuickBooks accepts

Какие данные видит

Нужен ли аккаунт

Не нужен: сервер работает без входа

DeepLedger MCP connects QuickBooks Online with Claude, ChatGPT, Grok, Gemini, Cursor and other AI agents.

Your AI agent can categorize bank-feed transactions, record bills and invoices, draft journal entries, generate reports, and help complete the month-end close. When the agent is uncertain, it creates a task for your review. DeepLedger remembers your approved decisions, helping the agent make better choices and ask fewer questions over time.

Key features

  1. Record bills, expenses, invoices, payments, deposits, journal entries, and other transactions.
  2. Connect multiple QuickBooks companies, switch between them by asking, and simplify intercompany transactions.
  3. Generate financial reports using natural-language prompts.
  4. Build long-term AI memory based on your QuickBooks data and approved accounting decisions.
  5. Create review tasks whenever the agent needs clarification or approval.
  6. Track and complete the month-end close.
  7. Add Gmail, Outlook, Google Drive, or shared-drive connectors directly to your preferred AI agent. The agent can use information from those sources while DeepLedger handles its accounting actions in QuickBooks.

Tools

  • Transactions: qbBill, qbBillPayment, qbExpense, qbInvoice, qbReceivePayment, qbDeposit, qbTransfer, qbJournalEntry, qbSalesReceipt, qbRefundReceipt, qbEstimate, qbCredit, qbRecurringTransaction, qbVoidTransaction
  • Lookups & reports: qbMasterData, qbFetchTransactions, qbReports, customReports, qbCompanyProfile (list and switch companies)
  • Workflow: bankFeed, tasks, closeRun, agentMemory, documents, qbAttachFile, qbSendEmail, getGuide

Built for real books

DeepLedger works directly in your live QuickBooks Online company. Before recording anything, the agent checks the vendor or customer's history, open bills and invoices, and possible duplicates. When it isn't sure how to categorize a transaction, it creates a review task instead of guessing, and your approvals become rules it follows next time.

Setup

Sign-in uses OAuth, so no API key is needed. The first time you connect, a browser window opens to sign in to DeepLedger and authorize QuickBooks.

  • Claude (web or desktop): Settings → Connectors → Add custom connector → https://mcp.deepledger.ai/mcp
  • Claude Code: claude mcp add --transport http deepledger https://mcp.deepledger.ai/mcp
  • ChatGPT, Cursor, VS Code, and other MCP clients: add https://mcp.deepledger.ai/mcp as a remote (Streamable HTTP) server.

Requires QuickBooks Online.

DeepLedger turns your AI agent into a staff accountant for QuickBooks.

Как подключить

Как подключить DeepLedger к Claude

Адрес сервера, который нужно вставить в настройках Claude:

https://deepledger--nag79938.run.tools

  1. Откройте Claude (сайт claude.ai или приложение для компьютера).
  2. Перейдите в Настройки → Коннекторы (в английском интерфейсе: Settings → Connectors).
  3. Нажмите Добавить пользовательский коннектор (Add custom connector).
  4. Вставьте адрес сервера, скопированный ниже, в поле URL удалённого MCP-сервера и нажмите Добавить.
  5. В чате нажмите + → Коннекторы и включите новый коннектор.

Пользовательские коннекторы доступны на тарифах Free, Pro, Max, Team и Enterprise. На Free можно добавить только один. В Team и Enterprise коннектор сначала добавляет владелец организации в Настройки организации → Коннекторы.

Авторизация

После нажатия Добавить откроется окно входа в сервис. Войдите под своим аккаунтом и подтвердите доступ. Пароль Claude не видит.

Как подключить DeepLedger к ChatGPT

Адрес сервера, который нужно вставить в настройках ChatGPT:

https://deepledger--nag79938.run.tools

  1. Откройте ChatGPT в браузере (chatgpt.com). Нужен тариф Plus, Pro, Business, Enterprise или Edu.
  2. Один раз включите режим разработчика: Настройки → Приложения → Дополнительные настройки → Режим разработчика (Settings → Apps → Advanced settings → Developer mode).
  3. Откройте Настройки → Коннекторы и нажмите Создать (Create).
  4. Заполните форму: Название (любое), Описание (одна фраза, что делает сервис), URL MCP-сервера (скопируйте ниже).
  5. В поле Аутентификация выберите OAuth, если сервис требует вход, иначе Нет. Нажмите Создать.
  6. В новом чате откройте + → Приложения/Коннекторы и включите коннектор.

Название раздела у OpenAI менялось (Connectors → Apps/Plugins). Если пункт называется иначе, ищите «режим разработчика» в настройках. В рабочих пространствах Business/Enterprise администратор должен разрешить пользовательские коннекторы.

Авторизация

При первом использовании ChatGPT откроет окно входа в сервис. Войдите и подтвердите доступ.

Как подключить DeepLedger к Cursor

Адрес сервера, который нужно вставить в настройках Cursor:

https://deepledger--nag79938.run.tools

Быстрый способ: нажмите кнопку Открыть в Cursor ниже, Cursor сам предложит добавить сервер.

Вручную:

  1. В Cursor откройте Settings → Cursor Settings → MCP и нажмите Add new global MCP server.
  2. Вставьте JSON, скопированный ниже, в открывшийся файл ~/.cursor/mcp.json (для одного проекта: .cursor/mcp.json в корне проекта).
  3. Сохраните файл. Сервер появится в списке MCP; при необходимости пройдите авторизацию по кнопке рядом с ним.

Авторизация

Если сервис требует вход, рядом с сервером в списке MCP появится кнопка авторизации.

Список инструментов сервера (27)

Технические названия из tools/list. Нужны только разработчикам.

qbCompanyProfileRead the active QuickBooks company's identity and settings, list the companies you can work in, or switch the active company. OPERATIONS: operation="profile" (default, no parameters required) reads the active company; operation="list" returns every company this user can work in with QuickBooks status (works before any company is chosen); operation="switch" with organizationId (or an unambiguous name) makes that company the active one for EVERY later tool call, across all of this user's AI clients, until the next switch, and proves it by reading the new company's CompanyInfo. If any tool answers NO_ACTIVE_COMPANY, list then switch. State the company name back to the user after a switch and before the first write in a session. WHAT PROFILE RETURNS: profile = CompanyInfo (companyName, legalName, country, company/legal address, email, phone, website, fiscal year start, company start date, plus a nameValues passthrough carrying CompanyType, industry and subscription flags); preferences = the settings that decide what other tools can do (homeCurrency, multicurrencyEnabled, salesTaxEnabled, defaultTaxCode, classTracking, trackDepartments, useAccountNumbers, bookCloseDate, reportBasis, customer/vendor default terms, billableExpenseTracking, inventoryTracking, autoApplyCredit, eDeliveryStatus, onlinePaymentsEnabled, plus an otherPreferences passthrough). WHEN TO CALL PROFILE: once at the start of a session or before the first write to a company you have not worked in — it answers questions that otherwise get guessed. Specifically: bookCloseDate says whether a backdated transaction can be recorded at all (a date on or before it is refused or needs the closing password — hand it to a human instead); salesTaxEnabled says whether taxCodeId can be set on any line, which country alone does NOT tell you; classTracking="off" means classRef is unusable and qbReports(ProfitAndLossByClass) will answer 5020 Permission Denied; useAccountNumbers=false means accounts have no AcctNum, so do
qbMasterDataRetrieve, create, or update master data. MODES: RETRIEVE (default): pass entityTypes array (account, vendor, item, class, customer, taxRate, taxCode, paymentMethod, term) for IDs, or detailedInfo='vendor'/'customer' for full details. Add filter to narrow either mode to one record instead of pulling the whole list (a number matches the Id, text is a partial name match). TAX: txnTaxCodeId takes a TaxCode Id from entityTypes:["taxCode"]; line-level taxCodeId takes the LITERAL "TAX" or "NON" — QB rejects numeric ids there ("Valid line TaxCodes for US should be TAX or NON"). taxRate is reference-only (rates/percentages) and is never valid for either param. LOOKUPS FOR WRITE TOOLS: entityTypes:["paymentMethod"] supplies paymentMethodId (qbReceivePayment, qbSalesReceipt, qbRefundReceipt, qbDeposit); entityTypes:["term"] supplies salesTermId (qbInvoice). taxRate/taxCode/paymentMethod/term are retrieve-only. CREATE: operation='create', entityType, name. Account needs accountType (CREATE-only, cannot be changed on update) and optionally accountSubType, QuickBooks' 'detail type' — omit it and a sensible default for the accountType is applied, because QuickBooks itself picks an arbitrary one (an Expense account becomes 'Travel'). Item needs itemType + incomeAccountId; Inventory items additionally need expenseAccountId AND assetAccountId, and QuickBooks enforces their detail types: the asset account must be accountSubType 'Inventory', the COGS account 'SuppliesMaterialsCogs' and the income account 'SalesOfProductIncome' — create those accounts here first, passing accountSubType, or the item is rejected with 6430. Inventory items auto-set TrackQtyOnHand=true and QtyOnHand=0. UPDATE: operation='update', entityType, id, syncToken + fields to change. Address updates merge field-by-field into the stored address (send only the fields to change; the rest are preserved). Item incomeAccountId/expenseAccountId/assetAccountId can be re-pointed on update; itemType can flip Service↔NonInvent
qbBillCreate/Update Bill (unpaid vendor liability). CREATE: vendorId, txnDate, lines (amount + accountId|itemId) required. DATES: txnDate and dueDate must be REAL calendar dates — QuickBooks accepts an impossible one like 2026-13-45 without complaint and treats it differently per field (txnDate books TODAY, and an update moves an existing bill into the current period; dueDate is silently dropped so the bill ages from the wrong day); both are rejected here. UPDATE: provide billId + only fields to change (sparse update). Omit lines to keep existing lines unchanged. REPLACING LINES REPLACES THE WHOLE ARRAY — sparse protects fields, not the line array, so re-send every line you are keeping WITH its accountId/itemId, classId, customerId, billableStatus and taxCodeId (the result's `lines` carry all of those ids for exactly this; a dropped classId is silently wrong class reporting). MONEY ALREADY PAID — two ways an update strands it, both a clean success, each warned with the amounts: replacing lines so the new total falls below the amount already paid (QuickBooks shrinks the payment's application and floats the difference as unapplied vendor cash — fix via qbBillPayment or restore the total), and CHANGING vendorId on a bill that has a payment (QuickBooks moves the bill and drops the application entirely, leaving the cash on the OLD vendor applied to nothing and this bill fully open again). If a replaced line was BILLABLE, QuickBooks also deletes the pending re-billable charge it was holding for that customer, so the cost can never be invoiced — warned with the customer and amount. OPTIONAL: memo (maps to QB PrivateNote — include bank-feed memo verbatim here), docNumber (the vendor's invoice number, max 21 chars; "" clears a stored one), dueDate, salesTermId (Net 30 etc — computes DueDate on create when dueDate is omitted; without either, DueDate = txnDate because vendor default terms do NOT apply through the API), apAccountId, departmentId. ITEM LINES: itemId requires an item m
qbBillPaymentCreate/Update Bill Payment. CREATE: vendorId, paymentDate, bankAccountId, bills (billId + amount) required. UPDATE: provide paymentId + only fields to change (sparse update). Omit bills to keep existing bill links unchanged. OPTIONAL: paymentType (Check|CreditCard, CREATE default Check), docNumber, memo (maps to QB PrivateNote), printStatus. PAYMENT METHOD: bankAccountId is a Bank account for Check payments or a Credit Card account for CreditCard payments — QuickBooks derives the method from the account's type. UPDATE + paymentType: changing the method requires bankAccountId (an account of the new type); a paymentType change without bankAccountId is rejected pre-flight because QuickBooks silently ignores it. AMOUNTS MUST FIT THE OPEN BALANCE: QuickBooks NEVER rejects an over-application — it applies only what is open, still moves the full cash amount, and leaves the difference applied to nothing (the vendor balance goes negative). This tool refuses that pre-flight, naming the bill's real open balance, and also refuses paying a bill whose balance is already 0 (the duplicate-payment trap). Pay the GROSS balance on a taxed bill — tax is part of what is owed. Each transaction may appear only ONCE in bills. IDS ARE DIGITS ONLY, verbatim from qbFetchTransactions: QuickBooks integer-parses ids, so a padded id ("0123") reaches a different record than the balance check inspected — it is refused rather than written blind. DATES must be real calendar dates; QuickBooks answers an impossible one by booking the company's today. UPDATE CANNOT: change the vendor (QuickBooks drops every link and strands the cash — void and re-record instead) or edit a VOIDED payment (it would come back to life and lose its void marker). bills.txnType supports Bill (default), VendorCredit, or JournalEntry — a JournalEntry must have a line DEBITING Accounts Payable with this vendor as its Entity (no line id is needed, unlike qbDeposit), and every linked document must belong to vendorId or QuickBooks r
qbJournalEntryCreate/Update Journal Entry. CREATE: txnDate, lines (accountId, amount, postingType: Debit|Credit) required. Total Debits MUST equal Total Credits (QuickBooks rejects imbalance with error 2300; this tool pre-flights it). UPDATE: provide journalEntryId + only fields to change (sparse update). Omit lines to keep existing lines unchanged; providing lines REPLACES ALL existing lines and REASSIGNS every lineId — re-read lineIds from the response before using one elsewhere (qbDeposit JE-linking consumes them). A/R AND A/P LINES: a line posting to Accounts Receivable requires customerId on that line, Accounts Payable requires vendorId — QuickBooks rejects the entry otherwise (error 6000, and the entity TYPE matters). ALREADY-APPLIED ENTRIES: an entry with an Accounts Payable debit line can be applied as a credit on a bill payment, and replacing the lines of such an entry makes QuickBooks DROP it from that payment — the bill reopens and the credit applies to nothing, even when the lines you re-send are identical. The tool refuses a line change on an applied entry and names the payment to void first; date-only and memo-only updates are safe and still allowed. DEPOSITED ENTRIES: once a line debiting Undeposited Funds has been deposited, QuickBooks refuses every line change with error 6540 (remove it from the deposit first) — date and memo updates still work. TAXED ENTRIES (VAT/GST): a journal entry carrying tax balances on GROSS and this tool cannot send line tax codes, so a line change on one is refused pre-flight (hand off to a human); an update without lines keeps the tax intact. DATES: txnDate must be a real calendar date — QuickBooks does not reject an impossible one, it books the entry with the COMPANY's today, which on an update moves a period-end entry into the current period. ADJUSTMENT FLAG is one-way: update can set adjustment:true on a normal entry, but the QBO API cannot un-flag an adjusting entry (adjustment:false on one is rejected pre-flight — recreate the ent
qbExpenseCreate/Update Expense (Purchase). CREATE: paymentType (Cash|Check|CreditCard), accountId (source Bank/CC), txnDate, lines required. SOURCE ACCOUNT: the account the money LEFT — QuickBooks accepts Bank, Credit Card, Other Current Asset and Other Current Liability, and refuses Expense/Income/Accounts Payable/Accounts Receivable/Equity/Fixed Asset with error 6430 (rejected up front here, naming the fix — an expense CATEGORY belongs in lines.accountId, not here). paymentType must also match that account: CreditCard needs a Credit Card account and Cash/Check must not point at one; mismatches are rejected (QB would otherwise silently coerce the payment type). DATES: txnDate must be a real calendar date — QuickBooks accepts an impossible one like 2026-13-45 without complaint and books the expense with TODAY's date instead (an update moves an existing expense into the current period the same way); refused here. CREDIT-CARD REFUNDS: credit:true with paymentType "CreditCard" records a credit-card CREDIT (vendor refund back onto the card) — amounts stay positive, the flag sets the direction; credit is rejected on Cash/Check (QB would silently book a charge). UPDATE: provide expenseId + only fields to change (sparse update). Omit lines to keep existing lines unchanged. REPLACING LINES REPLACES THE WHOLE ARRAY — sparse protects fields, not the line array, so re-send every line you are keeping WITH its accountId/itemId, classId, customerId, billableStatus and taxCodeId (the result's `lines` carry all of those ids for exactly this; a dropped classId is silently wrong class reporting). If a replaced line was BILLABLE, QuickBooks also deletes the pending re-billable charge it was holding for that customer, so the cost can never be invoiced — the tool warns with the customer and amount when that happens. OPTIONAL: vendorId (highly recommended for audit trail), memo (maps to QB PrivateNote — include bank-feed memo verbatim here), docNumber (max 21 chars), departmentId, paymentMethodId
qbEstimateEstimates / quotes / proposals (QuickBooks Estimate). NON-POSTING: an estimate touches NO account, no A/R and no report — nothing hits the books until operation="convert" creates the invoice. Quoting a customer therefore never changes the financials; billing them does. OPERATIONS • get — read one estimate: status, lines, totals, delivery info, and the invoice it became (linkedInvoiceId). To LIST or search estimates use qbFetchTransactions(transactionType:"Estimate") (filter by customer with entityId, by date, by amount, by docNumber). • create — customerId, txnDate, lines required. Line fields: amount (the line total — QuickBooks does NOT multiply quantity x unitPrice for you), description (the scope text the customer reads), itemId, quantity, unitPrice, taxCodeId. Optional: expirationDate, customerMemo, docNumber, billEmail, txnStatus, discount, shipAddr. • update — sparse: provide estimateId + only the fields to change. Omit lines to keep existing lines. Replacing lines REPLACES ALL of them and resets line-level taxability, so re-send taxCodeId on EVERY line (the tool warns when tax falls, and on non-US companies forces QuickBooks to recalculate — it otherwise keeps the OLD tax amount on the NEW subtotal). • close — mark the quote Closed (lost, withdrawn, superseded). A Closed estimate can NO LONGER be converted. Idempotent: closing an already-closed estimate writes nothing and reports alreadyClosed. • convert — create the Invoice this quote becomes. This is the step that puts revenue on the books. The estimate's lines are copied into the invoice, the two are linked, and QuickBooks flips the estimate to status "Converted". Override the invoice's date/due date/number with invoiceTxnDate, invoiceDueDate, invoiceDocNumber. CONVERSION IS THE DANGEROUS ONE — READ THIS. QuickBooks does NOT refuse a conversion it cannot perform. Creating an invoice against a Closed, Rejected or ALREADY-CONVERTED estimate returns HTTP 200 and creates a real invoice with the link silently
qbInvoiceCreate/Update Invoice (AR). CREATE: customerId, txnDate, lines (amount) required. UPDATE: provide invoiceId + only fields to change (sparse update). Omit lines to keep existing lines unchanged — lines only needed if replacing ALL line items. TAX ON UPDATE: replacing lines resets line-level taxability — re-send taxCodeId on EVERY line or QuickBooks recalculates sales tax to zero (the tool warns when this happens). Line taxCodeId is the literal "TAX"/"NON"; only txnTaxCodeId takes a qbMasterData TaxCode id. Non-US (VAT/GST) companies: replacing lines makes the tool force QuickBooks to recalculate the document tax from the new lines' taxCodeIds (QuickBooks would otherwise silently keep the OLD tax amount on the new subtotal); the message reports the recalculated tax. DISCOUNT ON UPDATE: changing the discount requires re-sending ALL lines with it (QuickBooks replaces the whole line array together — a discount alone is rejected pre-flight); replacing lines WITHOUT discount preserves the existing discount automatically. OPTIONAL: dueDate, salesTermId, memo, customerMemo, docNumber, billEmail, emailStatus (enum: NeedToSend | EmailSent | NotSet — NeedToSend only queues the invoice in QuickBooks' "Send later" list; nothing is emailed until a human sends it from the QuickBooks UI), txnTaxCodeId (enables automatic tax calculation by QB), allowOnlineCreditCardPayment / allowOnlineACHPayment (per-invoice "Pay now" options — false hides that payment method for this invoice, omit for the company default; QuickBooks has no per-customer setting, so keep each customer's policy in agentMemory and pass the flags on every invoice). Use qbMasterData for IDs. PARTIALLY-PAID INVOICES: replacing lines so the new total falls below what the customer has already paid does NOT fail — QuickBooks silently shrinks the payment's application and leaves the difference as unapplied credit on the customer; the tool warns with the exact amounts when this happens (fix via qbReceivePayment or restore the
qbReceivePaymentCreate/Update customer payment (closes outstanding invoices). CREATE: customerId, paymentDate, totalAmount required — and ALWAYS pass the invoices array: the API does NOT auto-apply (omitting invoices leaves the ENTIRE amount unapplied as a customer credit and closes nothing; the QuickBooks UI auto-applies, the API does not). UPDATE: provide paymentId + only fields to change (sparse update). Omit invoices/creditMemos to keep existing links unchanged. OPTIONAL: depositAccountId, paymentMethodId (recommended for bank reconciliation), paymentRefNum (max 21 chars), memo, creditMemos (apply customer credit memo balances alongside payment — works on both create and update when explicitly provided). CREDIT MEMO RULE: creditMemos requires an explicit invoices array, and each invoice line amount is the TOTAL applied to that invoice (cash + credit) — sum(invoices) must equal totalAmount + sum(creditMemos); the tool rejects unbalanced applications because QB would otherwise leave the credit floating unapplied. totalAmount:0 is valid — it records a pure credit-memo application (existing credit pays the invoice, no cash). SILENT ADJUSTMENT: QuickBooks never rejects an over-application — it CLAMPS each application to the invoice's open balance (or drops it entirely if the invoice is already paid) and returns success; the tool compares what you requested against what was applied and reports every shortfall in the message. UPDATING APPLICATIONS REOPENS WHAT YOU DROP: sending invoices on an update REPLACES the whole set, so any invoice left out goes back to fully open (QuickBooks reports nothing — this tool warns INVOICE REOPENED). That works even on a payment already included in a deposit, where QuickBooks does block a totalAmount or depositAccountId change with error 6540. OUTPUT: paymentId, unappliedAmount (amount not applied to any invoice — warned in message when > 0), depositAccountName, invoices array (invoiceId, amount ACTUALLY applied, documentNumber, openBalanceBefore and
qbSalesReceiptCreate/Update Sales Receipt (immediate payment, no AR). CREATE: txnDate, lines (amount) required; customerId is optional (supports anonymous sales). UPDATE: provide salesReceiptId + only fields to change (sparse update). Omit lines to keep existing lines unchanged. TAX ON UPDATE: replacing lines resets line-level taxability — re-send taxCodeId on EVERY line or QuickBooks recalculates sales tax to zero (the tool warns when this happens). Line taxCodeId is the literal "TAX"/"NON"; only txnTaxCodeId takes a qbMasterData TaxCode id. Non-US (VAT/GST) companies: replacing lines makes the tool force QuickBooks to recalculate the document tax from the new lines' taxCodeIds (QuickBooks would otherwise silently keep the OLD tax amount on the new subtotal); the message reports the recalculated tax. DISCOUNT ON UPDATE: changing the discount requires re-sending ALL lines with it (QuickBooks replaces the whole line array together — a discount alone is rejected pre-flight); replacing lines WITHOUT discount preserves the existing discount automatically. OPTIONAL: customerId, itemId, quantity, unitPrice, depositAccountId, paymentMethodId, paymentRefNum (check number), memo, customerMemo, docNumber, billEmail (stored only — QuickBooks does not email the receipt), txnTaxCodeId (enables automatic tax calculation by QB). DATES: txnDate, shipDate and line serviceDate must be real calendar dates — QuickBooks accepts impossible ones like "2026-13-45" and silently books TODAY / drops the ship date / stores 9999-12-31 on the line, so the tool refuses them first. ALREADY-DEPOSITED RECEIPTS: once a receipt has been included on a deposit, QuickBooks refuses every change to it with 6540 ("Deposited Transaction cannot be changed") — remove it from that deposit before updating it. LINE REPLACEMENT re-sends the whole line array: pass back each line's itemId, classId and taxCodeId (all three are in this tool's output) or the item, the class and the taxability are lost. PREREQUISITE — open-document c
qbDepositCreate/Update Deposit. CREATE: depositAccountId (Bank account), txnDate, lines required. TWO LINE SHAPES: (1) RAW line — amount + accountId, the account the money came FROM (income account for revenue, equity for owner contributions, expense account for a vendor refund or a fee); must differ from depositAccountId (a line pointing at the deposit bank account books the bank funding itself — rejected pre-flight) and must NOT be Undeposited Funds (QB rejects that with a misleading "Select a bank account" error). (2) LINKED line — linkedPaymentId (+ linkedTxnType: "Payment" default, "SalesReceipt", or "JournalEntry") deposits a transaction held in Undeposited Funds into the bank and clears it. Payments recorded without a depositAccountId land in Undeposited Funds — THIS is how you move them to the bank. JournalEntry links additionally require linkedTxnLineId: the Id of the JE line that DEBITS Undeposited Funds (qbJournalEntry output lineId; JE lines number from 0 — any other line is a QB 6000). Never send amount on a linked line: QB always deposits the linked amount in full and silently ignores a different amount. QB rejects linking a transaction that is already deposited or that went straight to a bank account (errorCode 6000), and rejects the same transaction linked twice. Shapes can be mixed in one deposit. FEES WITHHELD: raw line amounts may be negative — the merchant-payout shape is a linked payment plus a negative fee line against the fee expense account; raw lines must net above 0 unless a linked line supplies the funds (QB would otherwise book a NEGATIVE deposit on a clean success — money leaving the bank). VENDOR REFUNDS: raw line with vendorId + the original expense account as accountId (+ checkNum for the check number). TAX (non-US/VAT/GST): raw lines take taxCodeId + taxApplicableOn ("Sales" = income/output tax, "Purchase" = vendor refund/input tax — REQUIRED together; QB's silent default is Purchase, wrong for income). Line amounts are NET; QB computes the t
qbTransferCreate/Update Transfer. Moves money between two accounts the company holds: bank-to-bank transfers AND bank-to-credit-card payments (the standard booking for a "payment to credit card" bank-feed line — transfer FROM the bank TO the Credit Card account). A transfer has no lines, no customer/vendor and no tax; direction comes entirely from the two accounts. ACCOUNTS: Bank, Credit Card, Other Current Asset, Fixed Asset, Other Asset, Other Current Liability, Long Term Liability and Equity all work (every one live-proven, both directions). REFUSED: income, expense and Cost of Goods Sold categories (QuickBooks error 6000 "must be valid balance sheet accounts") — to move money to or from a category use qbExpense (out), qbDeposit (in) or qbJournalEntry; and ALSO Accounts Receivable and Accounts Payable, which QuickBooks refuses with the same 6000 despite being balance-sheet accounts — A/R and A/P move only through customer/vendor documents (qbReceivePayment, qbBillPayment, or a qbJournalEntry line carrying the customer/vendor as its Entity). NEVER Undeposited Funds: QuickBooks ACCEPTS that transfer and it corrupts the account — it moves the balance without clearing any payment held there, so the held payments stay depositable and the same money leaves twice (proven live). Move money out of Undeposited Funds by depositing the held payments with qbDeposit(linkedPaymentId); this tool refuses it. NOR a SYSTEM TAX ACCOUNT (VAT Control / VAT Suspense on a UK company, GST/HST Payable / Suspense on a Canadian one): QuickBooks refuses those with error 6000 because their balance comes from the tax subledger, not from transactions — a tax bill is paid in the QuickBooks tax centre, which no tool reaches, so hand that off to a human (tasks operation="create"). CONSTRAINTS (refused before the write, each naming the fix): fromAccountId ≠ toAccountId (QuickBooks error 9023); both accounts must use the same currency; amount positive with at most 2 decimals (QuickBooks silently rounds a long
qbRefundReceiptCreate/Update Refund Receipt. Records actual money returned to customer (unlike Credit Memo which is a balance adjustment). CREATE: customerId, txnDate, depositAccountId (the account the refund is paid from — a Bank account, or an Other Current Asset account such as Undeposited Funds; QuickBooks requires it and accepts NO other type, so a credit-card refund cannot be recorded here), lines (amount + itemId) required. QuickBooks VALIDATES amount = quantity × unitPrice on refund lines (error 6070) — keep the three consistent. A line amount may be NEGATIVE (a restocking fee or other amount withheld from the refund) and QuickBooks taxes the net, but the document total must be 0 or greater. UPDATE: provide refundReceiptId + only fields to change (sparse update). Lines are sparse: omit to preserve existing lines, provide to REPLACE ALL lines. TAX ON UPDATE — US companies: replacing lines resets line-level taxability — re-send taxCodeId ("TAX"/"NON") on EVERY line or QuickBooks recalculates the sales tax to zero (the tool warns when this happens). Non-US (VAT/GST) companies: replacing lines makes the tool force QuickBooks to recalculate the document tax from the new lines' taxCodeIds (QuickBooks would otherwise silently keep the OLD tax amount on the new subtotal); taxCalculation may be omitted on update — the stored setting is carried forward. DISCOUNT: posts as its own line; on update changing it requires re-sending ALL lines, and replacing lines without discount preserves the existing one automatically. paymentRefNum records the refund check number (must be unused — reuse is QuickBooks error 6140). REVERSAL: refund receipts CAN be voided — use qbVoidTransaction(transactionType:"RefundReceipt"). OPTIONAL: paymentMethodId, paymentRefNum, discount, memo, customerMemo, docNumber (max 21 chars), departmentId, lines.quantity, lines.unitPrice, lines.serviceDate, lines.classId, lines.taxCodeId. Use qbMasterData for IDs. DUPLICATE CHECK (ANALYZE): Before creating, use qbFetchTran
qbCreditCreate/Update Credit. TYPES: "customer" (Credit Memo, reduces AR) or "vendor" (Vendor Credit, reduces AP). CREATE: creditType, txnDate, lines required. Customer: customerId + lines(amount, itemId). Vendor: vendorId + lines(amount + accountId|itemId). DATES: txnDate and line serviceDate must be REAL calendar dates — QuickBooks accepts an impossible one like 2026-13-45 without complaint and treats it differently per field (txnDate books the COMPANY's today, and on an update that MOVES an existing credit into the current period; serviceDate is stored as 9999-12-31); both are rejected here. UPDATE (both types): sparse update — provide creditId + only fields to change. Omit lines to keep existing lines unchanged; provided lines REPLACE ALL existing lines; omitted fields (txnDate, memo, docNumber, ...) are preserved. TAX — US companies: line taxCodeId is the literal "TAX"/"NON"; replacing customer-memo lines without re-sending taxCodeId on each resets taxability and silently drops the tax (the tool warns when that happens). Non-US (VAT/GST): line taxCodeId is a real TaxCode id; customer memos need taxCalculation "TaxExcluded" on taxed creates (on update the stored value is carried); replacing lines forces QuickBooks to recalculate the document tax (it would otherwise keep the OLD tax on the new subtotal); vendor credits need NO taxCalculation — purchase tax computes and recomputes natively from line taxCodeIds. DISCOUNT (customer only): posts as its own line; on update changing it requires re-sending ALL lines, and replacing lines without discount preserves the existing one automatically. APPLYING A CREDIT: a credit memo is applied to an invoice via qbReceivePayment (creditMemos array); a vendor credit is applied when paying bills. CREDIT ALREADY APPLIED — two ways an update silently reopens the document it was settling, both a clean success, each warned with the amounts: replacing lines so the new total falls below the amount already applied (QuickBooks shrinks the appli
qbSendEmailEmail a sales document to the customer through QuickBooks, and confirm delivery. Types: Invoice, Estimate, SalesReceipt, CreditMemo, RefundReceipt (purchase-side records have no send endpoint — they are internal, not customer-facing). THIS IS THE ONLY THING IN THIS SERVER THAT EMAILS A CUSTOMER, and it is always deliberate — recording a transaction never sends anything. qbInvoice/qbEstimate's emailStatus:"NeedToSend" ONLY queues the document in QuickBooks' "Send later" list for a human to send; it delivers nothing and records no delivery time. If you want the customer to receive something, you must call this tool. AN EMAIL CANNOT BE UNSENT. Confirm with the user before calling it — the customer receives a real document from the company's QuickBooks account. Elicitation-capable MCP clients also get an in-tool confirmation prompt that defaults to NOT sending, but do not rely on that: an agent-forwarded call has no prompt, so the responsibility is yours. Never send "to check that it works", and never re-send to be sure — every call delivers again. REQUIRED: documentType, documentId (the numeric QuickBooks id from qbFetchTransactions, not the document number). The id must belong to the type you name; the tool checks and refuses a mismatch rather than emailing whatever else has that id. DELIVERY CONFIRMATION: read delivered and deliveryTime, NOT emailStatus. QuickBooks sets EmailStatus to "EmailSent" as a flag; only DeliveryInfo.DeliveryTime is evidence something actually left. If delivered comes back false the tool says so loudly — treat that as "not delivered", not as a success. sendTo REWRITES THE RECORD: QuickBooks does not treat sendTo as a one-off redirect — it overwrites the document's stored BillEmail permanently, so all later sends go to the new address too (reported as addressOverwritten). Omit sendTo to use the address already on the document. REFUSALS (nothing is emailed): NO_EMAIL_ADDRESS when the document has no address and the customer has none on fi
qbFetchTransactionsFetch QB transactions. Used in FOUR distinct modes in the ANALYZE → DECIDE → EXECUTE framework: MODE A — OPEN-DOCUMENT CHECK (ANALYZE): transactionType=Bill|Invoice, outstandingOnly=true, entityId=vendorId|customerId, NO date range. Returns outstanding Bills/Invoices for payment routing. If a match exists, use qbBillPayment (vendor) or qbReceivePayment (customer) — the document already encodes the account. Do NOT re-infer. MODE B — ACCOUNT INFERENCE (DECIDE): entityId=..., entityType=Vendor|Customer, startDate=(today-180d), endDate=today, NO transactionType (report scan). Returns 6-month history for CONSISTENCY RULE evaluation. Evaluate the rule on splitAccount (the offsetting CATEGORY account) — NOT on account, which is the anchor (bank/CC/AP) and identical across most rows. splitAccount "-Split-" = multi-line txn: fetch it in query mode and use lines[].accountName. Use dominant category ONLY IF ≥3 txns, dominant ≥70%, no second ≥20%, amount within 5× median. Otherwise → create a CPA task (tasks operation="create") with breakdown. MODE C — DUPLICATE CHECK (ANALYZE): transactionType=Bill|Invoice|Purchase, entityId=..., narrow date range (txnDate±15d), minAmount/maxAmount. Returns potential duplicates before writing. MODE D — REPORT SCAN (reconciliation): omit transactionType, provide accountId + startDate + endDate. Returns all txns touching an account. Summary only (no SyncToken/lines) — follow with MODE A/query for full detail. QUERY MODE (single-type, full detail): transactionType + filters → IDs, SyncTokens, line items. Types: Bill, BillPayment, VendorCredit, Invoice, Payment, CreditMemo, SalesReceipt, Purchase, Deposit, JournalEntry, Transfer, RefundReceipt, Estimate. Deposit lines have two shapes: raw lines carry accountId/accountName, linked lines (deposited payments) carry linkedPaymentId + linkedTxnType + linkedTxnLineId instead. ESTIMATES (quotes) are NON-POSTING — they are in no report and on no account until converted, so they never appear in report
qbReportsGenerate financial reports. REQUIRED: reportType, endDate. STARTDATE RULES: startDate required for ProfitAndLoss, ProfitAndLossByClass, CashFlow, TrialBalance, CustomerIncome, VendorExpenses, TransactionList, GeneralLedger, ProfitAndLossDetail, SalesByCustomer, SalesByProduct, BudgetVsActuals. NOT needed for BalanceSheet (point-in-time: endDate is the as-of date and works alone — a bare end_date is ignored by QuickBooks, so the tool auto-sends start_date=endDate), AgedReceivables/AgedPayables/AgedReceivablesDetail/AgedPayablesDetail (aging snapshots), VendorBalance, CustomerBalance. AS-OF REPORTS: the four aging reports plus VendorBalance and CustomerBalance use endDate as the as-of date (sent as report_date; aging buckets computed relative to it) — set endDate to the period close date to pull a prior-period aging. OPTIONAL: accountingMethod, summarizeBy, budgetId (BudgetVsActuals only), and per-report FILTERS: customerIds, vendorIds, classIds, departmentIds, itemIds, employeeIds, accountIds, sourceAccountIds, nameIds, termIds (QuickBooks ids from qbMasterData), accountType, accountStatus, sourceAccountType, transactionType, cleared, printed, arPaid, apPaid, paymentMethod, amount, memo, docNum, groupBy, dueDateStart/dueDateEnd, modifiedStart/modifiedEnd, agingPeriod, numPeriods, pastDue, shipVia, columns (choose the columns of a detail report), sortBy, sortOrder, adjustedGainLoss. Each filter is honoured only on the reports its own description names and is refused elsewhere (PARAM_NOT_SUPPORTED, naming the reports that honour it); an unknown column name is INVALID_PARAM with the allowed list. A P&L for ONE class or location is classIds/departmentIds; one column PER class or location is summarizeBy=Classes/Departments. PARAMETERS ARE PER-REPORT: QuickBooks accepts every parameter on every report and silently ignores the ones that do not apply, so this tool refuses those combinations up front (errorCode PARAM_NOT_SUPPORTED) rather than returning a report that is not w
qbVoidTransactionVoid transaction. Zeros amounts and every line but keeps the record for the audit trail — QuickBooks marks it by prepending "Voided" to the memo. REQUIRED: transactionType, transactionId (both from qbFetchTransactions — an id under a different type returns 610 and the tool names the real type). CONFIRM WITH THE USER FIRST — voids cannot be undone (elicitation-capable MCP clients also get an in-tool confirmation prompt, but do not rely on it). SUPPORTED TYPES: BillPayment, Invoice, Payment, RefundReceipt, SalesReceipt. NOT SUPPORTED (each returns the reversal that DOES work for that type): Purchase/Expense, CreditMemo, Transfer, Bill, Estimate, VendorCredit, JournalEntry, Deposit — for all of these QuickBooks either rejects the void or, worse, answers HTTP 200 while changing nothing, so this tool refuses them rather than report a void that never happened. ALREADY VOIDED: if the transaction was voided before this call, the tool changes nothing and returns success with alreadyVoided:true — QuickBooks would otherwise accept a second void and prepend a second "Voided" marker to the memo. Do not report an alreadyVoided result as your own action, and never retry a void to "make sure". WHAT A VOID RELEASES: voiding a Payment or BillPayment puts every Invoice/Bill it paid back to OPEN (returned in reopenedDocuments) — the debt is live again — and hands back every credit it consumed (returned in releasedCredits: a VendorCredit or CreditMemo it used is available again). voidedAmount is CASH only, so a replacement payment must apply those credits again or it overpays by the credit amount. Voiding an Invoice that has payments/credits applied does NOT fail: QuickBooks voids it and releases that cash as an unapplied credit on the customer's A/R, leaving the balance negative until it is re-applied with qbReceivePayment or refunded (returned as releasedAmount + releasedPayments). DEPOSITED FUNDS: a Payment or SalesReceipt whose funds sit in a Deposit cannot be voided (error 6540) un
qbRecurringTransactionManage Recurring Transactions (templates) in QuickBooks Online. A recurring transaction is a TEMPLATE, not a transaction. An "Automated" template makes QuickBooks POST a real transaction on its schedule with no human review, so treat creating or changing one as a bigger commitment than a single write: check the lines it will post. OPERATIONS: - LIST: all templates. OPTIONAL filterType (exact QuickBooks type, no spaces). - READ: full template by recurringTransactionId — including the LINES it will post. - CREATE: new template. Requires recurringInfo + createTransactionType + the transaction details. - UPDATE: change an existing template (SPARSE — omitted fields keep their stored value). Requires recurringTransactionId. - DELETE: remove by recurringTransactionId. syncToken and transactionType are optional; the tool reads the template first. CREATE REQUIRED: recurringInfo + createTransactionType + transaction details. - recurringInfo: name (UNIQUE per company — a reused name is error 6240), recurType, scheduleInfo. - recurringInfo.scheduleInfo (required for Automated/Reminded): intervalType (Daily | Weekly | Monthly | Yearly) + numInterval, plus the interval's required subfield: dayOfMonth (Monthly, Yearly), dayOfWeek (Weekly), monthOfYear (Yearly). Optional: startDate, endDate OR maxOccurrences (not both — QuickBooks silently drops maxOccurrences when endDate is present), daysBefore (Automated: create N days early; Reminded: remind N days ahead). - Per type: vendorId is required for Bill/Purchase/VendorCredit/PurchaseOrder, customerId for Invoice/Estimate/SalesReceipt/CreditMemo/RefundReceipt, depositAccountId additionally for RefundReceipt. Every type except Transfer needs at least one line; expense-type lines need accountId or itemId. - Transfer is the exception: fromAccountId + toAccountId + transferAmount, and no lines. THINGS THAT ARE NOT WHAT THEY LOOK LIKE: - THERE IS NO PAUSE FLAG. QuickBooks accepts active:false, returns success, and stores active:true —
qbAttachFileAttach a file (receipt, invoice, bank statement, signed document) to a QuickBooks transaction or record. WORKFLOW — TWO STEPS, BOTH REQUIRED: 1. Call this tool with fileName, entityType, entityId. It checks the record exists in QuickBooks and returns a curlCommand plus expiresInSeconds. 2. You MUST execute the curlCommand. The file is NOT attached until you do. Read its JSON reply: "attached": true with a qb_attachable_id means the file is in QuickBooks; "attached": false means it is not, and the error names the fix. The token is single-use — call this tool again for a retry. THINGS THAT ARE NOT WHAT THEY LOOK LIKE: - This tool returning success:true means a TOKEN was issued, not that a file was attached. It always returns attached:false. - QuickBooks' upload endpoint answers EVERY failure with HTTP 200 and an error buried in the body. Trust the "attached" field in the curl reply, never the HTTP status. FILE TYPES: .ai .csv .doc .docx .eps .gif .jpeg .jpg .ods .pdf .png .tif .tiff .txt .xls .xlsx .xml — QuickBooks rejects everything else with error 6041, including .webp, .heic, .rtf, .zip and any name with no extension. Max 10 MB (DeepLedger's limit, enforced by the upload endpoint). WHAT YOU CAN ATTACH TO: Bill, BillPayment, CreditMemo, Customer, Deposit, Estimate, Invoice, Item, JournalEntry, Payment, Purchase, PurchaseOrder, RefundReceipt, SalesReceipt, Transfer, Vendor, VendorCredit. "Expense" in the QuickBooks UI is "Purchase" in the API. Accounts cannot carry attachments. Use qbFetchTransactions for a transaction id, qbMasterData for a customer, vendor or item id. NOTE: The default filePath is a Linux path (/mnt/user-data/uploads/<fileName>). Provide filePath explicitly when running on another platform. WHEN SOMETHING FAILS: getGuide(guideType:"error_recovery", tool:"qbAttachFile") has the full code-by-code recovery table for both steps.
tasksCollaborative task list shared between the AI agent and the human reviewer. Every task has an assignee baton: whose turn it is ("human" or "agent"). RUN FIRST, EVERY SESSION: tasks(operation="list") returns open tasks assigned to the AI agent — approved categorize tasks in stage "record" (record each in QuickBooks using effectiveCategory VERBATIM — the reviewer's decisions are final, never override them) plus custom tasks the human delegated. AFTER EVERY QB WRITE: tasks(operation="complete", taskNumber, qbTransactionId) — required to prevent re-processing on the next bank-feed cycle. ESCALATE uncertain items with tasks(operation="create") — requires specific aiReasoning (min 20 chars): vendor name + history summary, bank-feed memo verbatim, suggested account, amount and date. Deduplicates on bankTransactionId. CREATE triggers: new vendor (no QB history), multi-vendor master-data match (>1 result), split history (consistency rule fails), materiality (amount >5x median for the vendor), ambiguous description, any transaction >$10,000, capitalization candidates (above the org threshold from agentMemory policies; default $5,000). Use operation="get" to read a task's event thread (the human's answers and comments), "comment" to reply, "handoff" to flip the baton with a note, "dismiss" (with reason) to close a task that is not actionable. THINGS THAT ARE NOT WHAT THEY LOOK LIKE: (1) A FULL PAGE IS NOT THE WHOLE QUEUE — when list says INCOMPLETE, more approved work is waiting behind it; finish and complete these, then call again. (2) A suggestion is not a decision: record ONLY when effectiveCategory is non-null, and you cannot grant that yourself — handing an undecided categorize task to the agent is refused. (3) complete() on a bank-linked task writes a PERMANENT recorded marker on the bank transaction; no tool clears it, so a wrong qbTransactionId retires work that was never recorded. (4) A dismissed task keeps its bank transaction out of the feed for good — dismiss means
agentMemoryMaintain the company's AI Brain: About company, Vendor rules, Customer rules, and General notes. A rule has a name, preferred accounts, and an optional note. Source is recorded automatically as Human or AI generated; never supply a source or claim human approval. READ: operation="read" with section, search or memoryId. Use maxResults/offset until truncated=false. Read complete matching rules before making a categorization decision; the profile is only a partial index. No results and an unavailable store are different. WRITE: operation="write", section and name, optional preferredAccounts, entityId and note. Read first and update an existing topic instead of creating duplicates. Retrieve current accounts/vendors/customers using qbMasterData; use actual IDs from this company. If a valid record is missing from the portal cache, use Refresh QuickBooks records in AI Brain and retry. Human-provided preferences and well-supported AI learning can become rules. Explain the evidence and any limits in the note. A rule guides account choice; normal transaction approval and accounting checks still apply. A one-off transaction approval is not a new general rule. UPDATE: operation="update", memoryId, and just the fields to change. No revision or authority fields. Preserve conditions, exceptions and human corrections. Human-edited notes are protected from automatic overwrites; explain a proposed correction and direct the person to AI Brain if needed. DELETE: operation="delete", memoryId. Removes an obsolete AI-generated note from searches. Never delete a human note during automatic cleanup. Delete duplicates only after all useful details have been saved in the surviving note and that save succeeded. MAINTENANCE: during active work, review relevant memory after a meaningful correction and at the end of a monthly review or close. Consolidate duplicates, update outdated facts when supported by newer evidence, simplify wording, and rewrite long AI notes into focused linked notes when u
documentsRead documents from DeepLedger storage or QuickBooks attachments. TWO SOURCES: source="deepledger": uploaded invoices/receipts/statements, filtered by folder, status, file type, monthYear (the accounting period a reviewer assigned — this is the month-end filter), upload date range, file-name search, or attachedToTaskId. Use listFolders=true for the folder structure. DEEPLEDGER-ONLY fields: search, folder, listFolders, status, fileType, monthYear, createdAfter, createdBefore, maxResults, offset, documentIds, attachedToTaskId. source="quickbooks": pull attachments off a QB transaction — requires entityId + entityType. Use metadataOnly=true to inspect before downloading. Downloading copies the file into DeepLedger storage under an "AI Agent Downloads" folder. QUICKBOOKS-ONLY fields: entityId, entityType, metadataOnly. STRICT VALIDATION: deepledger-only and quickbooks-only fields must not be mixed. "Expense" in QB UI = "Purchase" entityType. THINGS THAT ARE NOT WHAT THEY LOOK LIKE: (1) A full page is NOT the whole set — a result saying INCOMPLETE has more behind it; pass offset to continue. (2) attachedToTaskId filters ONE database column that holds either a task id or a bank-transaction id, because a human can attach a file on the Tasks page or on the Bank Feed page. Pass whichever record you are asking about. (3) createdAfter/createdBefore are UPLOAD dates and both bounds are inclusive; monthYear is the accounting period the document belongs to, which is usually what a close needs. (4) search and fileType match literally — % and _ are not wildcards, and fileType is a bare extension ("pdf"), never "application/pdf". (5) A QuickBooks attachment can be a NOTE with no file (isNote=true, storagePath=null) — that is not a failed download. (6) Downloading the same QB attachment twice does NOT create a second copy; the second read reports alreadyInStorage=true. (7) Nothing in this server can DELETE a document — removal is the portal Documents page only.
bankFeedFetch bank transactions for the organization (unrecorded and unflagged by default) and stamp direct recordings. BEFORE CALLING THIS: run tasks(operation="list") first — record all CPA-approved tasks using effectiveCategory verbatim, then complete each. EVERY ROW NAMES ITS BANK ACCOUNT: qbAccountId is the QuickBooks account the money actually moved through — use it as the payment/deposit account on the write. qbAccountId null means the bank account has no QuickBooks mapping; escalate with tasks(operation="create"), never guess an account. WORKFLOW per transaction (ANALYZE → DECIDE → EXECUTE): ANALYZE — RESOLVE ENTITY: qbMasterData(detailedInfo="vendor", filter=counterpartyName). Exactly 1 match → use entityId. 0 matches → new vendor, flag. >1 matches → flag with candidate list, do not pick. ANALYZE — OPEN-DOCUMENT CHECK (separate call, no date window): qbFetchTransactions(transactionType="Bill", outstandingOnly=true, entityId=vendorId). If outstanding Bill found → route to qbBillPayment, skip account inference. DECIDE — ACCOUNT INFERENCE (bounded history): qbFetchTransactions(entityId=vendorId, entityType="Vendor", startDate=today-180d, endDate=today). Apply CONSISTENCY RULE: use dominant account ONLY IF ≥3 prior txns in 180d, dominant account ≥70%, no second account ≥20%, amount within 5× median. Any criterion fails → tasks(operation="create"). Amount >5× median → tasks(operation="create") (materiality). EXECUTE — RECORD: include bank-feed memo verbatim in the memo field. EXECUTE — CLOSE OUT (required after every write): task items → tasks(operation="complete", taskNumber, qbTransactionId); DIRECT recordings (no task) → bankFeed(action="markRecorded", bankTransactionId, qbTransactionId). Unstamped items reappear in the next sweep and waste a full re-analysis. THINGS THAT ARE NOT WHAT THEY LOOK LIKE: (1) A FULL PAGE IS NOT THE WHOLE FEED — when the message says INCOMPLETE there are more transactions behind it; finish and stamp this page, then call again. (2) status="
customReportsSaved report definitions for the active company, run by name. QuickBooks has no API for its own saved custom reports, so this is where a user's "monthly pack", "board pack" or "weekly cash view" lives: a NAME, ordered SECTIONS (each a qbReports parameter set with a relative date rule) and INSTRUCTIONS for reading the result. No data is stored; every run reads QuickBooks live through qbReports, so a section's result is exactly what qbReports would return for those parameters. WHEN TO USE: the user names a report they have saved ("run my month-end pack", "what does the board pack look like for August") → operation="run". The user asks what reports exist → "list". The user wants to keep a report shape for reuse ("save this as...", "every month I want...") → "save". One-off reports still go straight to qbReports; save only what will be run again. DATE RULES resolve on every run against asOf (default: today on the server): month_to_date, last_month, quarter_to_date, last_quarter, year_to_date, last_year, fiscal_year_to_date, last_fiscal_year (both read the company's fiscal year start from QuickBooks), last_12_months (twelve full months ending last month), last_30_days, last_90_days, fixed (stored startDate/endDate), run_time (caller passes periodStart/periodEnd on each run). On an as-of report (BalanceSheet, the four agings, VendorBalance, CustomerBalance) the rule's end date is the as-of date, so a pack of ProfitAndLoss + BalanceSheet + AgedReceivables all on last_month lines up at month end. Pass periodStart/periodEnd on a run to override every section's dates ("run the pack for Q1"). COMPARISONS: a section with compare="prior_period" or "prior_year" is run twice and the comparison period comes back beside it; the run call can override with compare. Use it for the variance commentary the instructions usually ask for. SAVING: sections are validated with qbReports' own rules (a parameter the report type ignores is refused, so a saved report can never silently answer
closeRunManage month-end close workflow. firm_id is auto-resolved from the organization. OPERATIONS: - "read" — Get current close run status and results for a period (or the newest open one). - "start" — Begin a close for a period, or RESUME the one already there. REQUIRED: period (YYYY-MM) + periodLabel. Resuming keeps every check, entry and financial already recorded; pass restart:true to discard them and start the period over. - "updateStep" — Update a single workflow step (reconciliation, ap_ar, accruals, depreciation, statements, trial_balance). MERGES into existing steps by step id (does not replace other steps). - "updateChecks" — Update one or more of the 16 checklist items. MERGES into existing checks by check id and preserves portal-written resolution fields. Give every warning/action_needed check an "anchor" (pl:<line_id> | bs:<line_id> | tie:<tie_id>) so the Close Sheet can pin it to the statement line it affects; write "detail" in first person to the reviewer. Use kind:"observation" for FYI variance notes that should never block sign-off. - "addEntries" — Add proposed adjusting entries for reviewer review. APPENDS only; ids are auto-assigned and an id already on the run is refused. Include anchors (statement lines the entry moves), first-person reasoning, source_ref, and auto_reverse_on for accruals. - "setFinancials" — Set the financial summary AND statement_lines (line-level P&L + balance sheet from qbReports, stable snake_case line ids). statement_lines power the portal's Close Sheet — always emit them in the statements step. - "complete" — Mark the close run as needs_review and create the sign-off task. REQUIRED: score AND actionItems, both checked against the checks and entries on the run. THINGS THAT ARE NOT WHAT THEY LOOK LIKE - A SIGNED PERIOD IS LOCKED. Once a reviewer signs, every operation here is refused (PERIOD_LOCKED) — including start. The signature attests to the statements, checks and entries as they stood; corrections to a closed month belong
getGuideGet a structured step-by-step workflow guide for accounting tasks. transaction_recording is live: the universal recording protocol (lookup → memory → duplicate check → decide gate → tool selection → edge cases → record → attach & learn). month_end_closing is live: the close workflow and the Close Sheet data contract (anchors, statement_lines, observations, first-person details). reconciliation prepares statement-to-ledger matches and a workbook, with matching and final reconciliation completed in the QuickBooks UI. financial_analysis explains comparable figures and evidence-backed drivers. audit_preparation builds traceable supporting schedules and an evidence packet for review. error_recovery is live: a per-tool error playbook. Pass tool (e.g. tool:"qbSalesReceipt") AND the errorCode you got (e.g. errorCode:"6000") to get just that code's entry - a few hundred characters instead of the whole playbook. A pre-flight refusal that carries no errorCode can be looked up by pasting its message into errorCode. Omit errorCode for the full playbook, or pass topic to read one section. Call it whenever a tool returns a failure you are not sure how to handle. Workflow guides are loaded from the published Supabase library. A draft or missing guide returns GUIDE_NOT_AVAILABLE. A failed read or invalid stored guide returns GUIDE_FETCH_FAILED; never treat either as an empty checklist. READING THE RESULT: workflow guides answer in `steps`/`safetyChecklist`/`commonMistakes` and carry no `content`; error_recovery answers in `content` (markdown) and carries no steps, plus `codes` (every code the playbook covers), `recovery` (structured actions for matched errors), and `topics` (its narrative sections). On success:false branch on `errorCode` (TOOL_REQUIRED, NO_GUIDE_FOR_TOOL, NO_ENTRY_FOR_CODE, GUIDE_FETCH_FAILED, GUIDE_NOT_AVAILABLE, NO_TOPIC_FOR_GUIDE), never on the message. NO_ENTRY_FOR_CODE means the playbook loaded but covers no such code - read `codes` and ask again. Guide types: