Terms, Reminders and Credits
Payment terms
Terms & Reminders sets the global due-date rule, which each tenant can override.
Due dates can be expressed as days from the invoice date, or as a day of the following month. The computed due date is printed on the invoice and is required before an invoice can be pushed to an accounting package.
An invoice with no due date is a push blocker rather than being pushed with a guessed one.
Reminders
Reminders chase unpaid invoices on a schedule you define: a list of offsets relative to the due date, each with its own message.
A typical schedule is a courtesy notice three days before the due date, a first reminder on the day after, then escalating notices at seven, fourteen and thirty days.
Reminders are configured globally and overridden per tenant.
What stops a reminder
- The invoice is paid. Payment sync from the accounting package is what keeps this accurate, so verify sync is running before enabling reminders.
- The invoice is void.
- The tenant has no billing recipients.
- The invoice was externally delivered and the tenant is configured for the accounting package to chase it.
Reminder ticks run hourly from container boot. A reminder configured for 09:00 is sent somewhere within that hour, not exactly at 09:00. It is never sent early.
If a reminder pass evaluates every invoice and correctly decides to send
none, it writes no log line. An absence of reminder logging therefore means
either "nothing was due" or "the tick did not run", and the two are not
distinguishable from the log alone. Check
docker compose logs api for scheduler activity generally.
Overdue alerts
Two detectors watch due dates:
invoice_overdue— past due by more than the grace period. Default grace is three days, because post takes a day and a bank transfer takes three, and an alert on the morning after every due date is an alert nobody reads.invoice_overdue_unsent— past due and never sent, which is a different and more urgent problem.
Severity escalates from medium to high after the escalation threshold, default fourteen days. Both thresholds are configurable under Detection.
Credits
A credit is money you owe the customer: a service failure, a billing error, a goodwill gesture.
- Credits apply after tax, because the amount is money already owed and taxing it again would be wrong.
- Credits attach to an invoice as an adjustment link, not as a decrement of a balance. Voiding the invoice therefore restores the credit rather than destroying it.
- Credit application is audited.
Credits and external tax
Credits can only be raised in Vodia Billing when Vodia Billing owns the tax: self-billed tenants, and Xero tenants.
On a QuickBooks tenant the credit control is greyed out and the API refuses the request. Raise the credit note in QuickBooks, which owns the total.
Payments
Payments arrive two ways.
Synced from the accounting package, on an interval configured under Settings. Each synced payment records a reference naming the invoice.
Recorded by hand for self-billed tenants, from the invoice.
For QuickBooks tenants, the QuickBooks total is the authority for what is owed, so paid and unpaid state follows QuickBooks rather than the internally computed figure.
Worked examples
Payment terms
Global default, overridable per tenant:
| Rule | Invoice dated | Due |
|---|---|---|
| Net 14 | 2026-09-01 | 2026-09-15 |
| Net 30 | 2026-09-01 | 2026-10-01 |
| Day 20 of following month | 2026-09-01 | 2026-10-20 |
An invoice with no computed due date cannot be pushed to an accounting package. It is reported as a blocker rather than pushed with a guessed date.
A reminder schedule
| Offset | Message |
|---|---|
| -3 days | Courtesy notice: invoice due Friday |
| +1 day | First reminder |
| +7 days | Second reminder |
| +14 days | Escalation — account may be suspended |
| +30 days | Final notice |
Applied to INV-00151, dated 2026-09-01 with Net 14 terms:
Due 2026-09-15
2026-09-12 courtesy notice sent
2026-09-16 first reminder sent
2026-09-19 payment received -> remaining reminders cancelled
2026-09-22 second reminder SKIPPED (paid)
Payment sync from the accounting package is what makes that cancellation work. Verify sync is running before you enable reminders, or a paid customer gets chased.
Overdue alerts on the same invoice, unpaid
overdueGraceDays: 3, overdueEscalateDays: 14:
2026-09-15 due
2026-09-16 nothing — inside grace
2026-09-18 nothing — inside grace
2026-09-19 invoice_overdue 4 days severity medium
2026-09-29 invoice_overdue 14 days severity high (escalated)
Three days of grace exists because post takes a day and a bank transfer takes three. An alert on the morning after every due date is an alert nobody reads.
An invoice that was never sent
invoice_overdue_unsent INV-00157 due 2026-09-15, never sent high
A different and more urgent problem than an unpaid invoice: the customer cannot pay something they never received. Usually a missing recipient or an SMTP failure at the send stage.
A credit, and a void restoring it
2026-09-05 Credit 50.00 issued to Acme Corp
reason: outage 2026-08-22, 4 hours
2026-09-05 Credit applied to INV-00151
887.29 - 50.00 = 837.29 due
2026-09-08 INV-00151 VOIDED (wrong tax profile)
credit of 50.00 RESTORED to Acme Corp
2026-09-08 INV-00159 issued, 887.29
credit of 50.00 applied -> 837.29 due
The credit survived the void because it is an adjustment link, not a decrement of a balance. A system that had subtracted 50.00 from a running balance would have lost it.
Credits on a QuickBooks tenant
POST /api/credits { tenant: "acme-qbo.example.com", amount: 5000 }
409 Conflict — tenant tax is owned by QuickBooks Online.
Raise the credit note in QuickBooks.
The control is greyed out in the interface and the API refuses it. A credit computed against a total Vodia Billing does not own would disagree with the accounting record.