VeriBaiDocs
Acceder

VeriFactu

Alta, subsanación y anulación de registros VeriFactu. El contrato completo.

Los endpoints VeriFactu remiten registros de facturación a la AEAT. Base URL: https://sandbox.veribai.com (Sandbox) o https://api.veribai.com (LIVE), con autenticación x-api-key.

Método Endpoint Descripción
POST /v1/verifactu/crear Alta de un registro de facturación.
PUT /v1/verifactu/subsanar Subsanación de un registro remitido.
POST /v1/verifactu/anular Anulación de un registro remitido.

El alta es síncrona y la remisión asíncrona: la petición valida, firma y encola el registro, y devuelve el QR en el momento. El envío a la AEAT se completa en segundo plano. Síguelo con GET /v1/facturas/{idFactura}/estado o recíbelo por webhook.

En LIVE, todas las operaciones exigen que el emisor tenga su representación firmada; en caso contrario devuelven 403 REPRESENTATION_PENDING. En Sandbox no se exige.

POST /v1/verifactu/crear

POST/v1/verifactu/crear

curl -X POST https://sandbox.veribai.com/v1/verifactu/crear \
  -H "x-api-key: TU_CLAVE" \
  -H "Content-Type: application/json" \
  -d '{
    "version": "1.0",
    "cabecera": {
      "serie": "A",
      "numero": "12345",
      "fechaExpedicion": "03-04-2026",
      "tipoFactura": "F1",
      "descripcion": "Servicios de consultoría"
    },
    "emisor": {
      "nif": "B00000000",
      "nombre": "Prueba 1 SL"
    },
    "destinatario": {
      "nif": "B11111119",
      "nombre": "Nombre cliente, SL"
    },
    "detalleDesglose": [
      {
        "impuesto": "01",
        "claveRegimen": "01",
        "calificacionOperacion": "S1",
        "baseImponible": "100.00",
        "tipoImpositivo": "21.00",
        "cuotaRepercutida": "21.00"
      }
    ],
    "totales": {
      "cuotaTotal": "21.00",
      "importeTotal": "121.00"
    }
  }'

Respuesta

200 OK
{
  "nifEmisor": "B00000000",
  "idFactura": "…",
  "estado": "pendiente_proceso",
  "numSerieFactura": "A12345",
  "serie": "A",
  "numero": "12345",
  "tipoFactura": "F1",
  "importeTotal": "121.00",
  "cuotaTotal": "21.00",
  "creadoEn": "2026-04-03T10:00:00Z",
  "urlValidacion": "https://…",
  "qrBase64": "…"
}

El registro queda validado, firmado y encolado. Guarda idFactura: es la clave para consultar estado, QR y XML. avisos, cuando aparece, lista avisos de validación no bloqueantes.

Los duplicados son idempotentes: reenviar un alta con la misma identidad (emisor, serie, número, fecha) devuelve 200 con el registro ya existente, no un error. La repetición lleva "yaExistente": true, estado trae el valor de ciclo de vida (registrada, anulada o rectificada) y el QR se regenera. yaExistente: true indica que VeriBai ya tenía esa factura y no ha creado nada; en una factura nueva el campo no aparece.

Campos

Campo Notas
version Obligatorio. Actualmente "1.0".
cabecera.serie Opcional. Se admite factura sin serie.
cabecera.numero Obligatorio. La numeración debe ser correlativa por serie.
cabecera.fechaExpedicion Obligatorio. Formato dd-mm-aaaa; no puede ser posterior a hoy, en hora peninsular. Ver Fechas de la factura.
cabecera.fechaOperacion Opcional, dd-mm-aaaa, si difiere de la expedición. Cada administración aplica sus propias reglas. Ver Fechas de la factura.
cabecera.tipoFactura Obligatorio: F1–F3, R1–R5. Ver tipos de factura.
cabecera.descripcion Obligatorio. Descripción de la operación, máx. 500 caracteres.
emisor Obligatorio: nif y nombre. El NIF debe corresponder a un cliente dado de alta.
destinatario Obligatorio salvo en F2 y R5 (simplificadas), que no lo admiten. nombre más nif o idOtro, nunca los dos. Ver destinatario extranjero.
detalleDesglose Obligatorio. De 1 a 12 líneas de desglose fiscal: baseImponible obligatoria; impuesto, claveRegimen, calificacionOperacion, operacionExenta, tipoImpositivo, cuotaRepercutida y los campos de recargo de equivalencia según la operación. Importes como cadenas decimales. Ver claves por impuesto.
totales Obligatorio: cuotaTotal e importeTotal, exactamente como figuran en la factura, con el recargo de equivalencia incluido. Se validan contra el desglose con una tolerancia de ±10 € (no se comprueba con las claves 03, 05, 06, 08 o 09) y se transmiten tal como se declaran.
rectificativa Solo en tipos R1–R5: tipo (S sustitución, I diferencias), facturasRectificadas y, si tipo=S, importe con los importes rectificados.
especial Opcional. Indicadores especiales de la AEAT: simplificadas cualificadas, factura sin identificación del destinatario, emisión por tercero o por destinatario, cupón y acuerdos registrados. Ver supuestos especiales.
aux.refExterna Opcional. Se acepta y no se transmite a la AEAT.
subsanacion Opcional, booleano (false por defecto). En la práctica no lo envías: PUT /subsanar lo marca automáticamente.
idMaquina Opcional. Identificador del dispositivo o TPV emisor (máx. 64, A-Za-z0-9._-). Es metadato salvo en los emisores con Alta capacidad activa, donde encamina la cadena de encadenado.

Tipos y formato del cuerpo

La validación ocurre antes de construir el XML: un dato mal formado responde 400 de inmediato, sin llegar a firmarse.

  • cabecera.serie y cabecera.numero admiten solo ASCII imprimible: letras sin acentos, dígitos y puntuación ASCII. A/2026, A 26, A#1 y A-B_C.D son válidos. Ñ, cualquier acento o € devuelven 400 nombrando el campo, con la regla AEAT 1130, la misma que aplica la AEAT a un NumSerieFactura no ASCII.
  • totales.importeTotal no puede ser negativo en F1, F2 ni F3: los importes negativos corresponden a las rectificativas R1–R5. -0.00 no cuenta como negativo.
  • En las referencias de rectificativa.facturasRectificadas y facturasSustituidas, serie y numero suman 60 caracteres como máximo (el TextoIDFacturaType de la especificación, el mismo límite que el número de la propia factura).
  • Los campos de texto no pueden contener caracteres de control ilegales en XML, ni entidades XML o HTML ya codificadas (<, �, &…). El escapado del XML lo hace la API, así que ese texto es una doble codificación y devuelve 400 nombrando el campo. Un & suelto dentro de un nombre (Tom & Jerry) es correcto.
  • En simplificadas F2 rige el tope de 3.000 € de la regla AEAT 1150. Ver el tope de las simplificadas.
  • VeriFactu no tiene lineas: el desglose fiscal va en detalleDesglose y no existe un bloque de líneas de detalle en el esquema de la AEAT. Una clave lineas se ignora. Las líneas de detalle existen solo en TicketBAI.

El tope de las simplificadas F2

La regla AEAT 1150 limita las simplificadas F2 a 3.000 €. La API la comprueba en el alta.

Qué se compara La suma de baseImponible y cuotaRepercutida de todas las líneas de detalleDesglose. No totales.importeTotal: el total incluye el recargo de equivalencia, que la regla no cuenta. Una F2 con recargo puede superar 3.000 € de total y ser válida.
Límite Exclusivo. 3.000,00 € se acepta; 3.000,01 € devuelve 400 nombrando detalleDesglose con la regla 1150.
Exentas Las R5: la regla nombra solo a F2. La F2 con especial.facturaSinIdentifDestinatarioArt61d: "S": es una factura sin identificación del destinatario (art. 6.1.d RD 1619/2012), no una simplificada. La F2 con especial.numRegistroAcuerdoFacturacion.

facturaSinIdentifDestinatarioArt61d es la cadena "S", no un booleano. true devuelve 400 por tipo de campo, no por el tope. "N" deja la factura sujeta al tope.

Supuestos especiales

El bloque especial recoge los indicadores opcionales del registro de alta. Todos se transmiten a la AEAT.

Campo Notas
facturaSimplificadaArt7273 "S" o "N". "S" solo en F1, F3 y R1–R4: simplificada cualificada (arts. 7.2 y 7.3 RD 1619/2012).
facturaSinIdentifDestinatarioArt61d "S" o "N". "S" solo en F2 y R5. Exime a la F2 del tope de 3.000 €.
emitidaPor "D" o "T". Omitido, se aplica el modelo de emisión de tu cuenta. "D": la factura la expide el destinatario; exige destinatario. "T": la expide el tercero indicado en tercero.
tercero Con emitidaPor: "T": nombre más nif o idOtro. Sustituye al tercero por defecto. Un nif igual al del emisor devuelve 400 con la regla AEAT 1188.
cupon "S" o "N". "S" solo en R1 y R5.
numRegistroAcuerdoFacturacion Máx. 15 caracteres. Número de registro del acuerdo de facturación autorizado por la AEAT, que comprueba que existe. Exime a la F2 del tope de 3.000 €.
idAcuerdoSistemaInformatico Máx. 16 caracteres. Identificador del acuerdo del sistema informático registrado en la AEAT.

Los indicadores son las cadenas "S" y "N", no booleanos. "N" equivale a omitir el campo. Un "S" en un tipo de factura no admitido devuelve 400 nombrando el campo.

Macrodato no es un campo: se declara automáticamente cuando el valor absoluto de totales.importeTotal alcanza 100.000.000 €.

Destinatario extranjero

Un destinatario sin NIF español se identifica con destinatario.idOtro en lugar de nif. Enviar nif e idOtro a la vez devuelve 400.

"destinatario": {
  "nombre": "Kunde GmbH",
  "idOtro": { "codigoPais": "DE", "idType": "02", "id": "DE123456789" }
}
Campo Regla
idOtro.idType 02 NIF-IVA, 03 pasaporte, 04 documento oficial del país de residencia, 05 certificado de residencia, 06 otro documento probatorio, 07 no censado.
idOtro.codigoPais Código ISO 3166-1 alfa-2. Opcional con 02. Obligatorio ES con 07. Con ES, solo se admiten 03 y 07.
idOtro.id Solo letras y dígitos, de 1 a 20 caracteres. Espacios, puntos, guiones u otros símbolos devuelven 400.

El formato de identificación de cada país lo comprueba la AEAT: un id que no corresponde al país vuelve como registro rechazado.

Una entrega intracomunitaria exenta (operacionExenta: "E5" con IVA) exige un destinatario con idOtro.

Claves por impuesto

Las claves admitidas en cada línea de detalleDesglose dependen de impuesto. Una claveRegimen fuera de la lista de su impuesto devuelve 400.

impuesto claveRegimen operacionExenta
01 IVA Obligatoria: 01–11, 14, 15, 17–20. E1–E6.
03 IGIC (Canarias) Obligatoria: 01–11, 14, 15, 17–21. E1–E8.
02 IPSI (Ceuta y Melilla) Opcional: 01, 08, 11, 18, 19, 20. Si se omite, se transmite 01. E1–E6.
05 Otros No se admite. Enviarla devuelve 400. E1–E6.

Un mismo código puede significar cosas distintas según el impuesto: 17 es comerciante minorista en IGIC y OSS/IOSS en IVA.

operacionExenta sustituye a calificacionOperacion: la línea lleva una de las dos, nunca ambas, y una línea exenta no lleva tipo ni cuota.

PUT /v1/verifactu/subsanar

Corrige un registro ya remitido cuando la operación es correcta pero el registro contiene datos mal informados. El cuerpo es el mismo que el de crear, con los datos corregidos. La API marca la subsanación automáticamente.

Respuesta

200 OK
{
  "nifEmisor": "B00000000",
  "idFactura": "…",
  "estado": "pendiente_proceso",
  "numSerieFactura": "A12345",
  "serie": "A",
  "numero": "12345",
  "tipoFactura": "F1",
  "importeTotal": "121.00",
  "cuotaTotal": "21.00",
  "creadoEn": "2026-04-03T12:00:00Z",
  "avisos": [
    "El endpoint /subsanar establece automáticamente subsanacion=true (valor recibido: 'N'). Si necesitas control explícito sobre este campo, usa el endpoint /crear."
  ],
  "urlValidacion": "https://…",
  "qrBase64": "…"
}

La respuesta tiene la misma forma que la del alta. avisos aparece cuando el cuerpo no traía subsanacion: true: la API lo ha fijado por ti y lo dice.

Un campo adicional: rechazoPrevio ("N" por defecto, "S" si la AEAT rechazó el registro original, "X" si el registro no consta en la AEAT). Es el único de estos indicadores que no es booleano: tiene tres estados.

rechazoPrevio pertenece a la subsanación. La AEAT no admite "S" ni "X" en un alta normal. Enviarlos en POST /crear sin marcar subsanacion responde 400 VALIDATION_ERROR nombrando la combinación.

Cuándo subsanar y cuándo emitir rectificativa: Rectificar y anular.

POST /v1/verifactu/anular

Deja sin efecto un registro remitido. El cuerpo identifica la factura original:

POST/v1/verifactu/anular

curl -X POST https://sandbox.veribai.com/v1/verifactu/anular \
  -H "x-api-key: TU_CLAVE" \
  -H "Content-Type: application/json" \
  -d '{
    "version": "1.0",
    "facturaAnulada": {
      "nifEmisor": "B00000000",
      "serie": "A",
      "numero": "12345",
      "fechaExpedicion": "03-04-2026"
    }
  }'

Respuesta

200 OK
{
  "code": "SUCCESS",
  "message": "Anulación registrada correctamente",
  "data": {
    "nifEmisor": "B00000000",
    "idFactura": "…",
    "estado": "pendiente_envio",
    "numSerieFactura": "A12345",
    "serie": "A",
    "numero": "12345",
    "fechaExpedicion": "03-04-2026",
    "creadoEn": "2026-04-03T11:00:00Z",
    "mensaje": "La anulación ha sido registrada y será procesada para envío a AEAT. …"
  }
}

A diferencia del alta, la respuesta viene envuelta en el sobre { code, message, data }; la remisión a la AEAT es asíncrona y estado arranca en pendiente_envio.

Campo Notas
version Obligatorio. Actualmente "1.0".
facturaAnulada Obligatorio: nifEmisor, numero y fechaExpedicion; serie si la factura la tenía. serie + numero + fechaExpedicion deben coincidir exactamente con la factura original (la fecha forma parte de su identidad), y la fecha no puede ser futura: identifica una factura ya expedida.
facturaAnulada.nombreEmisor Opcional. Nombre o razón social del emisor tal como figura en el censo de la AEAT. Solo hace falta cuando VeriBai no tiene el alta de la factura; si la tiene, se ignora. Ver el nombre del emisor.
refExterna Opcional. Referencia interna.
sinRegistroPrevio Opcional, booleano (false por defecto). true declara que el alta no llegó a registrarse.
rechazoPrevio Opcional, cadena "N"/"S". "S" si la AEAT rechazó la anulación anterior. La anulación no admite "X". Ese valor solo existe en el alta y en la subsanación.
idMaquina Opcional.

La anulación genera su propio registro encadenado: nada desaparece de la cadena.

Repetir una anulación

Una anulación repetida es un reintento, no una segunda anulación. Si la factura ya tiene una anulación, en cualquier estado salvo rechazada, la respuesta es 200 SUCCESS con esa anulación y data.yaAnulada: true: data.creadoEn y data.estado son los de la anulación existente. No se crea ni se envía nada a la AEAT.

Si la AEAT rechazó la anulación anterior, la petición se acepta como anulación nueva.

El nombre del emisor en la anulación

La AEAT comprueba el NIF y el nombre del emisor juntos; en una persona física, contra el censo. El registro de anulación no lleva nombre, así que la API envía el que declaró el alta en emisor.nombre. Cuando VeriBai tiene ese alta, facturaAnulada.nombreEmisor no hace falta y se ignora si lo envías.

Para una factura que VeriBai no ha visto, normalmente con sinRegistroPrevio: true, envía facturaAnulada.nombreEmisor tal como figura en el censo de la AEAT. Sin él, la respuesta es 400 VALIDATION_ERROR con una entrada en errors que empieza por facturaAnulada.nombreEmisor:, y no se envía nada a la AEAT.

Tipos de factura

Tipo Descripción
F1 Factura completa, con destinatario identificado.
F2 Factura simplificada, sin destinatario. Tope de 3.000 € sobre el desglose; ver tipos y formato del cuerpo.
F3 Factura emitida en sustitución de simplificadas.
R1 Rectificativa: error fundado en derecho.
R2 Rectificativa: art. 80.3 LIVA (concurso de acreedores).
R3 Rectificativa: art. 80.4 LIVA (créditos incobrables).
R4 Rectificativa: resto de casos.
R5 Rectificativa de factura simplificada.

Errores

Los códigos comunes a toda la API están en Errores. Específicos de estos endpoints:

Código Cuándo
400 VALIDATION_ERROR El cuerpo no supera la validación; errors enumera cada campo. En crear, también una fecha mal formada en rectificativa.facturasRectificadas[i].fechaExpedicion. En anular, también cuando falta facturaAnulada.nombreEmisor para una factura sin alta en VeriBai: la entrada empieza por facturaAnulada.nombreEmisor:.
400 R3_TIMING_ERROR crear: rectificativa R3 con menos de seis meses desde la factura que rectifica. Ver Rectificar y anular.
400 MISSING_BODY anular: la petición no lleva cuerpo.
403 UNAUTHORIZED_NIF El emisor.nif no está dado de alta bajo tu cuenta.
403 REPRESENTATION_PENDING Solo LIVE: el emisor no tiene la representación firmada.
409 INVOICE_HAS_RECTIFICATIVAS anular: la factura tiene rectificativas. Anula primero las rectificativas; details.rectificativas las lista.
409 DUPLICATE_INVOICE Caso límite: el registro existe pero sus datos no pudieron recuperarse. El duplicado normal no es un error, sino una repetición idempotente con 200.
500 AUTH_ERROR anular: falló el paso de autorización. No es una denegación. Reintentable.