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 (TEST) 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 TEST 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": "B76116342",
      "nombre": "Prueba 1 SL"
    },
    "destinatario": {
      "nif": "B17195595",
      "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"
    }
  }'

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: F1F3, R1R5. 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 (identificación extranjera: codigoPais, idType, id).
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.
totales Obligatorio: cuotaTotal e importeTotal. Deben cuadrar con el desglose.
rectificativa Solo en tipos R1R5: tipo (S sustitución, I diferencias), facturasRectificadas y, si tipo=S, importe con los importes rectificados.
especial Opcional. Supuestos especiales: emisión por tercero o por destinatario (emitidaPor, tercero), simplificadas cualificadas.
aux.refExterna Opcional. Tu referencia interna; viaja con el registro.
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, para que un dato mal formado sea un 400 inmediato y no un rechazo de la AEAT con el registro ya firmado:

  • 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 responde la AEAT ante un NumSerieFactura no ASCII.
  • totales.importeTotal no puede ser negativo en F1, F2 ni F3: los importes negativos corresponden a las rectificativas R1R5. -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. Lo que se compara es la suma de baseImponible y cuotaRepercutida de todas las líneas de detalleDesglose, no totales.importeTotal: el total arrastra además el recargo de equivalencia, que la regla no cuenta, así que una F2 con recargo puede pasar de 3.000 € de total y seguir siendo válida. El tope es exclusivo: 3.000,00 € se acepta y 3.000,01 € devuelve 400 nombrando detalleDesglose con la regla 1150. Las R5 no tienen tope, porque la regla nombra solo a F2. La API tampoco lo aplica a la F2 que declara especial.facturaSinIdentifDestinatarioArt61d: "S", por ser una factura sin identificación del destinatario (art. 6.1.d RD 1619/2012) y no una simplificada. Atención al tipo: ese campo es la cadena "S", no un booleano. true devuelve un 400 por tipo de campo, no por el tope, y "N" deja la factura sujeta a él.
  • 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 son cosa de TicketBAI.

Respuesta

200 OK. El registro queda validado, firmado y encolado:

{
  "nifEmisor": "B76116342",
  "idFactura": "…",
  "estado": "pendiente_proceso",
  "serieNumero": "A12345",
  "tipoFactura": "F1",
  "importeTotal": "121.00",
  "cuotaTotal": "21.00",
  "creadoEn": "2026-04-03T10:00:00Z",
  "urlValidacion": "https://…",
  "qrBase64": "…"
}

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. En la repetición, estado trae el valor de ciclo de vida (registrada, anulada o rectificada) y el QR se regenera.

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.

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: son tres estados, y "X" no se puede expresar con true/false.

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. En PUT /subsanar no hay nada que vigilar: la API marca la subsanación por ti.

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": "B76116342",
      "serie": "A",
      "numero": "12345",
      "fechaExpedicion": "03-04-2026"
    }
  }'
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.
refExterna Opcional. Referencia interna.
sinRegistroPrevio Opcional, booleano (false por defecto). true declara que el alta nunca 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.

Respuesta

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:

{
  "code": "SUCCESS",
  "message": "Anulación registrada correctamente",
  "data": {
    "nifEmisor": "B76116342",
    "idFactura": "…",
    "estado": "pendiente_envio",
    "serieNumero": "A12345",
    "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. …"
  }
}

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

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.
400 R3_TIMING_ERROR crear: rectificativa R3 con menos de seis meses desde la factura que rectifica. Ver Rectificar y anular.
400 DATE_FORMAT_ERROR crear, solo en R3: una de las fechas de la comparación no pudo interpretarse (DD-MM-YYYY).
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 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.