Skip to content
Back to skills

Reports

ASecurity

Use this skill when working with QuickBooks Online reports - generating Profit & Loss, Balance Sheet, Accounts Receivable Aging, Accounts Payable Aging, General Ledger, and other financial reports. Covers report parameters, date ranges, column customization, and MSP-specific financial analysis patterns like client profitability and aged receivables for collections.

  • 48 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 27, 2026
data-aijavascriptgojavabashapidocumentation

Works with

  • cli
  • api

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add wyre-technology/msp-claude-plugins --skill reports --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Reports?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Reports
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/wyre-technology-reports/badge)](https://www.skillsdirectory.com/skills/wyre-technology-reports)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: "QuickBooks Online Reports"
description: >
  Use this skill when working with QuickBooks Online reports -
  generating Profit & Loss, Balance Sheet, Accounts Receivable Aging,
  Accounts Payable Aging, General Ledger, and other financial reports.
  Covers report parameters, date ranges, column customization, and
  MSP-specific financial analysis patterns like client profitability
  and aged receivables for collections.
when_to_use: "When generating Profit & Loss, Balance Sheet, Accounts Receivable Aging, Accounts Payable Aging, General Ledger, and other financial reports"
triggers:
  - quickbooks report
  - qbo report
  - profit and loss
  - balance sheet
  - accounts receivable aging
  - accounts payable aging
  - general ledger
  - financial report
  - p&l report
  - ar aging
  - ap aging
  - aged receivables
  - client profitability
---

# QuickBooks Online Reports

## Overview

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.

## Key Concepts

### Report Categories

| 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 |

### Report Parameters

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` |

### Date Macros

| 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 |

### Report Response Structure

All reports return a common structure:

```json
{
  "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": [...]
  }
}
```

## Report Types

### Profit & Loss (Income Statement)

Shows revenue and expenses over a period.

```http
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
```

```bash
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:**
```http
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):**
```http
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:**
```http
GET /v3/company/{realmId}/reports/ProfitAndLoss?start_date=2026-01-01&end_date=2026-01-31&customer=123&minorversion=73
```

### Balance Sheet

Shows assets, liabilities, and equity at a point in time.

```http
GET /v3/company/{realmId}/reports/BalanceSheet?date_macro=Today&minorversion=73
Authorization: Bearer {access_token}
```

```bash
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:**
```http
GET /v3/company/{realmId}/reports/BalanceSheet?start_date=2025-01-01&end_date=2026-01-31&summarize_column_by=Quarter&minorversion=73
```

### Accounts Receivable Aging

Shows outstanding customer balances grouped by aging period. Critical for MSP collections.

**Summary (by customer):**
```http
GET /v3/company/{realmId}/reports/AgedReceivables?date_macro=Today&minorversion=73
Authorization: Bearer {access_token}
```

```bash
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):**
```http
GET /v3/company/{realmId}/reports/AgedReceivableDetail?date_macro=Today&minorversion=73
```

**For a specific customer:**
```http
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 |

### Accounts Payable Aging

Shows outstanding vendor balances.

```http
GET /v3/company/{realmId}/reports/AgedPayables?date_macro=Today&minorversion=73
```

**Detail level:**
```http
GET /v3/company/{realmId}/reports/AgedPayableDetail?date_macro=Today&minorversion=73
```

### General Ledger

Transaction-level detail for all accounts.

```http
GET /v3/company/{realmId}/reports/GeneralLedger?start_date=2026-01-01&end_date=2026-01-31&minorversion=73
```

**For a specific account:**
```http
GET /v3/company/{realmId}/reports/GeneralLedger?start_date=2026-01-01&end_date=2026-01-31&account=35&minorversion=73
```

### Customer Sales Summary

Revenue by customer.

```http
GET /v3/company/{realmId}/reports/CustomerSales?start_date=2026-01-01&end_date=2026-01-31&minorversion=73
```

### Customer Income

Income detail by customer.

```http
GET /v3/company/{realmId}/reports/CustomerIncome?start_date=2026-01-01&end_date=2026-01-31&minorversion=73
```

### Cash Flow Statement

```http
GET /v3/company/{realmId}/reports/CashFlow?start_date=2026-01-01&end_date=2026-01-31&minorversion=73
```

## Parsing Report Data

### Row Structure

Report rows are nested and can contain groups (sections) and data rows:

```json
{
  "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"
    }
  ]
}
```

### Recursive Row Parser

```javascript
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;
}
```

## Common Workflows

### MSP Monthly Financial Review

```javascript
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)
  };
}
```

### Client Profitability Dashboard

```javascript
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
  };
}
```

### A/R Aging Collections Alert

```javascript
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);
}
```

### Monthly Revenue Trend

```javascript
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
  };
}
```

### Cash Flow Forecast

```javascript
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)
  };
}
```

## Error Handling

### Common API Errors

| 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 |

### Validation Errors

| 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 |

### Error Recovery Pattern

```javascript
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;
  }
}
```

## Best Practices

1. **Use date macros** - Prefer `date_macro` for standard periods (less error-prone than manual dates)
2. **Specify accounting method** - Always set `accounting_method` explicitly for consistency
3. **Summarize by column** - Use `summarize_column_by=Month` for trend analysis
4. **Cache reports** - Reports are read-only; cache results for dashboards
5. **Filter by customer** - Use the `customer` parameter for client-specific reports
6. **Parse recursively** - Report rows are nested; use recursive parsing
7. **Include minor version** - Always add `minorversion=73` for latest report features
8. **Run reports in parallel** - Fetch multiple reports concurrently for dashboards
9. **Monitor A/R aging** - Set up automated alerts for overdue accounts
10. **Track trends** - Compare P&L by month to spot revenue changes early

## Endpoint Reference

| 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` |

## Related Skills

- [QBO Customers](../customers/SKILL.md) - Customer data for report filtering
- [QBO Invoices](../invoices/SKILL.md) - Invoice data behind A/R aging
- [QBO Payments](../payments/SKILL.md) - Payment data behind cash flow
- [QBO Expenses](../expenses/SKILL.md) - Expense data behind P&L
- [QBO API Patterns](../api-patterns/SKILL.md) - API reference

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…