Recurring invoices
Skip a cycle, create a recurrence from an invoice, choose whether each run issues or emails the invoice, fix or follow the PDF template, generate an invoice now, configure auto-delivery, preview the next document and set per-line fiscal fields.
A recurring invoice is a template plus a cadence: Factuarea generates a real invoice on every scheduled run. This guide covers the controls that go beyond basic create/update — skipping a cycle, bootstrapping a recurrence from an existing invoice, automatic email delivery, previewing the computed document and the per-line fiscal fields that VeriFactu requires.
All endpoints below live under https://api.factuarea.com/v1 and use the same
error envelope, cursor pagination and
scopes as the rest of the API. They hang off
/v1/companies/{company}/…: {company} is the id (UUID v7) of the tax ID you
operate on, and in the curl examples it is $COMPANY_ID.
Reach a tax ID explains how to resolve it.
Skip the next generation
POST /v1/companies/{company}/recurring-invoices/{recurring_invoice}/skipAdvances next_run_at by exactly one period without generating an invoice
for the current cycle. The skipped occurrence is not counted against
max_occurrences — the generated-invoice counter does not move. Use it to jump
over a billing period (holidays, a paused client) while keeping the schedule
intact.
Requires the recurring_invoices:write scope. Returns 200 with the
recurring-invoice resource (note the advanced next_run_at). A cancelled or
completed recurrence responds 422; a recurring invoice that belongs to another
company responds 404.
curl -X POST https://api.factuarea.com/v1/companies/$COMPANY_ID/recurring-invoices/01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a0b/skip \
-H "Authorization: Bearer $FACTUAREA_API_KEY"Skipping logs a skipped entry in the recurring-invoice activity — the
history shows the cycle was deliberately omitted, not missed.
Create a recurrence from an existing invoice
POST /v1/companies/{company}/invoices/{invoice}/create-recurringCopies the lines, client and series of a source invoice into a brand-new
recurring invoice (its own UUID v7) and applies the cadence supplied in the
body. The source invoice is untouched. Requires the recurring_invoices:write
scope.
| Field | Type | Required | Notes |
|---|---|---|---|
frequency | string | yes | daily, weekly, biweekly, monthly, quarterly, semiannual, yearly. |
start_on | string (YYYY-MM-DD) | yes | First scheduled run. |
end_on | string (YYYY-MM-DD) | no | Last allowed run. |
name | string | no | Label for the recurrence (≤255). |
description | string | no | |
notes | string | no | |
metadata | object | no | Your key/value pairs. |
holiday_handling | string | no | How to shift a run that falls on a holiday. |
days_before_due | integer | no | Due date offset for each generated invoice. |
max_occurrences | integer | no | Stop after N generated invoices. |
auto_delivery | object | no | See Auto-delivery. |
generation_mode | string | no | draft, issue or issue_and_send. See Generation mode. |
presentation_policy | object | no | Which PDF template each run uses. See Presentation policy. |
payment_terms_template | array | no | Relative instalments for every generated invoice. See Payment schedule template. |
Returns 201 with the new recurring invoice and a Location header pointing to
it. A source invoice that belongs to another company responds 404.
curl -X POST https://api.factuarea.com/v1/companies/$COMPANY_ID/invoices/01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a42/create-recurring \
-H "Authorization: Bearer $FACTUAREA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"frequency": "monthly",
"start_on": "2026-07-01",
"max_occurrences": 12,
"generation_mode": "issue_and_send",
"auto_delivery": {
"recipients": ["billing@acme.example"]
}
}'Generation mode
generation_mode decides what each run does with the invoice it generates. It is
available on POST /v1/companies/{company}/recurring-invoices,
PATCH /v1/companies/{company}/recurring-invoices/{recurring_invoice} and
POST /v1/companies/{company}/invoices/{invoice}/create-recurring, in every API version, and every
recurring invoice response returns it.
generation_mode | Each run | Delivery |
|---|---|---|
draft | Leaves the invoice as a draft, without a definitive number. | None. |
issue | Issues the invoice: definitive number, VeriFactu record, stock movements. | None: the invoice stays is_sent: false. |
issue_and_send | Issues the invoice and emails it to the auto-delivery recipients. | Marked as sent when the mail server accepts the email. |
In issue_and_send, a failed delivery does not undo the run: the invoice stays
issued and not sent, and you can email it later with
POST /v1/companies/{company}/invoices/{invoice}/send. Any other value answers 422. On PATCH,
omitting generation_mode keeps the current mode.
send_automatically remains as a compatibility field derived from the mode:
true only with issue_and_send. Without generation_mode, true still
selects issue_and_send and false selects draft; sending both with
contradictory values answers 422. Recurring invoices created before the mode
existed keep their behavior: the ones that emailed their invoices are
issue_and_send and the rest draft.
Presentation policy
Every invoice a recurrence generates needs a PDF template. The presentation
policy says which one. It is available on POST /v1/companies/{company}/recurring-invoices,
PATCH /v1/companies/{company}/recurring-invoices/{recurring_invoice} and
POST /v1/companies/{company}/invoices/{invoice}/create-recurring, and the recurring-invoice
resource returns presentation_policy, presentation_policy_version and
presentation_pause_reason.
The policy is an object with exactly five keys, all required:
| Key | latest | pinned |
|---|---|---|
mode | "latest" | "pinned" |
template_id | A template UUID, or null to follow the selection each document would normally get (series, customer, company). | Required. |
revision_id | null | Required: the exact revision of the template. |
commercial_identity_id, commercial_identity_revision_id | null | Both set, or both null when the template has no brand. |
latestfollows the template: each run uses the revision that is published at that moment, so editing the template changes the next invoices.pinnedfreezes it: every run uses exactly that revision (and brand revision), however the template changes later. A pin that can no longer be used never degrades tolatest.
curl -X PATCH https://api.factuarea.com/v1/companies/$COMPANY_ID/recurring-invoices/0197c6a0-5e14-7b29-8d73-4a1f9c2e6b35 \
-H "Authorization: Bearer $FACTUAREA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"presentation_policy": {
"mode": "pinned",
"template_id": "0197c4a1-02aa-7c31-9e5b-6d2f8a4c1b73",
"revision_id": "0197c4a1-02ab-7e08-8a14-3b7c9d5e2f40",
"commercial_identity_id": null,
"commercial_identity_revision_id": null
},
"expected_presentation_policy_version": 1
}'On update, omitting presentation_policy keeps the current one and null resets it
to a latest policy that follows the usual selection. {}, extra keys and partial
combinations are rejected with 422 invalid_recurring_presentation_policy
(error.param points at the offending key); a pinned brand revision without its
brand, for example.
Version. The policy has its own counter, presentation_policy_version, which a
new recurrence starts at 1. To edit an existing recurrence you must send
expected_presentation_policy_version (an integer of 1 or more) whenever the
request carries the policy, the payment schedule template, the client or the
series, even if the value is null or does not change. Without it:
428 recurring_presentation_policy_precondition_required. With a stale one:
409 recurring_presentation_policy_version_conflict, and nothing is written.
Read the recurrence again and retry on the new version. Changing only labels does
not need it, a new recurrence does not accept it, and saving never reactivates a
paused recurrence.
If the revisioned presentation is not active for your company, presentation_policy,
payment_terms_template and expected_presentation_policy_version are ignored
(even as null): the request behaves as before and never answers
pdf_presentation_not_ready because of them. Once it is active they are validated.
When the template is no longer available
Between runs a template can be archived, lose compatibility or stop being included in your plan. What happens depends on the mode:
latest: the run is rolled back (no invoice is created), the recurrence moves topausedandpresentation_pause_reasonsays why —template_archived,template_blocked,template_incompatible,commercial_identity_archived,commercial_identity_blocked,entitlement_unavailableorconfiguration_invalid. The company receives a single notification asking for an available template.pinned: the recurrence is not paused or changed. The failed run is recorded in the logs and the pin keeps pointing at its revision.
Restoring the template or editing the recurrence does not resume it. Resume
explicitly with POST /v1/companies/{company}/recurring-invoices/{id}/resume; it checks the policy again
and, if the template is still unavailable, answers
422 recurring_presentation_unavailable and leaves the pause reason in place.
Failures that are not visual (transport, tax data, bank data) are never turned into a
pause.
Payment schedule template
payment_terms_template gives every generated invoice its payment schedule, with
offsets instead of dates. Each row has offset_days (an integer, 0 or more, not
decreasing along the list), amount (a string with exactly two decimals, not zero,
all rows with the same sign), payment_means ("01" to "19") and account (an
{ "iban", "bic" } object or null; only for 02 and 04). On update, omitting it
keeps the template, null means unknown and [] means "no instalments".
{
"payment_terms_template": [
{ "offset_days": 0, "amount": "50.00", "payment_means": "01", "account": null },
{ "offset_days": 30, "amount": "50.00", "payment_means": "01", "account": null }
]
}The due date of each instalment is the date of that invoice plus its offset, and each
invoice gets its own instalment identities. The amounts must add up to the total to
pay of the generated invoice: if they do not, that run fails with the error of the
invoice (see Invoice business data), not with a
visual pause. A bad row answers 422 invalid_recurring_payment_terms_template.
Generate an invoice now
POST /v1/companies/{company}/recurring-invoices/{recurring_invoice}/generateForces the generation of today's invoice outside the cycle
(reference,
scope recurring_invoices:write). It follows the same rules as a scheduled run:
the generation mode decides whether the invoice stays a draft,
is issued or is emailed, and the presentation policy decides the
template. It answers 200:
{
"data": {
"object": "recurring_invoice.generate_result",
"invoice_id": "0197c6b3-2a48-7d15-9e60-5c8b1f3a7d92",
"invoice_number": "F-2026-0048"
}
}A recurrence generates one invoice per day: repeating the call the same day
returns the invoice already generated. A recurrence that cannot run in its
current state answers 422 recurring_invoice_cannot_run, and a latest policy
whose template is unavailable answers 422 recurring_presentation_unavailable.
If the company switches its presentation system on or off twice in a row while the invoice is generated, the call answers a retryable 409 recurring_presentation_activation_changed: nothing was written, so repeat the request (it is not stored for replay, and the same Idempotency-Key does not duplicate the invoice).
The MCP server offers the same operation as the tool generate_recurring_invoice.
If the company switches its presentation system on or off twice in a row while the invoice is generated, the call answers a retryable 409 recurring_presentation_activation_changed. Nothing was written: repeat the request, and the same Idempotency-Key does not duplicate the invoice.
Auto-delivery
The auto_delivery object — available on recurring create, update and
create-recurring — emails every generated invoice automatically.
| Field | Type | Notes |
|---|---|---|
send_automatically | boolean | Derived from generation_mode: true only with issue_and_send. Prefer sending the mode. |
recipients | string[] | Primary recipients (email). |
cc | string[] | Carbon-copy recipients (email). |
subject | string | Custom subject (≤255). null uses the default. |
body | string | Custom body (≤5000). null uses the default. |
With generation_mode: "issue_and_send", each generated invoice is emailed to
recipients (with optional cc) using subject/body. draft and issue
send nothing. With issue_and_send, an auto_delivery object needs at least
one recipient: an empty recipients list answers
422 auto_delivery_recipients_required — there is no one to send to.
{
"generation_mode": "issue_and_send",
"auto_delivery": {
"recipients": ["billing@acme.example"],
"cc": ["copy@acme.example"],
"subject": "Your monthly invoice",
"body": "Hi, here is your invoice for this period."
}
}Preview the computed document
GET /v1/companies/{company}/recurring-invoices/{recurring_invoice}/previewWithout expand, preview returns only the date forecast — the upcoming
run dates (use count to control how many).
Pass expand=document to also compute the next document as a dry-run: the
response adds a next_invoice block with the resolved lines and
totals (subtotal, tax, total), built from template_data without
persisting anything. Use it to show the customer exactly what the next invoice
will contain before it is issued.
curl -G https://api.factuarea.com/v1/companies/$COMPANY_ID/recurring-invoices/01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a0b/preview \
-H "Authorization: Bearer $FACTUAREA_API_KEY" \
--data-urlencode "expand=document" \
--data-urlencode "count=2"{
"data": [
{
"date": "2026-04-01",
"due_date": "2026-05-01",
"occurrence_number": 4,
"estimated_total": 242,
"will_be_sent": true
},
{
"date": "2026-05-01",
"due_date": "2026-05-31",
"occurrence_number": 5,
"estimated_total": 242,
"will_be_sent": true
}
],
"next_invoice": {
"lines": [
{
"description": "Cuota soporte mensual",
"quantity": 1,
"unit_price": 200,
"tax_rate": 21,
"retention_rate": 0,
"surcharge_rate": 0,
"commercial_quantity": 1,
"commercial_unit_code": "MON",
"base_unit_code": "MON",
"conversion_factor": "1.000000",
"base_quantity": "1.0000",
"price_list_uuid": null,
"price_list_name": null,
"price_source": "manual",
"price_unit_code": "MON",
"configuration_uuid": null,
"configuration_name": null,
"configuration_signature": null,
"option_labels": [],
"price_semantics": "per_commercial_unit",
"price_adjustment_total": "0.0000",
"option_adjustments_absorbed": false,
"subtotal": 200,
"tax": 42,
"line_total": 242
}
],
"totals": {
"subtotal": 200,
"tax": 42,
"total": 242
}
}
}preview never creates an invoice. It is a pure read — the dry-run totals are
computed in memory from template_data.
Per-line fiscal fields
Each line of template_data accepts the fiscal fields VeriFactu and the Spanish
tax model need. They flow into every generated invoice.
| Field | Type | Notes |
|---|---|---|
exemption_reason | string | LIVA exemption / non-subjection cause. One of E1–E6, N1, N2. null if not informed. |
regime_key | string | VeriFactu regime key (ClaveRegimen, AEAT list L8.1). null if not informed. |
retention | number | IRPF withholding percentage (0–100). |
retention_rate_id | string (UUID v7) | Catalog tax applied as IRPF withholding. Opaque — does not change the taxes/total computation (the flat retention percentage does). |
surcharge | number | Equivalence surcharge percentage (0–100). |
surcharge_rate_id | string (UUID v7) | Catalog tax applied as equivalence surcharge. Opaque — the flat surcharge percentage drives the maths. |
The equivalence surcharge is tied to the line VAT by law. The only legal
tax_rate → surcharge pairs are:
VAT (tax_rate) | Surcharge (surcharge) |
|---|---|
21 | 5.2 |
10 | 1.4 |
4 | 0.5 |
Sending an exemption_reason, regime_key, retention_rate_id or
surcharge_rate_id outside its catalog responds 422 with an allowed_values
list in the error. An illegal VAT↔surcharge pair (e.g. 21 with 1.4) is also
rejected with 422.
{
"template_data": {
"lines": [
{
"description": "Consulting",
"quantity": 1,
"unit_price": 1000,
"tax_rate": 21,
"surcharge": 5.2,
"retention": 15,
"regime_key": "01",
"exemption_reason": null
}
]
}
}Catalog price-list snapshots
Recurring templates accept price_list_id and reprice_strategy, just like a
sales-document draft. Catalog-backed template_data.lines can store
product_id, variant_id, presentation_id, confirmed_base_quantity and the
resolved snapshot fields (item_kind, units, conversion factor, base quantity,
price_source, price-list id/name and price unit). Generated invoices inherit
that snapshot deterministically; later catalog edits do not silently rewrite the
template. Use existing_catalog_lines to re-resolve catalog-priced lines or
future_lines_only to preserve them. Manual, supplier-offer and pack prices are
never silently repriced. See Price lists.
Forecasts, logs and monthly estimates
preview returns an array of forecast dates under data; count is capped at 36. expand=document adds the next computed document. Execution logs use limit (up to 100) and cursor. estimated_revenue_this_month estimates the remaining scheduled template amounts from today through month end. It is not issued revenue, collections or guaranteed cash. Per-line additional_description is retained in templates.