Billing Reconciler
A worked example of an advanced workflow: a Claude-managed scheduled agent that, once a week, reads WYRE's QuickBooks Online books, flags invoices that are past due or look anomalous, and publishes a reconciliation report to Slack as a canvas with a one-line summary. It is strictly read-only and never writes to QuickBooks. There are no servers and no code to deploy: the agent is a saved prompt plus a schedule plus two MCP connectors, running in Claude's cloud.
What it builds
The finished workflow runs unattended on a weekly cron. Each run it pulls a bounded window of open invoices and the aged-receivables report, reasons about which invoices are overdue and which look wrong, and notifies your team, turning a spreadsheet review into a single billing-reconciliation report with a Slack trail. Anything that looks off but cannot be confirmed from read-only data is surfaced for a human rather than acted on.
Prerequisites
Before building, your Claude account needs all of the following:
| Requirement | Notes |
|---|---|
| WYRE MCP Gateway connector | Connected in claude.ai (https://mcp.wyre.ai/v1/mcp). Provides the qbo_* tools. See the Gateway overview. |
| QuickBooks Online enabled in the gateway | The gateway's QuickBooks Online connection must be able to read invoices, customers and reports. The routine uses only read tools — no write scope is required. |
| Slack connector | The first-party Slack connector connected in claude.ai (https://mcp.slack.com/mcp). Provides slack_create_canvas and slack_send_message. |
| Scheduled routines access | Created via the /schedule capability; managed at claude.ai/code/routines. |
| A destination Slack channel | e.g. #sw-dev (ID C0931CKJ75X). The Slack connector must have access to it. |
Known gotchas
These are the things that cost real time the first time through. Account for them up front and the build genuinely takes minutes.
- This workflow is deliberately read-only — keep it that way. The
gateway's QuickBooks connector exposes
qbo_*_createandqbo_*_updatetools, but the routine'spermitted_toolslists none of them — onlyqbo_status,qbo_invoices_list,qbo_invoices_get,qbo_customers_listandqbo_reports_aged_receivables. That is the safety guarantee: even a misbehaving run cannot alter the books. When you extend the routine, never add a write tool. - The
qbo_invoices_listpayload is heavy — request a bounded window. Each invoice record carries its fullLine[]array, billing and shipping addresses and tax detail — roughly 2 KB per row. An unbounded sweep of every invoice ever raised swamps a single routine run. The routine asks forstatus"Unpaid", astartDateabout 120 days back, andmaxResults50, paging withstartPositiononly if a full page comes back. That window is wide enough to catch every open and overdue invoice while keeping the run small. - Flag overdue from the list payload — don't fan out per invoice.
Each
qbo_invoices_listrow already carriesDueDate,Balance,TotalAmt,CustomerRef(with name) andLine[]. Overdue detection (DueDatebefore today andBalance> 0) and the anomaly scan both run off that row.qbo_invoices_getis a fallback for the rare malformed row, not a per-invoice loop. - Faster-than-hourly cadences are rejected. The scheduler will not
accept a cron more frequent than once an hour. A weekly cron
(
0 13 * * 1) is well within that limit. -
permitted_toolsmust be populated per connector. A routine with a connector attached but an emptypermitted_toolslist runs with no tools and silently does nothing — no error, no output. List the exact tool names the routine needs. - A routine reaches only its attached connectors. The routine sandbox blocks arbitrary network egress, so notifications must go through the Slack connector's tools, not an outbound webhook. Attach every connector the workflow touches.
The one-shot build prompt
With the connectors above in place, paste this to Claude. It confirms the gateway, creates the routine, and confirms its configuration from the API response.
Build me a scheduled Billing Reconciler agent. Do all of this end to end:
1. Confirm the WYRE MCP Gateway works and QuickBooks Online is reachable: call
qbo_status and check it reports credentials configured. QuickBooks here is a
single company file (WYRE's own books) — there is no per-tenant loop.
2. Inspect the live invoice data so the routine is scoped sensibly. The
qbo_invoices_list payload is heavy — each invoice carries its full Line[]
array, billing/shipping addresses and tax detail (~2 KB per row). An
unbounded sweep drowns a routine run. Plan to request a bounded window:
status "Unpaid", a startDate ~120 days back, and maxResults 50, paging only
if a full page comes back.
3. Confirm a Slack connector is connected. Note the destination channel name
and ID (#sw-dev, C0931CKJ75X). If Slack does not show in the /schedule
connector list, read its connector_uuid and url from an existing routine
that already uses Slack (RemoteTrigger list -> get -> mcp_connections).
4. Create a Claude-managed scheduled routine named "Billing Reconciler":
- Schedule: weekly, cron "0 13 * * 1" (Monday 09:00 America/New_York =
13:00 UTC). Faster-than-hourly cadences are rejected.
- Attach TWO connectors, each with permitted_tools populated:
* WYRE MCP Gateway: qbo_status, qbo_invoices_list, qbo_invoices_get,
qbo_customers_list, qbo_reports_aged_receivables — READ tools only.
Do NOT list any qbo_*_create or qbo_*_update tool.
* Slack: slack_create_canvas, slack_send_message
An empty permitted_tools list = the routine runs with no tools.
- Routine prompt: every run, pull the bounded window of open invoices and
the aged-receivables report, flag overdue invoices and billing anomalies,
build a report, publish it as a Slack canvas, and post a one-line summary
linking the canvas. Use the exact routine prompt below.
5. Confirm the API response echoes both connectors with the correct
permitted_tools (verify NO write tools are present) and the routine prompt
verbatim. Record the trig_... ID. The resulting routine prompt
This is the lean prompt the build process installs into the scheduled routine itself.
Substitute your own destination channel if it is not #sw-dev.
You are the Billing Reconciler for WYRE. You run weekly, READ-ONLY against QuickBooks Online (WYRE's single company file). You must NEVER create or modify any QuickBooks record — only read.
Steps:
1. Call qbo_status to confirm the QuickBooks connection is live. If it is not, post a short Slack message to #sw-dev saying the run was skipped because QuickBooks was unreachable, and stop.
2. Call qbo_invoices_list with status "Unpaid", startDate set to 120 days before today, and maxResults 50 to pull a bounded window of recent open invoices. If 50 rows are returned, page with startPosition to capture the rest of the window. Each invoice row already carries DueDate, Balance, TotalAmt, CustomerRef (id and name), DocNumber, TxnDate and Line[]; you do not need qbo_invoices_get unless a row looks malformed and you need its line detail.
3. Call qbo_reports_aged_receivables for the aging totals.
4. Call qbo_customers_list if you need to resolve any customer name not already on an invoice.
Flag two things:
- OVERDUE: invoices whose DueDate is before today and whose Balance is greater than zero. Record customer name, invoice number (DocNumber), amount (Balance), and days late (today minus DueDate).
- ANOMALIES: invoices that are zero-dollar (TotalAmt is 0), have no SalesItemLineDetail line items, or whose TotalAmt is materially out of line with that customer's other recent invoices in the window. Anything that looks wrong but cannot be confirmed from read-only data goes here for a human — never act on it.
Build a report with an overdue section (customer, invoice #, amount, days late, sorted by days late descending) and an anomalies section (invoice #, customer, what looks wrong). Include totals: count and summed dollar amount of overdue invoices, and the aged-receivables grand total.
Deliver:
- Call slack_create_canvas titled "Billing Reconciliation - <today's date YYYY-MM-DD>" with the full report in Markdown.
- Call slack_send_message to channel C0931CKJ75X (#sw-dev) with a one-line summary stating the total overdue amount and overdue invoice count, and a link to the canvas.
This routine is strictly read-only: never call any qbo write or update tool. If the data is empty, say so plainly — do not fabricate invoices or amounts. How it works
A weekly cadence, by design
Billing drift is a slow-moving signal — invoices age past their due dates over days and weeks, not minutes. The routine runs every Monday morning so the reconciliation report is waiting before the week's collections work starts. A weekly cron is also kind to the API: one read sweep per week, not one per hour.
Overdue and anomalies, flagged from read data
An invoice is overdue if its DueDate is before today
and its Balance is still greater than zero; the report records how many
days late and the outstanding amount. An anomaly is an invoice that
is zero-dollar, has no line items, or whose total is materially out of line with that
customer's other recent invoices. Anything that looks wrong but cannot be confirmed
from read-only data goes under anomalies for a human — the routine never acts on it.
It reads a bounded window, not the whole ledger
Each qbo_invoices_list record carries its full Line[]
array, addresses and tax detail. Listing every invoice ever raised floods a single
routine run with that detail and the run never reaches the report. So the routine
requests a bounded window of unpaid invoices from roughly the last 120 days, 50 rows
per page, which is wide enough to hold every open and overdue invoice while keeping
the run small. Widening the window is a one-line change to the startDate
in the routine prompt.
Read-only by construction
The routine's QuickBooks connector is attached with a permitted_tools
list containing only read tools. The qbo_*_create and
qbo_*_update tools are not reachable from inside the routine, so
there is no path by which a run could change WYRE's books. The safety guarantee
lives in the connector configuration, not in the prompt.
Delivery is a Slack canvas plus a summary
The full overdue-and-anomaly report can be long, so the routine publishes it as a Slack canvas, a durable document teammates can scroll and reference, and posts only a one-line summary to the channel linking it. The channel stays readable; the detail lives in the canvas. See delivery adapters for other ways to surface a routine's output.
Extending it
The natural next step is to cross-check billing against delivered work: attach the
gateway's autotask connector and match invoices to PSA contracts and
tickets, so the report can also flag unbilled work — time and projects
delivered in Autotask with no corresponding QuickBooks invoice. That keeps the
read-only contract intact (Autotask tickets and contracts are read too) while
closing the loop from "what we billed" to "what we should have billed."
Questions or a workflow you'd like documented?
Open an issue
in the msp-claude-plugins repository.