Factuarea APIDevelopers

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}/skip

Avanza 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-recurring

Copia 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.

CampoTipoObligatorioNotas
frequencystringsídaily, weekly, biweekly, monthly, quarterly, semiannual, yearly.
start_onstring (YYYY-MM-DD)síPrimera ejecución programada.
end_onstring (YYYY-MM-DD)noÚltima ejecución permitida.
namestringnoEtiqueta de la recurrencia (≤255).
descriptionstringno
notesstringno
metadataobjectnoTus pares clave/valor.
holiday_handlingstringnoCómo desplazar una ejecución que cae en festivo.
days_before_dueintegernoDesfase de vencimiento de cada factura generada.
max_occurrencesintegernoDetener tras N facturas generadas.
auto_deliveryobjectnoVer Envío automático.
generation_modestringnodraft, issue o issue_and_send. Ver Modo de generación.
presentation_policyobjetonoQué plantilla PDF usa cada ejecución. Ver Política de presentación.
payment_terms_templatearraynoPlazos 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_modeCada ejecuciónEnvío
draftDeja la factura en borrador, sin número definitivo.Ninguno.
issueEmite la factura: número definitivo, registro VeriFactu, movimientos de stock.Ninguno: la factura queda con is_sent: false.
issue_and_sendEmite 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:

Clavelatestpinned
mode"latest""pinned"
template_idEl UUID de una plantilla, o null para seguir la selección que recibiría normalmente cada documento (serie, cliente, empresa).Obligatoria.
revision_idnullObligatoria: la revisión exacta de la plantilla.
commercial_identity_id, commercial_identity_revision_idnullLas dos informadas, o las dos null cuando la plantilla no tiene marca.
  • latest sigue a la plantilla: cada ejecución usa la revisión publicada en ese momento, así que editar la plantilla cambia las facturas siguientes.
  • pinned la 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 a latest.
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 a paused y presentation_pause_reason dice por qué: template_archived, template_blocked, template_incompatible, commercial_identity_archived, commercial_identity_blocked, entitlement_unavailable o configuration_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}/generate

Fuerza 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.

CampoTipoNotas
send_automaticallybooleanDerivado de generation_mode: true solo con issue_and_send. Es preferible enviar el modo.
recipientsstring[]Destinatarios principales (email).
ccstring[]Destinatarios en copia (email).
subjectstringAsunto personalizado (≤255). null usa el predeterminado.
bodystringCuerpo 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}/preview

Sin 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.

CampoTipoNotas
exemption_reasonstringCausa de exención / no sujeción según LIVA. Uno de E1–E6, N1, N2. null si no se informa.
regime_keystringClave de régimen VeriFactu (ClaveRegimen, lista L8.1 de la AEAT). null si no se informa.
retentionnumberPorcentaje de retención IRPF (0–100).
retention_rate_idstring (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).
surchargenumberPorcentaje de recargo de equivalencia (0–100).
surcharge_rate_idstring (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)
215.2
101.4
40.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.

En esta página

¿Te echamos una mano?Contactar con soporte