Help us improve
Share bugs, ideas, or general feedback.
From quickbooks-online
Generates QuickBooks Online financial reports including Profit & Loss, Balance Sheet, Accounts Receivable Aging, Accounts Payable Aging, General Ledger. Covers report parameters, date ranges, column customization, and MSP analysis like client profitability.
npx claudepluginhub wyre-technology/msp-claude-plugins --plugin quickbooks-onlineHow this skill is triggered — by the user, by Claude, or both
Slash command
/quickbooks-online:reportsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
QuickBooks Online provides a comprehensive set of financial reports accessible via the API. For MSPs, the most critical reports are Accounts Receivable Aging (tracking which clients owe money and how overdue), Profit & Loss (measuring overall and per-client profitability), Balance Sheet (financial position), and Accounts Payable Aging (tracking vendor obligations). Reports are read-only API cal...
Guides financial statement preparation, analysis, and plain-language explanation of accounting concepts for small business owners.
Generates income statements, balance sheets, cash flows with period-over-period comparisons and variance analysis for monthly/quarterly P&L, budget vs actuals, and GAAP-compliant reporting.
Creates, searches, and manages QuickBooks Online expense records, bills, and vendor payments. Covers Purchase (checks, credit cards, cash) and Bill entities, per-client cost tracking, vendor management, MSP categorization.
Share bugs, ideas, or general feedback.
QuickBooks Online provides a comprehensive set of financial reports accessible via the API. For MSPs, the most critical reports are Accounts Receivable Aging (tracking which clients owe money and how overdue), Profit & Loss (measuring overall and per-client profitability), Balance Sheet (financial position), and Accounts Payable Aging (tracking vendor obligations). Reports are read-only API calls that return structured data suitable for dashboards, alerts, and automated analysis.
| Category | Reports | MSP Relevance |
|---|---|---|
| Profit & Loss | ProfitAndLoss, ProfitAndLossDetail | Revenue and expense by period |
| Balance Sheet | BalanceSheet, BalanceSheetDetail | Financial position snapshot |
| A/R Aging | AgedReceivables, AgedReceivableDetail | Client collections tracking |
| A/P Aging | AgedPayables, AgedPayableDetail | Vendor payment obligations |
| General Ledger | GeneralLedger, GeneralLedgerDetail | Transaction-level audit trail |
| Sales | CustomerSales, CustomerIncome, ItemSales | Revenue by customer/item |
| Expenses | ExpensesByVendor | Cost tracking by vendor |
| Tax | TaxSummary | Sales tax obligations |
| Cash Flow | CashFlow | Cash inflows and outflows |
All reports support common parameters for filtering and customization:
| Parameter | Description | Example |
|---|---|---|
start_date | Period start | 2026-01-01 |
end_date | Period end | 2026-01-31 |
accounting_method | Accrual or Cash | Accrual |
date_macro | Preset period | Last Month, This Fiscal Year |
summarize_column_by | Column grouping | Month, Quarter, Year, Customers |
customer | Filter by customer ID | 123 |
department | Filter by department | 1 |
| Macro | Description |
|---|---|
Today | Current day |
This Week | Current week |
This Month | Current month |
This Fiscal Quarter | Current fiscal quarter |
This Fiscal Year | Current fiscal year |
Last Month | Previous month |
Last Fiscal Quarter | Previous quarter |
Last Fiscal Year | Previous fiscal year |
This Fiscal Year-to-date | Year to date |
All reports return a common structure:
{
"Header": {
"ReportName": "ProfitAndLoss",
"DateMacro": "Last Month",
"StartPeriod": "2026-01-01",
"EndPeriod": "2026-01-31",
"Currency": "USD",
"Option": [
{ "Name": "AccountingMethod", "Value": "Accrual" }
]
},
"Columns": {
"Column": [
{ "ColTitle": "", "ColType": "Account" },
{ "ColTitle": "Total", "ColType": "Money" }
]
},
"Rows": {
"Row": [...]
}
}
Shows revenue and expenses over a period.
GET /v3/company/{realmId}/reports/ProfitAndLoss?start_date=2026-01-01&end_date=2026-01-31&accounting_method=Accrual&minorversion=73
Authorization: Bearer {access_token}
Accept: application/json
curl -s -H "Authorization: Bearer $QBO_ACCESS_TOKEN" \
-H "Accept: application/json" \
"https://quickbooks.api.intuit.com/v3/company/$QBO_REALM_ID/reports/ProfitAndLoss?start_date=2026-01-01&end_date=2026-01-31&accounting_method=Accrual&minorversion=73"
By month:
GET /v3/company/{realmId}/reports/ProfitAndLoss?start_date=2026-01-01&end_date=2026-06-30&summarize_column_by=Month&minorversion=73
By customer (MSP client profitability):
GET /v3/company/{realmId}/reports/ProfitAndLoss?start_date=2026-01-01&end_date=2026-01-31&summarize_column_by=Customers&minorversion=73
Filtered to a single customer:
GET /v3/company/{realmId}/reports/ProfitAndLoss?start_date=2026-01-01&end_date=2026-01-31&customer=123&minorversion=73
Shows assets, liabilities, and equity at a point in time.
GET /v3/company/{realmId}/reports/BalanceSheet?date_macro=Today&minorversion=73
Authorization: Bearer {access_token}
curl -s -H "Authorization: Bearer $QBO_ACCESS_TOKEN" \
-H "Accept: application/json" \
"https://quickbooks.api.intuit.com/v3/company/$QBO_REALM_ID/reports/BalanceSheet?date_macro=Today&minorversion=73"
Comparison by quarter:
GET /v3/company/{realmId}/reports/BalanceSheet?start_date=2025-01-01&end_date=2026-01-31&summarize_column_by=Quarter&minorversion=73
Shows outstanding customer balances grouped by aging period. Critical for MSP collections.
Summary (by customer):
GET /v3/company/{realmId}/reports/AgedReceivables?date_macro=Today&minorversion=73
Authorization: Bearer {access_token}
curl -s -H "Authorization: Bearer $QBO_ACCESS_TOKEN" \
-H "Accept: application/json" \
"https://quickbooks.api.intuit.com/v3/company/$QBO_REALM_ID/reports/AgedReceivables?date_macro=Today&minorversion=73"
Detail (individual invoices):
GET /v3/company/{realmId}/reports/AgedReceivableDetail?date_macro=Today&minorversion=73
For a specific customer:
GET /v3/company/{realmId}/reports/AgedReceivableDetail?date_macro=Today&customer=123&minorversion=73
Aging periods in the response:
| Column | Description |
|---|---|
| Current | Not yet due |
| 1-30 | 1-30 days past due |
| 31-60 | 31-60 days past due |
| 61-90 | 61-90 days past due |
| 91 and over | 91+ days past due |
Shows outstanding vendor balances.
GET /v3/company/{realmId}/reports/AgedPayables?date_macro=Today&minorversion=73
Detail level:
GET /v3/company/{realmId}/reports/AgedPayableDetail?date_macro=Today&minorversion=73
Transaction-level detail for all accounts.
GET /v3/company/{realmId}/reports/GeneralLedger?start_date=2026-01-01&end_date=2026-01-31&minorversion=73
For a specific account:
GET /v3/company/{realmId}/reports/GeneralLedger?start_date=2026-01-01&end_date=2026-01-31&account=35&minorversion=73
Revenue by customer.
GET /v3/company/{realmId}/reports/CustomerSales?start_date=2026-01-01&end_date=2026-01-31&minorversion=73
Income detail by customer.
GET /v3/company/{realmId}/reports/CustomerIncome?start_date=2026-01-01&end_date=2026-01-31&minorversion=73
GET /v3/company/{realmId}/reports/CashFlow?start_date=2026-01-01&end_date=2026-01-31&minorversion=73
Report rows are nested and can contain groups (sections) and data rows:
{
"Row": [
{
"Header": { "ColData": [{ "value": "Income" }] },
"Rows": {
"Row": [
{
"ColData": [
{ "value": "Managed Services Revenue", "id": "1" },
{ "value": "25000.00" }
]
},
{
"ColData": [
{ "value": "Project Revenue", "id": "2" },
{ "value": "8500.00" }
]
}
]
},
"Summary": { "ColData": [{ "value": "Total Income" }, { "value": "33500.00" }] },
"type": "Section",
"group": "Income"
}
]
}
function parseReportRows(rows, depth = 0) {
const results = [];
for (const row of rows || []) {
if (row.type === 'Section') {
// Section with header, nested rows, and summary
const sectionName = row.Header?.ColData?.[0]?.value || '';
const children = parseReportRows(row.Rows?.Row, depth + 1);
const summary = row.Summary?.ColData?.map(c => c.value);
results.push({
type: 'section',
name: sectionName,
children,
summary,
depth
});
} else if (row.ColData) {
// Data row
const values = row.ColData.map(c => c.value);
results.push({
type: 'data',
values,
depth
});
}
}
return results;
}
async function monthlyFinancialReview(month) {
const startDate = `${month}-01`;
const endDate = new Date(new Date(startDate).setMonth(new Date(startDate).getMonth() + 1) - 1)
.toISOString().split('T')[0];
// Fetch all key reports in parallel
const [pnl, arAging, apAging, customerSales] = await Promise.all([
fetchReport('ProfitAndLoss', { start_date: startDate, end_date: endDate }),
fetchReport('AgedReceivables', { date_macro: 'Today' }),
fetchReport('AgedPayables', { date_macro: 'Today' }),
fetchReport('CustomerSales', { start_date: startDate, end_date: endDate })
]);
return {
period: month,
profitAndLoss: parsePnl(pnl),
accountsReceivable: parseAging(arAging),
accountsPayable: parseAging(apAging),
revenueByClient: parseCustomerSales(customerSales)
};
}
async function clientProfitabilityReport(startDate, endDate) {
// P&L summarized by customer
const report = await fetchReport('ProfitAndLoss', {
start_date: startDate,
end_date: endDate,
summarize_column_by: 'Customers'
});
const parsed = parseReportRows(report.Rows?.Row);
// Extract income and expense sections
const income = parsed.find(r => r.name === 'Income');
const expenses = parsed.find(r => r.name === 'Expenses');
const netIncome = parsed.find(r => r.name === 'Net Income');
return {
period: `${startDate} to ${endDate}`,
columns: report.Columns.Column.map(c => c.ColTitle),
income: income?.summary,
expenses: expenses?.summary,
netIncome: netIncome?.summary
};
}
async function collectionsAlert(thresholdDays = 60, thresholdAmount = 1000) {
const report = await fetchReport('AgedReceivableDetail', { date_macro: 'Today' });
const rows = parseReportRows(report.Rows?.Row);
const alerts = [];
for (const section of rows) {
if (section.type !== 'section') continue;
// Check 61-90 and 91+ columns
for (const child of section.children || []) {
if (child.type === 'data') {
const amount = parseFloat(child.values[child.values.length - 1]) || 0;
const daysOverdue = parseInt(child.values[3]) || 0;
if (daysOverdue >= thresholdDays && amount >= thresholdAmount) {
alerts.push({
customer: section.name,
invoiceNumber: child.values[1],
amount,
daysOverdue
});
}
}
}
}
return alerts.sort((a, b) => b.daysOverdue - a.daysOverdue);
}
async function revenueTrend(months = 12) {
const endDate = new Date().toISOString().split('T')[0];
const startDate = new Date(new Date().setMonth(new Date().getMonth() - months))
.toISOString().split('T')[0];
const report = await fetchReport('ProfitAndLoss', {
start_date: startDate,
end_date: endDate,
summarize_column_by: 'Month'
});
const parsed = parseReportRows(report.Rows?.Row);
const incomeSection = parsed.find(r => r.name === 'Income');
return {
period: `${startDate} to ${endDate}`,
columns: report.Columns.Column.map(c => c.ColTitle).filter(c => c),
monthlyRevenue: incomeSection?.summary?.slice(1) // Skip label column
};
}
async function cashFlowSnapshot() {
const today = new Date().toISOString().split('T')[0];
const thirtyDaysAgo = new Date(Date.now() - 30 * 86400000).toISOString().split('T')[0];
const [arAging, apAging, balanceSheet] = await Promise.all([
fetchReport('AgedReceivables', { date_macro: 'Today' }),
fetchReport('AgedPayables', { date_macro: 'Today' }),
fetchReport('BalanceSheet', { date_macro: 'Today' })
]);
return {
date: today,
receivables: parseAgingTotals(arAging),
payables: parseAgingTotals(apAging),
netCashPosition: parseBalanceSheetCash(balanceSheet)
};
}
| Code | Message | Resolution |
|---|---|---|
| 400 | Invalid report parameter | Check date format and parameter names |
| 401 | Auth Failed | Refresh access token |
| 3000 | Report not available | Check report name spelling |
| 3001 | Throttled | Wait and retry |
| Error | Cause | Fix |
|---|---|---|
| Invalid date range | start_date > end_date | Fix date order |
| Unknown parameter | Misspelled parameter | Check API documentation |
| Invalid accounting method | Bad method value | Use "Accrual" or "Cash" |
| Invalid date macro | Unrecognized macro | Use supported macro values |
async function safeFetchReport(reportName, params) {
try {
return await fetchReport(reportName, params);
} catch (error) {
const fault = error.Fault;
if (!fault) throw error;
if (fault.type === 'AuthenticationFault') {
await refreshAccessToken();
return await fetchReport(reportName, params);
}
if (fault.type === 'THROTTLE') {
await new Promise(r => setTimeout(r, 60000));
return await fetchReport(reportName, params);
}
throw error;
}
}
date_macro for standard periods (less error-prone than manual dates)accounting_method explicitly for consistencysummarize_column_by=Month for trend analysiscustomer parameter for client-specific reportsminorversion=73 for latest report features| Report | Endpoint |
|---|---|
| Profit & Loss | /v3/company/{realmId}/reports/ProfitAndLoss |
| Profit & Loss Detail | /v3/company/{realmId}/reports/ProfitAndLossDetail |
| Balance Sheet | /v3/company/{realmId}/reports/BalanceSheet |
| Balance Sheet Detail | /v3/company/{realmId}/reports/BalanceSheetDetail |
| A/R Aging Summary | /v3/company/{realmId}/reports/AgedReceivables |
| A/R Aging Detail | /v3/company/{realmId}/reports/AgedReceivableDetail |
| A/P Aging Summary | /v3/company/{realmId}/reports/AgedPayables |
| A/P Aging Detail | /v3/company/{realmId}/reports/AgedPayableDetail |
| General Ledger | /v3/company/{realmId}/reports/GeneralLedger |
| Customer Sales | /v3/company/{realmId}/reports/CustomerSales |
| Customer Income | /v3/company/{realmId}/reports/CustomerIncome |
| Cash Flow | /v3/company/{realmId}/reports/CashFlow |
| Tax Summary | /v3/company/{realmId}/reports/TaxSummary |