---
name: quickbooks-expenses-by-category
description: "Every weekday: month-to-date spend by expense category from QuickBooks, against the same days of last month, with the categories running hot. Use when the user asks for this, or when it runs on a schedule."
---

# QuickBooks expenses by category

Every weekday: month-to-date spend by expense category from QuickBooks, against the same days of last month, with the categories running hot.

Overspending shows up in the P&L a month after it happens. Halfway through the month you can still act on it, if you know that software is up a third or that travel tripled.

This skill compares like with like: the first 16 days of this month against the first 16 days of last month, category by category, using the expense accounts your bookkeeper already keeps. With semimonthly payroll the paydays line up as well, so payroll doesn't distort the comparison.

## Two ways to get the data

Use the API or MCP route when you have an MCP server, a CLI or an API token for the app. Otherwise, if you can drive a browser the user is signed in to (Claude in Chrome, a Playwright or Chrome DevTools MCP server), use the browser route. Say which one you used.

## Route A: API or MCP server

### What you need

- A QuickBooks Online connection that can run reports (a connector, an MCP server, or an Intuit app with `com.intuit.quickbooks.accounting`), pointed at the right company.

If something is missing, say what and stop; do not invent numbers.

### Steps

1. **Pull month to date** (QuickBooks): Call `GET /v3/company/{realmId}/reports/ProfitAndLoss` from the 1st of this month to today with `accounting_method=Accrual`, and keep the rows in the `COGS` and `Expenses` sections.
2. **Pull the same days last month** (QuickBooks): Run the same report from the 1st of last month to the same day number (or the last day, if last month was shorter), so 16 days are compared with 16 days.
3. **Group into categories**: Use the top-level expense accounts as categories and roll sub-accounts into their parent, following the report's section headers. Keep `Uncategorized Expense` as its own line.
4. **Flag what is running hot** (QuickBooks): Mark categories up 25% or more and by at least $500, and any category new this month. Name the largest transactions behind each flag from `ProfitAndLossDetail` for the same dates.
5. **Write the report**: Month-to-date total and its change against the same days last month, a bar per category, the flagged categories with their reasons, and the uncategorized amount if there is one.

## Route B: the browser

### What you need

- Signed in to QuickBooks Online with access to reports, with the right company open.

If something is missing, say what and stop; do not invent numbers.

### Steps

Start at https://app.qbo.intuit.com/app/reports. The user is signed in already; never type passwords or codes, and ask if a sign-in page appears.

1. **Run month to date**: Open `https://app.qbo.intuit.com/app/reports`, open **Profit and Loss** under **Standard reports**, set **Report period** to a custom range from the 1st of this month to today and **Accounting method** to **Accrual**, and run it. Click **Collapse** so sub-accounts roll into their parent, then read every row in **Cost of Goods Sold** and **Expenses**.
2. **Run the same days last month**: Change the range to the 1st of last month through the same day number (or its last day, if last month was shorter) and run again, so 16 days are compared with 16 days. The previous-period comparison does not do this for a partial month, which is why it takes two runs.
3. **Flag what is running hot**: For each category compute the change. Mark categories up 25% or more and by at least $500, and any category that has spend this month and none last month.
4. **Name the transactions behind each flag**: Click the month-to-date amount of each flagged category to open its **Transaction Report** and note the largest transactions with their vendors and dates.
5. **Write the report**: Month-to-date total and its change against the same days last month, each category with both amounts and the change, the flagged categories with their reasons, and the **Uncategorized Expense** amount if there is one.

Note: Transactions still sitting in the bank feed's For review tab are not in the report, so a low total may mean the feed has not been worked through. Control names differ slightly between the classic and the newer report view.

## The report

- **Expenses month to date** and the change against the same days last month.
- **By category**: this month, last month, change.
- Categories up 25% or more, with the transactions behind them.
- Money sitting in Uncategorized Expense, if any.

## Post it to Briefkin

When `BRIEFKIN_TOKEN` is set, or `~/.config/briefkin/env` exists (the installer from https://briefkin.dev/docs writes it), also send the result to the user's Briefkin board as one card. Keep the `item_key` the same on every run so the card updates in place, use a new `event_id` per run, and put today's real numbers in place of the example ones. If the request fails, say so once and carry on.

```bash
[ -n "${BRIEFKIN_TOKEN:-}" ] || . ~/.config/briefkin/env
curl -sS -X POST "${BRIEFKIN_URL:-https://briefkin.dev}/v1/events" \
  -H "authorization: Bearer $BRIEFKIN_TOKEN" \
  -H "content-type: application/json" \
  -d @- <<'JSON'
{
  "v": 1,
  "event_id": "quickbooks-expenses-by-category/<date of this run, e.g. 2026-10-16>",
  "item_key": "quickbooks-expenses-by-category",
  "project": "Acme",
  "source": "QuickBooks expenses by category skill",
  "kind": "metric",
  "title": "October spend $44,620, 5% above September",
  "metric": {
    "label": "Expenses month to date",
    "value": 44620,
    "prefix": "$",
    "better": "lower",
    "period_start": "<start of the period, ISO 8601>",
    "period_end": "<end of the period, ISO 8601>"
  },
  "glance": [
    {
      "type": "Grid",
      "props": {
        "columns": 2
      },
      "children": [
        {
          "type": "Stat",
          "props": {
            "label": "Same days in September",
            "value": 42500,
            "prefix": "$"
          }
        },
        {
          "type": "Stat",
          "props": {
            "label": "Difference",
            "value": "+$2,120",
            "delta": 5,
            "better": "lower"
          }
        }
      ]
    },
    {
      "type": "Bars",
      "props": {
        "unit": "USD",
        "items": [
          {
            "label": "Payroll",
            "value": 18300
          },
          {
            "label": "Rent",
            "value": 6500
          },
          {
            "label": "Software",
            "value": 5840
          },
          {
            "label": "Contractors",
            "value": 4900
          },
          {
            "label": "Advertising",
            "value": 4250
          },
          {
            "label": "Travel",
            "value": 2180
          }
        ]
      }
    }
  ],
  "widget": [
    {
      "type": "Table",
      "props": {
        "columns": [
          "Category",
          "Oct 1-16",
          "Sep 1-16",
          "Change"
        ],
        "align": [
          "left",
          "right",
          "right",
          "right"
        ],
        "rows": [
          [
            "Payroll",
            "$18,300",
            "$18,300",
            "0%"
          ],
          [
            "Rent",
            "$6,500",
            "$6,500",
            "0%"
          ],
          [
            "Software",
            "$5,840",
            "$4,230",
            "+38%"
          ],
          [
            "Contractors",
            "$4,900",
            "$6,400",
            "-23%"
          ],
          [
            "Advertising",
            "$4,250",
            "$3,900",
            "+9%"
          ],
          [
            "Travel",
            "$2,180",
            "$640",
            "+241%"
          ],
          [
            "Office",
            "$1,120",
            "$1,050",
            "+7%"
          ],
          [
            "Other",
            "$1,530",
            "$1,480",
            "+3%"
          ],
          [
            "Total",
            "$44,620",
            "$42,500",
            "+5%"
          ]
        ]
      }
    },
    {
      "type": "Callout",
      "props": {
        "text": "Travel is up $1,540, mostly flights booked on Oct 7 for the November offsite. Software is up $1,610 on two annual renewals.",
        "tone": "warn"
      }
    }
  ],
  "size": "md"
}
JSON
```

Widgets come from the Briefkin catalog (https://briefkin.dev/v1/catalog). Without a token, just give the user the report.

Set `period_start` and `period_end` to the window the number covers: the tile names it from them ("Last 7 days", "Today", "This month"). For a count right now (open items, a balance), send `"period_label": ""` so no period shows; for another name, send it as `period_label` (at most 24 characters).

## Run it on a schedule

Suggested: Weekdays at 7:30 (`30 7 * * 1-5`). From cron, with the agent signed in on that machine:

```bash
30 7 * * 1-5 claude -p "Run the quickbooks-expenses-by-category skill"
# Codex: codex exec, Gemini CLI: gemini -p, Copilot CLI: copilot -p, OpenCode: opencode run
```

Or use the agent's own scheduler (Claude Code routines, Codex automations, Claude or ChatGPT scheduled tasks): https://briefkin.dev/skills/quickbooks-expenses-by-category#schedule

Source: https://briefkin.dev/skills/quickbooks-expenses-by-category
