Accounting Integration Skills (read-only and publish)

Accounting Integration Skills #

Classification: Internal-Only

One Claude Code command covers every accounting-provider job, from a brand-new connector to adding one missing document type:

research integration for <Provider>

The accounting-integration skill behind it first checks what already exists for the provider, then runs the right track through the same five phases: research, plan, implement, test, release. The skills live in .claude/skills/ and are available in any Claude Code session started in the repo.

What gets built #

  • Publish AP bills, AR invoices and direct expenses. Bank statements too, when the provider’s API can take them; when it can’t, the research brief records why and the work ships without them.
  • Read only what publish and the AI features need: catalogs (accounts, items, tax rates, tags, locations, currencies), contacts with contact write-back, and document reads. Publish uses the detail read to check a document’s status before deleting or republishing it; Hinoki knowledge setup and self-prompt use the history reads.
  • No import. Syncing provider documents into tofu is no longer a product feature (the Import button and auto-import were removed in #5269), so no track builds it.

How the skill picks a track #

Step 0 reads the code: the provider’s arm in IntegrationProvider::capabilities(), the bank statement arm of supports_publish_for_type(), whether the connector’s read and publish methods are real or stubs, and what .plans/<slug>/ already holds.

What the code shows Track What runs
no connector A read-only research and publish research in one pass, then read-only implementation followed by publish on the same branch
a read-only connector B publish only: AP, AR, DE, and BS if possible
publishes, but some types are missing C only the missing types, usually bank statements
publishes all four types D nothing; it reports the support matrix

On 2026-09-28 the providers mapped onto the tracks like this. The skill re-detects on every run, so trust its Step 0 over this table.

Providers State Track
MYOB, AutoCount, Dynamics 365 Business Central, Odoo, FreeAgent, Jaz, NetSuite read-only B
Sage, Bukku, Money Forward, QuickBooks Online publish AP/AR/DE, no bank statements C
Xero, freee, Zoho Books publish all four D
any provider without a connector none A

Before anything starts, Claude shows what it found and asks you to confirm the track, the types (default AP + AR + DE, BS if possible), and the integration branch.

Phases and stops #

Phase Track A (new provider) Track B (publish only) Track C (missing types)
1. Research readonly-research-integration-research-tasks, then publish-integration-research in new-provider mode with publish-integration-error-research alongside; publish-integration-validator-combinations runs once the error catalog is written publish-integration-research and publish-integration-error-research in parallel, then publish-integration-validator-combinations publish-integration-research for the missing types, plus the error research and validator combinations if AP, AR or DE is among them
Stop 1: you review both briefs, the publish error catalog, the resources Claude asks for, the decisions the error catalog and the decisions same as B
2. Plan readonly-integration-plans workflow (one plan per 3.x task), then the publish plan the publish plan the publish plan
Stop 2: you approve plans, branch, release shape plan plan
3. Implement read-only waves (readonly-integration-implement), then publish P0–P5 and PV, the tofu validator (publish-integration-orchestrator), on the same branch publish P0–P5 and PV the missing type’s task
4. Test readonly-research-review on 3.x PRs, the publish-integration-testing checklist on P PRs, then the whole branch and the manual test publish-integration-testing and the manual test same, scoped
Stop 3: you check support matrix and manual-test log same same
5. Release one PR to main, release notes once same same

Claude marks each approved file (the briefs and the error catalog at Stop 1, the plan at Stop 2, the manual-test log at Stop 3) with a Status: APPROVED <date> line, and the plan’s header records the track and the branch. Those are how a later session knows where to resume, even after the code on the branch has changed, so you can close the session at any stop and come back with:

continue integration for <Provider>

The skills behind it #

Skill Role
accounting-integration entry point: detects the state, picks the track, runs the phases
readonly-research-integration-research-tasks track A research: the read-only brief, plus the publish brief in the same pass
readonly-integration-plans (workflow) track A plans: one per read-only task 3.0–3.6, each reading the publish brief’s read-side requirements
readonly-integration-implement track A read-only waves, then hands over to publish on the same branch
readonly-research-* (per task) and readonly-research-review the read-only task manuals and their review gate
publish-integration-orchestrator the publish track: decisions, the publish plan (P0–P5), implementation waves, release
publish-integration-research the publish brief, with a decision table per document type
publish-integration-error-research the publish error catalog: every way the provider rejects an AP bill, AR invoice or direct expense, sorted by the layer that must catch it
publish-integration-validator-combinations the errors that only appear when two or more values meet, probed in the sandbox and added to the catalog
publish-integration-implement the publish implementation manual, with one reference file per task
publish-integration-testing tests, the publish review checklist, and the manual test guide

The publish implementation skill’s reference files are read by Claude when needed; you don’t call them directly:

.claude/skills/publish-integration-implement/references/
├── foundation.md               # capabilities, policy, scopes, error keys, HTTP helpers, auto-publish, docs
├── invoice-shared.md           # what AP and AR share: validate/upload, merge-back, status, delete
├── ap-bill.md                  # AP bill specifics
├── ar-invoice.md               # AR invoice specifics
├── direct-expense.md           # direct expense specifics
├── bank-statement.md           # bank statement (runs synchronously in bonsapi)
├── tofu-validator.md           # task PV: the provider's rules in tofu-validator-core, run live in the review form
├── attachments.md              # attachment upload, linking, limits
├── journal-based-providers.md  # providers with journals only (Money Forward style)
└── webapp.md                   # deep links, status labels, gates, contact fields

Before you start #

  • A sandbox company for the provider, or a test company you’re allowed to write to. Some providers (Money Forward) have none; then every test document must be cleaned up afterwards.
  • Write access: write scopes on the provider’s OAuth app, or a write-enabled API key. The integration-account owner registers scopes on the provider’s portal.
  • A working local environment: mise run dev, and the provider credentials in .config.yaml (see Configuration File).

Walkthrough: MYOB (track B, read-only → publish) #

The Claude replies below are illustrative; the real wording will differ.

1. Start #

you    › research integration for MYOB
claude › MYOB: variant Myob, module myob, slug myob.
         State: read-only. The capabilities arm has no .with_publish();
         fetch_invoice, fetch_direct_expense and create_contact are implemented;
         no .plans/myob/publish/ yet; no open MYOB branch.
         Track B: publish only. Confirm:
         1. Types: AP bill, AR invoice, direct expense, bank statement if possible?
         2. Branch: <you>/eng-XXXX-myob-publish?
you    › yes, yes

2. Research #

Claude runs publish-integration-research. It reads the MYOB write API and the existing accounting/myob/ connector, and writes .plans/myob/publish/<date>-myob-publish-research.md with:

  • a field-mapping table per type (tofu field → MYOB field, required or not, the error key if missing);
  • the bank statement verdict: in scope, or not possible with the evidence;
  • the error keys users will see (for example errorMessage.myob.BILL_NUMBER_REQUIRED);
  • webapp impact (deep-link URL per type, status labels);
  • Decisions for the user, and a NEEDS CONFIRMATION list.

If MYOB’s docs are incomplete, drop sample request/response JSON or screenshots into .plans/myob/resources/; the research treats them as ground truth.

3. Plan (Stop 1 and Stop 2) #

Claude asks the decisions that change behaviour for customers:

claude › 5 decisions need you:
         1. Republish: MYOB can update bills in place. Update in place,
            or delete-then-create like the other providers?
         2. A bill paid in MYOB: block republish with a message?
         3. Auto-publish off on first connect?
         4. Direct expense: Spend only, or Receive too?
         5. MYOB is already live read-only: existing connections must
            reconnect for the write scopes. Reset their auto-publish flags
            when publish ships?
you    › 1 update in place, 2 yes, 3 yes, 4 Spend only, 5 yes

Claude records the answers in the brief, then writes the publish plan, .plans/myob/publish/<date>-myob-publish-plan.md: the target support matrix and one section per task (P0 foundation, P1 invoices, P2 direct expense, P3 bank statement if in scope, P4 webapp, P5 docs), each with its files, error keys, decisions, tests and done-criteria. Nothing is coded until you approve the plan.

4. Implement #

  • P0 foundation, alone first. Capabilities (.with_publish()), write scopes and the reconnect message, error keys plus translations, HTTP write helpers, the attachment size limit, auto-publish off on first connect, and stubs for every publish method. Then mise run ci, the review checklist, and a PR into the integration branch.
  • P1–P5, parallel agents (one worktree each, each opening a PR into the integration branch): invoices (AP and AR together, because they share the write model), direct expense, bank statement (if in scope), webapp (deep links, status labels, removing the read-only banner and Early badge), docs (the internal publish section, plus the user-facing guide or help-center article if MYOB has one).

Each agent reports back with a PR link, what it verified in the sandbox, and what still needs a live check. Working alone without agents? Tell Claude “do it sequentially on one branch”; the order stays the same.

5. Test (Stop 3) #

Claude fills test gaps (write-model JSON tests, every error-mapping arm, status mapping, webapp deep-link tests), runs mise run ci and the publish review checklist over the whole branch, then walks you through publish-integration-testing/references/manual-testing.md:

  • connect, and check auto-publish is off;
  • publish an AP bill, AR invoice and direct expense; open each “View in MYOB” link and check totals match to the cent;
  • edit and republish; mark a bill paid in MYOB and confirm republish is refused;
  • delete in tofu; check error messages are translated (switch the UI to Japanese);
  • check the database through the tofu-internal-mcp execute_sql tool.

Results go into .plans/myob/publish/<date>-myob-publish-manual-test.md. Clean up every test document in MYOB afterwards.

6. Release #

Claude writes the release notes once, on the integration branch → main PR, one per affected project (bonsapi, bonsai-accounting-sync, webapp), each 150 characters or fewer, and opens the PR with the submit-github-pr skill. The description lists the support matrix, your decisions, the reconnect note for the new scopes, and anything still untested.

Track A: a provider with no connector #

The same command starts it. What differs from track B:

  • Research writes two briefs in one pass: .plans/<slug>/<date>-<slug>-phase1-research.md (API, auth, regions, per-object coverage, test data, readiness verdict, task list 3.0–3.6 plus P0–P5) and the publish brief, whose section 11 lists what each read-only task must deliver for publish (write scopes, payment state in the detail read, account flags the write endpoints accept, contact fields).
  • Write scopes are requested from day one (tasks 3.0 and 3.1), so no customer ever reconnects for publish.
  • Plans: the readonly-integration-plans workflow writes one plan per read-only task, then Claude writes the publish plan, which also fixes the order of the read-only tasks.
  • Implementation order: the read-only waves (3.1 first, then 3.0/3.4/3.5/3.6, then 3.2.x, then 3.3) on branch X, then P0–P5 on the same X. The docs tasks 3.5 and 3.6 describe the read side; P5 adds publishing to them once it exists.
  • Release shape (you choose at Stop 2): by default read-only and publish ship together in one PR to main. If read-only should go live earlier, X goes to main after the read-only waves, and publish continues as track B.
  • The manual test adds a read-side section (pickers, contact creation, CSV export, heartbeat) before the publish checks.

Track C: adding bank statements to a provider that publishes #

you    › research integration for Sage
claude › Sage publishes AP/AR/DE; bank statements are not enabled.
         Track C: research bank statement publish for Sage?

The research answers one question first: can the provider’s API take a bank statement (a statement import, one transaction per line, or a bank feed)? If not, the brief records “not possible” with the evidence, Claude offers to note the verdict in the provider’s internal doc so nobody researches it again, and the job ends there. If it can, the plan is a single bank statement task plus the small shared changes it needs (the gate in both supports_publish_for_type and the webapp’s supportsPublishForType, error keys), followed by the bank statement parts of the checklist and the manual test.

Running one step on its own #

Each skill still works alone:

You want to… Type
write only the publish research brief /publish-integration-research myob
implement one publish type (needs the approved brief and plan) /publish-integration-implement implement AP bill publish for myob
fix a publish bug in an existing connector describe the bug, e.g. Zoho bank statement publish sends split children, fix it; the implement skill loads the matching reference
review your changes against the publish checklist /publish-integration-testing review my diff
review a teammate’s PR /publish-integration-testing review PR #<number>
walk through the manual test /publish-integration-testing how do I manually test myob publish
review a read-only task diff /readonly-research-review

Where files end up #

Path What
.plans/<slug>/<date>-<slug>-phase1-research.md track A read-only brief, readiness verdict and task list
.plans/<slug>/<date>-<slug>-<task>-plan.md track A read-only task plans
.plans/<slug>/publish/<date>-<slug>-publish-research.md publish brief and decisions
.plans/<slug>/publish/<date>-<slug>-publish-plan.md publish plan (P0–P5), and in track A the order of the read-only tasks
.plans/<slug>/publish/<date>-<slug>-publish-manual-test.md manual test log
.plans/<slug>/resources/ provider samples you supply
libs/rust/bonsai-integration/src/accounting/<module>/ connector code: mod.rs, model/*_write.rs, model/error.rs
libs/rust/bonsai-model/src/integration.rs capabilities and publish policy
apps/webapp/src/shared/utils/integration.ts deep links, status labels, bank statement gate
config/i18n/en/error.json, config/i18n/en/webapp.json error and status label strings (other locales generated with mise run i18n)
docs/internal/content/docs/integrations/<provider>.md the provider’s publish documentation

Everything under .plans/ is gitignored. <slug> is the connector module with _ replaced by - (money_forward → money-forward).

Tips #

  • Answer every decision before implementation starts; a guessed decision usually means reverted code.
  • Read the precedent connectors’ code (sage/, freee/, bukku/, zoho/, money_forward/), not their PR descriptions; several PR descriptions no longer match what shipped.
  • Keep concurrent Rust agents to about three; parallel cargo builds slow the machine down.
  • To change how the skills behave, edit the files in .claude/skills/. Each rule lives in one file; the others point to it.