Pick a path
Effort vs. transactional integrity.
The CSV path is highest-control / lowest-trust; the OAuth path is lowest-effort / highest-trust. Most shops start with Zapier (Pattern 2) because it's real-time and 30 minutes to set up — and graduate to Pattern 3 when QBO becomes load-bearing.
Pattern 1 — CSV export, monthly drop (works today)
Pull the month's repair logs via /api/v1/repairs?since=…, transform to QuickBooks' IIF or QBO-import CSV format, and drop it on a shared drive the bookkeeper imports manually.
When to pick this
Right when accounting wants the data but hasn't approved an automated integration yet. Low-trust, high-control — every entry is a human review before it lands in QBO.
Show the setup
# Monthly repair-log export → QBO-import CSV # Run on the 1st of each month for the prior month's logs. node ./export-repairs-to-qbo.mjs # export-repairs-to-qbo.mjs (Node 20+): const KEY = process.env.DIRTFLEET_API_KEY; const today = new Date(); const lastMonth = new Date(today.getFullYear(), today.getMonth() - 1, 1); const since = lastMonth.toISOString(); const url = new URL("https://dirtfleet.app/api/v1/repairs"); url.searchParams.set("since", since); url.searchParams.set("limit", "200"); console.log("Date,Vendor,Account,Amount,Memo"); let cursor = ""; while (true) { if (cursor) url.searchParams.set("cursor", cursor); else url.searchParams.delete("cursor"); const r = await fetch(url, { headers: { Authorization: `Bearer ${KEY}` } }); const body = await r.json(); for (const log of body.repairs) { if (!log.cost) continue; console.log([ log.createdAt.slice(0, 10), "DirtFleet", // or look up parts supplier from log.parts "Maintenance & Repair", // your QBO account name log.cost.toFixed(2), `WO# ${log.workOrderId ?? "—"} asset ${log.assetId ?? "—"}`, ].join(",")); } if (!body.nextCursor) break; cursor = body.nextCursor; }Pattern 2 — Zapier glue (also works today)
Subscribe to workorder.completed via Zapier. Each completion → a QuickBooks Online "Create Expense" action. Map asset's project to a QBO class for P&L reporting.
When to pick this
When you want real-time sync and don't mind paying for Zapier's per-zap cost. No code, 30 minutes of setup.
Show the setup
Zapier setup: 1. Trigger: Webhooks by Zapier → Catch Hook URL: copy, register as a DirtFleet subscription (Settings → Webhooks → New, events: workorder.completed) 2. Filter: Only continue if data.cost > 0 3. Action: QuickBooks Online → Create Expense Vendor: "DirtFleet" (or look up from data.parts) Account: Maintenance & Repair Amount: {{data.cost}} Memo: WO# {{data.number}} — {{data.title}} Class (optional): map {{data.projectId}} → your QBO class Date: {{data.completedAt}} That's it. Total setup time: 30 minutes. Zapier charges per task — common shops run 50-200 of these a month, which fits the cheaper Zapier plans.Pattern 3 — Direct OAuth via the lib/integrations/quickbooks helpers
The OAuth client and request helpers are already scaffolded in the repo (lib/integrations/quickbooks). The live wiring activates with the first paying customer's QBO contract — at which point the in-app /settings/integrations/quickbooks-online flow handles the OAuth dance and sync becomes built-in.
When to pick this
Once you've outgrown Zapier's costs OR need transactional integrity (each repair is committed in QBO before DirtFleet considers it 'reconciled'). Status on /integrations/directory: scaffolded — the OAuth + token helpers are in the repo, the in-app /settings/integrations/quickbooks-online flow activates with your first paying QBO contract.
Show the setup
# Pre-shipped helpers visible in repo: # lib/integrations/quickbooks.ts — OAuth client + token refresh # /api/integrations/quickbooks/connect (OAuth callback) # /api/integrations/quickbooks/disconnect (revoke + cleanup) # # Status: SCAFFOLDED (not live). When you're ready, email # hello@dirtfleet.app — first-customer activation is the # expected unlock path. # # The OAuth flow goes through Intuit's standard developer.intuit.com # app registration. We do the QBO request signing + token refresh + # error mapping; the customer just clicks "Connect QuickBooks Online" # in /settings/integrations.
Field mapping reference
DirtFleet RepairLog → QBO Expense.
| DirtFleet field | QBO field | Notes |
|---|---|---|
| RepairLog.cost | Expense.Amount | Direct. Floor at $0. |
| RepairLog.createdAt | Expense.TxnDate | Use the YYYY-MM-DD substring; QBO doesn't store the time. |
| RepairLog.workOrderId + assetId | Expense.PrivateNote | Concatenate as "WO #1234 / asset Excavator 47" for greppable audit. |
| RepairLog.projectId | Expense.Class (or Customer) | Most shops map DirtFleet projects → QBO classes for P&L by project. Some map → QBO customers for invoiceable jobs. |
| RepairLog.parts (free-form text) | (usually skip) | Bookkeepers prefer the canonical PrivateNote; parts text gets fragile if anyone edits it in DirtFleet later. |
What we don't do today
Honest scope.
- Bidirectional sync. DirtFleet → QBO works (push); QBO → DirtFleet is not on the roadmap. Most shops don't want it — the data flow is one-directional.
- Invoice creation. Pattern 2 + Pattern 3 create expenses, not invoices. If you charge end-customers for repair work, build that flow in QBO directly using the project breakdown DirtFleet provides.
- Direct QBO Desktop. Use the Pattern 1 CSV with QBD's IIF import; the OAuth path is online-only.
Want to try Pattern 3 (OAuth) early? Email hello@dirtfleet.app — first-customer activation is the unlock path.
See also: /integrations/zapier for the Pattern 2 setup walk-through.