Reports and tax
Reports, exports, insights, subscriptions and tax preparation.
71 actions
Give an AI assistant only these actions with the reports MCP toolset.
https://app.getoatmilk.com/api/mcp?toolset=reportscompany.shareholders
- POST
company.shareholders.exportDownload the shareholder register as a spreadsheet with full tax numbers, to file Schedule 50 or T5 slips, with a reason. Only an administrator in the dashboard can; the download is audited with the reason and the number of shareholders, never the numbers. Not available to API keys or MCP clients. - GET
company.shareholders.getRead the shareholder register: the classes of shares (common or preferred, voting or not), each shareholder (person, corporation or trust; address; resident in Canada or not) with their holdings (number of shares, class, issue date, certificate, what was paid, end date), directors and officers, shares outstanding by class, each holder's share of the common, preferred and voting shares, the T2 Schedule 50 rows (holders of 10% or more, with the tax number masked), how much of the vote Canadians hold, and names found in company documents that aren't in the register yet. Tax numbers are only ever masked. - POST
company.shareholders.taxNumberSave or remove a shareholder's tax number for Schedule 50 and T5 slips: { holderId, kind (sin, bn with an optional program account, itn, or foreign with country), value, country? } or value null to remove it. SINs are checked with their check digit. The number is stored encrypted and only ever returned masked; it is never logged or audited. - POST
company.shareholders.updateReplace the shareholder register with { register: { classes, holders, holdings, officers }, expectedRevision, idempotencyKey } (from company.shareholders.get, changed). Ids are 8 to 40 lowercase letters, digits and hyphens; shares are whole numbers; every holding names a holder and class in the register. Removing a shareholder removes their saved tax number. Audited with counts only.
handoff
- GET
handoff.exportExport a versioned professional handoff with stable record identifiers, unresolved items and provider control totals. Employee records require supporting originals access. Generates no ledger postings. - GET
handoff.getRead professional handoff, separate authorizations, filing confirmations and calendar-year payroll completeness. Prepared records never establish filing or create ledger postings. - POST
handoff.updateRecord one handoff item with its owner, source, state, original documents and current revision. Filing acceptance requires retained confirmation. Stores no full payroll identity numbers and creates no postings.
insights.finance
- GET
insights.finance.getRead gross income, expenses, refunds, profit or loss and category totals per currency for an accounting period, with reviewed and unresolved coverage. - GET
insights.finance.trendsRead up to 24 months of ledger income, expenses, profit or loss, net burn proxy and top vendors per currency, with period and review coverage.
insights.forecast
insights.map
- GET
insights.map.config.getRead availability of the interactive Google map and image export, plus its public referrer-restricted browser key. Private provider keys are never returned. - GET
insights.map.drilldownInspect up to 100 map records with the same filters, an optional returned clusterKey and an opaque nextCursor. Logical records and stable cursor paging preserve source access and missing-location coverage. - GET
insights.map.exportGenerate a private PNG of the permitted Insights Map filters and supplied viewport, with native map attribution. Return a short-lived private download URL; no accounting records change. - GET
insights.map.getRead a bounded geographic map of transactions, trips, saved hotel groups and saved event venues. Filter by layers, dates, transaction types, currency, search, explicit country/region/city, bounds and zoom; group by saved city/region or geographic grids. Counts include missing-location coverage, and transaction values stay separate by currency. Only locations and sources the actor can read are included; no geocoding or model calls. - GET
insights.map.viewCreate a safe local Insights Map URL from canonical filters and renderer center/selection. No financial records or saved locations change.
insights.people
insights.projects
insights.tax
reports
- POST
reports.exportCreate a CSV or evidence ZIP export. Supply format, date filters and idempotencyKey. Tax exports require completed fiscal settings. Exports identify provisional records and unresolved items. - GET
reports.summaryReturn accounting totals separated by currency and provisional or reviewed record counts.
subscriptions.duplicates
subscriptions
- GET
subscriptions.listList software subscription candidates from reviewed ledger charges, saved finance corrections, evidence-backed quantities, actual paid totals by calendar year and annualized estimates. Plans a person merged are combined within one currency, and possible duplicate plans are listed with the classifier's and the second opinion's views. A possibly stopped charge is not proof of cancellation. - POST
subscriptions.saveConfirm or correct one subscription's cadence, status, renewal, amount or explicitly evidenced seat or usage quantity. Requires expectedRevision and idempotencyKey. Does not change accounting entries or cancel a provider subscription. - GET
subscriptions.suggestOn demand, use the Classifier over the dates and amounts of up to 32 software purchase patterns to suggest recurring or one-off, without sending merchant names or receipt contents to the model. Returns uncertainty and coverage; no records change.
subscriptions.merges
- POST
subscriptions.merges.createMerge two or more plans in the same currency by hand, combining their history and yearly estimate into one plan. Requires an idempotencyKey. Changes no accounting entries and can be undone. - POST
subscriptions.merges.decideMerge suggested duplicate plans, or keep them separate so they are not suggested again, for one group or many at once. Requires each group's expectedRevision and an idempotencyKey. Changes no accounting entries and can be undone. - POST
subscriptions.merges.undoUndo the last decision on one merge group: a merge returns to a suggestion (or, when made by hand, the plans stay separate), and plans kept separate return to a suggestion. Requires expectedRevision and idempotencyKey.
tax.adjustments
tax.checks
tax.entries
tax
- POST
tax.exportCreate a private year-end package for { period, expectedSnapshotHash, idempotencyKey }. The core zip (url) holds a README, a summary PDF, the workpaper JSON and CSV, record listings and a hash manifest; corporate years add a draft income statement by GIFI line, a general ledger, the year-end questionnaire, contractor payments and, when registered, a GST/HST summary, and GST/HST periods add the return lines and the period's records instead. Original receipts, statements, Stripe records and tax documents come as separate originals parts (parts[], each under 40 MB, hashed in the manifest). Repeating the request with the same key returns the same package with fresh links. Requires the current snapshot hash. Nothing is filed. - GET
tax.readinessRead draft corporate or GST/HST tax workpapers, reviewed totals, unresolved records and preparation tasks. This does not file a return or calculate final T2 liability.
tax.filings
- GET
tax.filings.getRead a return or slips the company files itself, step by step: { kind: gst_hst (a GST/HST period), t4a (contractor T4A and T4A-NR slips for a calendarYear) or t2 (a corporate year), period? or calendarYear? (defaults to the newest one that ended) }. Returns the due dates (with weekend and holiday shifts), each walkthrough step and whether it's done, what was recorded (filed on, CRA confirmation number, paid on and amount, copies given), the matching compliance checklist items, the business number on file, the other periods to choose from and the official sources. The numbers to enter come from tax.prep.overview (GST/HST lines) and contractorOps.taxForms.get (slips). Oatmilk never files or pays anything. - POST
tax.filings.updateRecord progress on a return or slips the company files itself: { kind, periodKey (from tax.filings.get), expectedRevision, idempotencyKey, steps? ({ stepKey: true|false }), filedOn?, confirmation? (the CRA confirmation number), paidOn?, amountPaidMinor?, copiesSentOn?, note? }. Once filed (and paid, or the copies given), the matching compliance checklist items are marked done. It only records what the person did on the CRA's site; nothing is sent to the CRA.
tax.financialCounterparts
- POST
tax.financialCounterparts.clearA signed-in person clears a separate financial counterpart review using its current revision and current source fingerprint. Dashboard only. Preserves all underlying accounting records and earlier review evidence. - GET
tax.financialCounterparts.getRead an existing CAD transfer's current original bank evidence, full allocation proof and separately reviewed balance-sheet counterpart. Unsupported or changed sources remain unresolved. Read-only. - POST
tax.financialCounterparts.reviewA signed-in person explicitly confirms a supported CAD transfer's loan or common-share balance-sheet counterpart, using current entry/review revisions, source fingerprint, original evidence and reason. Dashboard only. Amount and direction come from the fully allocated bank source. Never changes purchases, tax, bank rows or allocations.
tax.gifi
- POST
tax.gifi.confirmConfirm a corporate year's tax lines (GIFI) for { period, snapshotHash, idempotencyKey } once every category with activity has a confirmed GIFI code and every record has a category: saves the financial statements and GIFI mapping checks the way the tax lines step does. Refused (INVALID_STATE) while a category still needs a code (tax.gifi.update) or a record has no category. A changed snapshotHash means records changed: read again. Nothing is filed. - GET
tax.gifi.getRead the categories with records in a tax period ({ period }), their reviewed CAD totals, suggested and confirmed GIFI codes from the CRA RC4088 index, the supported code list, the mapping revision and a draft income statement by GIFI line. Suggestions are never saved until confirmed. - POST
tax.gifi.updateConfirm GIFI codes for categories with mappings [{ categoryId, code }], the current mapping revision (expectedRevision) and an idempotency key. Codes must be in the supported GIFI list. Changes are audited and do not change any accounting record.
tax.packages
- POST
tax.packages.downloadDownload a ready package in Oatmilk: { packageId }. Returns a link that works for five minutes. Each download is audited. - GET
tax.packages.listList the year-end data packages asked for in the last 60 days, newest first: each one's fiscal year, status (queued and building while it's being put together, then ready for seven days, failed or expired), size, what's inside (reports, bank statements, Stripe, company documents, shareholder register, and anything left out), who it was sent to, whether each person's email went out, and every download (through a link or in Oatmilk, and whether the person was signed in). Read-only; the links themselves are only ever in the emails. - POST
tax.packages.requestAsk for one ZIP of everything an outside accountant needs for a fiscal year: { period? (a corporate year; defaults to the newest ended one), recipients? (up to 10 email addresses besides the person asking), note? (up to 500 characters, shown in the email), idempotencyKey }. It's put together in the background, usually within a few minutes; the person asking and each recipient then get an email with their own download link, which works without signing in for seven days. Each download is audited. Tell the person it's being put together and that the email will come when it's ready; check on it with tax.packages.list. - POST
tax.packages.revokeTurn off a package's download links: { packageId, linkId? (one person's link; leave it out to turn off every link), idempotencyKey }. The team can still download the package in Oatmilk until it expires. - POST
tax.packages.shareSend a package to more people: { packageId, emails (1 to 10 addresses), idempotencyKey }. Each new person gets their own link by email, at once if the package is ready, otherwise as soon as it is. Someone whose link was turned off gets a new one. A package can go to 25 people at most.
tax.personalExpenses
- POST
tax.personalExpenses.confirmConfirm (confirmed: true) or withdraw (false) 'No business expenses were paid personally' for { period }, with an optional note, the current confirmation revision (0 when none) and an idempotency key. Audited. - GET
tax.personalExpenses.exportThe personally paid expenses for { period } as a CSV for the accountant: { filename, csv }. - GET
tax.personalExpenses.getThe list the accountant asked for: business expenses paid personally in { period }, with date, amount, currency, category, merchant, who paid, receipt link, and whether each was reimbursed by Wise transfer, is still owed or was not claimed, plus totals and whether the company confirmed that none were paid personally.
tax.prep
tax.prizes
- POST
tax.prizes.events.saveCreate or edit a prize event (hackathon, competition, event) with its date, notes and the event or promo documents kept as evidence. Input { id?, expectedRevision, name, kind, heldOn?, notes, evidenceIds?, venue?, archived?, idempotencyKey }. Optional venue { lat, lon, label, address?, city?, region?, countryCode? } adds its location to Insights Map; countryCode is an explicit two-letter uppercase country code. Omit venue to keep it, or set null to remove it. Never infer a venue from an event name. - GET
tax.prizes.overviewPrize payouts for a calendar year (default: the latest with payouts): events, winners with masked details and intake link state, confirmed payouts, suggestions detected from outgoing transfers (never applied automatically), the per-winner total against the $500 T4A box 028 threshold with a plain status (under $500, T4A needed, waiting for winner details, ask your accountant), sponsorship invoices shown separately, and the checklist for the CRA RZ account and the filing deadline. Optional eventId includes that active workspace event when opening an older map result, without changing totals or other source counts. No HST applies to prizes. Nothing is sent or filed. - POST
tax.prizes.payouts.decideDecide about one outgoing transfer or record: op record confirms it as a prize (optionally to a winner and event), dismiss says it wasn't a prize, update changes its winner, event, note or returned amount, undo puts it back to a suggestion. Never sends money. - POST
tax.prizes.recipients.linkIssue a fresh winner details link, send a reminder (at most every 12 hours and six times), or copy the current link ({ recipientId, mode: issue|remind|copy }). Copying and issuing without an email return the private link and work in the signed-in dashboard only. - POST
tax.prizes.recipients.saveAdd a winner (individual or business), edit them, or archive them. With sendLink and an email address Oatmilk emails the winner a private, account-free link for their legal name, SIN or business number and mailing address, before any payment. The email never contains a number. - POST
tax.prizes.scheduleThe T4A summary schedule for a calendar year { year } as a CSV: winner, legal name, SIN or business number, address, amount, box 028 and payment dates. Numbers are masked. reveal { reason } shows full numbers to an administrator in the dashboard or to the accountant, and is audited with the reason.
tax.questions
- POST
tax.questions.answerAnswer one year-end question of a corporate tax year: { period, question, answer: yes|no|not_sure, choice?, choice2?, text?, amount?, note?, evidenceIds?, snapshotHash, idempotencyKey }. tax.questions.list names the follow-up fields each answer needs; amounts are in dollars ("1250.00") and files are evidence ids, from tax.sources.confirm or an original already kept with a record. Oatmilk checks the answer as the questions step does, words it the same way and saves it as the question's tax checks at their current revisions, exactly like tax.checks.update. not_sure leaves the question open for the accountant. Returns the saved checks. A changed snapshotHash means records changed: read the questions again. Nothing is filed. - GET
tax.questions.listRead the year-end questions of a corporate tax year ({ period }), the questions step of tax preparation: each question's key, title, question and plain hint; the answers it takes (yes, no, not_sure) with what each one means and the follow-up fields it asks for (choice and choice2 with their options, text, amount in dollars, note, files); the saved answer read back from the tax checks, open (left for the accountant) or stale (records changed after it was saved); Oatmilk's suggested answer from the books where it has one; progress; and the snapshotHash to answer with. Read-only.
tax.reports
- POST
tax.reports.downloadDownload those reports for { period, format (xlsx default, or pdf) } and either report (everything, financial_statements, trial_balance, balance_sheet, income_statement, general_ledger, gst_hst, stripe, contractors) or reports, a list of the ones a person picked (balance_sheet, income_statement, trial_balance, general_ledger, gst_hst, stripe, contractors), which come as one file. The period can be any fiscal year from tax.reports.overview or any dates up to a year apart. Excel workbooks have one sheet per report with formulas for the totals; everything is the whole workbook. Returns a short-lived download. Each request builds a fresh file and is audited. - GET
tax.reports.overviewRead the reports an accountant asks for at year-end, for { period? (defaults to the newest ended fiscal year) }: the trial balance with debit and credit columns, the balance sheet and income statement (draft, from every record, in CAD, by account and GIFI line), plus GST/HST collected and claimed and money set aside for it, Stripe charges before fees with the fees and payouts, contractors and the T4A slips they likely need, key dates (balance due, GST/HST, T2, T4A) and the company documents on file. Also lists the fiscal years. Read-only.
tax.requests
- POST
tax.requests.addAdd one item to the accountant's checklist by hand, to a new request or to an existing one (requestId). Input { requestId?, kind, title, detail?, idempotencyKey }. - POST
tax.requests.extractTurn the text of an accountant's email into checklist items with a strict-schema AI extraction. The text is untrusted: instructions inside it are ignored, no tools run, only the accountant's short quotes are kept, and nothing is sent or changed besides the new checklist. Input { text, receivedOn?, from?, to?, idempotencyKey }. - GET
tax.requests.listList what the accountant asked for as a checklist. Each item has a status (open, ready or sent), a link to where Oatmilk fulfils it, and whether Oatmilk already holds the answer (statements, personally paid expenses, payroll answer, accounting access). Also returns the one year-end question when the accountant names a year-end that differs from the company's. Input { from?, to? } picks the period whose facts decide what is ready. - POST
tax.requests.updateChange a checklist item ({ op: item, itemId, expectedRevision, status?: open|ready|sent|dismissed, note?, evidenceIds? }) or record how the year-end question was answered ({ op: year_end, requestId, expectedRevision, resolution: kept|changed }). Changing the company's year-end itself uses company.update.
tax.sources
- POST
tax.sources.confirmVerify uploaded company/tax source original bytes and preserve evidence without creating accounting entries. Returns an evidence ID for source references. - POST
tax.sources.preparePrepare an immutable private PDF or photo company/tax source upload. Returns a signed upload URL; this does not create an expense or run AI.
tax.statements
- POST
tax.statements.acknowledgeTell the accountant that an account's statement gap is known and nothing more can be done ({ period, accountId, acknowledged, note (required, plain words such as 'The card wasn't used this year'), expectedRevision (0 when new), idempotencyKey }). The account stops counting as needing attention, the gap stays listed for the accountant with the note, and the answer is audited. acknowledged false withdraws it. - GET
tax.statements.overviewRead every bank, card and payment account's statement bundle for a period { period }: opening and closing balance as of the period end, transaction counts and totals, the original statement files kept, and a plain-words completeness check per account (no statements, a late start, an early end, a missing month, or balances that don't agree with the imported lines). Read-only. - POST
tax.statements.packBuild the statements pack for { period }: one folder per account with its balances, a CSV and a PDF transaction listing for the period, and the original statement files kept for it, plus an index and a completeness summary. Returns a short-lived download. Each request builds a fresh pack and is audited.
tax.treatment
- POST
tax.treatment.applyConfirm a whole group of records at once for { period, code, entryIds, idempotencyKey }: each record listed that is still in the group gets the treatment Oatmilk suggests for that reason (for example no tax credit for purchases without a receipt), saved as a tax review in the caller's name and marked as confirmed together. Records that changed or are no longer in the group are skipped and reported. The closed-period and revision guards still apply. - POST
tax.treatment.autoSettle the sales tax of every record in a tax period that the rules can prove, as Autopilot, for { period, snapshotHash, pass, idempotencyKey }: bank fees, interest, payments to companies outside Canada or to contractors, small purchases with no receipt (no credit claimed) and receipts whose tax adds up. Each is saved as the same tax review a person would save, marked automatic with its plain reason, and can be undone. Records a person reviewed or undid, and records that changed, are left alone. Returns { applied, skipped, remaining }. - POST
tax.treatment.undoUndo a tax review that Autopilot or a group confirmation saved, for { entryId, expectedRevision, idempotencyKey }: the record goes back to open and Autopilot leaves it alone until it changes. A review a person saved cannot be undone this way. Refused in a closed period.