Issuing Credit Notes for Partial Refunds and Write-Offs
An issued invoice is a legal document. Once it carries a sequential number and has been sent, it cannot be edited, deleted, or renumbered — corrections happen through a credit note that references it. This guide sits under Invoicing & Credit Notes and covers issuing those corrections properly, including the cases teams get wrong: partial credits, tax reversal, and the difference between refunding money and forgiving a debt.
The distinction that governs the design is between correcting the document and moving the money. A credit note corrects the document. Whether cash goes back to the customer, sits on their account as a balance, or is simply written off is a separate decision that follows from it. Systems that conflate the two end up unable to express “we forgave this invoice” without pretending to refund a payment that never arrived.
The second governing rule is that a credit note is itself a numbered document in its own sequence, dated when it is issued rather than when the invoice was.
Trade-offs
| Correction mechanism | Legally sound | Reversible | Ledger clarity | When |
|---|---|---|---|---|
| Edit the invoice | No | N/A | Destroys history | Never, after issue |
| Credit note, refunded | Yes | Yes | Clear | Money goes back |
| Credit note, to account balance | Yes | Yes | Clear | Applied to a future invoice |
| Credit note, write-off | Yes | Yes | Clear | Debt forgiven, no cash moved |
| Negative line on a future invoice | Usually not | Hard | Muddled | Avoid |
Negative lines on a future invoice deserve their own warning. They are convenient, they are how many systems drift into handling credits, and they produce an invoice whose total does not correspond to what was supplied in that period — which breaks both the customer’s reconciliation and your tax reporting.
Step-by-Step Implementation
1. Model the credit note as its own document
CREATE TABLE credit_notes (
credit_note_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
number TEXT UNIQUE NOT NULL, -- own gapless sequence
invoice_id UUID NOT NULL REFERENCES invoices(invoice_id),
reason_code TEXT NOT NULL, -- 'service_credit' | 'billing_error'
-- | 'cancellation' | 'bad_debt'
issued_at TIMESTAMPTZ NOT NULL DEFAULT now(),
currency CHAR(3) NOT NULL,
settlement TEXT NOT NULL -- 'refund' | 'balance' | 'write_off'
);
CREATE TABLE credit_note_lines (
credit_note_line_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
credit_note_id UUID NOT NULL REFERENCES credit_notes(credit_note_id),
invoice_line_id UUID NOT NULL, -- what is being credited
amount_minor BIGINT NOT NULL, -- positive; the document is a credit
tax_minor BIGINT NOT NULL,
tax_rate_bps INT NOT NULL -- the ORIGINAL line's rate
);
Its own sequence matters: credit notes and invoices generally must each be gapless within their own numbering, and interleaving them into one sequence breaks both.
2. Allocate to lines, not to the total
A partial credit against a multi-line invoice must say which lines it credits. Crediting “£40 off invoice 1043” leaves the tax treatment ambiguous when the invoice contains lines taxed differently, and it makes revenue attribution impossible.
def allocate(credit_minor: int, lines: list[dict]) -> list[dict]:
"""Proportional allocation when the credit is not line-specific."""
total = sum(l["amount_minor"] for l in lines)
out, allocated = [], 0
for i, l in enumerate(lines):
share = credit_minor * l["amount_minor"] // total
if i == len(lines) - 1:
share = credit_minor - allocated # last line absorbs the rounding
out.append({"invoice_line_id": l["invoice_line_id"], "amount_minor": share})
allocated += share
return out
Letting the last line absorb the rounding remainder guarantees the allocation sums to the credit exactly, which a naive proportional split does not.
3. Reverse tax at the original rate
This is why the invoice line stores tax_rate_bps rather than looking the rate up when needed.
4. Choose the settlement
def settle(credit_note, payments, balances) -> str:
if credit_note.settlement == "refund":
# Refund against the original charge, through the provider that took it.
payments.refund(charge_ref=credit_note.original_charge_ref,
amount_minor=credit_note.total_minor,
idempotency_key=f"cn:{credit_note.credit_note_id}")
elif credit_note.settlement == "balance":
balances.credit(credit_note.account_id, credit_note.total_minor,
reference=credit_note.number)
# write_off moves no money at all
return credit_note.settlement
Write-off is the one most often missing. An unpaid invoice that will never be collected must be removed from receivables, and doing that with a fake refund of a payment that never happened corrupts both the cash records and the payment provider’s data.
5. Post the ledger entries
Each settlement type has a distinct posting: a refund reduces revenue and cash, a balance credit reduces revenue and increases a liability, and a write-off reduces receivables against a bad-debt expense. Keeping them distinct is what lets finance see how much was given away as service credit versus lost to non-payment — two numbers with very different implications.
Reasons, Approvals, and What the Numbers Tell You
The reason_code on a credit note is the field that turns a compliance artefact into management information, and it is worth constraining tightly.
Distinguish billing errors from service credits. A billing error means the invoice was wrong — a duplicate charge, a wrong quantity, a plan that should not have renewed — and a rising count is an engineering signal. A service credit means the invoice was right and you chose to give money back, which is a customer-relations signal. Blending them into “credit” hides both.
Distinguish cancellation credits too. A mid-term cancellation with a refund of the unused portion is a routine, expected credit whose volume tracks churn rather than quality, and including it in the same bucket as billing errors makes the error trend unreadable.
Bad debt is its own category and belongs to the write-off settlement. It is the only reason where the credit note documents a loss rather than a decision, and finance will want it reported separately for provisioning.
Gate credits by amount, as with any outbound money movement. A support agent issuing up to a modest amount is routine; larger credits deserve a second approval, and the approval should be recorded against the credit note rather than living in a chat log. That record is also what makes the aggregate reporting trustworthy.
Review the totals monthly against revenue. Credits as a percentage of billed revenue is a small, stable number in a healthy business, and a shift in it — in either direction — is worth understanding. A sudden drop sometimes means the credit path became too hard to use rather than that quality improved.
Credit Notes in the Customer’s Experience
The document is a compliance artefact; the experience around it determines whether a credit resolves a situation or extends it.
Send the credit note, do not merely file it. A customer who is told “we have credited you” without receiving a document cannot pass it to their own finance team, and in many jurisdictions they need it to adjust their own tax position. Attach it in the same format as the invoice it corrects.
State the effect in plain terms alongside the document. “Credit note CN-0421 for £48.00 against invoice INV-1043. This will be refunded to your card within five working days” answers the three questions a customer actually has: how much, against what, and when will I see it.
Show credits in the billing history next to the invoices they correct, not in a separate list. A customer scanning their history should see the invoice and its credit adjacent, because the pairing is what makes the net position obvious.
For balance credits, show the balance and where it will be applied. A credit sitting on an account with no visible effect until a future invoice is the most-queried state in this whole area, and one line on the billing page removes it.
Set expectations on refund timing honestly. Card refunds are not instant and the delay is the issuer’s rather than yours, but the customer experiences it as yours. Saying five to ten working days and beating it is better than promising immediacy and being contradicted by their bank.
Verification & Testing
Test that the sum of allocated line credits equals the credit note total exactly, across amounts that do not divide evenly. The rounding-absorption rule must hold for every input, and a property test over random amounts is the right shape here.
Test tax reversal against a rate that has since changed: create an invoice at an old rate, change the current rate, issue a credit note, and assert the credit uses the original rate. Then assert the tax report for the period nets to zero for that invoice and credit pair.
Test each settlement path end to end: a refund reaches the provider that took the original charge with a stable idempotency key, a balance credit appears and is applied to the next invoice, and a write-off moves no money while still clearing the receivable. Assert that reissuing the same credit note does not double-refund.
Gotchas & Production Pitfalls
- Editing the issued invoice. Breaks the numbering sequence and invalidates any filed return that included it.
- Crediting the total without line allocation. Makes tax treatment and revenue attribution ambiguous, especially on mixed-rate invoices.
- Reversing tax at today’s rate. Leaves a permanent residue in the tax account that nobody can explain.
- Write-offs modelled as refunds. Records cash movement that never happened and confuses the provider’s records with yours.
- Credit notes sharing the invoice sequence. Breaks the gapless requirement for both document types.
Frequently Asked Questions
Can a credit note exceed the invoice it references? No. Credits are bounded by what was invoiced. A goodwill payment beyond that is a separate transaction, not a correction of the document.
What if the invoice was never paid? Then the credit note reduces the receivable, and the settlement is a write-off rather than a refund. This is the normal path for uncollectable enterprise invoices.
Do credit notes affect recurring revenue metrics? Not directly. A credit is a concession against a specific invoice and leaves the recurring amount unchanged, which is why the MRR bridge treats credits as a named reconciling item rather than a movement.
How long after the invoice can a credit note be issued? There is no universal limit, but crediting an invoice from a closed and filed period means the correction lands in the current period’s return. That is normal and expected; what matters is that both documents are dated correctly.