Facturas recurrentes
Omite un ciclo, crea una recurrencia desde una factura, elige si cada ejecución emite o envía la factura, fija o sigue la plantilla PDF, genera una factura ahora, configura el envío automático, previsualiza el próximo documento y define campos fiscales por línea.
Una factura recurrente es una plantilla más una cadencia: Factuarea genera una factura real en cada ejecución programada. Esta guía cubre los controles que van más allá del create/update básico — omitir un ciclo, arrancar una recurrencia a partir de una factura existente, el envío automático por correo, la previsualización del documento calculado y los campos fiscales por línea que exige VeriFactu.
Todos los endpoints de abajo viven bajo https://api.factuarea.com/v1 y usan el
mismo envoltorio de error, paginación por
cursor y scopes que el
resto de la API. Cuelgan de /v1/companies/{company}/…: {company} es el id
(UUID v7) del NIF con el que operas, y en los ejemplos curl es $COMPANY_ID.
Alcanzar un NIF explica cómo resolverlo.
Omitir la próxima generación
POST /v1/companies/{company}/recurring-invoices/{recurring_invoice}/skipAvanza next_run_at exactamente un periodo sin generar una factura para el
ciclo actual. La ocurrencia omitida no cuenta para max_occurrences — el
contador de facturas generadas no se mueve. Úsalo para saltarte un periodo de
facturación (festivos, un cliente en pausa) manteniendo intacto el calendario.
Requiere el scope recurring_invoices:write. Devuelve 200 con el recurso de la
factura recurrente (fíjate en el next_run_at avanzado). Una recurrencia
cancelada o completada responde 422; una factura recurrente que pertenece a
otra empresa responde 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"Omitir registra una entrada skipped en la actividad de la factura recurrente
— el historial muestra que el ciclo se omitió a propósito, no que se perdió.
Crear una recurrencia desde una factura existente
POST /v1/companies/{company}/invoices/{invoice}/create-recurringCopia las líneas, el cliente y la serie de una factura origen en una factura
recurrente nueva (con su propio UUID v7) y aplica la cadencia indicada en el
cuerpo. La factura origen queda intacta. Requiere el scope
recurring_invoices:write.
| Campo | Tipo | Obligatorio | Notas |
|---|---|---|---|
frequency | string | sí | daily, weekly, biweekly, monthly, quarterly, semiannual, yearly. |
start_on | string (YYYY-MM-DD) | sí | Primera ejecución programada. |
end_on | string (YYYY-MM-DD) | no | Última ejecución permitida. |
name | string | no | Etiqueta de la recurrencia (≤255). |
description | string | no | |
notes | string | no | |
metadata | object | no | Tus pares clave/valor. |
holiday_handling | string | no | Cómo desplazar una ejecución que cae en festivo. |
days_before_due | integer | no | Desfase de vencimiento de cada factura generada. |
max_occurrences | integer | no | Detener tras N facturas generadas. |
auto_delivery | object | no | Ver Envío automático. |
generation_mode | string | no | draft, issue o issue_and_send. Ver Modo de generación. |
presentation_policy | objeto | no | Qué plantilla PDF usa cada ejecución. Ver Política de presentación. |
payment_terms_template | array | no | Plazos relativos para cada factura generada. Ver Plantilla de calendario de pagos. |
Devuelve 201 con la nueva factura recurrente y una cabecera Location que
apunta a ella. Una factura origen que pertenece a otra empresa responde 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"]
}
}'Modo de generación
generation_mode decide qué hace cada ejecución con la factura que genera. Está
disponible en POST /v1/companies/{company}/recurring-invoices,
PATCH /v1/companies/{company}/recurring-invoices/{recurring_invoice} y
POST /v1/companies/{company}/invoices/{invoice}/create-recurring, en todas las versiones de la
API, y todas las respuestas de facturas recurrentes lo devuelven.
generation_mode | Cada ejecución | Envío |
|---|---|---|
draft | Deja la factura en borrador, sin número definitivo. | Ninguno. |
issue | Emite la factura: número definitivo, registro VeriFactu, movimientos de stock. | Ninguno: la factura queda con is_sent: false. |
issue_and_send | Emite la factura y la envía por correo a los destinatarios del envío automático. | Se marca como enviada cuando el servidor de correo acepta el email. |
En issue_and_send, una entrega fallida no deshace la ejecución: la factura
queda emitida y sin enviar, y puedes enviarla después con
POST /v1/companies/{company}/invoices/{invoice}/send. Cualquier otro valor responde 422. En
PATCH, omitir generation_mode conserva el modo actual.
send_automatically se mantiene como campo de compatibilidad derivado del modo:
true solo con issue_and_send. Sin generation_mode, true sigue
seleccionando issue_and_send y false selecciona draft; enviar ambos con
valores contradictorios responde 422. Las facturas recurrentes creadas antes
de que existiera el modo conservan su comportamiento: las que enviaban sus
facturas por correo son issue_and_send y el resto draft.
Política de presentación
Cada factura que genera una recurrencia necesita una plantilla PDF. La política de
presentación dice cuál. Está disponible en POST /v1/companies/{company}/recurring-invoices,
PATCH /v1/companies/{company}/recurring-invoices/{recurring_invoice} y
POST /v1/companies/{company}/invoices/{invoice}/create-recurring, y el recurso de factura recurrente
devuelve presentation_policy, presentation_policy_version y
presentation_pause_reason.
La política es un objeto con exactamente cinco claves, todas obligatorias:
| Clave | latest | pinned |
|---|---|---|
mode | "latest" | "pinned" |
template_id | El UUID de una plantilla, o null para seguir la selección que recibiría normalmente cada documento (serie, cliente, empresa). | Obligatoria. |
revision_id | null | Obligatoria: la revisión exacta de la plantilla. |
commercial_identity_id, commercial_identity_revision_id | null | Las dos informadas, o las dos null cuando la plantilla no tiene marca. |
latestsigue a la plantilla: cada ejecución usa la revisión publicada en ese momento, así que editar la plantilla cambia las facturas siguientes.pinnedla fija: cada ejecución usa exactamente esa revisión (y la revisión de marca), por mucho que la plantilla cambie después. Un pin que ya no se puede usar no degrada nunca alatest.
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
}'Al actualizar, omitir presentation_policy conserva la actual y null la restablece a
una política latest que sigue la selección habitual. {}, las claves de más y las
combinaciones parciales se rechazan con 422 invalid_recurring_presentation_policy
(error.param señala la clave afectada); por ejemplo, una revisión de marca fijada sin
su marca.
Versión. La política tiene su propio contador, presentation_policy_version, que en
una recurrencia nueva empieza en 1. Para editar una recurrencia existente debes
enviar expected_presentation_policy_version (un entero de 1 o más) siempre que la
petición lleve la política, la plantilla de calendario de pagos, el cliente o la serie,
incluso si el valor es null o no cambia. Sin él:
428 recurring_presentation_policy_precondition_required. Con uno obsoleto:
409 recurring_presentation_policy_version_conflict, y no se escribe nada. Vuelve a
leer la recurrencia y reintenta sobre la versión nueva. Cambiar solo etiquetas no lo
necesita, una recurrencia nueva no lo acepta y guardar nunca reactiva una recurrencia
pausada.
Si la presentación con revisiones no está activa para tu empresa, presentation_policy,
payment_terms_template y expected_presentation_policy_version se ignoran
(incluso como null): la petición se comporta como antes y nunca responde
pdf_presentation_not_ready por ellos. Cuando está activa se validan.
Cuando la plantilla deja de estar disponible
Entre ejecuciones, una plantilla puede archivarse, perder compatibilidad o dejar de estar incluida en tu plan. Lo que ocurre depende del modo:
latest: la ejecución se revierte (no se crea ninguna factura), la recurrencia pasa apausedypresentation_pause_reasondice por qué:template_archived,template_blocked,template_incompatible,commercial_identity_archived,commercial_identity_blocked,entitlement_unavailableoconfiguration_invalid. La empresa recibe un único aviso pidiendo una plantilla disponible.pinned: la recurrencia no se pausa ni se modifica. La ejecución fallida queda anotada en los registros y el pin sigue apuntando a su revisión.
Restaurar la plantilla o editar la recurrencia no la reanuda. Reanúdala de forma
explícita con POST /v1/companies/{company}/recurring-invoices/{id}/resume: vuelve a comprobar la política
y, si la plantilla sigue sin estar disponible, responde
422 recurring_presentation_unavailable y deja el motivo de pausa. Los fallos que no
son visuales (transporte, datos fiscales, datos bancarios) nunca se convierten en una
pausa.
Plantilla de calendario de pagos
payment_terms_template da a cada factura generada su calendario de pagos, con
desplazamientos en lugar de fechas. Cada fila tiene offset_days (un entero de 0 o más,
no decreciente a lo largo de la lista), amount (un string con exactamente dos decimales,
distinto de cero, con el mismo signo en todas las filas), payment_means ("01" a
"19") y account (un objeto { "iban", "bic" } o null; solo para 02 y 04). Al
actualizar, omitirla conserva la plantilla, null significa desconocido y [] significa
«sin plazos».
{
"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 }
]
}La fecha de vencimiento de cada plazo es la fecha de esa factura más su desplazamiento, y
cada factura recibe sus propias identidades de plazo. Los importes deben sumar el total a
pagar de la factura generada: si no, esa ejecución falla con el error de la factura
(consulta Datos de negocio de la factura),
no con una pausa visual. Una fila incorrecta responde 422
invalid_recurring_payment_terms_template.
Generar una factura ahora
POST /v1/companies/{company}/recurring-invoices/{recurring_invoice}/generateFuerza la generación de la factura de hoy fuera del ciclo
(referencia,
scope recurring_invoices:write). Sigue las mismas reglas que una ejecución programada:
el modo de generación decide si la factura se queda en borrador, se
emite o se envía por correo, y la política de presentación decide la
plantilla. Responde 200:
{
"data": {
"object": "recurring_invoice.generate_result",
"invoice_id": "0197c6b3-2a48-7d15-9e60-5c8b1f3a7d92",
"invoice_number": "F-2026-0048"
}
}Una recurrencia genera una factura por día: repetir la llamada el mismo día devuelve la
factura ya generada. Una recurrencia que no puede ejecutarse en su estado actual responde
422 recurring_invoice_cannot_run, y una política latest cuya plantilla no está
disponible responde 422 recurring_presentation_unavailable. El servidor MCP ofrece
la misma operación como la herramienta generate_recurring_invoice.
Si la empresa activa o desactiva su sistema de presentación dos veces seguidas mientras se genera la factura, la llamada responde un 409 reintentable recurring_presentation_activation_changed. No se escribió nada: repite la petición, y la misma Idempotency-Key no duplica la factura.
Envío automático
El objeto auto_delivery — disponible al crear y actualizar una recurrente y en
create-recurring — manda por correo cada factura generada de forma automática.
| Campo | Tipo | Notas |
|---|---|---|
send_automatically | boolean | Derivado de generation_mode: true solo con issue_and_send. Es preferible enviar el modo. |
recipients | string[] | Destinatarios principales (email). |
cc | string[] | Destinatarios en copia (email). |
subject | string | Asunto personalizado (≤255). null usa el predeterminado. |
body | string | Cuerpo personalizado (≤5000). null usa el predeterminado. |
Con generation_mode: "issue_and_send", cada factura generada se manda por
correo a recipients (con cc opcional) usando subject/body. draft e
issue no envían nada. Con issue_and_send, un objeto auto_delivery necesita
al menos un destinatario: una lista recipients vacía responde
422 auto_delivery_recipients_required — no hay a quién enviar.
{
"generation_mode": "issue_and_send",
"auto_delivery": {
"recipients": ["billing@acme.example"],
"cc": ["copy@acme.example"],
"subject": "Tu factura mensual",
"body": "Hola, aquí tienes tu factura de este periodo."
}
}Previsualizar el documento calculado
GET /v1/companies/{company}/recurring-invoices/{recurring_invoice}/previewSin expand, preview devuelve solo la previsión de fechas — las próximas
fechas de ejecución (usa count para controlar cuántas).
Pasa expand=document para calcular además el próximo documento en seco: la
respuesta añade un bloque next_invoice con las lines resueltas y los
totals (subtotal, tax, total), construidos desde template_data sin
persistir nada. Úsalo para mostrarle al cliente qué contendrá exactamente la
próxima factura antes de emitirla.
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 nunca crea una factura. Es una lectura pura — los totales en seco se
calculan en memoria desde template_data.
Campos fiscales por línea
Cada línea de template_data acepta los campos fiscales que necesitan VeriFactu
y el modelo tributario español. Se trasladan a cada factura generada.
| Campo | Tipo | Notas |
|---|---|---|
exemption_reason | string | Causa de exención / no sujeción según LIVA. Uno de E1–E6, N1, N2. null si no se informa. |
regime_key | string | Clave de régimen VeriFactu (ClaveRegimen, lista L8.1 de la AEAT). null si no se informa. |
retention | number | Porcentaje de retención IRPF (0–100). |
retention_rate_id | string (UUID v7) | Impuesto del catálogo aplicado como retención IRPF. Opaco — no cambia el cálculo de taxes/total (lo hace el porcentaje plano retention). |
surcharge | number | Porcentaje de recargo de equivalencia (0–100). |
surcharge_rate_id | string (UUID v7) | Impuesto del catálogo aplicado como recargo de equivalencia. Opaco — el porcentaje plano surcharge gobierna el cálculo. |
El recargo de equivalencia va ligado por ley al IVA de la línea. Los únicos pares
legales tax_rate → surcharge son:
IVA (tax_rate) | Recargo (surcharge) |
|---|---|
21 | 5.2 |
10 | 1.4 |
4 | 0.5 |
Enviar un exemption_reason, regime_key, retention_rate_id o
surcharge_rate_id fuera de su catálogo responde 422 con una lista
allowed_values en el error. Un par IVA↔recargo ilegal (p.ej. 21 con 1.4)
también se rechaza con 422.
{
"template_data": {
"lines": [
{
"description": "Consultoría",
"quantity": 1,
"unit_price": 1000,
"tax_rate": 21,
"surcharge": 5.2,
"retention": 15,
"regime_key": "01",
"exemption_reason": null
}
]
}
}Snapshots de catálogo y tarifa
Las plantillas recurrentes admiten price_list_id y reprice_strategy, igual
que un borrador de venta. Las líneas de catálogo en template_data.lines
pueden guardar product_id, variant_id, presentation_id,
confirmed_base_quantity y los campos resueltos de snapshot (item_kind,
unidades, factor, cantidad base, price_source, id/nombre de tarifa y unidad de
precio). Las facturas generadas heredan ese snapshot; editar después el catálogo
no reescribe la plantilla. Usa existing_catalog_lines para recalcular líneas de
catálogo o future_lines_only para conservarlas. Los precios manuales, de oferta
o de pack nunca se reprician silenciosamente. Consulta Tarifas.
Previsiones, registros y estimaciones mensuales
preview devuelve un array de fechas previstas en data; count admite hasta 36. expand=document añade el siguiente documento calculado. Los registros de ejecución usan limit (hasta 100) y cursor. estimated_revenue_this_month estima los importes de las plantillas que quedan programadas desde hoy hasta fin de mes. No representa ingresos emitidos, cobros ni efectivo garantizado. Las plantillas conservan additional_description por línea.