Month-end recurring billing is the same chore at almost every MSP we talk to. Someone opens last month's invoices, opens the RMM in another tab to check the device count, opens the distributor portal to count licenses, maybe opens a backup report, and then hand-edits each client's invoice to match reality. Multiply by 40 clients and you've burned a day or two — every single month, on work that is mostly counting.
The frustrating part is that none of that counting requires judgment. The device count is already in your RMM. The license count is already in your distributor portal. Every client's plan is already written down somewhere (or it's in someone's head, which is its own problem). So the question we kept asking while building Morton Command Center was: if the numbers all live in tools you already pay for, why are you the one moving them by hand?
This post walks through how recurring invoice generation can assemble itself — pulling current counts from your existing stack, applying each client's plan, stacking everything into a review queue, and pushing the approved batch into your accounting system. We use QuickBooks Desktop as the worked example throughout, but the platform is API-driven end to end: your RMM, your distributor, your backup tooling, and your books can each be whatever you already run, and we build each connection custom to your stack. If your tool has an API, we build the integration for it. The deliberate emphasis throughout: a human reviews before anything sends.
Step one: pull the counts that drive each line item
Recurring MSP invoices are almost always a small set of quantity-times-rate lines. Managed workstations and servers, per-user licenses, backup seats, maybe a flat managed-services block. The hard part has never been the math — it's gathering current, accurate quantities at the moment of billing. So that's where the build starts.
- Device counts come from your RMM. MortonCC reads device health and inventory through a normalized device adapter we build for your stack — NinjaOne, Datto RMM, or whatever you run is connected the same way because the adapter layer is API-driven — so the workstation and server counts on the invoice reflect what's actually checking in, not a spreadsheet someone last updated in February.
- License counts come from your distributor. The distributor adapter reads each company's subscriptions and usage — Pax8 or any distributor that exposes an API is integrated the same way — so Microsoft 365 and other resold-license quantities are pulled from the distributor of record rather than re-counted by hand.
- Backup seats come from your backup tooling. CC rolls up backup status per company — Datto BCDR and SaaS through their feeds, and Veeam, Acronis, or Barracuda parsed from the alert emails they already send. Any backup platform with an API (or a consistent alert email) is built to roll up the same way, custom to your stack. That rollup gives you a defensible seat count to bill against, with the right backup connection wired in for your tools.
- M365 seat counts come from your identity provider, read-only. Where you bill against directory or license seats, CC reads those counts straight from Microsoft 365 through an integration we build for you. It does not write anything back to your tenant — it's purely a count for the invoice — and any identity or licensing source with an API can be built to feed the same line.
Two things matter here. First, these are reads from the tenant's real tools through normalized adapters — CC does not replace your RMM, your distributor, or your backup vendor, and it does not migrate their data anywhere. Second, the data is cron-warmed on short cycles, so when invoices generate, the counts are current to within minutes, not stale from a nightly export you forgot to refresh.
Step two: apply each client's plan
Counts are only half the line item; the other half is the plan. This is where the "build down a list" logic lives. Each company in CC carries a billing configuration — the recurring items, the quantities or count-sources, the rates, and which customer in your accounting system it maps to (the QuickBooks customer in our example, but the mapping works the same against any accounting system with an API). When recurring generation runs, it walks each client and turns their plan plus their current counts into a draft invoice.
A few details we cared about getting right:
- Plans live on the company, not in a generic template. A client billed flat-rate per workstation and a client on a fixed managed block both come out correct because the rule set is per-company, stored alongside everything else about that client.
- Mappings, not name matching. Tying a CC company to its customer in your accounting system is done through an explicit mapping, validated server-side at create time, so "Acme Inc." in your RMM and "Acme Incorporated" in your books don't quietly become two customers — or worse, get billed to the wrong one. (We show QuickBooks here; the same mapping discipline applies to any accounting system we connect.)
- Unmapped items get flagged, not guessed. If a billing line can't resolve to a catalog item or a customer in your accounting system, it surfaces as a warning rather than silently dropping off the invoice. A missing line on a recurring invoice is revenue you never get back, so the system would rather make noise than make it disappear.
If you want the deeper version of the QuickBooks side of this — why it's Desktop specifically, and how the sync stays off the open internet — we wrote that up separately in QuickBooks Desktop MSP billing automation.
Step three: assemble a review queue (and a dry run)
Here is the line we will not cross: invoices do not silently fly into your accounting system and out to clients. Generation produces a batch you review, not a batch that sends.
Recurring generation supports a dry run — you can see exactly what would be created, line by line, client by client, before a single invoice is committed. That's the moment to catch the off-month: the client who offboarded fifteen seats, the new site that doubled its device count, the license bundle that changed mid-cycle. You scan the queue, the deltas from last month are right there, and you either approve or you fix the underlying plan and regenerate.
The reason this matters is trust. Automation that you can't inspect is automation you can't sign off on, and an invoice with your name on it is not a place to be surprised. The dry run turns "I hope the script got it right" into "I can see the script got it right" — and the review takes minutes, not hours, because everything is already counted and priced.
Step four: push the approved batch to QuickBooks Desktop
Once you've approved the queue, CC creates the invoices in your accounting system. In our worked example that's QuickBooks Desktop through Conductor — a local sync agent that talks to your QuickBooks company file without exposing it to the open internet. There's no firewall port to open to the world; the agent reaches out, so your accounting data never sits behind a public endpoint. Because the platform is API-driven, any accounting system that exposes an API gets a custom integration built to post the approved batch the same way.
A couple of properties worth calling out, because they're the difference between "a script that posts invoices" and something you can run every month without holding your breath:
- Creation is idempotent. If a run is interrupted or you re-run it, you don't get duplicate invoices for the same client and period. Double-billing a client is one of the few accounting mistakes that actively damages a relationship, so the create path is built to refuse it.
- Customer mapping is validated at create time. The accounting-system customer link — the QuickBooks customer in our example — is checked when the invoice is written, not assumed; another guard against the wrong client getting someone else's invoice.
- It's the same surface as the rest of billing. The invoices land in your accounting system right next to your A/R balances and invoice history, which CC also reads, so you can watch the batch you just created move through aging and payment without leaving the dashboard.
This is where custom-built beats one-size-fits-all: your integration is built to fit your exact stack, not forced onto whatever connectors a rigid all-in-one suite happened to ship. QuickBooks Online, Xero, Sage, NetSuite — wherever your books live, we build that connection custom to your books as part of your engagement. The rule is simple: if your accounting system has an API, we build the integration for it. You get a connector shaped around how you actually work, instead of bending your workflow to fit theirs.
What you're left doing each month
When the counting, pricing, and posting all happen on rails, the human job shrinks to the part that actually needs a human: reviewing the deltas and approving the batch. The device counts pulled themselves. The licenses pulled themselves. The backup seats rolled themselves up. Each plan applied itself. The only thing left for you is to look at what changed since last month and say yes — or catch the one client whose situation changed and fix it first.
That's the whole point of building it this way. Not "the robot sends your invoices," but "the boring 90% builds itself, and you spend your attention on the 10% that's actually a decision." It's the same philosophy behind how we handle automated MSP invoicing end to end, and why we lean so hard on billing reconciliation to catch the gaps between what you're managing and what you're actually charging for.
If month-end recurring billing is still a manual counting exercise at your shop, the counts already live in tools you pay for. The work isn't generating the numbers — it's stopping yourself from copying them by hand.