Una factura de venta (invoice) es el documento fiscal que emite la empresa para cobrar a sus clientes. Es el recurso más cargado del catálogo: además del flujo CRUD normal toca dos regímenes fiscales electrónicos (TicketBAI en País Vasco y VeriFactu en el resto de España), tiene flujos especiales para facturas rectificativas y sustitutivas, y genera asientos contables, PDFs, ficheros Facturae y envíos por email.

Para no inflar esta página, los temas regulatorios y los flujos especializados están en guías propias:

En los ejemplos, los UUIDs (inv_…, con_…, ban_…, etc.) son ilustrativos.

Subtipos implícitos

FacturaDirecta no tiene un campo invoiceType en el schema de factura. El “tipo” emerge de la combinación de varios campos:

SubtipoCómo se identifica
Completamain.simplified !== true. Normalmente tiene main.contact con datos fiscales. Si TicketBAI no está activo, también puede dirigirse a un contacto extracomunitario sin identificador fiscal; con VeriFactu se registra como F2.
Simplificadamain.simplified === true. Típicamente main.contact es null o el destinatario no tiene datos fiscales suficientes.
Externamain.external === true — generada fuera del programa. No emite a TicketBAI ni VeriFactu, aunque sí contabiliza.
Sustitutivamain.substitution === true con main.substitutedInvoices rellenado. Se crea con el endpoint dedicado POST /invoices/substitution. Corresponde a F3 en VeriFactu. Ver guía de Sustitutivas.
RectificativaAsociada a una factura previa vía main.correctedInvoice y/o emitida en una serie de rectificativas (invoiceType: complete_correction o simplified_correction en settings). Ver guía de Rectificativas.

El campo main.corrective existe en el schema pero está marcado como reservado para uso futuro y no debe usarse aún. Su activación está pendiente de una fase posterior.

Estados

state es un campo calculado (no se envía en el body). Se deriva de los flags del documento y del importe pagado vs total:

EstadoCuándo
draftmain.draft === true. Provisional, no contabiliza.
pendingDefinitiva, sin cobros suficientes y no vencida.
overdueDefinitiva, sin cobrar al llegar main.dueDate.
paidCobros totales = importe.
overpaidCobros totales > importe.
voidedmain.voided === true. Anulada.

Cada factura tiene un único estado en cada momento: los valores son excluyentes entre sí. En el filtro state se pueden indicar varios y se devuelven las facturas cuyo estado sea cualquiera de ellos.

Las transiciones no se imponen como máquina de estados: se calculan en cada lectura desde los pagos y fechas actuales.

Estructura mínima

Invoice tiene tres bloques raíz: main (datos editables), accounting/books (apuntes y libros, generados) y meta (metadatos internos — incluye los xml y respuestas de TicketBAI y VeriFactu, ver TicketBAI y VeriFactu).

Los campos de main más importantes para crear una factura:

CampoRequeridoSignificado
contactSí salvo simplificadaID del cliente (con_*). En simplificadas: null.
dateSíFecha de emisión (YYYY-MM-DD).
dueDateSíFecha de vencimiento. Si hay installments, se ajusta a la fecha más tardía del calendario.
installmentsNoCalendario de vencimientos múltiples; cada elemento contiene date, amount y currency.
currencySíISO 4217.
exchangeRateNoTipo de cambio a la moneda de la empresa. Si no lo envías, se toma el de la fecha de la factura (date). En la moneda de la empresa es siempre 1.
docNumberSí{ series: "...", number?: N }. Si omites number, se asigna automáticamente.
fiscalPositionSíRégimen tributario aplicable. Determina los impuestos disponibles.
linesSíArray de líneas (ver más abajo).
themeNoID de la plantilla de impresión. Si se omite, se usa la plantilla de la serie o la predeterminada de la empresa. Obtenible con GET /settings/themes.
languageNoIdioma en que se imprime y se envía la factura, como código ISO 639-1 (es, ca, en…). Si se omite, se toma el del contacto al asignarlo y, si el contacto no tiene, el idioma base de la plantilla. Solo tiene efecto si la plantilla tiene ese idioma; si no, se imprime en su idioma base.
accountSíCuenta contable de ingresos (típicamente 700*).
draftSítrue → provisional; false → definitiva (contabiliza).
voidedSítrue solo para anular.
taxIncludedPricesNo (default false)Si los precios de las líneas incluyen impuestos.
externalNotrue para facturas creadas fuera del programa (no emite TicketBAI/VeriFactu).
paymentMethodNoID de método de cobro: uno de la empresa (sin contacto) o uno del contacto de la factura, y de cobro (receive o sin dirección).
emails, emailsCc, emailsBccNoDestinatarios por defecto para el envío por email (CSV en un solo string).
correctedInvoiceNoID de la factura rectificada cuando esta es rectificativa. Ver Rectificativas.
verifactuNoParámetros específicos VeriFactu (TipoFactura, DescripcionOperacion, etc.). Ver VeriFactu.
ticketbaiNoParámetros específicos TicketBAI (causaExencion, claveTipoFacturaRectificativa, etc.). Ver TicketBAI.
customFieldsNoMapa de valores indexado por el ID estable cfi_<uuid v4> de cada campo personalizado. Las definiciones borradas siguen siendo válidas; un ID desconocido o con formato incorrecto produce 400 Bad Request.
ownerNoUsuario responsable de la factura. Se usa con roles personalizados para limitar la visibilidad a documentos asignados.
warehouseNoAlmacén del documento a efectos de stock. Solo tiene efecto si la empresa usa el control de stock y varios almacenes; si no lo indicas se usa el almacén por defecto. Ver Productos.

Si creas la factura con OAuth y no envías owner, la API asigna como responsable al usuario autenticado. Si usas una apiKey, la factura queda sin responsable salvo que envíes owner en el body.

Vencimientos múltiples

Para dividir el cobro de una factura, envía main.installments al crearla o actualizarla. Cada elemento requiere:

  • date — fecha del vencimiento en formato YYYY-MM-DD.
  • amount — importe previsto para ese vencimiento.
  • currency — moneda del importe en formato ISO 4217.

Por ejemplo, una factura de 121 EUR puede dividirse en dos vencimientos:

json
{
  "dueDate": "2026-08-15",
  "installments": [
    { "date": "2026-07-15", "amount": 60.5, "currency": "EUR" },
    { "date": "2026-08-15", "amount": 60.5, "currency": "EUR" }
  ]
}

Incluye estos campos dentro de content.main en POST /{companyId}/invoices o PUT /{companyId}/invoices/{invoiceId}. El servidor ajusta el importe del último elemento para que la suma coincida con el total calculado de la factura y establece dueDate en la fecha más tardía. Con un método de cobro por domiciliación SEPA, cada vencimiento pendiente se puede seleccionar como un cobro separado al preparar una remesa. Si no necesitas dividir el cobro, dueDate basta para representar un único vencimiento.

Cada elemento de lines (InvoiceMainLine):

CampoRequeridoSignificado
accountSíCuenta contable de la línea (override de main.account).
quantitySíCantidad.
unitPriceSíPrecio unitario.
discountSíDescuento absoluto en la moneda de la factura.
discountRateSíDescuento porcentual (0–100).
lineTotalSíTotal de la línea (quantity * unitPrice * (1 - discountRate/100) - discount).
taxSíArray de IDs de impuestos. Ver guía de Impuestos.
textSíDescripción.
originNoID del documento de origen (presupuesto, albarán o pedido de cliente) para trazar procedencia.
documentNoID del producto si la línea se genera de catálogo.
facturae, verifactuNoSub-campos específicos para Facturae y VeriFactu por línea.

Enlace a la aplicación

Las respuestas que devuelven una factura incluyen webUrl junto al resto de los datos. En los listados, cada elemento de items lleva su propio enlace.

webUrl abre la ficha del elemento en la aplicación web de FacturaDirecta. Puedes mostrarlo como enlace en tu integración sin construir rutas internas. El navegador pedirá iniciar sesión con un usuario que tenga acceso a la empresa. Es un campo opcional y no es un endpoint de la API: trátalo como una URL para el usuario, no como una URL a la que enviar $ACCESS_TOKEN.

Operaciones

Lista de facturas

GET /{companyId}/invoices devuelve las facturas de la empresa, paginadas.

Parámetros de consulta específicos (los más comunes; lista completa en el Swagger UI):

  • search — búsqueda aproximada en el texto de la factura (título, líneas, notas) y en el nombre de su cliente: todas las palabras, sin distinguir acentos ni mayúsculas. Es la búsqueda rápida del listado de la aplicación; la búsqueda global la lanza sobre todos los tipos a la vez.
  • minDate, maxDate — rango por fecha de la factura.
  • series — filtrar por serie de numeración.
  • contact — filtrar por ID de cliente.
  • state — filtrar por estado (draft, pending, overdue, paid, overpaid, voided). Cada factura solo tiene uno; si se indican varios, el filtro incluye cualquiera de ellos.
  • draft — por defecto solo se incluyen facturas definitivas; only devuelve solo borradores y all mezcla definitivas y borradores. Los borradores no generan asientos contables, por lo que al incluirlos el recuento puede no coincidir con el diario.
  • simplified — true para solo simplificadas.
  • external — true para solo externas.
  • voided — true para solo anuladas.
  • sortBy — admite date, series, formattedSeries, number, total, currency, country, creationDate y modificationDate. Repite el parámetro para combinar criterios y antepone - para orden descendente (por ejemplo, sortBy=-date&sortBy=number).
  • Tags: allTheseTags y anyOfTheseTags filtran por etiquetas concretas; hasTags=true devuelve solo facturas con al menos una etiqueta y hasTags=false, solo facturas sin etiquetas.
  • totals — true añade a la respuesta las sumas de todas las facturas que cumplen los filtros, no solo de la página. Ver Totales del conjunto filtrado.

Parámetros globales aceptados:

Acepta además los parámetros estándar offset, limit, minCreationDate, maxCreationDate, minModificationDate, maxModificationDate y el header accept-version. Ver Paginación.

Copy as cURL
shell
curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \
  "https://app.facturadirecta.com/api/$COMPANY_ID/invoices?minDate=2026-01-01&state=pending&limit=50"

Totales del conjunto filtrado

Con totals=true, la respuesta añade totals: el recuento y las sumas de todas las facturas que cumplen los filtros, con una fila por moneda. Es la forma de responder «¿cuánto suman las facturas vencidas?» sin recorrer las páginas.

shell
curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \
  "https://app.facturadirecta.com/api/$COMPANY_ID/invoices?state=overdue&limit=1&totals=true"
json
{
  "pagination": { "offset": 0, "limit": 1, "total": 3 },
  "items": [ { "...": "..." } ],
  "totals": [
    { "currency": "EUR", "count": 3, "total": 1815, "totalBeforeTaxes": 1500, "taxes": 315, "pending": 1815, "companyCurrencyPending": 1815 }
  ]
}
CampoSignificado
currencyMoneda de las facturas de la fila.
countFacturas que cumplen los filtros.
total, totalBeforeTaxes, taxesSuma del total, de la base imponible y de los impuestos (total menos totalBeforeTaxes), en la moneda de la fila.
pendingImporte pendiente de cobro, en positivo, en la moneda de la fila. Negativo si se ha cobrado de más.
companyCurrencyPendingEl pendiente en la moneda de la empresa.

Para agrupar esas sumas por cliente, provincia, estado o mes, o para compararlas con otro periodo, usa la consulta analítica.

Crear factura

POST /{companyId}/invoices crea una factura nueva.

Body: { content: Invoice, tags?: string[] }. El content contiene todo el cuerpo de la factura (típicamente solo main; los campos calculados se generan en el servidor).

Reglas de negocio relevantes:

  • Si omites docNumber.number, se asigna automáticamente basándose en el último número emitido para esa serie ese año. La serie controla además la sustitución de ##/#### por el año (ver settings).
  • Si omites draft, se aplica el valor por defecto de la empresa (sales.newInvoiceMode, ver settings), que puede ser final: la factura nace definitiva, es decir, emitida. Envía draft siempre de forma explícita (true para un borrador).
  • Guardar la factura como definitiva (draft: false) la emite: se le asigna número y, si la empresa tiene TicketBAI o VeriFactu activos y la factura encaja en su ámbito (no es external), se genera el registro fiscal y se envía al organismo en el momento del guardado. Una factura emitida no vuelve a ser provisional y, con esos sistemas, no se puede modificar ni borrar: solo anular, rectificar o, con VeriFactu, subsanar. Ver TicketBAI y VeriFactu.
  • main.simplified es obligatorio en el schema; envíalo explícitamente. Si main.contact es null, debe ser true.

Parámetros globales aceptados: accept-version.

Ejemplo de request JSON (factura completa, regalo a un cliente)
json
{
  "content": {
    "main": {
      "contact": "con_2e3f4a18-9b7c-4d6a-8e1f-5c2d3b4a7e9f",
      "date": "2026-05-13",
      "dueDate": "2026-06-12",
      "currency": "EUR",
      "docNumber": { "series": "F-##-" },
      "fiscalPosition": "general",
      "account": "700000",
      "theme": "default",
      "draft": false,
      "voided": false,
      "simplified": false,
      "lines": [
        {
          "account": "700000",
          "quantity": 1,
          "unitPrice": 100,
          "discount": 0,
          "discountRate": 0,
          "lineTotal": 100,
          "tax": ["S_IVA_21"],
          "text": "Servicios de consultoría"
        }
      ]
    }
  },
  "tags": ["consultoria"]
}
Copy as cURL
shell
curl -s -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \
  -d @invoice.json \
  "https://app.facturadirecta.com/api/$COMPANY_ID/invoices"

Crear factura sustitutiva

POST /{companyId}/invoices/substitution crea una factura completa que sustituye a un conjunto de facturas simplificadas (F3 en VeriFactu). Es un endpoint dedicado porque la construcción del cuerpo (líneas, separadores, totales) la hace el servidor.

Body:

  • contact (obligatorio) — ID del cliente al que se le emite la factura completa.
  • substitutedInvoices (obligatorio) — array no vacío de UUIDs de facturas simplificadas a sustituir. Sin duplicados.
  • date, docNumber, notes, internalNotes, tags — opcionales.

Validaciones:

  • Todas las facturas referenciadas deben existir y ser simplificadas (main.simplified === true).
  • Ninguna puede ser externa (main.external === true).
  • Misma currency en todas.
  • Mismo taxIncludedPrices en todas.
  • Si alguna tiene main.contact, todas las que lo tengan deben compartir el mismo con_*.
  • Ninguna puede estar ya sustituida en otra F3.

Detalles del flujo y ejemplos: ver guía de Facturas sustitutivas.

Parámetros globales aceptados: accept-version.

Obtener una factura

GET /{companyId}/invoices/{id} devuelve una factura por ID.

Parámetros globales aceptados: accept-version.

Copy as cURL
shell
curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \
  "https://app.facturadirecta.com/api/$COMPANY_ID/invoices/inv_4a9c2e58-3b1f-4d6a-8e2c-7f1a9b3d5c4e"

Actualizar factura

PUT /{companyId}/invoices/{id} reemplaza el content de la factura. Las líneas se reemplazan por las que envíes y los campos opcionales que no envíes se eliminan. Los campos obligatorios que no envíes conservan su valor, igual que docNumber y meta, que no cambian. Lo que FacturaDirecta rellena al crear (account, dueDate, fiscalPosition, theme…) se rellena igual al actualizar si no lo envías. Si no envías exchangeRate, se conserva el anterior mientras no cambien ni la moneda ni la fecha; si cambia alguna de las dos, se toma el tipo de cambio de la nueva fecha.

Reglas relevantes:

  • docNumber inmutable una vez asignado.

  • Pasar main.voided=true anula la factura (ver Anular).

  • Si la factura ya tiene un registro de alta VeriFactu, el primer intento de hacer un cambio subsanable devuelve HTTP 400 con type: "verifactuSubsanacion". Revisa la clasificación de los cambios en hint.verifactuChanges.changes y, si quieres confirmar la subsanación, repite el mismo PUT añadiendo este campo al nivel superior del body:

    json
    {
      "verifactuOperation": { "subsanacion": true }
    }

    El segundo PUT devuelve la factura actualizada y genera un nuevo registro de alta con meta.verifactu.registroAlta.Subsanacion = "S". Esta confirmación no permite modificar importes, impuestos ni datos del destinatario: esos cambios requieren anular o emitir una factura rectificativa. Ver Subsanaciones VeriFactu.

  • Si la factura tiene metadatos de TicketBAI ya enviados, algunos cambios disparan reemisión o anulación + nueva emisión. Ver la guía de TicketBAI.

Parámetros globales aceptados: accept-version.

Anular factura

No existe un endpoint /void dedicado. La anulación se hace con PUT /{companyId}/invoices/{id} enviando main.voided = true.

Efectos:

  • El estado pasa a voided.
  • Se generan asientos contables de reversión.
  • Si la factura está en TicketBAI o VeriFactu, se prepara el envío de anulación al organismo (meta.ticketbai.voided / meta.verifactu.registroAnulacion).
  • Se emite el webhook invoice.voided.

Anular no es corregir. Anular es declarar que la factura no tenía que emitirse: conserva su número y queda visible como anulada, sin dejar huecos en la numeración. Si la factura tenía que existir pero contiene errores, lo que corresponde es una subsanación (VeriFactu) o una factura rectificativa.

Restaurar una factura anulada. Sin TicketBAI ni VeriFactu, un PUT con main.voided = false la restaura (la actividad lo registra como unvoided). Si la factura tiene información de VeriFactu, TicketBAI o Batuz, la restauración devuelve HTTP 400: el registro de anulación ya se ha comunicado y no se puede revertir.

Una factura anulada no es lo mismo que una factura borrada: la anulada queda en el sistema con estado voided; la borrada se conserva con archived: true y desaparece de los listados (ver siguiente sección).

Borrar factura

DELETE /{companyId}/invoices/{id} borra la factura (borrado recuperable).

Restricciones:

  • Solo se pueden borrar facturas no contabilizadas (típicamente en draft). Si la factura ya generó asientos contables, el borrado fallará: usa anulación en su lugar.
  • El borrado es lógico y recuperable (archived = true); la factura no se elimina físicamente y desaparece de los listados.

Parámetros globales aceptados: accept-version.

Copy as cURL
shell
curl -s -H "Authorization: Bearer $ACCESS_TOKEN" -X DELETE \
  "https://app.facturadirecta.com/api/$COMPANY_ID/invoices/inv_4a9c2e58-3b1f-4d6a-8e2c-7f1a9b3d5c4e"

Actualizar etiquetas

PUT /{companyId}/invoices/{id}/tags reemplaza el array de tags de la factura sin tocar el content. Body: { tags: string[] }. Ver los recursos análogos (bills, estimates) para el patrón común de tags.

Registrar cobros

POST /{companyId}/invoices/{id}/payments registra uno o varios cobros contra la factura.

Parámetros de cada cobro:

  • bank (obligatorio) — ID de la cuenta bancaria (ban_*) donde se anota el cobro.
  • amount (opcional) — importe en la moneda de la factura. Si lo omites, se cobra el saldo pendiente completo. Solo se admite un cobro sin amount y debe ir en la última posición del array.
  • date (opcional) — fecha del cobro. Por defecto se usa la fecha actual.
  • companyCurrencyAmount (opcional) — importe real en la moneda de la empresa cuando la factura usa otra moneda. Si lo omites, el sistema aplica el tipo de cambio de la fecha del cobro.
  • description, notes (opcionales) — descripción visible del movimiento y notas adicionales.

La moneda de la cuenta bancaria debe coincidir con la moneda de la empresa o con la de la factura. Si es una tercera moneda, la API devuelve 400 porque la petición no indica el importe real recibido en esa cuenta.

Para registrar el cobro o pago a partir de un movimiento del extracto bancario (también desde una cuenta en una tercera moneda), concilia el movimiento con document en conciliar creando nuevo asiento: la API construye el asiento con el importe real del movimiento y la diferencia de cambio.

En una factura en moneda extranjera cobrada en una cuenta que usa la moneda de la empresa, envía amount en la moneda de la factura y companyCurrencyAmount en la moneda de la cuenta. El apunte bancario queda así en la misma moneda que el extracto y se puede conciliar con él.

Body:

json
{
  "payments": [
    {
      "date": "2026-08-11",
      "bank": "ban_5e7d8a31-9c4b-4f6e-a1d3-2b5c7e9f1a4d",
      "amount": 7000,
      "companyCurrencyAmount": 5909.56
    }
  ],
  "related": ["journal"]
}

related: ["journal"] incluye en la respuesta los apuntes que afectan al saldo de la factura. Toma de related.journal el id del asiento tra_* que se ha generado y úsalo como transactionId para PUT /statements/{id}/reconciliation. Después puedes localizar el movimiento conciliado con GET /statements?matchedDocument=<id-asiento>.

Efectos:

  • Genera los apuntes contables del banco y del cliente.
  • Si los cobros cierran el saldo, el estado pasa a paid. Si lo exceden, pasa a overpaid.
  • Emite el webhook invoice.updated (no hay evento específico de cobro).

Parámetros globales aceptados: accept-version.

Enviar por email

PUT /{companyId}/invoices/{id}/send envía la factura por email desde la cuenta SMTP configurada en la empresa.

Body (todos opcionales):

  • to, cc, bcc — CSV de destinatarios. Si los omites, se usan main.emails, main.emailsCc, main.emailsBcc del documento.
  • subject — asunto custom; en su defecto se aplica el configurado por la empresa.
  • html — cuerpo HTML del mensaje.
  • sendFacturae — true para adjuntar también el XML Facturae (la factura debe ser elegible: cliente con taxCode, etc.).
  • continueWithoutFacturae — true para no fallar si el Facturae no puede generarse (sin él, solo se envía el PDF).

Efectos:

  • Adjunta automáticamente el PDF de la factura.
  • Adjunta el Facturae XML si se solicita y es elegible.
  • Emite el webhook invoice.sent al completarse.

Parámetros globales aceptados: accept-version.

Generar PDF

PUT /{companyId}/invoices/{id}/pdf genera el PDF de la factura aplicando la plantilla main.theme.

Respuesta: { url, filename }. La URL es temporal (~24h); vuelve a llamar al endpoint para obtener una nueva si caduca.

Parámetros globales aceptados: accept-version.

Generar Facturae

PUT /{companyId}/invoices/{id}/facturae genera el XML Facturae versión 3.2.1 (formato estándar B2B español).

Respuesta: { url, filename } con la URL del XML.

Restricciones:

  • La factura debe ser elegible (cliente con taxCode, dirección fiscal completa, etc.).
  • Para facturas rectificativas, los campos Corrective_* se generan automáticamente desde main.correctedInvoice y los campos VeriFactu/TicketBAI.

Parámetros globales aceptados: accept-version.

Adjuntos

Patrón estándar compartido con el resto de documentos:

  • POST /{companyId}/invoices/{id}/attachments — body { uploadIds: string[] }. Adjunta archivos previamente subidos con Uploads.
  • GET /{companyId}/invoices/{id}/attachments — lista los adjuntos.
  • DELETE /{companyId}/invoices/{id}/attachments/{attachmentIndex} — borra por índice (0-based) en el array.

Límite: 10 adjuntos por documento. El POST con un total superior devuelve 400.

Webhooks

Eventos que emite el recurso:

  • invoice.created — alta de factura.
  • invoice.updated — modificación del content, incluido el registro de pagos.
  • invoice.archived — borrado (lógico, recuperable).
  • invoice.unarchived — recuperación de una factura borrada (no expuesta hoy por API pública; reservada para procesos internos).
  • invoice.sent — envío por email completado.
  • invoice.voided — anulación.
  • invoice.verifactu_sent — envío exitoso a AEAT confirmado. Ver VeriFactu.

No existen eventos invoice.ticketbai_*. Para TicketBAI, el estado se consulta en meta.ticketbai del documento. Ver TicketBAI.

Detalle de la entrega (HMAC, reintentos, formato) en Webhooks.

Recomendaciones

  • Tres lecturas obligadas antes de tu integración: la guía de TicketBAI, la guía de VeriFactu y la guía de Impuestos. Conocer cuándo y cómo se emite electrónicamente la factura evita la mayoría de los errores reportados.
  • Para crear, deja que el servidor asigne el número: no envíes docNumber.number salvo que tengas un caso de uso muy concreto (importaciones desde otro sistema). Así evitas colisiones y errores de huecos en la numeración.
  • Para integraciones que envían a clientes finales, registra los webhooks invoice.created, invoice.voided, invoice.sent y invoice.verifactu_sent. Son los que disparan acciones externas (notificaciones, replicación a CRMs, descargas).
  • Multiprovincias: si tu integración opera con empresas en País Vasco y en el resto de España, no asumas el mismo flujo. Lee ambas guías regulatorias.
  • Factura externa: úsala con cuidado. main.external = true marca la factura como creada fuera del programa. Contabiliza normalmente, pero no la envía a TicketBAI ni VeriFactu. Es el modo apropiado para registrar facturas históricas o las emitidas con otro software fiscal, pero no para evitar la obligación electrónica.
  • Rectificativas y sustitutivas: cada una tiene su flujo específico. Las rectificativas se crean con el CRUD normal marcando correctedInvoice y la serie apropiada; las sustitutivas tienen un endpoint dedicado. Lee las guías antes de implementar.

Versiones

GET /{companyId}/invoices/{id}/versions lista las versiones de la factura: su alta, cada modificación, su eliminación y su recuperación, con quién y cuándo, aunque se haya eliminado. GET /{companyId}/invoices/{id}/versions/{versionId} devuelve la copia de una versión, en el mismo formato que GET /{companyId}/invoices/{id}, y los cambios respecto a la versión anterior. Sirven para ver qué se cambió y recuperar datos: ver Versiones de documentos.

Errores comunes

  • 400 — minDate o maxDate es una fecha que no existe, como 2026-02-30. Ver Fechas que no existen.
  • 400 ValidationError — combinaciones incoherentes:
    • main.contact = null con main.simplified = false.
    • main.exchangeRate <= 0.
    • main.docNumber.series no presente en settings.numbering.invoice.
    • line.tax con un TaxId que no está en el catálogo de impuestos de venta de la empresa.
  • 400 en POST /payments — el array contiene más de un pago sin amount, o el pago sin amount no está en la última posición.
  • 400 en POST /invoices/substitution — alguna restricción no se cumple (factura no simplificada, monedas distintas, ya sustituida…).
  • 404 Not Found — factura inexistente o borrada.
  • 409 Conflict — intento de borrar una factura ya contabilizada. Anula en su lugar.
  • 413 Payload Too Large — adjuntos por encima del límite (10).

Ver Errores y validaciones para el formato general.

Referencia exhaustiva

Esta página resume el contrato y los matices funcionales del recurso. Para el contrato completo del schema (todos los campos del Invoice, los $ref a documentCounterpart, invoiceMainFacturae, invoiceMainVerifactu, invoiceMetaTicketbai, invoiceMetaVerifactu, etc.), consulta el Swagger UI o el openapi crudo.

Los detalles regulatorios y los flujos especiales viven en las guías dedicadas:

Endpoints

MétodoPathoperationIdScopesDescripción
GET/{companyId}/invoicesgetInvoicesinvoices:readLista de facturas
GET/{companyId}/invoices/{id}getInvoiceinvoices:readObtener una factura
GET/{companyId}/invoices/{id}/attachmentsgetInvoiceAttachmentsinvoices:readListar adjuntos de una factura de venta
GET/{companyId}/invoices/{id}/versionsgetInvoiceVersionsinvoices:readVersiones de la factura
GET/{companyId}/invoices/{id}/versions/{versionId}getInvoiceVersioninvoices:readVersión de la factura
POST/{companyId}/invoicescreateInvoiceinvoices:writeCrear factura
POST/{companyId}/invoices/{id}/attachmentsaddInvoiceAttachmentsinvoices:writeVincular adjuntos a una factura
POST/{companyId}/invoices/{id}/paymentscreateInvoicePaymentsinvoices:writeCrear pagos para una factura
POST/{companyId}/invoices/substitutioncreateSubstitutionInvoiceinvoices:writeCrear factura sustitutiva de simplificadas
PUT/{companyId}/invoices/{id}updateInvoiceinvoices:writeActualizar factura
PUT/{companyId}/invoices/{id}/facturaecreateInvoiceFacturaeinvoices:readGenerar la factura en formato facturae
PUT/{companyId}/invoices/{id}/pdfcreateInvoicePDFinvoices:readGenerar la factura en formato PDF
PUT/{companyId}/invoices/{id}/sendsendInvoiceinvoices:writeEnviar factura por correo electrónico
PUT/{companyId}/invoices/{id}/tagsupdateInvoiceTagsinvoices:writeActualizar etiquetas de factura
DELETE/{companyId}/invoices/{id}deleteInvoiceinvoices:writeBorrar factura
DELETE/{companyId}/invoices/{id}/attachments/{attachmentIndex}deleteInvoiceAttachmentinvoices:writeEliminar adjunto de una factura de venta

Scopes

  • invoices:read — Lectura de facturas.
  • invoices:write — Modificación de facturas.

Empieza gratis y factura con flow

Únete a los más de 90.000 negocios que ya gestionan sus facturas de forma rápida y sencilla con FacturaDirecta
Empieza Gratis

Fácil de usar · Sin permanencia · Sin tarjeta de crédito