Factuarea APIDevelopers

Datos de negocio de la factura

Calendario de pagos, referencias de cabecera y de línea, periodo de facturación, literales legales y centros administrativos DIR3 en una factura — omitir frente a null, versiones y cómo cambiarlos tras emitir.

Además de su contenido fiscal, una factura puede llevar datos de negocio estructurados: el calendario de pagos acordado, las referencias que el cliente pide ver (contrato, transacción, expediente, albaranes), el periodo de facturación, literales legales comerciales y los centros administrativos DIR3 que la encaminan en el sector público. Estos datos son aditivos: una factura sin ellos se comporta exactamente como antes.

Todo vive en los mismos cuerpos que ya usas (POST /v1/companies/{company}/invoices, PATCH /v1/companies/{company}/invoices/{invoice} y las filas de bulk-create) y vuelve en el recurso de la factura. Todas las rutas de esta guía 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.

El ciclo de vida tiene dos fases:

  • Mientras la factura es un borrador puedes crear, reemplazar y quitar cada campo.
  • Al emitirla los datos se congelan con ella. Editarlos se rechaza con 422 business_rule_violation y subcode invoice_not_editable_in_current_state. Lo único que aún puedes cambiar es el encaminamiento administrativo, añadiendo una revisión administrativa.
BloqueCampos de la peticiónSe congela al emitir
Calendario de pagospayment_terms, expected_payment_terms_versionSí. Se valida y se congela una sola vez.
Referenciasbusiness_references, lines[].business_references, lines[].delivery_notes_references, lines[].sequence_numberSí
Periodo de facturación y literalesinvoicing_period, legal_literalsSí
Centros DIR3administrative_centresEl encaminamiento capturado, más las revisiones

Omitir, null y []

La API distingue una clave ausente de una null o vacía, y la diferencia importa. La misma regla vale para todos los campos de esta guía:

EnvíasAl crearAl actualizar (PATCH)
La clave está ausenteNo se guarda nada (el calendario queda desconocido). En DIR3 se heredan los centros del destinatario.Conserva el valor actual.
nullIgual que ausente.Lo quita (en administrative_centres, elimina tu sustitución y vuelve a los centros heredados). Solo en un borrador.
[] (listas)«Ninguno» explícito. En payment_terms: un calendario vacío conocido.Reemplaza toda la lista por una vacía.
Una listaSe guarda tal cual, con su orden y sus duplicados.Reemplaza toda la lista.
Un objetoSe guarda tal cual.Combina por clave: solo cambian las claves que envías (business_references, business_references de línea). {} no cambia nada.

No se recorta, deduplica ni reordena nada. Los límites de texto cuentan caracteres Unicode.

Calendario de pagos

payment_terms es el calendario acordado de plazos. Es información contractual y no registra cobros.

Con plazos, la factura vence el día de su último plazo.

  • Para emitirla, due_on debe ser esa fecha. Un due_on ausente o distinto responde 422 invoice_due_date_not_last_instalment (param: due_on) antes de asignar número, y no se emite nada. Se aplica a issue, a una creación que emite en el acto mediante options y a cualquier otra vía de emisión.
  • Un borrador conserva el due_on que envíes, aunque sea distinto; la comprobación se hace al emitirlo.
  • Una actualización (PATCH) que sustituye payment_terms y omite due_on lleva due_on al último plazo. Si envías los dos y no coinciden, el borrador los conserva y emitirlo responde el mismo 422.
  • Programar o reprogramar la emisión (schedule, reschedule) comprueba la misma regla: la factura conserva su estado y su fecha y la petición responde el mismo 422.
  • Duplicar una factura emite el borrador con fecha de hoy: cada plazo conserva su importe y se desplaza los días que hay entre las dos fechas de emisión, y due_on es el último plazo desplazado.
  • Sin calendario, due_on es la fecha que envíes.
CampoTipoNotas
idstring (UUID v7)Opcional. Si se omite, el backend asigna uno al crear. Al actualizar, envíalo para conservar la identidad de un plazo existente. null y vacío no son válidos.
due_datestring (YYYY-MM-DD)Una fecha real. Las fechas nunca decrecen a lo largo de la lista; se permiten fechas iguales.
amountstringDecimal con signo y exactamente dos decimales, distinto de cero: "40.00", "-20.00". Nunca un número JSON.
payment_meansstringDe "01" a "19", los medios de pago de Facturae 3.2.2.
accountobjeto o null{ "iban": "...", "bic": "..." }. Solo para 02 (adeudo domiciliado) y 04 (transferencia). bic es opcional.

Los importes deben sumar exactamente total_to_pay (el total más los suplidos), con el signo de ese importe, de modo que un total negativo lleva plazos negativos. Factuarea nunca convierte, reparte ni redondea importes por ti. Un importe a pagar de cero solo admite null o [].

curl -X POST https://api.factuarea.com/v1/companies/$COMPANY_ID/invoices \
  -H "Authorization: Bearer fact_test_3pXnR2VbY7TcA9eFmN5z8KqW" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "client_id": "0197c5a0-4d2b-7c18-9f63-1a8e5b3d7c24",
    "issued_on": "2026-10-14",
    "due_on": "2026-12-13",
    "lines": [
      { "description": "Mantenimiento mensual", "quantity": 1, "unit_price": 100, "tax_rate": 21 }
    ],
    "payment_terms": [
      { "due_date": "2026-11-13", "amount": "60.50", "payment_means": "01" },
      {
        "due_date": "2026-12-13",
        "amount": "60.50",
        "payment_means": "04",
        "account": { "iban": "ES9121000418450200051332", "bic": "CAIXESBBXXX" }
      }
    ]
  }'

La respuesta devuelve payment_terms con las identidades, además de:

CampoSignificado
payment_terms_versionVersión del calendario. 0 cuando no hay ninguno, 1 tras la primera escritura.
payment_terms_sourcenone, explicit (lo enviaste tú) o legacy (lo capturó Factuarea de una factura emitida anterior).
payment_terms_currency, payment_terms_captured_at, payment_terms_frozen_atLa moneda (EUR) y los momentos en que se capturó y se congeló.

Esos cinco campos son de solo lectura: enviarlos responde 422.

Cambiar el calendario de una factura existente exige expected_payment_terms_version, un valor JSON entero (0 o más), incluso cuando envías null, [] o la misma lista. Es el compare-and-set del calendario, independiente de cualquier otra cosa. Sin él recibes 428 invoice_payment_terms_precondition_required; con uno obsoleto, 409 invoice_payment_terms_version_conflict con details.current_version. Para recuperarte, vuelve a leer la factura y reaplica tu cambio sobre su payment_terms_version.

curl -X PATCH https://api.factuarea.com/v1/companies/$COMPANY_ID/invoices/0197c5b1-7e90-7a42-b5d8-3c6f2e1a9b07 \
  -H "Authorization: Bearer fact_test_3pXnR2VbY7TcA9eFmN5z8KqW" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "payment_terms": [],
    "expected_payment_terms_version": 1
  }'

Reglas que conviene conocer:

  • Editar solo descripciones, o reordenar líneas mientras el importe a pagar no cambia, no pide un calendario nuevo. Cambiar el importe a pagar mientras existe un calendario sí: debes enviar el reemplazo completo y equilibrado y su versión.
  • Un plazo con 02 o 04 puede carecer de account mientras la factura es un borrador. Al emitir (directa, programada o por cambio de estado) se comprueban todas las cuentas y que el calendario suma, y se congela. Un fallo revierte toda la emisión: no se consume ningún número.
  • Los IBAN se validan (formato y dígitos de control, de cualquier país) y se normalizan los espacios; el BIC, si se envía, conserva sus 8 u 11 caracteres.
  • Duplicar una factura copia el calendario con identidades nuevas. Una rectificativa empieza sin calendario salvo que envíes uno (allí los plazos negativos son válidos).
  • En un POST /v1/companies/{company}/invoices con un external_id que ya existe, la petición es una actualización y sigue las reglas de actualización.

Referencias, periodo y literales

Las referencias de cabecera van en business_references, un objeto cerrado con tres claves, de hasta 20 caracteres cada una: receiver_contract_reference, receiver_transaction_reference y file_reference.

Las referencias de línea van en lines[].business_references: cinco pares de referencia (hasta 20 caracteres) y fecha (YYYY-MM-DD):

ReferenciaFecha
issuer_contract_referenceissuer_contract_date
issuer_transaction_referenceissuer_transaction_date
receiver_contract_referencereceiver_contract_date
receiver_transaction_referencereceiver_transaction_date
file_referencefile_date

Una referencia sin fecha es válida; una fecha sin su referencia no lo es, una vez combinada la edición parcial. Los objetos de cabecera no admiten fechas.

Cada línea admite también delivery_notes_references, una lista de { "number": "ALB-2026-014", "date": "2026-10-02" } (number de hasta 30 caracteres, date opcional), y sequence_number: null o un entero de 0 a 2147483647 (0 es válido; un string, un decimal o un booleano no).

invoicing_period es un objeto con las dos fechas, start_date y end_date, fechas reales con el inicio igual o anterior al fin. legal_literals es una lista ordenada de textos comerciales de hasta 250 caracteres, que conserva los duplicados. Son independientes de las legal_mentions fiscales que la factura ya calcula y nunca las sustituyen.

{
  "business_references": { "file_reference": "EXP-2026-0042" },
  "invoicing_period": { "start_date": "2026-10-01", "end_date": "2026-10-31" },
  "legal_literals": ["Operación sujeta al convenio marco 2026."],
  "lines": [
    {
      "description": "Mantenimiento mensual",
      "quantity": 1,
      "unit_price": 100,
      "tax_rate": 21,
      "business_references": {
        "issuer_contract_reference": "C-1",
        "issuer_contract_date": "2026-01-15"
      },
      "delivery_notes_references": [{ "number": "ALB-2026-014", "date": "2026-10-02" }],
      "sequence_number": 0
    }
  ]
}

Identidad de línea. En la respuesta cada línea tiene un id (UUID v7). En un PATCH, envía ese id para indicar qué línea quieres decir: puedes reordenar las líneas y cada una conserva sus referencias. Una línea sin id es nueva. Al crear, lines[].id se rechaza, igual que un id que pertenezca a otra factura, una línea retirada o un duplicado. Las rectificativas heredan las referencias de línea solo de la línea que nombras con lines[].source_line_id, nunca por posición; los datos de cabecera (business_references, invoicing_period, legal_literals y administrative_centres) no se aceptan en una rectificativa.

Centros administrativos DIR3

Las facturas a la administración pública llevan hasta cuatro centros DIR3, cada uno con un rol. Son los que usa la facturación a FACe para encaminar:

role_codeRol
01Oficina contable
02Órgano gestor
03Unidad tramitadora
04Proponente (opcional)

administrative_centres es una lista de { "role_code", "code", "name" }. Los roles son únicos. code se recorta y se pasa a mayúsculas, y debe cumplir ^[A-Z][A-Z0-9]{8,9}$ (por ejemplo L01280796); name es un texto opcional de hasta 40 caracteres, que se conserva tal cual. La respuesta ordena los centros por rol.

De dónde salen los centros, por orden:

  1. La sustitución de la factura (lo que envías en administrative_centres).
  2. La instantánea que la factura capturó del perfil del cliente al crearse (y que se actualiza mientras es un borrador y cambias el cliente).
  3. Solo en una factura anterior cuyo encaminamiento capturado es desconocido, los códigos actuales del cliente.

La respuesta expone los tres: administrative_centres (efectivos), administrative_snapshot y administrative_override, con administrative_version y administrative_revision_id. Fíjate en los dos tipos de vacío:

ValorSignifica
administrative_snapshot: nullDesconocido (legacy). Se permite recurrir al cliente.
administrative_snapshot: []Conocido y sin centros. Nunca se rellena con el cliente actual.
administrative_override: nullSin sustitución: se aplica la instantánea.
administrative_override: []Sustitución explícita sin ningún centro.

Al actualizar, null elimina tu sustitución y [] la reemplaza por una vacía. El rol 04 sobrevive aunque 01 a 03 estén vacíos. El encaminamiento capturado no se reescribe cuando el cliente cambia después.

Los códigos del cliente (decisión DEC-06)

Los códigos salen del perfil de cliente: PATCH /v1/companies/{company}/contacts/{contact}/customer-profile acepta dir3_accounting_office, dir3_managing_body, dir3_processing_unit y dir3_proponent, cada uno con el mismo formato de código. Los cuatro códigos siguen una misma regla de omisión: la operación reemplaza el perfil de cliente, así que un código DIR3 que dejes fuera se borra, dir3_proponent incluido. Envía siempre los cuatro que quieras conservar. Cambiarlos nunca toca facturas ni envíos a FACe ya creados.

curl -X PATCH https://api.factuarea.com/v1/companies/$COMPANY_ID/contacts/0197a2a8-4cf0-7a31-9a5e-3f2b8c1d6e42/customer-profile \
  -H "Authorization: Bearer fact_test_3pXnR2VbY7TcA9eFmN5z8KqW" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "dir3_accounting_office": "L01280796",
    "dir3_managing_body": "L01280796",
    "dir3_processing_unit": "L01280796",
    "dir3_proponent": "L01280797"
  }'

Revisa el encaminamiento tras emitir

Una factura en estado issued u overdue no puede cambiar sus datos de negocio, pero su encaminamiento administrativo sí se puede corregir. Cada corrección es una adición: una revisión nueva con su identidad, versión, motivo y autor. No vuelve a emitir, no envía ni altera importes, fechas, identidades legales, el calendario de pagos ni el PDF.

curl https://api.factuarea.com/v1/companies/$COMPANY_ID/invoices/0197c5b1-7e90-7a42-b5d8-3c6f2e1a9b07/administrative-revisions \
  -H "Authorization: Bearer fact_test_3pXnR2VbY7TcA9eFmN5z8KqW"
{
  "data": {
    "invoice_id": "0197c5b1-7e90-7a42-b5d8-3c6f2e1a9b07",
    "version": 1,
    "snapshot": [
      { "role_code": "01", "code": "L01280796", "name": null },
      { "role_code": "02", "code": "L01280796", "name": null },
      { "role_code": "03", "code": "L01280796", "name": null }
    ],
    "override": null,
    "revisions": []
  }
}

Para corregir el encaminamiento, envía la lista completa y la versión que leíste:

curl -X POST https://api.factuarea.com/v1/companies/$COMPANY_ID/invoices/0197c5b1-7e90-7a42-b5d8-3c6f2e1a9b07/administrative-revisions \
  -H "Authorization: Bearer fact_test_3pXnR2VbY7TcA9eFmN5z8KqW" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "administrative_centres": [
      { "role_code": "01", "code": "L01280796", "name": null },
      { "role_code": "02", "code": "L01280796", "name": null },
      { "role_code": "03", "code": "L01280798", "name": "Unidad corregida" },
      { "role_code": "04", "code": "L01280797", "name": null }
    ],
    "reason": "La unidad tramitadora indicada por el cliente era otra.",
    "expected_version": 1
  }'

Responde 201 con la revisión recién creada y su versión, aunque se confirme otra revisión justo después:

{
  "data": {
    "id": "0197c5c2-0f31-7b74-8d29-6e4a1c7b3d58",
    "version": 2,
    "administrative_centres": [
      { "role_code": "01", "code": "L01280796", "name": null },
      { "role_code": "02", "code": "L01280796", "name": null },
      { "role_code": "03", "code": "L01280798", "name": "Unidad corregida" },
      { "role_code": "04", "code": "L01280797", "name": null }
    ],
    "reason": "La unidad tramitadora indicada por el cliente era otra.",
    "created_at": "2026-10-14T10:05:41.000000Z",
    "actor_id": "0197a001-3c5d-7e12-8a47-5b9c2d1f6e80",
    "correlation_id": null
  },
  "administrative_version": 2
}
  • expected_version es un campo del cuerpo (un entero de 1 o más) y la única protección: un If-Match de la factura no se aplica. Sin él, 428 invoice_administrative_precondition_required; obsoleto, 409 invoice_administrative_version_conflict con details.current_version. Vuelve a leer el historial y reintenta sobre esa versión.
  • reason es obligatorio, de 1 a 500 caracteres. administrative_centres es obligatorio y puede ser [] para registrar «sin encaminamiento».
  • Solo las facturas issued y overdue aceptan una revisión; un borrador, una factura programada, cancelada o anulada responde 422 business_rule_violation con subcode invoice_not_editable_in_current_state (un borrador se edita con PATCH).
  • La revisión forma parte del historial de la factura; un envío a FACe que ya estaba preparado conserva el encaminamiento que congeló, y el intento siguiente usa el nuevo.

Scopes: invoices:read para listar, invoices:write para añadir. El servidor MCP ofrece list_invoice_administrative_revisions y revise_invoice_administrative_routing.

Errores

HTTPcodeCuándo
422invalid_invoice_business_dataUna referencia, el periodo, un literal, un centro, el id de una línea o un sequence_number no es válido. param es la ruta exacta, por ejemplo lines.0.business_references.issuer_contract_date.
422invalid_invoice_payment_termsUn plazo no es válido (payment_terms.N.amount, .id, .account…), el calendario no suma o se envió un campo de solo lectura.
422invoice_due_date_not_last_instalmentLa factura tiene calendario de pagos y se emite con un due_on ausente o distinto de la fecha del último plazo (param: due_on). Pon esa fecha, o ajusta los plazos, y vuelve a emitir. Consulta Calendario de pagos.
428invoice_payment_terms_precondition_requiredCambias el calendario de una factura existente sin expected_payment_terms_version.
409invoice_payment_terms_version_conflictexpected_payment_terms_version está obsoleto (details.current_version).
428invoice_administrative_precondition_requiredUna revisión administrativa sin expected_version.
409invoice_administrative_version_conflictexpected_version está obsoleto (details.current_version).
422business_rule_violation / invoice_not_editable_in_current_stateEditas los datos de una factura emitida, o revisas el encaminamiento de un borrador.
404invoice_not_foundLa factura no existe o pertenece a otra empresa.
403insufficient_scopeA la clave le falta invoices:read o invoices:write.
{
  "error": {
    "type": "invalid_request_error",
    "code": "invalid_invoice_business_data",
    "message": "Los datos comerciales de la factura no son válidos.",
    "param": "lines.0.business_references.issuer_contract_date",
    "request_id": "req_01JBVH7K9Y4N3CDQ2EHJB1AGSV",
    "doc_url": "https://docs.factuarea.com/guides/errors#invalid_invoice_business_data"
  }
}

En bulk-create cada fila se valida por separado y una fila incorrecta se informa en results[] sin hacer fallar a las demás; consulta Operaciones masivas.

Relacionado

En esta página

¿Te echamos una mano?Contactar con soporte