QuickBooks Online Integration Skill
Use when integrating with QuickBooks Online for invoicing, estimates, customers, and payments.
by hewi333·MIT license·★ 122 Stars on the repo·GitHub ↗
Use now
npx degit hewi333/Mom-n-Pop-Skills/skills/quickbooks-online#main ~/.claude/skills/quickbooks-onlineChecked ·commit main
Inside this skill
SKILL.md · the author's own words# QuickBooks Online Integration Skill ## Overview Integrates a small business with QuickBooks Online for customer management, estimates, invoices, and payment tracking. Uses Intuit's OAuth… ## When to Use - **Create/send estimates** from estimator-engine output - **Convert estimates to invoices** when job is booked - **Record payments** (Stripe webhook → QB payment) - **Sync customers** between CRM and QB - **Pull reports** for weekly owner summary (AR aging, revenue, outstanding estimates) ## Don't Use For - Complex inventory/assembly tracking - Multi-currency or multi-entity accounting - Payroll or tax filing
Word-for-word excerpt, not a result. Read the full SKILL.md ↓
Files of QuickBooks Online Integration Skill
SKILL.md
Show the full text217 lines
QuickBooks Online Integration Skill
Overview
Integrates a small business with QuickBooks Online for customer management, estimates, invoices, and payment tracking. Uses Intuit's OAuth 2.0 flow with automatic token refresh. Designed for agent-mediated use: owners text commands, agent executes QB API calls.
When to Use
- Create/send estimates from estimator-engine output
- Convert estimates to invoices when job is booked
- Record payments (Stripe webhook → QB payment)
- Sync customers between CRM and QB
- Pull reports for weekly owner summary (AR aging, revenue, outstanding estimates)
Don't Use For
- Complex inventory/assembly tracking
- Multi-currency or multi-entity accounting
- Payroll or tax filing
Authentication (OAuth 2.0)
Required Credentials (Environment Variables)
QBO_CLIENT_ID=*** From Intuit Developer Dashboard
QBO_CLIENT_SECRET= # From Intuit Developer Dashboard
QBO_REDIRECT_URI= # e.g., https://your-domain.com/qbo/callback
QBO_ENVIRONMENT= # "sandbox" or "production"
QBO_COMPANY_ID= # Realm ID (set after first auth)
Token Storage
- Store access_token + refresh_token + realm_id in
~/.hermes/qbo_tokens.json - Auto-refresh: access_token expires in 1 hour; refresh_token expires in 101 days
- On 401: refresh token, retry once, then force re-auth if refresh fails
Auth Flow
- User visits:
https://appcenter.intuit.com/connect/oauth2?client_id={id}&redirect_uri={uri}&scope=com.intuit.quickbooks.accounting&response_type=code&state={random} - Callback receives
code+realmId - Exchange code for tokens:
POST https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer - Store tokens + realm_id
Core API Operations
Customers
# Create customer (from CRM lead)
POST /v3/company/{realmId}/customer
{
"DisplayName": "John Smith",
"PrimaryEmailAddr": {"Address": "[email protected]"},
"PrimaryPhone": {"FreeFormNumber": "555-555-0123"},
"BillAddr": {"Line1": "123 Main St", "City": "[CITY]", "CountrySubDivisionCode": "[STATE]", "PostalCode": "[ZIP]"},
"Notes": "Lead from referral"
}
# Query customer
GET /v3/company/{realmId}/query?query=SELECT * FROM Customer WHERE DisplayName='John Smith'
Estimates
# Create estimate (from estimator-engine output)
POST /v3/company/{realmId}/estimate
{
"CustomerRef": {"value": "123"},
"TxnDate": "2026-01-15",
"ExpirationDate": "2026-02-14",
"Line": [
{
"DetailType": "SalesItemLineDetail",
"Amount": 3125.00,
"Description": "Service - 2500 sqft, vaulted ceilings, moderate tobacco",
"SalesItemLineDetail": {"ItemRef": {"value": "1", "name": "Base Treatment"}, "Qty": 2500, "UnitPrice": 1.25}
},
{"DetailType": "SalesItemLineDetail", "Amount": 350.00, "Description": "Cigarette >5yrs extended treatment", "SalesItemLineDetail": {"ItemRef": {"value": "2"}, "Qty": 1, "UnitPrice": 350}},
{"DetailType": "SalesItemLineDetail", "Amount": 75.00, "Description": "Travel surcharge - Premium Region", "SalesItemLineDetail": {"ItemRef": {"value": "3"}, "Qty": 1, "UnitPrice": 75}}
],
"CustomerMemo": {"value": "Valid 30 days. 50% deposit to schedule."}
}
# Send estimate email
POST /v3/company/{realmId}/estimate/{estimateId}/send?sendTo={email}
Invoices
# Create invoice from accepted estimate
POST /v3/company/{realmId}/invoice
{
"CustomerRef": {"value": "123"},
"TxnDate": "2026-01-20",
"DueDate": "2026-02-03",
"Line": [...same line items...],
"Deposit": 1562.50, # 50% deposit collected via Stripe
"CustomerMemo": {"value": "Balance due upon completion"}
}
# Get invoice PDF
GET /v3/company/{realmId}/invoice/{invoiceId}/pdf
Payments
# Record payment (from Stripe webhook)
POST /v3/company/{realmId}/payment
{
"CustomerRef": {"value": "123"},
"TotalAmt": 1562.50,
"PaymentRefNum": "pi_3StripePaymentIntentId",
"PaymentMethodRef": {"value": "5"}, # Credit Card
"DepositToAccountRef": {"value": "35"}, # Undeposited Funds
"Line": [{"Amount": 1562.50, "LinkedTxn": [{"TxnId": "456", "TxnType": "Invoice"}]}]
}
Items (Products/Services)
# Ensure service items exist (run once on setup)
# Base Treatment (per sqft)
# Cigarette Extended Treatment (each)
# Travel Surcharge (each)
# HVAC Duct Treatment (each)
# High Rise Surcharge (each)
# Region Surcharge (each)
Agent Integration Patterns
"Create estimate for John Smith at 123 Main St..."
# 1. Look up or create customer in QB
# 2. Call estimator-engine with property details
# 3. Transform estimator output → QB estimate line items
# 4. Create estimate in QB
# 5. Email PDF to customer
# 6. Create CRM estimate record (status=sent)
# 7. Create follow-up task (3 days)
"Customer accepted estimate #EST-0001-0042"
# 1. Update CRM estimate status=accepted
# 2. Create invoice in QB (copy line items)
# 3. Generate Stripe Payment Link for 50% deposit
# 4. Text customer deposit link
# 5. Create job scheduled task for the owner
"Record payment from Stripe webhook"
# 1. Verify webhook signature
# 2. Find invoice by payment_intent metadata
# 3. Create QB Payment linked to invoice
# 4. Update CRM invoice status=paid
# 5. Notify owners via Telegram
Common Pitfalls
- Token expiry — Always wrap API calls in try/except 401 → refresh → retry
- Sandbox vs production — Different base URLs, different realm IDs, different client credentials
- Minor version — Always send
minorversion=75header for latest API - Idempotency — Use
RequestIdheader for estimate/invoice creation to avoid duplicates - Rate limits — 500 requests/minute; implement exponential backoff
- Custom fields — QB custom fields need definition before use; use
Notesfield for flexibility - Tax — Many services are non-taxable; ensure items are marked non-taxable per your local tax rules
Verification Checklist
- OAuth flow completes and stores tokens
- Token refresh works (wait 1hr or force expiry)
- Create customer → appears in QB UI
- Create estimate with line items → PDF renders correctly
- Send estimate email → customer receives it
- Convert estimate to invoice → amounts match
- Record payment → invoice shows paid
- Weekly report pulls: outstanding estimates, AR aging, revenue MTD
One-Shot Recipes
"Sync new CRM lead to QuickBooks"
customer = qbo.upsert_customer(
name="Jane Doe",
email="[email protected]",
phone="555-555-0199",
address="456 Palm Ave, [CITY], [STATE] [ZIP]",
notes="Referral - Example Realty"
)
"Push estimator output to QB estimate"
estimate = qbo.create_estimate(
customer_id=customer.id,
line_items=estimator_output.line_items, # [{desc, qty, unit_price, total}]
txn_date="2026-01-15",
expiration_date="2026-02-14",
memo="Valid 30 days. 50% deposit to schedule."
)
qbo.send_estimate(estimate.id, customer.email)
"Weekly owner report data"
report = qbo.get_weekly_report(realm_id)
# Returns: {outstanding_estimates: [...], ar_aging: {...}, revenue_mtd: 45000, payments_this_week: [...]}
| 1 | |
| 2 | name quickbooks-online |
| 3 | description "Use when integrating with QuickBooks Online for invoicing, estimates, customers, and payments. Handles OAuth 2.0 auth, token refresh, and sandbox/production environments." |
| 4 | version "1.0.0" |
| 5 | author Hermes Agent |
| 6 | license MIT |
| 7 | metadata |
| 8 | hermes |
| 9 | tags [quickbooks, intuit, accounting, invoicing, small-business] |
| 10 | related_skills [crm-lite, estimator-engine, stripe-payments] |
| 11 | |
| 12 | |
| 13 | # QuickBooks Online Integration Skill |
| 14 | |
| 15 | ## Overview |
| 16 | Integrates a small business with QuickBooks Online for customer management, estimates, invoices, and payment tracking. Uses Intuit's OAuth 2.0 flow with automatic token refresh. Designed for agent-mediated use: owners text commands, agent executes QB API calls. |
| 17 | |
| 18 | ## When to Use |
| 19 | **Create/send estimates** from estimator-engine output |
| 20 | **Convert estimates to invoices** when job is booked |
| 21 | **Record payments** (Stripe webhook → QB payment) |
| 22 | **Sync customers** between CRM and QB |
| 23 | **Pull reports** for weekly owner summary (AR aging, revenue, outstanding estimates) |
| 24 | |
| 25 | ## Don't Use For |
| 26 | Complex inventory/assembly tracking |
| 27 | Multi-currency or multi-entity accounting |
| 28 | Payroll or tax filing |
| 29 | |
| 30 | ## Authentication (OAuth 2.0) |
| 31 | |
| 32 | ### Required Credentials (Environment Variables) |
| 33 | |
| 34 | QBO_CLIENT_ID=*** From Intuit Developer Dashboard |
| 35 | QBO_CLIENT_SECRET= # From Intuit Developer Dashboard |
| 36 | QBO_REDIRECT_URI= # e.g., https://your-domain.com/qbo/callback |
| 37 | QBO_ENVIRONMENT= # "sandbox" or "production" |
| 38 | QBO_COMPANY_ID= # Realm ID (set after first auth) |
| 39 | |
| 40 | |
| 41 | ### Token Storage |
| 42 | Store access_token + refresh_token + realm_id in `~/.hermes/qbo_tokens.json` |
| 43 | Auto-refresh: access_token expires in 1 hour; refresh_token expires in 101 days |
| 44 | On 401: refresh token, retry once, then force re-auth if refresh fails |
| 45 | |
| 46 | ### Auth Flow |
| 47 | User visits: `https://appcenter.intuit.com/connect/oauth2?client_id={id}&redirect_uri={uri}&scope=com.intuit.quickbooks.accounting&response_type=code&state={random}` |
| 48 | Callback receives `code` + `realmId` |
| 49 | Exchange code for tokens: `POST https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer` |
| 50 | Store tokens + realm_id |
| 51 | |
| 52 | ## Core API Operations |
| 53 | |
| 54 | ### Customers |
| 55 | |
| 56 | # Create customer (from CRM lead) |
| 57 | POST /v3/company/{realmId}/customer |
| 58 | { |
| 59 | "DisplayName": "John Smith", |
| 60 | "PrimaryEmailAddr": {"Address": "[email protected]"}, |
| 61 | "PrimaryPhone": {"FreeFormNumber": "555-555-0123"}, |
| 62 | "BillAddr": {"Line1": "123 Main St", "City": "[CITY]", "CountrySubDivisionCode": "[STATE]", "PostalCode": "[ZIP]"}, |
| 63 | "Notes": "Lead from referral" |
| 64 | } |
| 65 | |
| 66 | # Query customer |
| 67 | GET /v3/company/{realmId}/query?query=SELECT * FROM Customer WHERE DisplayName='John Smith' |
| 68 | |
| 69 | |
| 70 | ### Estimates |
| 71 | |
| 72 | # Create estimate (from estimator-engine output) |
| 73 | POST /v3/company/{realmId}/estimate |
| 74 | { |
| 75 | "CustomerRef": {"value": "123"}, |
| 76 | "TxnDate": "2026-01-15", |
| 77 | "ExpirationDate": "2026-02-14", |
| 78 | "Line": [ |
| 79 | { |
| 80 | "DetailType": "SalesItemLineDetail", |
| 81 | "Amount": 3125.00, |
| 82 | "Description": "Service - 2500 sqft, vaulted ceilings, moderate tobacco", |
| 83 | "SalesItemLineDetail": {"ItemRef": {"value": "1", "name": "Base Treatment"}, "Qty": 2500, "UnitPrice": 1.25} |
| 84 | }, |
| 85 | {"DetailType": "SalesItemLineDetail", "Amount": 350.00, "Description": "Cigarette >5yrs extended treatment", "SalesItemLineDetail": {"ItemRef": {"value": "2"}, "Qty": 1, "UnitPrice": 350}}, |
| 86 | {"DetailType": "SalesItemLineDetail", "Amount": 75.00, "Description": "Travel surcharge - Premium Region", "SalesItemLineDetail": {"ItemRef": {"value": "3"}, "Qty": 1, "UnitPrice": 75}} |
| 87 | ], |
| 88 | "CustomerMemo": {"value": "Valid 30 days. 50% deposit to schedule."} |
| 89 | } |
| 90 | |
| 91 | # Send estimate email |
| 92 | POST /v3/company/{realmId}/estimate/{estimateId}/send?sendTo={email} |
| 93 | |
| 94 | |
| 95 | ### Invoices |
| 96 | |
| 97 | # Create invoice from accepted estimate |
| 98 | POST /v3/company/{realmId}/invoice |
| 99 | { |
| 100 | "CustomerRef": {"value": "123"}, |
| 101 | "TxnDate": "2026-01-20", |
| 102 | "DueDate": "2026-02-03", |
| 103 | "Line": [...same line items...], |
| 104 | "Deposit": 1562.50, # 50% deposit collected via Stripe |
| 105 | "CustomerMemo": {"value": "Balance due upon completion"} |
| 106 | } |
| 107 | |
| 108 | # Get invoice PDF |
| 109 | GET /v3/company/{realmId}/invoice/{invoiceId}/pdf |
| 110 | |
| 111 | |
| 112 | ### Payments |
| 113 | |
| 114 | # Record payment (from Stripe webhook) |
| 115 | POST /v3/company/{realmId}/payment |
| 116 | { |
| 117 | "CustomerRef": {"value": "123"}, |
| 118 | "TotalAmt": 1562.50, |
| 119 | "PaymentRefNum": "pi_3StripePaymentIntentId", |
| 120 | "PaymentMethodRef": {"value": "5"}, # Credit Card |
| 121 | "DepositToAccountRef": {"value": "35"}, # Undeposited Funds |
| 122 | "Line": [{"Amount": 1562.50, "LinkedTxn": [{"TxnId": "456", "TxnType": "Invoice"}]}] |
| 123 | } |
| 124 | |
| 125 | |
| 126 | ### Items (Products/Services) |
| 127 | |
| 128 | # Ensure service items exist (run once on setup) |
| 129 | # Base Treatment (per sqft) |
| 130 | # Cigarette Extended Treatment (each) |
| 131 | # Travel Surcharge (each) |
| 132 | # HVAC Duct Treatment (each) |
| 133 | # High Rise Surcharge (each) |
| 134 | # Region Surcharge (each) |
| 135 | |
| 136 | |
| 137 | ## Agent Integration Patterns |
| 138 | |
| 139 | ### "Create estimate for John Smith at 123 Main St..." |
| 140 | |
| 141 | # 1. Look up or create customer in QB |
| 142 | # 2. Call estimator-engine with property details |
| 143 | # 3. Transform estimator output → QB estimate line items |
| 144 | # 4. Create estimate in QB |
| 145 | # 5. Email PDF to customer |
| 146 | # 6. Create CRM estimate record (status=sent) |
| 147 | # 7. Create follow-up task (3 days) |
| 148 | |
| 149 | |
| 150 | ### "Customer accepted estimate #EST-0001-0042" |
| 151 | |
| 152 | # 1. Update CRM estimate status=accepted |
| 153 | # 2. Create invoice in QB (copy line items) |
| 154 | # 3. Generate Stripe Payment Link for 50% deposit |
| 155 | # 4. Text customer deposit link |
| 156 | # 5. Create job scheduled task for the owner |
| 157 | |
| 158 | |
| 159 | ### "Record payment from Stripe webhook" |
| 160 | |
| 161 | # 1. Verify webhook signature |
| 162 | # 2. Find invoice by payment_intent metadata |
| 163 | # 3. Create QB Payment linked to invoice |
| 164 | # 4. Update CRM invoice status=paid |
| 165 | # 5. Notify owners via Telegram |
| 166 | |
| 167 | |
| 168 | ## Common Pitfalls |
| 169 | |
| 170 | **Token expiry** — Always wrap API calls in try/except 401 → refresh → retry |
| 171 | **Sandbox vs production** — Different base URLs, different realm IDs, different client credentials |
| 172 | **Minor version** — Always send `minorversion=75` header for latest API |
| 173 | **Idempotency** — Use `RequestId` header for estimate/invoice creation to avoid duplicates |
| 174 | **Rate limits** — 500 requests/minute; implement exponential backoff |
| 175 | **Custom fields** — QB custom fields need definition before use; use `Notes` field for flexibility |
| 176 | **Tax** — Many services are non-taxable; ensure items are marked non-taxable per your local tax rules |
| 177 | |
| 178 | ## Verification Checklist |
| 179 | [ ] OAuth flow completes and stores tokens |
| 180 | [ ] Token refresh works (wait 1hr or force expiry) |
| 181 | [ ] Create customer → appears in QB UI |
| 182 | [ ] Create estimate with line items → PDF renders correctly |
| 183 | [ ] Send estimate email → customer receives it |
| 184 | [ ] Convert estimate to invoice → amounts match |
| 185 | [ ] Record payment → invoice shows paid |
| 186 | [ ] Weekly report pulls: outstanding estimates, AR aging, revenue MTD |
| 187 | |
| 188 | ## One-Shot Recipes |
| 189 | |
| 190 | ### "Sync new CRM lead to QuickBooks" |
| 191 | |
| 192 | customer = qbo.upsert_customer( |
| 193 | name="Jane Doe", |
| 194 | email="[email protected]", |
| 195 | phone="555-555-0199", |
| 196 | address="456 Palm Ave, [CITY], [STATE] [ZIP]", |
| 197 | notes="Referral - Example Realty" |
| 198 | ) |
| 199 | |
| 200 | |
| 201 | ### "Push estimator output to QB estimate" |
| 202 | |
| 203 | estimate = qbo.create_estimate( |
| 204 | customer_id=customer.id, |
| 205 | line_items=estimator_output.line_items, # [{desc, qty, unit_price, total}] |
| 206 | txn_date="2026-01-15", |
| 207 | expiration_date="2026-02-14", |
| 208 | memo="Valid 30 days. 50% deposit to schedule." |
| 209 | ) |
| 210 | qbo.send_estimate(estimate.id, customer.email) |
| 211 | |
| 212 | |
| 213 | ### "Weekly owner report data" |
| 214 | |
| 215 | report = qbo.get_weekly_report(realm_id) |
| 216 | # Returns: {outstanding_estimates: [...], ar_aging: {...}, revenue_mtd: 45000, payments_this_week: [...]} |
| 217 |
Discussion
Alternatives
AI Finance OpsAI-powered financial analysis suite. Generates executive CFO briefings from QuickBooks exports (P&L, Balance Sheet, General Ledger, Cash Flow, etc.) with anomaly detection, burn rate, runway analysis, and scenario modeling. Also estimates codebase development costs with organizational overhead and AI ROI analysis. Triggers on: 'CFO briefing', 'financial analysis', 'cost briefing', 'expense review', 'runway analysis', 'burn rate', 'cost estimate', 'how much would this cost to build', 'development cost', 'Claude ROI'.
Stripe payment builderAct as a Stripe payment setup assistant. Configure payment options with variables for payment type and amount.📒 Bookkeeper & Controller AgentExpert bookkeeper and controller specializing in day-to-day accounting operations, financial reconciliations, month-end close processes, and internal controls. Ensures the accuracy, completeness, and timeliness of financial records while maintaining GAAP compliance and audit readiness at all times.Payment integrationIntegrate Stripe, PayPal, and payment processors. Handles checkout flows, subscriptions, webhooks, and PCI compliance. Use PROACTIVELY when implementing payments, billing, or subscription features.
Browse more free Claude skills or everything in Finance.