Recurring invoices
What the recurring flag configures, what generates the next one, and what does not.
A recurring invoice is not a subscription. It is an ordinary invoice marked as a template: a nightly job copies it into a new document each cycle, and every copy still has to be opened for payment and settled by the customer. Nothing is debited automatically, because nothing is stored that could be debited.
No second invoice is produced today
The job decides what is due by reading next_invoice_date, and the only code that ever writes
that field is the job itself, on the copy it has just made. Creation never sets it, through the
API or the Dashboard, so the chain has no first link. A recurring invoice is stored, flagged and
listed as recurring, and no copy is ever generated. Issue each cycle yourself until that is
closed. What follows describes what the flag configures and what the job does with it.
Marking an invoice recurring
There is no separate endpoint and no subscription resource. Two fields on the ordinary create call are the whole surface.
https://api.wajub.com/invoicesis_recurringbooleanoptionaldefault : falserecurring_intervalenumoptionaldaily, weekly, monthly, quarterly or yearly. The gap between copies.recurring_frequencyenumoptionalrecurring_interval, same five values.customer_idstring (uuid)optionalEverything else on the invoice, the lines, the totals, the notes, behaves exactly as on a one-off.
curl https://api.wajub.com/invoices \
-H "Authorization: sk.kZ3qP8mWvL2xR7tB5nY4hC6dF9jS1aG0eU3i…" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "9d1f0c7a-4b2e-4f61-9d3a-7c8e5b21a940",
"customer_name": "Amina Traoré",
"invoice_date": "2026-09-11",
"currency": "XAF",
"items": [
{ "name": "Retainer", "quantity": 1, "unit_price": 250000 }
],
"is_recurring": true,
"recurring_interval": "monthly"
}'The response is the same 201 Created as any other invoice, status: "draft" included, carrying
two recurring fields and no more.
recurring_frequency is a spelling, not a multiplier
Underneath, recurring_interval holds the unit and recurring_frequency holds a number: how many
units between copies. The API does not expose that number. Sending recurring_frequency: "monthly" rewrites it onto recurring_interval and pins the multiplier to 1, so every two
months is not reachable through the API. Update also drops the alias entirely, so send
recurring_interval and treat the other spelling as legacy.
What the API cannot set
The recurrence has four more settings in the database, and none of them is on the API.
| Setting | What it does | Where |
|---|---|---|
recurring_end_type | never, date or occurrences | Dashboard |
recurring_end_date | Stop generating after this date | Dashboard |
recurring_occurrences | Stop after this many copies | Dashboard |
A multiplier above 1 | Every two months, every three weeks | Neither |
An invoice created through the API therefore has no end rule at all, which matters more than it
looks: read the section on stopping one below before you set is_recurring on anything.
What comes back, and what does not
is_recurring and recurring_interval are the only recurring fields on the invoice object. There
is no next_invoice_date, no recurring_parent_id and no end rule in the response.
Two consequences worth planning around. The API cannot tell you when the next document is due, and
it cannot tell a generated copy from the invoice it was copied from, since the link between them is
not exposed. GET /invoices has no recurring filter either, though the Dashboard list does, so
keep your own record of which invoice is the recurring one.
How a cycle runs
A job named invoices:generate-recurring runs every night at 02:00 and walks every invoice that is
flagged recurring, has a next date in the past, and is not cancelled. For each one it checks the
plan, then the end rule, then copies.
| The copy inherits | The copy recomputes |
|---|---|
| The lines, with their quantities, prices and taxes | Its own invoice_number |
| The currency, the notes, terms and footer | invoice_date, set to the cycle that just came due |
| The interval and the end rule | due_date, thirty days later |
| The template and the display settings | The customer block, re-read from the customer record |
Then the original's next date moves forward by one interval, and the cycle is armed again.
Two of those deserve a second look. The customer block is re-snapshotted from the live customer
each time, so an address or a company name corrected on the record flows into the next document and
leaves the earlier ones untouched. And due_date is always thirty days after the copy's date,
whatever terms the original carried, because the field the calculation reads does not exist on the
invoice.
A copy is a draft, not a charge
Each generated invoice lands as draft, with amount_paid back to zero. It is not opened for
payment, not emailed, and not paid. Somebody still has to call /send and deliver the URL every
cycle, exactly as for a one-off. Recurrence saves you writing the document, nothing else.
No customer record, no copy
Generation loads the invoice's customer and gives up if there is none, logging a warning and
moving on. An invoice addressed to a customer_name alone, which is perfectly valid for a one-off,
silently never generates. Pass customer_id when you set is_recurring.
Stopping a recurrence
While the original is still unpaid, either of two calls ends it. Cancelling takes it out of the job's reach for good, and an update clearing the flag leaves the document alive but inert.
https://api.wajub.com/invoices/{id}/cancelcurl -X PUT https://api.wajub.com/invoices/inv_LRQqYvlhrgUOE225KMKU \
-H "Authorization: sk.kZ3qP8mWvL2xR7tB5nY4hC6dF9jS1aG0eU3i…" \
-H "Content-Type: application/json" \
-d '{ "is_recurring": false }'Once it is paid, neither call is accepted
An invoice that is paid, refunded, cancelled or carries any amount_paid above zero is frozen:
the update answers 400 This invoice cannot be edited (paid, cancelled, or has payments). and the
cancel answers 400 This invoice cannot be cancelled (already paid, cancelled, or refunded). The
recurrence, however, is not frozen with it. Decide how a recurrence ends before the first cycle is
settled, and set the end rule in the Dashboard, since the API cannot.
What it costs in plan
Recurrence needs recurring_billing, which starts at Growth. The gate is enforced in two of the
three places it could be.
The Dashboard refuses to save a recurring invoice without it. The job checks it on every run and
skips the invoice, logging a notice and leaving the document untouched, so a downgraded account
resumes on its own if it upgrades again. POST /invoices checks neither, so creating a recurring
invoice through the API succeeds on any plan and simply never generates.