Skip to content

Billing & invoices

This article covers the invoices your MSP sends to your clients — generated by OpsMerge based on contracts, recurring lines, and time entries. For the invoices OpsMerge sends to you for your subscription, see Subscription invoices.

How invoices get created

Three ways, in rough order of frequency:

  1. Recurring runs — at the start of each billing cycle, OpsMerge generates invoices for every active contract. See Recurring invoices.
  2. Manual — Settings → Invoices → + New invoice → pick a client, add lines, save. For one-off charges (project deposits, hardware sales, etc.).
  3. From a ticket's time entries — for pay-as-you-go work that hasn't yet been billed. Open a ticket → Time tab → "Bill this time".

Invoice anatomy

FieldNotes
NumberAuto-assigned as INV-{YYYY}-{NNNN} — a gapless per-year sequence that resets each January. See Invoice numbering.
Issue dateThe date stamped on the invoice.
Due dateCalculated from issue date + payment terms (default: 30 days).
ClientRequired.
Billing contactDefaults to client's billing contact; overridable per invoice.
LinesOne or more line items: description, quantity, unit price, service, tax code, billing category.
Subtotal, tax, totalCalculated.
StatusDraft → Issued → Paid (or Partial along the way). Void and Written off are terminal. Unpaid invoices also move through the chasing statuses — Reminder sent, Overdue, Final notice, Suspended — see Payment chasing.
NotesFree text on the invoice PDF.
Internal notesVisible to your team only.

Invoice numbering

Invoice numbers are INV-{YYYY}-{NNNN} — for example INV-2026-0001. The sequence is gapless within a year and resets to 0001 each January, so the first invoice of 2027 is INV-2027-0001. Numbers are assigned atomically per tenant, so concurrent invoice runs never collide or skip.

If you're migrating from another system and want to carry on from where it left off, set the next number under Settings → PSA → Invoice branding. Enter the sequence value the next invoice should take (the INV-{YYYY}- part is fixed) and save.

This setting is forward-only: you can jump the current year's sequence ahead, but you can't rewind it onto a number that's already been issued. The field rejects any value at or below the current next number.

Tax handling

OpsMerge handles UK VAT natively. For other jurisdictions, the model is the same — set up tax codes appropriate to your country and they apply to invoice lines.

Tax codes

Settings → PSA → Tax codes. Define one per tax rate (Standard 20%, Reduced 5%, Zero-rated 0%, Reverse charge, Exempt, etc.).

Each line on an invoice has a tax_code_id. If unset, the line fails the pre-save guard (see "Common issues"). This is deliberate — silent zero-VAT lines on a UK invoice are a compliance problem.

UK VAT-specific notes

  • If you're VAT-registered, set your VAT number in Billing details. It appears on every invoice header.
  • For QuickBooks push, OpsMerge sends the line-level TaxCodeRef and lets QBO compute tax — we never send TxnTaxDetail.TaxLine. See QBO troubleshooting.
  • Tax-exempt clients can be flagged at the client level; their invoices auto-set lines to a zero-rated tax code.

Billing international and EU clients

To bill a client in another currency at the right VAT treatment, set two defaults on the client (edit the client → Billing details):

  • Billing currency — new contracts and draft invoices for the client default to it. You can still override per contract.
  • Default VAT treatment — a tax code applied to every auto-generated invoice line for the client, instead of the org-wide default from Billing Settings.

For an EU business customer (post-Brexit, GB-based MSP):

  1. Create a tax code with the Reverse charge treatment at 0% (Settings → PSA → Tax codes) if you don't have one.
  2. On the client, set the billing currency (say EUR) and pick the reverse-charge code as the default VAT treatment.
  3. That's it. Generated invoices come out in EUR at 0% with the HMRC reverse-charge note printed on the PDF, and the FreeAgent push classifies them as Reverse Charge so the net lands in box 6 of the VAT return rather than as domestic zero-rated revenue.

For non-EU overseas customers, use a zero-rated or outside-scope code as the default instead. When in doubt about the correct treatment for a supply, ask your accountant — OpsMerge applies whatever code you choose, it doesn't decide place-of-supply for you.

Two caveats:

  • A reverse-charge invoice must be reverse-charge throughout: issuing fails if any line still carries a positive VAT rate. Fix the line's tax code or clear the invoice's reverse-charge flag.
  • Multi-currency invoices can't be issued while a QuickBooks connection is active (QBO support is currently GBP-only). FreeAgent handles them fine.

Sending invoices

Manual:

  1. Open the invoice.
  2. Click Send.
  3. Confirm recipient (defaults to billing contact).

OpsMerge sends the invoice as a PDF attachment plus a HTML preview in the body. The reply-to is your billing address.

Automatic:

For recurring runs, Settings → PSA → Recurring → Auto-send. When on, generated invoices are sent immediately. When off, they're saved as Draft and you send manually.

We recommend leaving auto-send off until you've watched a couple of cycles run and confirmed they're producing what you expect.

Payment

OpsMerge doesn't process client payments itself. Three patterns:

Pattern A: External (BACS, cheque)

The invoice PDF has your bank details in the footer. Client pays externally. You mark the invoice as Paid via Invoice → Actions → Mark as paid when you see the payment in your bank.

Pattern B: Stripe-on-the-invoice

The invoice PDF includes a Stripe payment link. Client clicks, pays, the webhook updates the invoice automatically. Set up at Settings → PSA → Stripe.

Pattern C: QuickBooks payment sync

If you use QuickBooks Online, payments recorded there flow back into OpsMerge via webhook. Mark-as-paid is automatic. See QBO integration. (A Xero integration is coming soon.)

Payment chasing (overdue reminders and final notices)

When an issued invoice goes unpaid, OpsMerge moves it through an automatic chasing sequence keyed off the due date: a payment reminder email 3 days after due, an internal overdue marker at 7 days, a final notice email at 10 days, and a suspended badge at 14 days. The suspended status is informational only, it never locks the client out of anything, and the invoice can still be edited, sent and paid.

You control the emails in Settings → PSA → Billing → Payment chasing:

  • Send overdue payment reminders and Send final notices are org-wide defaults. Turning a stage off only skips the email; the invoice still moves through the same statuses so your ageing reports stay accurate.
  • Don't chase clients on active Direct Debit (on by default) skips both emails for clients that have auto-collection enabled and an active Direct Debit mandate, since their payment is collected automatically, chasing them would just cause confusion.
  • Email clients when an invoice is paid (off by default) sends a payment confirmation when a synced payment, such as a Direct Debit collection or a bank reconciliation from your accounts package, fully pays an invoice. Useful together with Direct Debit suppression: instead of being chased, the client simply hears "thanks, we've collected it".

Each client can override the two chasing stages individually: edit the client and set Payment reminder / Final notice to Always send, Never send, or Use billing settings default.

Editing the email wording

All client-facing billing emails are editable at Settings → PSA → Billing → Email Templates: invoice issued, payment received, the overdue reminder, and the final notice. Pick the email, then override any field — subject, greeting, main message, the detail lines, or the sign-off. Fields you leave empty keep the standard wording, shown greyed in the editor.

Each email lists its available placeholders above the editor — invoice number, amounts, due date, client name and so on — written in double curly braces, for example:

Invoice {{.Number}} for {{.Currency}} {{.Total}} is due on {{.DueDate}}.

Placeholders are filled in at send time. Saves are validated: a typo in a placeholder name is rejected with the list of valid ones rather than going out broken. Reset to standard wording removes all your customisations for that email.

Emails OpsMerge sends to you (subscription and plan-change notices) aren't editable — only the emails your clients see.

Voiding an invoice

Sometimes you issue an invoice you shouldn't have. OpsMerge's void is a full reversal:

  • Stripe payment links cancelled.
  • Time entries committed to the invoice are released (so they can re-bill on a future invoice).
  • CDR records (if any) attached to the invoice are released.
  • Credits applied are reversed.
  • Recurring period state is rolled back so the next run sees this period as un-billed.
  • A credit note is generated and linked to the invoice.

You can't void a paid invoice — partial void / refund is a separate flow.

Credit notes

A credit note offsets an invoice. Two flavours:

  • Full void credit — the void flow generates this automatically.
  • Partial credit — manually issued for "we agree to discount this £100 off next month's invoice".

Credit notes are themselves invoices, technically — they just have negative line totals.

QuickBooks push

If you've connected QuickBooks Online, invoices push automatically when:

  • They move out of Draft status.
  • All required mappings exist (service IDs, tax codes, customer IDs).

A push retry runs every few minutes for failed pushes; persistent failures appear in Settings → Integrations → QuickBooks → Sync log with the error.

See QBO for setup, customer/service mapping, and the per-realm throttling we apply.

Invoice templates

Invoice PDFs use a configurable template. Out of the box: clean two-column with logo, company details, line items, totals, footer.

Customise at Settings → PSA → Invoice template. You can edit:

  • Header / footer text and logo.
  • Section ordering.
  • Notes block.
  • Payment instructions.

The template is HTML; we render to PDF via headless Chrome. Anything legal in HTML is fair game in the template.

Template variables come from real data

Template variables (client name, invoice number, etc.) are inserted into HTML at render time. We use Go's html/template (not text/template) so values are HTML-escaped — XSS and SSRF via DB-sourced names can't escape into the rendered PDF. Don't change this if you fork.

Surcharges and discounts

Per-invoice:

  • Discount line — add a negative-quantity line with a discount description.
  • Surcharge — add a positive line for late fees, processing surcharges, etc.

Per-client (auto-applied to every invoice):

  • Settings → Client → Surcharges/Discounts. Useful for "this client always gets 10% off as a long-term loyalty deal".

Common patterns

"Invoice the entire previous month at the start of this month"

That's the default recurring behaviour. Issue date = the first of the new month; period billed = the previous month; time entries logged in the previous month are billable.

"Project deposit upfront, balance on delivery"

  1. Manual invoice → 50% deposit. Send.
  2. Manual invoice when delivered → balance.

Both are tagged with the project ticket for traceability.

"Annual invoice with monthly auto-pay"

OpsMerge doesn't natively split a single invoice into monthly payment schedules — that's a Stripe-side feature. Easier: monthly recurring invoices, totalled annually.

Common issues

Invoice push to QBO fails with "missing service". Every invoice line must have a service_id mapped to a QBO Item. The QBO sync UI flags lines without one. Edit the line, add the service, retry.

Invoice push to QBO fails with "missing tax code". UK QBO requires line-level TaxCodeRef. OpsMerge's invoice editor enforces tax_code_id on save for UK tenants; if you've got legacy lines without one (from before this guard), edit them.

Money input field accepts garbage. Money fields hold pounds (major units). Convert to pence at submit (Math.round(value * 100) server-side). Don't bind directly to a _cents field with a £ prefix — that's a common UI bug we've already eliminated, but worth knowing.

Invoice PDF won't generate. Headless Chrome failed. Look at the error in Audit log → Invoice render. Usually a malformed custom template variable.

Invoice shows VAT 0% but I'm VAT-registered. You haven't set your VAT number in Billing details. Set it; future invoices show VAT correctly.

Next

OpsMerge is a product of Brindleford Technologies Ltd, company number 16871436, registered in England and Wales.