Appearance
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:
- Recurring runs — at the start of each billing cycle, OpsMerge generates invoices for every active contract. See Recurring invoices.
- Manual — Settings → Invoices → + New invoice → pick a client, add lines, save. For one-off charges (project deposits, hardware sales, etc.).
- 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
| Field | Notes |
|---|---|
| Number | Auto-assigned as INV-{YYYY}-{NNNN} — a gapless per-year sequence that resets each January. See Invoice numbering. |
| Issue date | The date stamped on the invoice. |
| Due date | Calculated from issue date + payment terms (default: 30 days). |
| Client | Required. |
| Billing contact | Defaults to client's billing contact; overridable per invoice. |
| Lines | One or more line items: description, quantity, unit price, service, tax code, billing category. |
| Subtotal, tax, total | Calculated. |
| Status | Draft / Open / Paid / Voided / Overdue. |
| Notes | Free text on the invoice PDF. |
| Internal notes | Visible 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
TaxCodeRefand lets QBO compute tax — we never sendTxnTaxDetail.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.
Sending invoices
Manual:
- Open the invoice.
- Click Send.
- 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.)
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"
- Manual invoice → 50% deposit. Send.
- 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
- Recurring invoices — the engine producing most invoices
- Contracts — what defines what's billed
- QuickBooks integration — pushing invoices to QBO