QuickBooks Online API — SKILL.md
Raw skill file that agents receive when using this skill
---
name: "QuickBooks Online API"
description: "Integrate with QuickBooks Online: OAuth 2.0 auth, invoices, payments, customers, chart of accounts, webhooks, SDK usage, sandbox testing, and app review process. Use when building accounting sync for any SaaS product."
version: "1.0.0"
author: "skynet"
category: "dev"
agents: ["claude-code", "codex", "gemini"]
tags: ["quickbooks", "accounting", "api", "oauth", "invoicing", "saas"]
---
# QuickBooks Online API
---
name: "QuickBooks Online API"
description: "Integrate with QuickBooks Online: OAuth 2.0 auth, invoices, payments, customers, chart of accounts, webhooks, SDK usage, sandbox testing, and app review process. Use when building accounting sync for "
version: "1.0.0"
author: "skynet"
category: "dev"
tags: ["quickbooks", "accounting", "api", "oauth", "invoicing", "saas"]
---
This technical reference outlines the architecture, implementation patterns, and pitfalls of integrating with the QuickBooks Online (QBO) V3 REST API.
---
### 1. Authentication: OAuth 2.0 Flow
QBO uses standard OAuth 2.0. Every request requires an `AccessToken` and a `realmId` (Company ID).
* **Scopes:** Most integrations require `com.intuit.quickbooks.accounting`. Use `com.intuit.quickbooks.payment` for credit card processing.
* **Token Lifecycle:**
* **Access Token:** Expires in 60 minutes.
* **Refresh Token:** Lasts 100 days. **Note:** When you use a refresh token to get a new access token, the refresh token value *may* change. Always persist the latest refresh token returned in the response.
* **Authorization URL:**
`https://appcenter.intuit.com/connect/oauth2?client_id=CLIENT_ID&response_type=code&scope=SCOPE&redirect_uri=REDIRECT_URI&state=STATE`
**Token Exchange Example (Node.js/Axios):**
```javascript
const response = await axios.post('https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer',
qs.stringify({
grant_type: 'authorization_code',
code: authCode,
redirect_uri: REDIRECT_URI
}), {
headers: { 'Authorization': `Basic ${Buffer.from(CLIENT_ID + ":" + CLIENT_SECRET).toString('base64')}` }
});
```
---
### 2. Key Endpoints & Base URL
* **Sandbox:** `https://sandbox-quickbooks.api.intuit.com/v3/company/{realmId}/`
* **Production:** `https://quickbooks.api.intuit.com/v3/company/{realmId}/`
| Entity | Endpoint | Purpose |
| :--- | :--- | :--- |
| **CompanyInfo** | `/companyinfo/{realmId}` | Get currency, fiscal year, and address. |
| **Customer** | `/customer` | Mapping SaaS users to QBO entities. |
| **Invoice** | `/invoice` | Primary billing record. Linked to Items. |
| **Payment** | `/payment` | Records cash flow against an Invoice. |
| **Item** | `/item` | Products or Services. Must link to an Income Account. |
| **Account** | `/account` | The Chart of Accounts (COA). |
| **JournalEntry** | `/journalentry` | Low-level balancing for complex payroll/adjustments. |
---
### 3. Common Implementation Patterns
#### SaaS Order to Invoice Sync
1. **Check Customer:** Query by email. If not found, create.
2. **Verify Items:** Match SaaS SKUs to QBO `Item` objects. If they don't exist, you must create them and map them to a `Sales of Product Income` account.
3. **Create Invoice:** Reference the `Line.SalesItemLineDetail.ItemRef`.
4. **Apply Payment:** Once the SaaS payment clears, create a `Payment` object in QBO and link it to the `Invoice` ID to mark it as "Paid".
#### Handling Taxes (TaxService)
* **US Entities:** Often use "Automated Sales Tax" (AST). You provide the address, and QBO calculates the rate.
* **Global (UK/AU/CA):** You must specify a `TaxCodeRef` (e.g., "GST", "VAT 20%").
* **Manual Override:** Use the `TaxService` entity to create custom tax rates if the built-in engine is insufficient.
---
### 4. Webhooks & Event Processing
Webhooks are asynchronous. Intuit sends a JSON array of changed entities.
* **Setup:** Configured in the Intuit Developer Portal.
* **Verification:** Intuit provides a `verifierToken`. You must hash the payload using `HMAC-SHA256` and compare it to the `intuit-signature` header.
* **Event Types:** `Create`, `Update`, `Delete`, `Merge`, `Void`.
* **Payload Example:**
```json
{
"eventNotifications": [{
"realmId": "123456789",
"dataChangeEvent": {
"entities": [{"name": "Invoice", "id": "95", "operation": "Update"}]
}
}]
}
```
---
### 5. SDKs & Libraries
* **Node.js:** `node-quickbooks` (Community favorite, though wrapper-heavy).
* **Python:** `python-quickbooks` (Excellent ORM-style mapping).
* **PHP:** `quickbooks-online-php-sdk` (Official Intuit support).
---
### 6. Critical Gotchas (The "Pain Points")
1. **The Minor Version:** The API behavior changes based on the `minorversion` query parameter (e.g., `?minorversion=65`). Always lock this in your API calls to prevent breaking changes.
2. **Sparse Updates:** When updating an object, set `sparse=true` in the JSON. If you don't, and you omit a field (like a billing address), QBO will overwrite it with null.
3. **Rate Limits:** 500 requests per minute per Realm ID. 40 batch requests per minute.
4. **Idempotency:** The API does not natively support idempotency keys. You must implement your own "Sync Token" logic (QBO provides a `SyncToken` version number on every object to prevent concurrent edit collisions).
5. **Pagination:** QBO uses SQL-like syntax: `SELECT * FROM Invoice STARTPOSITION 1 MAXRESULTS 100`.
---
### 7. QuickBooks Desktop (QBD) vs. Online
* **Architecture:** QBO is a modern REST API. QBD is a local database.
* **Connectivity:** QBD requires the **QuickBooks Web Connector (QBWC)**, an archaic SOAP-based middleware that "polls" your server for XML (QBXML) commands.
* **Migration:** Moving from QBD to QBO usually requires a full data re-mapping as the underlying schemas differ (e.g., QBD "Jobs" become QBO "Sub-Customers").
---
### 8. App Review & Production Requirements
To move from Sandbox to Production, Intuit requires:
1. **Security Review:** Disclosure of data handling and protection.
2. **Naming:** You cannot use "QuickBooks" in your app name (use "for QuickBooks").
3. **Launch Requirements:**
* Provide a "Disconnect" URL.
* Provide a "Sign in with Intuit" button.
* Maintain a `< 0.5%` API error rate.
---
### Example: Creating an Invoice (Python)
```python
from quickbooks.objects.invoice import Invoice, DeliveryInfo
from quickbooks.objects.customer import Customer
invoice = Invoice()
invoice.CustomerRef = Customer.get(1, qb_client).to_ref()
line_item = SalesItemLine()
line_item.Amount = 100.00
line_item.Description = "Consulting Services"
line_item.SalesItemLineDetail = {
"ItemRef": {"value": "5", "name": "Services"},
"UnitPrice": 100.00,
"Qty": 1
}
invoice.Line.append(line_item)
invoice.save(qb_client)
```
curl -s https://skills.skynet.ceo/api/skills/quickbooks-api/skill.md