Recurring Invoices
A billing schedule that fires real customer invoices automatically on a weekly, monthly, quarterly, or annual cadence — define the template once and let the system generate the invoices.
A recurring invoice is a billing template, not an invoice itself. It holds the
customer, a set of line items, a frequency, a start date, and an optional end
date. Each day the system checks which templates are due and calls generateInvoice()
on them — creating a real customer invoice, advancing the
next invoice date by one period, and stamping last generated. The template stays
in place and fires again when its next date arrives, indefinitely or until the end date
passes.
Recurring invoices produce draft invoices. Review and send each one from Sales → Invoices, or automate approval with the Automation Builder.
Creating a recurring invoice #
New templates are created from Sales → Recurring Invoices → New Recurring Invoice. The form has two sections: Schedule and Line Items.
Schedule fields:
- Title — an internal name for the template (e.g. Monthly Retainer — Acme Corp). Required.
- Customer — the customer who will receive each generated invoice. Required.
- Frequency — how often to bill: Weekly, Monthly, Quarterly, or Annually. A schedule dated near the end of a month stays on the end of the month: bill on 31 January and the next invoice is 28 February (29 in a leap year), then 28 March — the date is clamped to the shortest month it meets and never rolls into the following month.
- Start Date — the date the schedule begins. Required.
- Next Invoice Date — the date the next invoice will fire. Required; set it to the same value as Start Date for immediate billing, or push it forward to skip an initial period. When creating via the API the field is optional and defaults to the start date if omitted.
- End Date — optional; leave blank to run forever. When today passes the end date, the template stops firing even if it is still marked active. "Today" here, and in the dates below, is today on your account's calendar, in the timezone set in Account Settings.
- Days Until Due — how many days after the invoice date the generated invoice is due. Defaults to
30. The due date on each generated invoice is computed astoday + days_until_dueat the moment of generation. - Tax Rate — an optional tax rate applied to each generated invoice. Leave blank for tax-exempt customers.
- Active — toggle off to pause the schedule without deleting the template. An inactive template is skipped during the daily run.
- Notes (printed on each invoice) — carried verbatim onto every generated invoice's notes, so it appears on each invoice's PDF and on the copy emailed to the customer.
Line items #
The Line Items section is a repeater of one or more rows. Each row is the template for a line on every generated invoice:
- Description — free-text label for the line. Required.
- Quantity — numeric, minimum 1. Required.
- Unit Price — entered in dollars in the UI (e.g.
250.00). Required.
Over the API, unit_price is in cents — send
25000for $250.00.
Line items are stored in RecurringInvoiceItem records linked to the template. They are
managed directly on the recurring invoice form, or independently via the
/api/v1/recurring-invoice-items endpoints. Every time the template fires, it creates
matching line items on the new CustomerInvoice with the same description, quantity, and
unit price.
How generation works #
When generateInvoice() runs:
- It sums
quantity × unit_priceacross all line items to compute the subtotal. - If a tax rate is attached, it calculates tax amount using that rate's formula.
- A
CustomerInvoiceis created in draft status withinvoice_date = today,due_date = today + days_until_due, andamount_due = subtotal + tax_amount. - Each line item is cloned onto the invoice.
last_generated_atis updated andnext_invoice_dateis advanced by one period.
The daily job generates a template when it is active, its next_invoice_date is today on
your account's calendar or up to two days behind it, and that date is not after its
end_date. Two days covers a day your account's clock skipped when the clocks changed and a
short outage. A template that is behind by up to two days is generated once, dated
today, and moved to its next period. A template more than two days behind (for example
one paused for weeks and switched back on, or created with a date in the past) does not
bill the periods it missed: it is moved to its first date within the last two days or later,
and is billed only if that date is within the last two days or today. This is noted in our
logs, and old dates never turn into a batch of invoices. A date typed in far in the past (a
weekly template dated before about the year 110, say) is too far back to move: it is left as
it is, not billed, and noted in our logs each day until its date is corrected. Running the job
twice in a day generates nothing the second time.
Generate Invoice Now, on a template's page, creates the invoice immediately, dated today, and the confirmation says what will happen to that template's next date. A template whose next date was today or ahead advances by one period as usual. A template that was behind moves to its first date after today: the invoice stands for the periods it missed, so they are not billed, and the daily job does not bill the same period again. The button is not shown once a template has run out (its next date is after its end date). If a template's date is too far in the past to move, the invoice is still created and a notice asks you to correct the date. Undo, on the entry in the new invoice's change log, removes the invoice and puts the template's date back.
The recurring invoices list #
The list at Sales → Recurring Invoices shows each template's title, customer, frequency, next invoice date, end date (or Ongoing if none is set), and an active indicator. Sort by any column; filter or search by customer name or title.
Pausing and ending a schedule #
To pause billing without deleting the template, toggle Active off. The template
remains intact with all its line items and history, and will resume firing as soon as
you toggle it back on. If its next_invoice_date is then no more than two days behind, it
generates once; if it is further behind, the periods it missed are skipped without
invoices, so set the next date yourself if you want the next invoice on a particular day.
To end the schedule on a specific date, set End Date. Invoices already generated are unaffected — the end date only controls when new ones stop being issued.
Relationship to customer invoices #
Each fired invoice is a standalone CustomerInvoice linked to the same customer. From
that point it behaves exactly like a manually created invoice: you can add payments,
apply credit memos, and share it through the customer portal. The recurring template does
not track which invoices it has generated beyond updating last_generated_at — to see
the history, filter the invoice list by customer. See
Customer Invoices & Payments.
Doing it from the API #
# List recurring invoices (filter to active templates for one customer)
curl "https://your-domain.com/api/v1/recurring-invoices?customer_id=<id>&active=true" \
-H "Authorization: Bearer $TOKEN"
# Create a monthly recurring invoice with one line item
curl -X POST "https://your-domain.com/api/v1/recurring-invoices" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "<customer-id>",
"title": "Monthly Retainer",
"frequency": "monthly",
"start_date": "2026-07-01",
"days_until_due": 30,
"active": true
}'
# Add a line item to the template (unit_price in cents)
curl -X POST "https://your-domain.com/api/v1/recurring-invoice-items" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"recurring_invoice_id": "<recurring-invoice-id>",
"description": "Monthly support retainer",
"quantity": 1,
"unit_price": 150000
}'
The list endpoint accepts
customer_idandactive(boolean) query filters and is paginated at 50 per page. Line items accept arecurring_invoice_idfilter to scope results to a single template.DELETEon either endpoint soft-deletes — previously generated invoices are not affected.