Skip to content

MCP Server

DayZero exposes a Model Context Protocol (MCP) server that lets AI assistants — Claude, ChatGPT, Cursor, and other MCP-compatible clients — interact with your accounting data through natural language.

The server is mounted at /mcp and uses the Streamable HTTP transport (stateless: each request stands alone, with no server-side session). Authentication uses OAuth 2.1 — your client discovers the auth server, registers itself, and opens a browser for you to sign in to DayZero and approve access. No tokens to copy.

Just want to connect? Claude users can click Add DayZero to Claude — claude.ai opens the connector dialog prefilled; review it, click Add, and sign in. Every other client: add https://mcp.ondayzero.com/mcp with no credentials and you'll be prompted to sign in to DayZero in your browser and approve access. See Quick Connect below.

Note: REST API keys (dz_…) from Settings → Developers authenticate the REST API only. They do not work on the MCP server — use the OAuth browser flow instead.

Quick Connect

The fastest path per client. Every row uses the same server URL — https://mcp.ondayzero.com/mcp — and ends in the same browser OAuth sign-in. Settings → Developers inside DayZero has one-click Add to Claude / Add to Cursor buttons and copy-paste snippets for the rest.

Client Fastest path
Claude (web, desktop & mobile) Add DayZero to Claude — one click, prefilled connector dialog
Claude Code claude mcp add --transport http dayzero https://mcp.ondayzero.com/mcp — see Connecting Claude Code
ChatGPT Enable Developer mode, then add the URL as a connector — see Connecting ChatGPT
Codex (CLI, IDE extension & ChatGPT desktop app) Two lines in ~/.codex/config.toml — see Connecting Codex
Cursor Settings → MCP Servers → Add with the URL above, or the Add to Cursor button in DayZero — see Connecting Cursor

On Claude Team/Enterprise plans only org owners can add custom connectors — send your admin the same link on the admin path:

text
https://claude.ai/admin-settings/connectors?modal=add-custom-connector&connectorName=DayZero&connectorUrl=https%3A%2F%2Fmcp.ondayzero.com%2Fmcp

Domain Profiles For Power Users

DayZero keeps the unified server at /mcp for general use and also exposes focused domain profiles for multi-server MCP setups. Each mount below core is one domain: the reads and the writes of the same records live together.

  • https://mcp.ondayzero.com/mcp/core (dayzero-core) — the default /mcp surface as a mount: business picker, daily brief, the AR and AP happy paths, transaction review, close status, reports
  • https://mcp.ondayzero.com/mcp/ar (dayzero-ar) — invoices from draft to paid, customers, credit memos, the AR snapshot
  • https://mcp.ondayzero.com/mcp/ap (dayzero-ap) — bills, vendors, vendor credits, AP approval routing, payment runs, aged payables
  • https://mcp.ondayzero.com/mcp/inbox (dayzero-inbox) — the Bill Inbox: mail at the business's intake address (get_business_info.inbox_email), what reading each attachment found, drafts turned into bills, emailed receipts filed on their transactions
  • https://mcp.ondayzero.com/mcp/banking (dayzero-banking) — transactions (categorize, split, bulk edit), bank accounts and balances, bank rules, receipts, transaction uploads
  • https://mcp.ondayzero.com/mcp/counterparties (dayzero-counterparties) — naming rules and renames, merges, counterparty default ledgers, the counterparty registry
  • https://mcp.ondayzero.com/mcp/ledger (dayzero-ledger) — chart of accounts, journal entries (including bulk upload and recurring), fixed assets and depreciation, prepaid amortization and tags
  • https://mcp.ondayzero.com/mcp/close (dayzero-close) — close status and verification, accounting periods and sign-off, close packages and the publication gate, anomalies, the close-run tracker
  • https://mcp.ondayzero.com/mcp/recon (dayzero-recon) — bank reconciliations, AI-match review, payment matching, Stripe payouts, external tie-outs
  • https://mcp.ondayzero.com/mcp/reports (dayzero-reports) — financial statements, generated reports, analytics, budgets, forecasts, models, exports, audit trails
  • https://mcp.ondayzero.com/mcp/o2c (dayzero-o2c) — order to cash: pipeline deals, sales orders, customer contracts, job-costing projects, and bulk import for each
  • https://mcp.ondayzero.com/mcp/commerce (dayzero-commerce) — products, standard costs and COGS, inventory, Shopify, invoice posting ledgers
  • https://mcp.ondayzero.com/mcp/people (dayzero-people) — the people roster, org chart and payroll reconciliation
  • https://mcp.ondayzero.com/mcp/credit (dayzero-credit) — the partner-credit book, credit lines and loans (read-only)
  • https://mcp.ondayzero.com/mcp/automations (dayzero-automations) — workflows: the graph schema (describe_workflow_schema) and the tools a node may call, drafting, validating, testing and publishing (publishing needs a passing test's test_run_hash and the user's approval; runs execute the published snapshot), running on DayZero's servers with an idempotency_key, polling a run, answering its approval steps, schedules
  • https://mcp.ondayzero.com/mcp/workers (dayzero-workers) — the agent workforce: the business's agent roles (list_agent_roles), their runs with what each agent is doing now and what waits on a person (list_agent_runs, get_agent_run), and handing a role a job, answering its question or stopping it (start_agent_run, answer_agent_run, stop_agent_run); a run's own writes are filed as proposed changes. Also SOPs from chat: the SOPs worth writing for the business (suggest_sops), drafting and editing one (create_sop, update_sop), setting it live, draft or retired with the person's approval (set_sop_status), and starting its team (build_team_from_sop; a person installs it on the SOP page)
  • https://mcp.ondayzero.com/mcp/agents (dayzero-agents) — what this connection may do (get_connection_info: email, scopes, write access, bound business, staff grant), pending changes and approval rounds, the decision log, background jobs, client-portal requests and messages

Each profile keeps tools/list scoped to that domain (at most 40 tools, under strict hosted-client limits). Every mount also lists search_tools, call_tool, confirm_write, get_job_status, list_ledgers, upload_file and generate_upload_url, so a tool another mount carries is one search_tools + call_tool(name, arguments) away; call_tool applies the same permissions, dry_run and approval gate as calling the tool directly. Claude clients (claude.ai, Claude Code, Claude desktop) load tool schemas on demand, so on /mcp they are listed every tool they can call. Self-hosted deployments can force one profile process-wide with MCP_DOMAIN_PROFILE (for example MCP_DOMAIN_PROFILE=dayzero-ar).

Multi-Server Setup Examples

Use multiple MCP server entries when you want separate focused tool surfaces in one client session.

Claude Desktop (claude_desktop_config.json):

json
{
  "mcpServers": {
    "dayzero-core": {
      "url": "https://mcp.ondayzero.com/mcp/core"
    },
    "dayzero-ar": {
      "url": "https://mcp.ondayzero.com/mcp/ar"
    },
    "dayzero-ap": {
      "url": "https://mcp.ondayzero.com/mcp/ap"
    },
    "dayzero-reports": {
      "url": "https://mcp.ondayzero.com/mcp/reports"
    },
    "dayzero-banking": {
      "url": "https://mcp.ondayzero.com/mcp/banking"
    },
    "dayzero-o2c": {
      "url": "https://mcp.ondayzero.com/mcp/o2c"
    },
    "dayzero-recon": {
      "url": "https://mcp.ondayzero.com/mcp/recon"
    }
  }
}

Cursor: add one MCP server per profile in Settings → MCP Servers (same URLs above). Cursor will run OAuth for each server entry and keep tools separated by server.

ChatGPT: create one connector per profile under Settings → Connectors → Create using the profile URLs above.

GitHub Copilot / VS Code MCP: add each profile as a separate server in your MCP configuration (same URLs above) so tool discovery stays focused and below hosted limits. Add only the profiles you need; the example above is a starting set, and every other profile in the list works the same way.

What You Can Do

The MCP server exposes one tool per job across accounting domains; the Full tool reference below is generated from the live registry and lists every tool with its access level and approval gate. The table here groups the main workflows. Tools marked (write) mutate data and require write permission; everything else is a read-only query. Call search_tools with no query for a compact category map, or with a query to rank the tools for a job.

Category Highlights Description
Discovery list_my_businesses, find_business, get_business_info, get_connection_info, search_tools, search_entities, search / fetch (ChatGPT only) Find businesses and entities without knowing IDs upfront; find_business fuzzy-searches by name; get_connection_info reports the connection's scopes, write permission and bound business
Banking list_bank_accounts, get_bank_balances, refresh_bank_balances (write) See connected bank accounts and current/available balances
Snapshots get_daily_brief, get_ar_snapshot, get_aged_payables, get_ap_snapshot, get_payment_priority_suggestions, get_uncategorized_summary, get_month_end_package High-signal summaries — prefer these over paging huge lists
Chart of Accounts list_ledgers, get_ledger, create_ledger, bulk_create_ledgers (write) Browse and inspect ledger accounts; add accounts one at a time or a whole list in one call (e.g. mirroring another system's chart before importing its history)
Transactions list_transactions, get_transaction, update_transaction, mark_reviewed, categorize_transaction, recategorize_by_counterparty, mark_transactions_reconciled (write) Search, inspect, categorize, and mark bank lines reconciled
Invoices (AR) create_invoice, update_invoice, finalize_invoice, send_invoice, mark_invoice_paid, void_invoice, list_invoice_payments, get_customer_statement, credit memos, payments (write) Full AR lifecycle, invoice payment history, and customer statement previews
Bills (AP) create_bill, approve_bill, mark_bill_received, update_bill, cancel_bill, list_bill_payments, vendor credits, payments (write) AP approval, receive, payment workflows, and bill payment history
Order-to-Cash list_deals, create_deal, update_deal, convert_deal_to_order, list_orders, create_order, update_order, advance_order_status, attach_order_document, list_contracts, create_contract, update_contract, link_invoice_to_contract, plus bulk_create_* / bulk_update_* for each (write except list/get) The whole pre-invoice chain: pipeline deal → sales order → countersigned contract → invoice. advance_order_status(target_status="countersigned") is what turns an order into a live contract. Working from a signed PDF: upload_file (base64 content) → extract_document_entities to read it → create_order with the line items → attach_order_document with the same s3_key. For more than a couple of rows always use the bulk_* variant — running them one at a time races on shared contract locks
Projects (job costing) list_projects, get_project, create_project, update_project, bulk_create_projects, bulk_update_projects (write except list/get) Jobs that orders, contracts and invoices hang off; assign invoices to them with bulk_assign_invoices
Journal Entries list_journal_entries, get_journal_entry, create_journal_entry, update_journal_entry (write) Read entry lists, inspect one entry, create journal entries, and fix an entry in place (the "fix the original entry" rule)
Customers & Vendors list_*, get_*, create_*, update_*, export_dataset, bulk_create_vendors, bulk_create_customers (write) CRUD-style contact management plus CSV export/import
Counterparties search_counterparty_registry, link_counterparty, unlink_counterparty, list_counterparty_defaults, export_counterparty_audit, create_counterparty_default, update_counterparty_default, delete_counterparty_default, bulk_create_counterparty_defaults, upload_file, generate_upload_url (write for link/unlink, default rules, and uploads) Registry search, link/unlink, default rules (merchant → ledger), audit export, and CSV bulk import (use upload_file for the s3_key)
Credit Memos & Vendor Credits list_*, get_*, create_*, issue_*, apply_*, unapply_*, void_* (write) Full credit lifecycle for AR credit memos and AP vendor credits — create, issue, apply to / unapply from invoices and bills, and void
Tags, Budgets, Products list_tags, list_budgets, get_budget, get_budget_vs_actual, list_products Supporting master data
Close & Quality get_close_status, get_close_verification, list_anomalies, dismiss_anomaly, list_attention_items, mark_attention_read, month-end packages DayZero close intelligence: phased checklist, readiness score, explicit blockers, plus anomalies/inbox workflows
Periods & close sign-off create_accounting_period, list_accounting_periods, get_accounting_period_settings, update_accounting_period_settings, record_period_sign_off, reopen_accounting_period (write except list/get) Open periods, record sign-offs, and reopen a closed period
Decisions & proposed changes record_decision, list_decisions, update_decision, delete_decision, create_pending_change, list_pending_changes, get_pending_change (write except list/get) A log of dated rulings the categorizer follows, and a way to queue a previewed write for a person to approve in the app
Fixed assets & GL maintenance create_fixed_asset, list_fixed_assets, get_fixed_asset, update_fixed_asset, place_asset_in_service, run_depreciation, dispose_asset, create_asset_category, amortize_transaction, create_recurring_journal_entry, list_recurring_templates (write except list/get) Depreciation, prepaid amortization and recurring entries (the ledger profile)
Audit & Oversight get_entity_audit_trail, get_user_activity, get_activity_feed, analyze_audit_log Change history for an entity, what a user did ("what did Quincy do today?"), the business-wide activity feed, and AI analysis of unusual activity
Firm-wide get_firm_client_overview, get_firm_user_activity For advisory-firm users: cross-client portfolio/AR-AP/work-queue overviews and one user's activity across all the firm's clients
Reports list_report_types, generate_report, get_report, list_reports, get_financial_statement, email_report, export_dataset P&L, balance sheet, and other exports, plus whole-dataset pulls
Async Jobs get_job_status, list_jobs, start_background_job, trigger_bulk_transaction_upload, trigger_bulk_je_upload, trigger_month_end_close (write except get_job_status and list_jobs) Start long-running workflows immediately and poll by job id
Reconciliation list_reconciliations, get_reconciliation, start_reconciliation, get_unmatched, get_match_review, approve_match, reject_match, bulk_approve_matches, bulk_reject_matches, complete_reconciliation, download_reconciliation_pdf (write for start/approve/reject/complete) The full bank reconciliation workflow: start_reconciliation takes an uploaded statement (statement_s3_key) or source_type="plaid" for a bank feed with no file; review AI-suggested matches side by side, approve or reject them, and finish with the PDF
Analytics get_financial_overview, get_top_expenses, get_cash_flow_totals, get_profit_trends, get_transaction_summary, compare_periods Server-side aggregations for analytical questions
Payroll, Shopify & models apply_gusto_reconciliation, check_shopify_connection, get_shopify_posting_summary, import_model_workbook, build_model, refresh_model Gusto payroll reconciliation, Shopify posting checks, and importing an Excel model as a live dashboard
Credit (read-only) get_credit_portfolio, list_credit_lines, get_credit_line, list_credit_loans The partner-credit book for funds and lenders; originations and payments stay on the REST API

Modules the app has switched off (estimates, delayed charges, tax settings and payment terms, among others) have no MCP tools. Personal Gmail is not available over MCP either: a linked mailbox can be searched only by Regent in the DayZero app (see QuickBooks, Meta Ads, Gmail and MCP Connections).

You don't need to know your business ID upfront. The business_id parameter is optional on every tool — the server automatically resolves it:

  • One business — selected automatically, zero friction.
  • Multiple businesses — clients that support elicitation show a scope picker (firm-wide vs a specific business) as a native dropdown; other clients get your business list so the assistant can ask which one to use. Firm users with many clients can call find_business("name") to fuzzy-search for the right ID.
  • Explicit ID — you can always pass a business_id directly if you prefer.

For cross-client questions ("across all our clients"), advisory-firm users should use the firm-wide tools (get_firm_client_overview, get_firm_user_activity) instead of looping per business.

The assistant remembers your selection for the rest of the conversation. Say "switch to [business name]" at any time to change.

Note: All monetary amounts are in cents (e.g. $150.00 = 15000). IDs are UUID v7 strings. Pagination is cursor-based — pass the next_cursor value from a response as cursor to get the next page. All date parameters use YYYY-MM-DD format.

Every tool marked write in the Full tool reference requires the write permission (a viewer-role token cannot call them): journal entries, transactions, bills, invoices, payments, contacts, counterparty registry links, counterparty default rules, vendor/customer exports and bulk imports, credit memos, vendor credits, reconciliation, anomalies, attention, file uploads (upload_file, generate_upload_url), and more. The assistant should confirm details with you before calling them.

Bulk imports (end-to-end): call upload_file(filename, content) with the file inline (text, or encoding="base64" for xlsx/pdf/images; up to 5 MB) to get an s3_key, then call bulk_create_vendors, bulk_create_customers, or bulk_create_counterparty_defaults (or the trigger_bulk_* uploads) with that s3_key. For a file over 5 MB, call generate_upload_url(filename) instead and send the file with an HTTP POST (outside MCP, e.g. curl -F) to the returned upload_url.

Every write tool accepts an optional dry_run=true parameter that returns a preview of the change without saving it. When a preview reports a plan_hash, pass that plan_hash back on the apply call. If the books moved between preview and apply (a sync landed, someone edited a row), the apply is refused as stale and you preview again. create_pending_change takes the same plan_hash so a person approving in the app is approving exactly what was previewed.

Journal entry drafts and standards:

  • A draft for a human to post is create_pending_change(tool_name="create_journal_entry", arguments={...}, preview_payload=..., summary=...). The approver applies it under their own identity, so the entry's created_by is the approver. The arguments are the assistant tool's own shape, signed cents (debit positive, credit negative), with the date given explicitly: {"entry_date": "2026-09-30", "description": "Clearco $300K loan - interest accrual Sep 2026", "line_entries": [{"ledger_id": "<interest expense>", "amount_cents": 125000}, {"ledger_id": "<accrued interest>", "amount_cents": -125000}]}.
  • An entry allows one line per ledger and side. create_journal_entry(combine_duplicate_lines=true) sums lines sharing a ledger, side and counterparty (descriptions joined with "; ") and the result reports how many were combined; without it the error names the ledger and side.
  • Called directly, create_journal_entry takes date, description and line_items of {ledger_id, amount_cents, type} (optional description per line), where amount_cents is a positive integer and type is debit or credit; debits must equal credits. update_journal_entry edits an entry in place (description, entry_date, line_updates or line_entries with signed amount_cents, debit positive and credit negative) and refuses an entry in, or moved into, a closed period.
  • create_journal_entry and update_journal_entry return warnings (never errors) when the description is empty, only an id such as "JE 15845", or has no month or year. Use " - ".
  • list_journal_entries filters by created_by_user_id and linked_to (bill, invoice, credit_memo, vendor_credit, none); ask for fields such as invoice_id, bill_ids, credit_memo_id, vendor_credit_id, created_by_user_id to see the links and creator. Entries made before attribution was recorded have no creator.
  • Reversal is not an MCP tool: use update_journal_entry to fix an entry in place, or the REST reverse route (POST /api/v1/journal-entries/{id}/reverse), which requires reversal_date (YYYY-MM-DD) and never defaults to today.

Full tool reference

Auto-generated from the live MCP registry: 417 tools (216 write, 165 approval-gated, 42 exposed by default). Regenerate with make mcp-manifest.

Tool Access Approval gate Default set
accept_payment_suggestion write confirm —
add_feedback_comment write — —
advance_order_status write confirm —
amortize_transaction write confirm —
analyze_audit_log read — —
answer_agent_run write — —
apply_bank_rules write confirm —
apply_counterparty_defaults_to_categorized write confirm —
apply_counterparty_naming_rule write confirm —
apply_counterparty_payee_lines write confirm —
apply_credit_memo write confirm —
apply_gusto_reconciliation write confirm —
apply_standard_cogs write — —
apply_stripe_payout write confirm —
apply_vendor_credit write confirm —
approve_bill write confirm ✓
approve_match write confirm —
approve_workflow_step write confirm —
attach_inbox_receipt write confirm —
attach_order_document write confirm —
attach_transaction_receipt write confirm —
backfill_customer_ar_subledgers write confirm —
book_held_categories write confirm —
build_model write — —
build_team_from_sop write — —
bulk_accept_payment_suggestions write confirm —
bulk_approve_matches write confirm —
bulk_assign_invoices write confirm —
bulk_create_contracts write confirm —
bulk_create_counterparty_defaults write confirm —
bulk_create_customers write confirm —
bulk_create_deals write confirm —
bulk_create_invoices write confirm —
bulk_create_ledgers write confirm —
bulk_create_orders write confirm —
bulk_create_projects write confirm —
bulk_create_vendors write confirm —
bulk_delete_draft_invoices write confirm —
bulk_edit_transactions write confirm —
bulk_mark_invoices_paid write confirm —
bulk_reject_matches write confirm —
bulk_update_contracts write confirm —
bulk_update_counterparty write confirm —
bulk_update_deals write confirm —
bulk_update_invoices write confirm —
bulk_update_projects write confirm —
call_external_read_tool read — —
call_external_write_tool write confirm —
call_tool write — ✓
cancel_bill write confirm —
cancel_workflow_run write confirm —
categorize_transaction write confirm ✓
check_ap_approval_health read — —
check_shopify_connection read — —
compare_ar_snapshots read — —
compare_external_bill_status read — —
compare_external_trial_balance read — —
compare_invoices_across_businesses read — —
compare_periods read — —
complete_reconciliation write confirm —
confirm_write write — ✓
connect_shopify_store_with_token write confirm —
convert_deal_to_order write confirm —
convert_inbox_bill write confirm —
convert_inbox_bills write confirm —
create_accounting_period write — —
create_asset_category write confirm —
create_bank_rule write confirm —
create_bill write confirm ✓
create_contract write confirm —
create_counterparty_default write confirm —
create_counterparty_naming_rule write confirm —
create_credit_memo write confirm —
create_customer write confirm —
create_deal write confirm —
create_document_request write — —
create_feedback write — —
create_feedback_linear_ticket write — —
create_fixed_asset write confirm —
create_invoice write confirm ✓
create_journal_entry write confirm —
create_ledger write confirm —
create_or_update_person write confirm —
create_order write confirm —
create_pending_change write — —
create_project write confirm —
create_recurring_journal_entry write confirm —
create_recurring_templates_from_detection write confirm —
create_sop write — —
create_transaction_clarification write — —
create_vendor write confirm ✓
create_vendor_credit write confirm —
create_workflow write — —
delete_bank_rule write confirm —
delete_counterparty_default write confirm —
delete_counterparty_naming_rule write confirm —
delete_decision write confirm —
delete_invoice write confirm —
delete_saved_rename write confirm —
delete_workflow write confirm —
describe_workflow_schema read — —
diff_transaction_export read — —
diff_trial_balance read — —
dismiss_anomalies_by_period write confirm —
dismiss_anomaly write confirm —
dispose_asset write confirm —
download_reconciliation_pdf read — —
email_report write — —
enrich_transactions_from_export write confirm —
export_counterparty_audit read — —
export_dataset write confirm —
extract_document_entities read — —
extract_statement_transactions read — —
fetch read — ✓
finalize_invoice write confirm ✓
find_business read — ✓
find_counterparty_inconsistencies read — —
firm_capital_overview write — —
generate_report write — ✓
generate_upload_url write — ✓
generate_workflow_graph read — —
get_accounting_period_settings read — —
get_activity_feed read — —
get_aged_payables read — —
get_agent_run read — —
get_ap_approval_settings read — —
get_ap_snapshot read — ✓
get_approval_round read — —
get_ar_snapshot read — ✓
get_automation_settings read — —
get_bank_balances read — —
get_bank_rule_health read — —
get_bank_rule_transactions read — —
get_bill read — ✓
get_budget read — —
get_budget_vs_actual read — —
get_bulk_je_template read — —
get_business_info read — —
get_cash_flow_totals read — —
get_cash_forecast read — —
get_client_portal_attention read — —
get_close_package_data read — —
get_close_publication_gate read — —
get_close_replay read — —
get_close_status read — ✓
get_close_verification read — ✓
get_connection_info read — ✓
get_contract read — —
get_counterparty_link_settings read — —
get_credit_line read — —
get_credit_memo read — —
get_credit_portfolio read — —
get_customer read — —
get_customer_balance read — —
get_customer_cash_receipts read — —
get_customer_statement read — —
get_daily_brief read — ✓
get_deal read — —
get_entity_audit_trail read — —
get_feedback read — —
get_feedback_attachment_url read — —
get_feedback_dom_snapshot read — —
get_feedback_screenshot read — —
get_feedback_stats read — —
get_financial_attestation read — —
get_financial_overview read — —
get_financial_statement read — —
get_firm_client_overview read — ✓
get_firm_user_activity read — —
get_fixed_asset read — —
get_inbox_email read — —
get_inventory_book_vs_physical read — —
get_inventory_gl_reconciliation read — —
get_inventory_gl_repair_plan read — —
get_inventory_valuation read — —
get_invoice read — ✓
get_invoice_create_requirements read — ✓
get_invoice_posting_settings read — —
get_job_status read — ✓
get_journal_entry read — —
get_ledger read — —
get_margin_by_sku read — —
get_match_review read — —
get_meta_ad_spend read — —
get_month_end_package read — —
get_order read — —
get_org_chart read — —
get_payment_priority_suggestions read — —
get_payment_run read — —
get_pending_change read — —
get_profit_trends read — —
get_project read — —
get_quickbooks_sync_status read — —
get_receipt_coverage read — —
get_reconciliation read — —
get_reconciliation_balance read — —
get_recurring_template read — —
get_report read — —
get_shopify_fees read — —
get_shopify_payout read — —
get_shopify_posting_gaps read — —
get_shopify_posting_summary read — —
get_shopify_refunds read — —
get_shopify_sales_report read — —
get_shopify_sales_tax read — —
get_shopify_sync_status read — —
get_sop read — —
get_subledger_gl_reconciliation read — —
get_suggested_matches read — —
get_top_expenses read — —
get_transaction read — ✓
get_transaction_summary read — —
get_trust_scorecard read — —
get_uncategorized_summary read — ✓
get_unmatched read — —
get_upload_headers read — —
get_user_activity read — —
get_vendor read — —
get_vendor_balance read — —
get_vendor_credit read — —
get_workflow read — —
get_workflow_run read — —
grade_close_replay write — —
import_model_workbook read — —
import_ramp_receipts write confirm —
import_stripe_payout_export write confirm —
issue_credit_memo write confirm —
issue_vendor_credit write confirm —
keep_bank_connection write confirm —
link_bill_payment write confirm ✓
link_counterparty write confirm —
link_invoice_payment write confirm ✓
link_invoice_to_contract write confirm —
list_accounting_periods read — —
list_agent_roles read — —
list_agent_runs read — —
list_anomalies read — —
list_ap_approval_queue read — —
list_approval_rounds read — —
list_asset_categories read — —
list_attention_items read — —
list_auto_approvals read — —
list_bank_accounts read — —
list_bank_rule_conflicts read — —
list_bank_rules read — —
list_bill_payments read — —
list_bills read — ✓
list_budgets read — —
list_close_steps read — —
list_contracts read — —
list_counterparty_defaults read — —
list_counterparty_naming_rules read — —
list_counterparty_payee_lines read — —
list_credit_lines read — —
list_credit_loans read — —
list_credit_memos read — —
list_customers read — ✓
list_deals read — —
list_decisions read — —
list_document_requests read — —
list_duplicate_bank_accounts read — —
list_external_tools read — —
list_feedback read — —
list_firm_conversations read — —
list_fixed_assets read — —
list_inbox_emails read — —
list_invoice_payments read — —
list_invoices read — ✓
list_jobs read — —
list_journal_entries read — —
list_ledgers read — ✓
list_month_end_packages read — —
list_my_businesses read — ✓
list_notifications read — —
list_orders read — —
list_pending_changes read — —
list_people read — —
list_products read — —
list_projects read — —
list_quickbooks_sync_issues read — —
list_reconciliations read — —
list_recurring_templates read — —
list_report_types read — ✓
list_reports read — —
list_saved_renames read — —
list_shopify_disputes read — —
list_shopify_orders read — —
list_shopify_payouts read — —
list_sops read — —
list_standard_costs read — —
list_stripe_payout_matches read — —
list_subscriptions read — —
list_suspect_transfers read — —
list_tags read — —
list_transaction_clarifications read — —
list_transactions read — ✓
list_uncosted_skus read — —
list_vendor_approval_routes read — —
list_vendor_credits read — —
list_vendors read — ✓
list_workflow_runs read — —
list_workflow_versions read — —
list_workflows read — —
manage_workflow_schedule write confirm —
mark_all_attention_read write confirm —
mark_attention_read write confirm —
mark_bill_received write confirm ✓
mark_invoice_paid write confirm —
mark_not_a_transfer write confirm —
mark_reviewed write — —
mark_transactions_reconciled write confirm —
match_transaction write confirm —
merge_counterparties write confirm —
open_approval_round write — —
place_asset_in_service write confirm —
plan_close_run write — —
post_inventory_cost_adjustments write confirm —
propose_inventory_write_down write confirm —
publish_workflow write confirm —
read_transaction_receipt write — —
rebuild_shopify_cogs write confirm —
recategorize_by_counterparty write confirm —
reconcile_meta_ad_spend read — —
record_close_step write — —
record_decision write confirm —
record_period_sign_off write confirm —
refresh_bank_balances write — —
refresh_model write — —
reject_match write confirm —
reject_payment_suggestion write confirm —
reject_workflow_step write — —
release_bill_hold write confirm —
remove_transaction_receipt write confirm —
reopen_accounting_period write confirm —
repair_quickbooks_sync write confirm —
repair_shopify_posting write confirm —
repair_stripe_fee_shortfalls write confirm —
reprocess_inbox_email write — —
request_transaction_receipts write confirm —
reset_counterparty_names write confirm —
resolve_quickbooks_sync_issue write confirm —
respond_to_workflow_run write confirm —
revert_workflow write — —
review_inbox_email write — —
revoke_auto_approval write — —
run_depreciation write confirm —
run_workflow write confirm —
search read — ✓
search_counterparty_registry read — —
search_entities read — ✓
search_tools read — ✓
search_workflow_node_tools read — —
send_firm_message write confirm —
send_invoice write confirm ✓
set_bill_hold write — —
set_quickbooks_sync_mode write confirm —
set_receipt_exempt write — —
set_sop_status write confirm —
split_transaction write confirm —
split_transaction_by_receipt write confirm —
start_agent_run write — —
start_background_job write confirm —
start_close_replay write — —
start_reconciliation write confirm —
start_stripe_payout_sync write — —
steer_agent_run write — —
stop_agent_run write — —
submit_approval_round write — —
suggest_sops read — —
sync_quickbooks_now write confirm —
sync_shopify write confirm —
test_workflow read — —
toggle_bank_rule write — —
trigger_bulk_je_upload write confirm —
trigger_bulk_transaction_upload write confirm —
trigger_month_end_close write confirm —
trigger_payment_matching write — —
trigger_ramp_repair write confirm —
unapply_credit_memo write confirm —
unapply_vendor_credit write confirm —
unlink_bill_journal_entry write confirm —
unlink_counterparty write confirm —
unlink_master_from_registry write confirm —
unpublish_workflow write — —
update_accounting_period_settings write — —
update_automation_settings write — —
update_bank_rule write confirm —
update_bill write confirm —
update_cash_forecast_timing write — —
update_contract write confirm —
update_conversation_status write confirm —
update_counterparty_default write confirm —
update_counterparty_link_settings write — —
update_counterparty_naming_rule write confirm —
update_customer write confirm —
update_deal write confirm —
update_decision write confirm —
update_feedback write — —
update_fixed_asset write confirm —
update_inventory_ledger_settings write confirm —
update_invoice write confirm —
update_invoice_posting_settings write confirm —
update_journal_entry write confirm —
update_order write confirm —
update_project write confirm —
update_sop write — —
update_standard_costs write confirm —
update_transaction write confirm ✓
update_vendor write confirm —
update_vendor_approval_route write confirm —
update_workflow write — —
upload_file write — ✓
validate_workflow read — —
void_credit_memo write confirm —
void_invoice write confirm —
void_vendor_credit write confirm —
widen_automation_settings write confirm —

Approval gates

High-risk writes — payments (link_invoice_payment, link_bill_payment, mark_invoice_paid), voids (void_invoice, void_credit_memo, void_vendor_credit, cancel_bill), external sends (send_invoice), irreversible state changes (finalize_invoice), journal entries, merges and bulk posts — wait for your approval before committing. The same flow works in Claude Code, Cursor, Codex and claude.ai:

  • Confirm dialog. A client that supports MCP elicitation on the 2026-07-28 protocol shows a native dialog summarizing the change (amounts in dollars). Choosing No in the form returns {"approved": false, "reason": "rejected"} and commits nothing.
  • Approve in DayZero. Every other client, including Claude Code hosts that can't draw the dialog and decline it on their own, gets {"status": "approval_required", "approval_id": "…", "approve_url": "…", "summary": "…"} back. The assistant gives you the link; you open it signed in to DayZero, review the tool, business and arguments, and choose Approve or Decline (with an optional note). The assistant then calls confirm_write(approval_id): an approved write runs exactly that call once and returns its result, a declined one comes back as reason: "rejected" with your note, and an undecided one as status: "awaiting_approval" (it can pass wait_seconds up to 50 to wait for you). The assistant can cancel a waiting write (cancel=true) but has no way to approve one: only you, signed in as the user the write belongs to, can. An approval belongs to the user and client that received it and expires after 30 minutes.
  • Large changes. A bulk tool's confirm_large_change=true (go past its blast-radius limit) always takes the same approval, even for a tool you have a standing grant for.

dry_run=true previews never ask. The gate is also applied to any write whose name starts with delete_, bulk_delete_, void_, bulk_void_ or cancel_, so a newly added destructive tool cannot slip through. The Approval gate column of the Full tool reference marks every gated tool.

Two ways around a prompt, both set up by a person:

  • Standing approvals. A per-tool "always allow" grant you have given in the DayZero in-app assistant lets that tool skip the prompt for that business. Only explicit per-tool grants count (the broader Auto-Approval toggles in the assistant settings are ignored on MCP), and tools that delete, are irreversible or have external effects can never be auto-approved. list_auto_approvals shows your grants; revoke_auto_approval removes one.
  • Proposed changes. An agent that should not apply a write itself can call create_pending_change with the tool name, its exact apply arguments, the preview and the plan_hash. A person approves or rejects it in the app's Proposed changes inbox; approval applies it as the approver, and the apply is refused as stale if the books moved since the preview. Read the outcome with list_pending_changes and get_pending_change.

Close agents: ask in the portal, report gaps

The agents profile (/mcp/agents) is for close agents run from the Agent window. Two things it does that the other profiles do not:

  • Ask the client in the portal, not by email. create_transaction_clarification puts a question beside one transaction (with optional one-tap answers) under the client's Questions page; create_document_request asks for a file under Document requests. Both are for firm staff, reach only that business's own client users, can be cancelled, and need no approval. Read the answers back with list_transaction_clarifications(status="answered") and list_document_requests(status="fulfilled"). list_firm_conversations shows the message threads, send_firm_message and update_conversation_status work on them (those two still ask for approval), and get_client_portal_attention shows what the client sees on their dashboard. A connection bound to one business only lists that business's threads.
  • Report a gap instead of pasting a transcript. create_feedback(kind="gap", tool_tried, error, sop_job?, expected?, excerpt?, severity?) files a feedback ticket for the DayZero team (source agent, tag platform_gap) and returns {feedback_id, deduplicated}. The same gap seen again within 14 days adds a note to the open ticket instead of a new one. It changes no books and needs no approval; it is rate limited.

UI cards (MCP Apps)

Hosts that advertise MCP Apps / mcp-ui support receive rendered HTML cards as embedded ui://dayzero/* resources alongside the JSON payload on snapshot tools: get_daily_brief, get_ar_snapshot, get_ap_snapshot, get_aged_payables, get_uncategorized_summary, and get_payment_priority_suggestions. Cards show key figures, aging tables, and dollar-formatted amounts with automatic light/dark styling. Clients without UI support receive identical JSON-only responses.

Note: create_invoice requires Stripe Connect (or a demo business, or historical=true for an invoice already issued elsewhere, which stays a local draft and never reaches Stripe) — call get_invoice_create_requirements first. For a migrated invoice pass historical=true with invoice_date and invoice_number. Typical flow: create_invoice → finalize_invoice → send_invoice or link_invoice_payment. mark_invoice_paid is for manual paid status without a bank match. AP: approve_bill → mark_bill_received (needs expense ledger_id). get_aged_payables pairs with get_ar_snapshot; get_payment_priority_suggestions ranks open bills late → early-pay discount → due within 7 days, with cash on hand.

Long-running workflows (trigger_bulk_transaction_upload, trigger_bulk_je_upload, trigger_month_end_close, generate_report, and bulk_create_invoices on a file of more than 500 rows) return immediately with {job_id, status: "started", poll_tool: "get_job_status"}. Do not wait inline; poll get_job_status(job_id) until completion/failure. list_jobs shows the business's recent jobs. Most MCP clients cancel a tool call after about a minute, which is why these tools hand back a job instead of blocking.

Multi-step agentic work that exceeds one call (hundreds of writes, many documents) can be handed to start_background_job: it runs detached on the server (about an hour of wall clock) under a write scope fixed at launch (allowed_write_tools, empty means read-only), and no human approves anything mid-run. Poll get_job_status for the summary and signed download links to any files it produced.

Reports follow: list_report_types → generate_report → poll get_job_status (use job_id) → optional get_report when the completed job returns a report_id. Pass wait=true to generate_report to wait up to wait_timeout_seconds (default 25, at most 55) and get the download link in one call; it falls back to the job handle if the report is not ready in time. output_format is csv (default) or xlsx. email_report sends a generated report's download link by email.

Whole datasets: export_dataset (up to 10,000 rows; format="jsonl" for programmatic use) replaces paging a list tool.

Download links (reports, exports, job files) are presigned, host-bound and expire after about an hour. Fetch them exactly as returned, with no Authorization header; changing the query string or adding auth breaks the signature.

When a tool fails

A failed tool returns a teaching error rather than a bare message — the same shape whether the failure came from the tool's own validation or from the data underneath it:

json
{
  "error": "You have access to multiple businesses. Please specify which one to use: …",
  "reason_code": "BUSINESS_SELECTION_REQUIRED",
  "how_to_fix": "Ask the user which of the businesses listed in the error to use, then pass its business_id.",
  "retryable": false,
  "did_you_mean": []
}

Read how_to_fix and did_you_mean before retrying, and don't retry at all when retryable is false. Common reason_code values:

reason_code Meaning
BUSINESS_SELECTION_REQUIRED You have several businesses; pass the business_id of the one you want (the error lists them)
FIRM_SELECTION_REQUIRED You belong to several advisory firms; pass firm_id
NOT_FOUND No record with that id in this business — look it up first, and check business_id
INVALID_ARGUMENT / VALIDATION_ERROR / INVALID_ENUM / INVALID_DATE_FORMAT Fix the named argument; did_you_mean lists the accepted values
UNKNOWN_TOOL No tool by that name; use search_tools and take a name from its result
MISSING_REQUIRED_FIELD An update with no fields to change
PERMISSION_DENIED Your role or grant doesn't cover this tool; access has to change first
CONFLICT The record changed, or one like it already exists — re-read it and decide whether the write is still wanted
AUTHENTICATION_REQUIRED The connection is not authenticated — re-authorize the integration
NO_BUSINESS_ACCESS / NOT_A_FIRM_MEMBER The account reaches no business, or a firm-wide tool was called without advisory-firm membership — use a business-scoped tool
UNSUPPORTED_REPORT_TYPE Call list_report_types and use one of its names
MISSING_SCOPE_READ / MISSING_SCOPE_WRITE Re-authorize with the missing OAuth scope
PARTIAL_SUCCESS A bulk call saved some rows — retry only the ones in data.failed
COMMIT_FAILED Nothing was saved; retry the whole call
RATE_LIMITED / TIMEOUT Retryable — wait a moment and call again
UNKNOWN_ERROR Unclassified; read error

A bare Error executing tool <name> with no envelope means an internal fault, not something to correct in the call — it is reported to DayZero automatically.

Working with Large Datasets

List tools return a total field showing total matching records. Use this to decide how to proceed:

  • Analytical questions ("what did we spend the most on?", "are we profitable?", "how much revenue last quarter?") — use the analytics tools (get_financial_overview, get_top_expenses, get_cash_flow_totals, get_profit_trends, get_transaction_summary). These compute summaries server-side and return small payloads regardless of data volume.
  • Under ~100 matching records — paginate through them if you need individual details.
  • Over ~100 matching records — narrow with filters (date range, search text, ledger_id, status) or use analytics tools. Don't page through thousands of records.
  • Full exports — use generate_report for a report file, or export_dataset for a raw dataset.

Strategy: summarize first, drill down second. Start with aggregate tools to understand the big picture, then use list tools with filters to examine specific subsets.

Prerequisites

  1. A DayZero login for the browser consent flow. MCP sign-in is open to every account; it needs no separate approval and no API key.
  2. API access not switched off for your user. If your advisory firm has disabled API access for you, authorization fails with access_denied.

Connecting Claude Desktop

The fastest path is the install link — Add DayZero to Claude. It opens claude.ai's Add custom connector dialog with the name and URL prefilled; review it, click Add, and sign in to DayZero when prompted. Connectors are account-level, so one add covers Claude on web, desktop, and mobile.

Prefer local config? Add the server with just its URL. Claude registers itself, opens a browser for you to sign in to DayZero and approve access, then stores the MCP OAuth token automatically:

json
{
  "mcpServers": {
    "dayzero": {
      "url": "https://mcp.ondayzero.com/mcp"
    }
  }
}

Config file location:

macOS: ~/Library/Application Support/Claude/claude_desktop_config.json Windows: %APPDATA%\Claude\claude_desktop_config.json

Restart Claude Desktop after saving. You should see DayZero tools appear in the tools menu.

Connecting Claude Code

One command:

bash
claude mcp add --transport http dayzero https://mcp.ondayzero.com/mcp

Then run /mcp inside Claude Code to complete the browser sign-in. To share the server with your whole team, commit a .mcp.json at the repo root instead:

json
{
  "mcpServers": {
    "dayzero": {
      "type": "http",
      "url": "https://mcp.ondayzero.com/mcp"
    }
  }
}

Calling your connected external MCP servers (Gusto and others)

Servers an admin connected under Integrations → External MCP servers (Gusto, Stripe, a CRM, ...) are reachable through the same DayZero connection, so Claude Code never holds the third-party credentials. Three tools do it (all on /mcp, and on the focused https://mcp.ondayzero.com/mcp/integrations mount); they need the admin role on the business:

  1. list_external_tools — the connected servers and their tools, each marked read (runs without approval) or write (needs your approval). Pass server + tool to get a tool's input schema before the first call.
  2. call_external_read_tool(server, tool, arguments) — runs a tool trusted as read-only, with no approval prompt. It refuses anything else.
  3. call_external_write_tool(server, tool, arguments) — runs any other non-blocked tool after you approve the exact server, tool and arguments in DayZero (the same approve_url → confirm_write flow as other gated writes). Write approval is never skipped.

Which tools count as read is an admin's call, made per connection on the Integrations page: either Trust read-only hints (tools the server itself marks read-only) or, for servers that don't annotate their tools, Allow as read on each tool. A tool the server marks destructive always needs approval, and a blocked tool never runs. For Gusto, mark the lookups you use — for example the company lookup (get_company, to see which company the connection is signed into), payroll detail (the per-employee breakdown of a payroll) and contractor payments (by date range) — as Allow as read. Tool names come from Gusto's server; check them with list_external_tools(server="gusto").

Example prompts in Claude Code:

  • "Which Gusto company is our DayZero connection signed into?"
  • "Pull the per-employee breakdown for the October 15 payroll from Gusto."
  • "List Gusto contractor payments from 2026-09-01 to 2026-09-30."

Every external call is logged in DayZero's audit trail: who (the person who owns the DayZero token), through what (the MCP client, e.g. claude-code, and its OAuth client id), which server and tool, read or write, the outcome, and — for a write — the approval. Arguments are stored as a digest, not the payload. Results are third-party text and are marked untrusted.

Who did it, and where it came from

Every change made through DayZero is attributed in the audit trail (the History panel on a record, get_entity_audit_trail, get_activity_feed) to the person and the channel: browser (the app), api (an API key, shown by name), mcp (an MCP client, shown by its name), ai_chat (the in-app assistant), workflow (a workflow run) or system (background jobs). Writes an agent made after you approved them in DayZero also carry the approval.

Connecting Cursor

The Add to Cursor button under Settings → Developers in DayZero opens Cursor with the server prefilled — confirm the install and sign in when the browser opens.

To add it manually, go to Settings > MCP Servers in Cursor and add a new server:

  • Name: DayZero
  • URL: https://mcp.ondayzero.com/mcp

Leave the headers blank — Cursor opens a browser for you to sign in and approve via OAuth.

Connecting ChatGPT

ChatGPT requires Developer mode before you can add an unlisted MCP connector:

  1. Enable it under Settings → Apps → Advanced settings → Developer mode (available on Plus and Pro; on Business, Enterprise, and Edu workspaces an admin may need to allow it — or an admin can add DayZero org-wide from the workspace admin console).
  2. Create a new connector with Name: DayZero, URL: https://mcp.ondayzero.com/mcp.

ChatGPT discovers the OAuth endpoints, opens a browser for you to sign in to DayZero, and stores the token automatically. Connectors are account-level, so the same connection works in the ChatGPT web, desktop, and mobile apps.

Connecting Codex

Codex CLI, the Codex IDE extension, and the ChatGPT desktop app share one MCP configuration. Add two lines to ~/.codex/config.toml:

plaintext
[mcp_servers.dayzero]
url = "https://mcp.ondayzero.com/mcp"

Or configure it in the ChatGPT desktop app under Settings → MCP servers → Add server — name dayzero, transport Streamable HTTP, URL above. Codex opens your browser to sign in to DayZero the first time it connects.

Connecting Other MCP Clients

Any MCP client that supports Streamable HTTP transport can connect. The server implements the full MCP authorization spec, so compatible clients configure themselves automatically from the server URL alone:

  • RFC 9728 Protected Resource Metadata — https://mcp.ondayzero.com/.well-known/oauth-protected-resource/mcp
  • RFC 8414 Authorization Server Metadata — https://api.ondayzero.com/.well-known/oauth-authorization-server

These advertise the authorization server, supported scopes, PKCE methods, and the authorize / token / registration endpoints.

A connector descriptor for directories and crawlers lives at https://mcp.ondayzero.com/.well-known/mcp.json (alias of /.well-known/mcp/server-card.json): the endpoints and domain mounts, both OAuth metadata URLs, logo, docs, categories, contact and the size of the default tool list.

The canonical resource URL (RFC 8707 audience) is https://mcp.ondayzero.com/mcp — no trailing slash.

Browser-based and hosted clients (Origin allowlist)

As part of the MCP spec's DNS-rebinding protection, the server validates the Origin header on every MCP request. Requests without an Origin header — desktop apps and CLIs such as Cursor, Claude Desktop, Codex CLI, and mcp-remote — always pass. Requests with an Origin header must match the allowlist, which covers:

  • Anthropic — https://claude.ai, https://claude.com
  • OpenAI — https://chatgpt.com, https://chat.openai.com, https://platform.openai.com
  • Cursor — https://cursor.com, https://cursor.sh
  • Local development — http://localhost:*, http://127.0.0.1:* (e.g. MCP Inspector)

A hosted client whose origin is not allowlisted receives 403 Invalid Origin header on every MCP call after OAuth succeeds — the connector shows as connected but lists zero tools. If you're building or operating a hosted MCP client that should connect to DayZero, contact us so we can add your origin (self-hosted deployments can extend the list via the MCP_ALLOWED_ORIGINS_EXTRA environment variable).

Environment URLs

Environment MCP server URL Authorization server
Production https://mcp.ondayzero.com/mcp https://api.ondayzero.com
Local dev http://localhost:8000/mcp http://localhost:8000

Use the MCP server URL in your client config. The protected-resource metadata is served from the MCP host (https://mcp.ondayzero.com/.well-known/oauth-protected-resource/mcp); the authorization server — its metadata at /.well-known/oauth-authorization-server, its issuer, and the /oauth2/* endpoints — stays on the API host (https://api.ondayzero.com). Clients discover this automatically from the MCP server URL.

Authentication

MCP uses OAuth 2.1 with browser consent. Tokens minted through this flow are MCP-only — they cannot call the REST API (/api/v1/*). Likewise, REST API keys and SPA session tokens cannot authenticate MCP tool requests.

OAuth 2.1 flow

  1. The client connects to /mcp and receives a 401 with a WWW-Authenticate header pointing at /.well-known/oauth-protected-resource.
  2. It reads RFC 9728 protected-resource metadata and discovers the authorization server.
  3. It reads RFC 8414 authorization-server metadata at /.well-known/oauth-authorization-server.
  4. It dynamically registers itself (RFC 7591) at POST /oauth2/register — no pre-shared client credentials needed. Alternatively a client can skip registration and use an https URL it controls as its client_id (Client ID Metadata Documents): DayZero fetches that URL, which must serve the client's metadata JSON with a matching client_id. These clients are always public (PKCE, no secret).
  5. It opens your browser to GET /oauth2/authorize with PKCE (S256) and an optional RFC 8707 resource parameter (must match the MCP server URL).
  6. You sign in via Clerk and review the consent page, which shows:
    • The app name and human-readable scope descriptions
    • Your signed-in identity and accessible businesses (via GET /oauth2/userinfo)
    • Which businesses the app may reach — all of them, or only one you pick — and an Allow changes box (see Choosing a business and allowing changes)
    • Where you'll be redirected after approval (with loopback / non-HTTPS warnings when applicable)
  7. After you click Allow access, the client exchanges the authorization code at POST /oauth2/mcp/token for an MCP access token (plus a refresh token when the grant includes offline_access).
  8. The access token (valid for 60 minutes) is sent as Authorization: Bearer … on every MCP request and refreshed automatically when it expires.

MCP sign-in is open to every account. If your firm has disabled API access for your user, authorization fails with access_denied.

OAuth scopes

Scopes appear on the consent page in plain English:

Scope What it allows
mcp:read View your accounting data — read transactions, balances, invoices, and reports
mcp:write Make changes on your behalf — create and modify journal entries, invoices, bills, and other records
offline_access Stay connected — keep this app authorized without signing in each time
mcp:admin Act as DayZero staff — staff-only tools (feedback triage, admin repairs) and every client business. Staff accounts only.

Write tools require both mcp:write and write permission on the business. A token with only mcp:read can query data but cannot call write tools.

mcp:admin is different from the other scopes: it is not advertised in scopes_supported, a client cannot obtain it by asking, and a superuser's token does not carry it by default. It is granted only when a DayZero staff member ticks Act as DayZero staff on the consent screen. Without it, a staff member's token behaves like any customer's, and the admin tools return 403 with a message pointing back to that box.

A few scope rules to be aware of:

  • Refresh tokens are only issued when the grant includes offline_access. Without it, the client gets an access token only and must re-authorize when it expires. A refresh may narrow the scopes but never widen them.
  • If a client requests no scopes at all, it is granted mcp:read offline_access (read-only, but able to stay connected).
  • If a client registered with an explicit scope, requests are narrowed to that registered set — a read-only registration cannot later escalate to mcp:write.

If the token is missing or invalid, the server returns 401 with a WWW-Authenticate header pointing to the resource metadata endpoint.

Choosing a business and allowing changes

The consent screen asks two things before you approve:

  • Businesses. Keep All businesses you can access, or pick Only one business from the list. If you can open just one business, that business is picked for you. A connection limited to one business:

    • works on that business only. Asking for any other business returns 403 — "This connection is limited to ProScore. Reconnect DayZero to work on another business." A tool call that names another business anywhere in its arguments is refused before it runs;
    • uses that business when a tool call names none;
    • cannot use firm-wide tools (get_firm_client_overview, get_firm_user_activity) or DayZero staff tools, and is never staff — the Act as DayZero staff box is turned off while one business is picked;
    • lists the full tool catalog on /mcp (minus staff tools), so agents can call any tool for that business;
    • keeps its business for good: refreshing the token never changes it. To work on another business, connect again and pick it. If you lose access to the business, the connection stops (401, asking you to reconnect).

    The token response names the business (business_id, business_name), get_connection_info reports it, and Settings → Developers shows it on the connected app. One app can hold one connection per business plus one for all businesses; disconnecting one leaves the others working.

  • Allow changes. Ticked, the app gets mcp:write and can create and edit records — each risky change still asks you first. Unticked, it can only read. The box starts ticked only when the app asked to make changes, and it is offered only when the app's registration allows changes.

Revoking access

OAuth-connected MCP clients (e.g. Claude Desktop, Cursor) create a connected app grant tied to your account. Revoke a grant to immediately invalidate its refresh token:

bash
# List connected apps (requires a DayZero session or REST OAuth token)
curl "https://api.ondayzero.com/api/v1/oauth/grants" \
  -H "Authorization: Bearer YOUR_SESSION_OR_API_TOKEN"

# Revoke a grant by ID
curl -X DELETE "https://api.ondayzero.com/api/v1/oauth/grants/GRANT_ID" \
  -H "Authorization: Bearer YOUR_SESSION_OR_API_TOKEN"

Revoking a grant blacklists its refresh token and every active access token issued under it, so access is cut off immediately — not when the access token would naturally expire. You can also disconnect an app from Connected apps under Settings → Developers.

Revoking an MCP grant does not affect REST API keys — manage those separately under Settings → Developers.

Security notes

  • MCP tokens carry JWT type=mcp and audience = the MCP resource URL — distinct from REST access, refresh, and api tokens.
  • The consent screen is a React page served at GET /oauth2/authorize; standalone error pages use Jinja2 templates. Both ship strict security headers (CSP nonce, X-Frame-Options: DENY, Referrer-Policy: no-referrer).
  • Dynamic client registrations expire after ~90 days of inactivity; clients re-register automatically.
  • Registered redirect URIs are validated: https, loopback http (with a callback path), and the app schemes cursor://, vscode://, cursor-ide:// and claude:// are allowed; javascript:/data:/file: and http on non-loopback hosts are rejected, as are URIs with credentials or a fragment.
  • PKCE (S256) is mandatory — authorization codes cannot be exchanged without a valid code verifier.
  • DNS-rebinding protection validates the Host header and allowlists the Origin header on every MCP request (see Browser-based and hosted clients). Requests with an unrecognized Origin are rejected with 403.
  • Refresh tokens are bound to the client they were issued to; the refresh grant requires client_id (and the client secret for confidential clients) and rejects tokens presented by a different client.
  • Refresh tokens rotate on every use. Replaying an already-rotated token after a short grace window (about 5 minutes, which absorbs retries) is treated as possible theft and revokes the grant, so the client must sign in again.
  • POST /oauth2/register, POST /oauth2/mcp/token and GET /oauth2/authorize are rate-limited per client IP and return 429 with a Retry-After header when exceeded.

Example Conversation

Once connected, just start asking questions — no setup required:

Single business (most common):

You: What invoices are overdue?

Assistant: You have 3 overdue invoices totaling $4,250.00…

The server auto-detected your business behind the scenes. No IDs needed.

Multiple businesses:

You: Show me last month's transactions

Assistant: You have access to 2 businesses:

  1. Acme LLC — admin
  2. Beta Corp — viewer

Which one would you like to use?

You: Acme

Assistant: Here are Acme LLC's transactions for last month…

After you pick, the assistant remembers your choice for the rest of the conversation. Say "switch to Beta Corp" at any time to change.

More things you can ask:

  • "List all bank accounts"
  • "Show me the last 10 transactions from checking"
  • "Generate a profit and loss report for Q1 2026"
  • "Create a journal entry for $500 rent expense on March 1"
  • "Search for anything related to Acme Corp"
  • "Compare the operating budget to actuals this quarter"
  • "What are our top expenses this year?"
  • "Are we cash-flow positive this quarter?"
  • "How have profit margins changed over the past 6 months?"
  • "How many transactions came in last month?"
  • "What should I look at today?" (Daily Brief)
  • "Mark that Amazon transaction as reviewed"
  • "Add a new vendor called Acme Hosting with email billing@acme.com"
  • "Create a $250 bill from Acme Hosting due next Friday"
  • "Finalize draft invoice #1042"
  • "Email invoice #1042 to the customer"
  • "How much do we owe vendors by aging bucket?"
  • "Void invoice #1042"
  • "Create a $25 credit memo for Acme and issue it"
  • "Cancel the draft bill from Acme Hosting"
  • "Mark these three bank transactions as reconciled"
  • "What's blocking us from creating invoices via API?"

Troubleshooting

Problem Solution
Tools don't appear Verify the URL is https://mcp.ondayzero.com/mcp (no trailing slash) and restart the client
Only ~40 tools show up Expected on /mcp: it advertises a curated starter set and the assistant finds the rest with search_tools. To see a whole domain, connect its profile mount
A write returns status: approval_required The write is gated and your client can't show a confirmation dialog. Give the user the approve_url from the result; they open it signed in to DayZero and choose Approve or Decline. Then call confirm_write(approval_id). The approval expires after 30 minutes, and the assistant cannot approve for the user. See Approval gates
Connected, but zero tools The client's Origin isn't allowlisted — MCP calls get 403 Invalid Origin header after OAuth. Claude, ChatGPT, and Cursor origins are supported; for other hosted clients see Origin allowlist
Browser sign-in doesn't open Ensure your MCP client supports OAuth 2.1 with dynamic client registration
access_denied during consent Your firm has switched off API access for your user — ask a firm admin to re-enable it
Stuck re-authorizing Your registered client may have expired — the client re-registers automatically; just retry the connection
401 Unauthorized Re-run the OAuth sign-in; REST API keys (dz_…) do not work on MCP
403 on write tools Your grant may only include mcp:read — re-authorize with write access, or check your business role
403 Forbidden (business) Your token doesn't have access to the specified business_id
404 Not Found Ensure the URL is exactly /mcp — not /mcp/ with a redirect, /mcp/mcp, or /api/mcp
Unknown application / registration expired Close the browser tab and reconnect from your MCP client so it re-registers
Connection timeout Ensure the DayZero API is reachable from your network
Session terminated on every tool call The MCP connector lost its Streamable HTTP session (common after a deploy or when the client doesn't re-initialize). Disconnect and reconnect the DayZero connector in your MCP client, or start a new chat. DayZero runs in stateless Streamable HTTP mode so new requests don't depend on a server-side session ID; if this persists after reconnecting, report it with the client name and timestamp.

What's Next?