VeriBaiDocs
Acceder

Facturas y registros

Los endpoints de lectura. Estado, detalle, QR, XML firmado y listados.

La remisión a la administración es asíncrona. Estos endpoints permiten seguir cada factura hasta su estado final y recuperar sus artefactos: el QR, el XML firmado y el historial de registros. Base URL: https://sandbox.veribai.com (Sandbox) o https://api.veribai.com (LIVE).

Método Endpoint Descripción
GET /v1/facturas Listado de facturas de un emisor.
GET /v1/facturas/buscar Localiza una factura por serie, número y fecha. Recupera su idFactura.
GET /v1/facturas/{idFactura} Detalle completo de una factura.
GET /v1/facturas/{idFactura}/estado Estado actual (sondeo).
GET /v1/facturas/{idFactura}/qr QR de la factura (PNG en base64).
GET /v1/facturas/{idFactura}/xml XML firmado del registro.
GET /v1/registros Listado de registros de facturación de un emisor.
GET /v1/registros/{idRegistro} Detalle de un registro.

{idFactura} es el idFactura que devuelve el alta. Todas las lecturas requieren el parámetro nifEmisor (el NIF del emisor) y verifican que pertenece a tu cuenta; un emisor ajeno devuelve 403.

GET /v1/facturas

GET/v1/facturas?nifEmisor=B00000000&limite=100

curl "https://sandbox.veribai.com/v1/facturas?nifEmisor=B00000000&limite=100" \
  -H "x-api-key: TU_CLAVE"

Respuesta

200 OK
{
  "facturas": [
    {
      "idFactura": "…",
      "nifEmisor": "B00000000",
      "nifClientePrincipal": "B99999999",
      "numSerieFactura": "A12345",
      "tipoFactura": "F1",
      "importeTotal": "121.00",
      "cuotaTotal": "21.00",
      "sistemaFiscal": "verifactu",
      "estadoFactura": "registrada",
      "fechaRegistro": "2026-04-03T10:01:03Z",
      "ultimaModificacion": "2026-04-03T10:01:03Z",
      "idRegistroActual": "UkVDIzIwMjYtMDQtMDNUMTA6MDA6MDBa",
      "urlQr": "https://…",
      "hashVerifactu": "…",
      "csvAeat": "…"
    }
  ],
  "total": 15,
  "nifEmisor": "B00000000",
  "nombreEmisor": "Restaurant La Buena Mesa SL",
  "nifClientePrincipal": "B99999999",
  "proximaPagina": null
}
Parámetro Notas
nifEmisor Obligatorio. NIF del emisor cuyas facturas quieres listar.
fechaInicio / fechaFin Opcionales. Acotan por fecha de registro, en ISO 8601 (2026-01-01T00:00:00Z). Otro formato responde 400 INVALID_DATE_FORMAT.
estado Opcional. Filtro por ciclo de vida: registrada, anulada o rectificada.
sistemaFiscal Opcional: verifactu o ticketbai.
limite Opcional, por defecto 100, máximo 500. Un valor por encima del máximo se recorta a 500; uno no entero o menor que 1 responde 400 INVALID_PARAMETER. ?limite= vacío usa el valor por defecto.
cursor Opcional. Cursor de paginación: el proximaPagina de la página anterior.

numSerieFactura es la serie y el número concatenados. El listado no incluye serie ni numero por separado: están en el detalle.

proximaPagina trae el cursor de la página siguiente; null significa que no hay más páginas. Los campos sin valor se omiten, y filtros aparece solo cuando la petición incluyó algún filtro. nifClientePrincipal es el NIF de tu cuenta, e idRegistroActual el registro vigente de la factura. csvAeat es el CSV de ese registro vigente; ver /estado.

GET /v1/facturas/{idFactura}/estado

El endpoint de sondeo: mínimo y estable, pensado para consultas diferidas.

GET/v1/facturas/ID_FACTURA/estado?nifEmisor=B00000000

curl "https://sandbox.veribai.com/v1/facturas/ID_FACTURA/estado?nifEmisor=B00000000" \
  -H "x-api-key: TU_CLAVE"

Respuesta con la remisión en curso

200 OK
{
  "idFactura": "…",
  "numSerieFactura": "A12345",
  "serie": "A",
  "numero": "12345",
  "estadoEnvio": "en_cola",
  "sistemaFiscal": "ticketbai",
  "ultimaModificacion": "2026-04-03T10:00:12Z"
}

Mientras la remisión está en curso, la respuesta trae el estado del envío en estadoEnvio. Cuando la administración acepta el registro, estadoFactura toma el relevo:

Respuesta con la factura registrada

200 OK
{
  "idFactura": "…",
  "numSerieFactura": "A12345",
  "serie": "A",
  "numero": "12345",
  "estadoFactura": "registrada",
  "csvAeat": "…",
  "sistemaFiscal": "verifactu",
  "ultimaModificacion": "2026-04-03T10:01:03Z"
}
Campo Notas
numSerieFactura La serie y el número concatenados (A12345).
serie / numero La serie y el número por separado. Siempre presentes: cadena vacía si la factura no tiene serie.
estadoEnvio Estado de la remisión en curso: pendiente_proceso, pendiente_envio, en_lote, en_cola, rechazada… (desconocido si el registro no informa estado). Presente hasta que la administración acepta un registro de la factura.
estadoFactura Estado de la factura una vez la administración acepta un registro suyo: registrada, anulada o rectificada. Ausente hasta entonces.
csvAeat Solo VeriFactu. El Código Seguro de Verificación del envío con el que la AEAT aceptó el registro vigente de la factura (idRegistroActual). Una subsanación lo sustituye; una anulación no. En una factura sin alta registrada, es el CSV de la anulación aceptada. La AEAT emite un CSV por envío, así que las facturas remitidas en el mismo envío comparten el mismo valor. Las facturas TicketBAI no lo llevan.

Un estadoEnvio de rechazada sin estadoFactura significa rechazo. Consulta el detalle y actúa según el caso: rectificar o subsanar.

Una factura que VeriBai solo conoce por una anulación, normalmente con sinRegistroPrevio: true porque se expidió antes de usar VeriBai, se consulta igual: estadoEnvio mientras la anulación está en curso o si se rechaza, y estadoFactura: "anulada" cuando la administración la acepta. En VeriFactu, csvAeat trae entonces el CSV de esa anulación, también en el detalle.

Si prefieres no sondear, los webhooks llaman a tu servidor cuando la factura alcanza su estado final: registrada, rechazada o anulada.

GET /v1/facturas/{idFactura} (detalle)

Devuelve la factura completa: datos de emisión, estado, artefactos y trazabilidad, en el sobre { "factura": {…}, "registros": […], "totalRegistros": N }. Los campos concretos dependen del sistema (VeriFactu o TicketBAI) y del estado. estadoFactura coincide con el de /estado. Mientras la factura no tiene ningún registro aceptado, la vista mínima devuelve estadoFactura: "procesando" si el último registro espera respuesta, y omite estadoFactura si la administración lo rechazó. factura incluye numSerieFactura y, por separado, serie y numero, siempre presentes (cadena vacía si la factura no tiene serie). Requiere ?nifEmisor=.

GET /v1/facturas/buscar

Localiza una factura a partir de la identidad que ya tienes (serie, número y fecha de expedición) cuando has perdido su idFactura. Sin ese identificador tampoco puedes llamar a /estado, /qr, /xml ni /registros.

GET/v1/facturas/buscar?nifEmisor=B00000000&serie=A&numero=12&fechaExpedicion=15-01-2026

curl "https://sandbox.veribai.com/v1/facturas/buscar?nifEmisor=B00000000&serie=A&numero=12&fechaExpedicion=15-01-2026" \
  -H "x-api-key: TU_CLAVE"

Respuesta

200 OK
{
  "factura": {
    "idFactura": "…",
    "nifEmisor": "B00000000",
    "nifClientePrincipal": "B99999999",
    "numSerieFactura": "A12",
    "serie": "A",
    "numero": "12",
    "tipoFactura": "F1",
    "importeTotal": "121.00",
    "cuotaTotal": "21.00",
    "sistemaFiscal": "verifactu",
    "estadoFactura": "registrada",
    "fechaRegistro": "2026-01-15T10:01:03Z",
    "ultimaModificacion": "2026-01-15T10:01:03Z",
    "idRegistroActual": "UkVDIzIwMjYtMDEtMTVUMTA6MDA6MDBa",
    "urlQr": "https://…",
    "hashVerifactu": "…",
    "csvAeat": "…"
  },
  "registros": [
    {
      "idRegistro": "UkVDIzIwMjYtMDEtMTVUMTA6MDA6MDBa",
      "tipo": "alta",
      "estado": "aceptada",
      "estadoEnvioFinal": "registrada",
      "codigoRespuestaAeat": "0",
      "csvAeat": "…",
      "creadoEn": "2026-01-15T10:00:00Z",
      "envioCompletadoEn": "2026-01-15T10:01:03Z",
      "idLote": "…"
    }
  ],
  "totalRegistros": 1
}
Parámetro Notas
nifEmisor Obligatorio. NIF del emisor.
numero Obligatorio. El número de factura sin la serie.
fechaExpedicion Obligatorio. dd-mm-aaaa, con dos dígitos en día y mes.
serie Opcional. Omítelo si la factura se emitió sin serie.

Devuelve el mismo sobre que el detalle ({ "factura": {…}, "registros": […], "totalRegistros": N }), con el idFactura dentro.

serie y numero van separados. El campo numSerieFactura de las respuestas es la concatenación de ambos: serie=A&numero=12 y serie=A1&numero=2 se leen igual ahí (A12), pero son facturas distintas. Si envías la concatenación en numero, la búsqueda no encuentra nada y responde 404.

Error Cuándo
400 MISSING_PARAMETER Falta numero o fechaExpedicion.
400 VALIDATION_ERROR fechaExpedicion mal formada: 15/01/2026 y 2026-01-15 se rechazan; el formato es 15-01-2026. También si serie o numero superan 60 caracteres.
404 NOT_FOUND Ninguna factura de ese emisor tiene esa identidad.

Los 400 nombran el parámetro afectado en campo.

QR y XML

GET /v1/facturas/{idFactura}/qr?nifEmisor=…    →  application/json
GET /v1/facturas/{idFactura}/xml?nifEmisor=…   →  application/xml

El QR responde 200 con un JSON de dos campos, los mismos que trae la respuesta del alta:

{
  "qrBase64": "iVBORw0KGgo…",
  "urlValidacion": "https://…"
}

qrBase64 es el PNG en base64, sin prefijo data:. urlValidacion es la URL que codifica el QR. Para obtener el fichero, decodifica qrBase64:

curl -s -H "x-api-key: $KEY" \
  "https://sandbox.veribai.com/v1/facturas/$ID/qr?nifEmisor=$NIF" \
  | jq -r .qrBase64 | base64 -d > qr.png

El XML firmado es el documento con valor probatorio remitido a la administración: descargable en cualquier momento para auditoría o custodia propia.

El QR se genera en cada petición a partir de la URL de validación de la factura: es idéntico al qrBase64 que devolvió el alta y está disponible para cualquier factura emitida, sin caducidad.

Ambas rutas responden 404 NOT_FOUND cuando la factura existe pero su documento no está disponible: en /xml, que el documento no está almacenado; en /qr, que la factura no tiene URL de validación, una situación anómala.

GET /v1/registros

Facturas y registros son recursos distintos. La factura es la operación; el registro documenta cada remisión: el alta y, si los hubo, su subsanación o anulación. Ver El registro de facturación.

GET/v1/registros?nifEmisor=B00000000

curl "https://sandbox.veribai.com/v1/registros?nifEmisor=B00000000" \
  -H "x-api-key: TU_CLAVE"

Respuesta

200 OK
{
  "registros": [
    {
      "idRegistro": "UkVDIzIwMjYtMDQtMDNUMTA6MDA6MDBa",
      "tipo": "alta",
      "nifEmisor": "B00000000",
      "idFactura": "…",
      "numSerieFactura": "A12345",
      "serie": "A",
      "numero": "12345",
      "tipoFactura": "F1",
      "importeTotal": "121.00",
      "estado": "aceptada",
      "estadoEnvio": "desconocido",
      "creadoEn": "2026-04-03T10:00:00Z",
      "estadoEnvioFinal": "registrada",
      "codigoRespuestaAeat": "0",
      "csvAeat": "…",
      "envioCompletadoEn": "2026-04-03T10:01:03Z",
      "idLote": "…",
      "idMaquina": "CAJA-01"
    }
  ],
  "total": 1,
  "nifEmisor": "B00000000",
  "nombreEmisor": "Restaurant La Buena Mesa SL",
  "nifClientePrincipal": "B99999999",
  "proximaPagina": null
}
Parámetro Notas
nifEmisor Obligatorio. NIF del emisor.
limite / cursor Paginación, como en /v1/facturas: por defecto 100, máximo 500, con las mismas reglas de validación.
veredictoDesde Opcional. Instante ISO 8601 con zona horaria (2026-09-27T10:00:00Z, 2026-09-27T12:00:00+02:00). Solo los registros cuya respuesta de la administración llegó en ese instante o después, de la más antigua a la más reciente. Precisión de segundo, límite inclusivo. Ver Registros por respuesta de la administración.
estado Opcional. aceptada, aceptada_con_errores o rechazada. Solo los registros con esa respuesta. Sin veredictoDesde, de la respuesta más reciente a la más antigua.

Sin veredictoDesde ni estado, devuelve los registros de alta y de anulación ordenados por fecha de creación descendente, en el sobre { "registros": […], "total", "nifEmisor", "nombreEmisor", "proximaPagina" }.

Campo Notas
idRegistro Identificador opaco del registro. Es lo que se envía de vuelta en la ruta del detalle. Para distinguir un alta de una anulación, el campo es tipo.
tipo alta, subsanacion o anulacion.
numSerieFactura La serie y el número de la factura, concatenados.
serie / numero La serie y el número por separado. Siempre presentes: cadena vacía si la factura no tiene serie.
estado Estado del registro. pendiente: sin respuesta todavía; la fase concreta está en estadoEnvio. aceptada: aceptado por la administración. aceptada_con_errores: solo VeriFactu; aceptado con errores que se corrigen con una subsanación, que crea un registro nuevo. Este registro no cambia. rechazada: rechazado por la administración. procesando: excepcional; el resultado no está confirmado. No requiere ninguna acción por tu parte.
estadoEnvio Estado de la remisión mientras está en curso: pendiente_proceso, pendiente_envio, en_lote, en_cola (desconocido si el registro no informa estado).
estadoEnvioFinal La respuesta de la administración: registrada o rechazada. Aparece al concluir la remisión; a partir de ese momento estadoEnvio deja de ser informativo.
codigoRespuestaAeat / descripcionRespuestaAeat El código y la descripción que devolvió la administración. En un rechazo, el motivo. El código viaja como cadena ("1286"), no como número. Compáralo como texto.
csvAeat Solo VeriFactu. El Código Seguro de Verificación del envío con el que la AEAT aceptó ese registro. Un CSV por envío: los registros remitidos juntos lo comparten.
creadoEn / envioCompletadoEn Cuándo se creó el registro y cuándo concluyó su remisión.
idLote Lote de envío, cuando el registro viajó agrupado.
idMaquina Presente cuando el envío lo indicó.

En TicketBAI se añaden tipoRespuestaTbai, codigoRespuestaTbai, mensajeRespuestaTbai, idRegistroTbai y provincia.

GET /v1/registros/{idRegistro} devuelve el detalle de un registro concreto. Requiere ?nifEmisor= y ?idFactura= (la factura a la que pertenece el registro), y añade a los campos anteriores cuotaTotal, sistemaFiscal, ultimaModificacion, esRectificativa, tipoRectificativa y, en los registros de anulación, fechaExpedicion, sinRegistroPrevio y rechazoPrevio.

GET/v1/registros/UkVDIzIwMjYtMDgtMjRUMDg6Mjk6MzZa?nifEmisor=B00000000&idFactura=ID_FACTURA

curl "https://sandbox.veribai.com/v1/registros/UkVDIzIwMjYtMDgtMjRUMDg6Mjk6MzZa?nifEmisor=B00000000&idFactura=ID_FACTURA" \
  -H "x-api-key: TU_CLAVE"

Respuesta

200 OK
{
  "registro": {
    "idRegistro": "UkVDIzIwMjYtMDgtMjRUMDg6Mjk6MzZa",
    "tipo": "alta",
    "idFactura": "…",
    "nifEmisor": "B00000000",
    "numSerieFactura": "A12345",
    "serie": "A",
    "numero": "12345",
    "tipoFactura": "F1",
    "importeTotal": "121.00",
    "cuotaTotal": "21.00",
    "sistemaFiscal": "verifactu",
    "estado": "aceptada",
    "estadoEnvioFinal": "registrada",
    "codigoRespuestaAeat": "0",
    "csvAeat": "…",
    "creadoEn": "2026-08-24T08:29:36Z",
    "ultimaModificacion": "2026-08-24T08:31:02Z",
    "envioCompletadoEn": "2026-08-24T08:31:02Z",
    "idLote": "…"
  }
}

El detalle llega en el sobre { "registro": { … } }.

El idRegistro es opaco

Llega en el listado de registros y en el detalle de la factura, y vuelve tal cual: sin recortarlo, sin normalizarlo y sin codificarlo para la URL. No lo interpretes ni lo construyas, y no supongas nada sobre su longitud ni sobre su alfabeto. Su forma es interna y puede cambiar. Guárdalo como una cadena, del mismo modo que el cursor de paginación. Un identificador que no se reconoce responde 400 INVALID_PARAMETER.

La forma anterior, con prefijo REC# o CNC#, ya no se devuelve ni se acepta. Si leías ese prefijo para saber ante qué registro estabas, el campo que lo dice es tipo.

Registros por respuesta de la administración

Con veredictoDesde o estado, el listado ordena los registros por el momento en que respondió la administración, no por el de su creación. Los webhooks son la vía principal para conocer esa respuesta; este listado recupera las que no llegaron a tu endpoint.

GET/v1/registros?nifEmisor=B00000000&veredictoDesde=2026-09-27T10:00:00Z

curl "https://sandbox.veribai.com/v1/registros?nifEmisor=B00000000&veredictoDesde=2026-09-27T10:00:00Z" \
  -H "x-api-key: TU_CLAVE"
  • Solo aparecen registros con respuesta. Un registro que todavía espera a la administración no está en el listado filtrado. Por eso estado=pendiente y estado=procesando responden 400 INVALID_PARAMETER. Para los registros en curso, usa el listado sin filtros o /estado. Excepcionalmente, el listado filtrado por veredictoDesde puede incluir un registro con estado: "procesando" y estadoEnvio: "desconocido". No es una respuesta final y no requiere ninguna acción por tu parte.
  • Consulta periódica. Llama con veredictoDesde=<T> y sigue proximaPagina hasta que valga null. Para la siguiente consulta, <T> es el envioCompletadoEn más reciente que recibiste, menos 60 segundos.
  • Deduplica entre consultas. Cada consulta se solapa un minuto con la anterior, así que los últimos registros de una vuelven al principio de la siguiente. La clave es idFactura + idRegistro + estado. idRegistro solo es único dentro de una factura: deduplicar sin idFactura descarta registros reales. Cada registro recibe una sola respuesta. Una subsanación es un registro nuevo, con su propio idRegistro, y aparece como una entrada más. Para seguir una factura hasta su estado final, agrupa por idFactura.
  • Limita la memoria de la deduplicación. Si tu proceso guarda las claves ya vistas, después de cada consulta borra las que tengan un envioCompletadoEn anterior al siguiente <T>: esos registros ya no pueden volver. Así solo guardas el último minuto de registros, aunque el proceso esté activo meses.
  • Una página puede llegar incompleta o vacía. Con estado, una página puede traer menos de limite registros, incluso ninguno, y aun así traer proximaPagina. Solo null indica que no hay más páginas.
  • El cursor depende de los filtros. Envía los mismos filtros en cada página. Un proximaPagina obtenido con otros filtros responde 400 INVALID_CURSOR.
  • Formato de veredictoDesde. Sin zona horaria (2026-09-27T10:00:00), solo la fecha o cualquier otro texto responde 400 INVALID_PARAMETER. Codifica el + del desfase como %2B, o usa Z.

Cada registro tiene los mismos campos que en el listado sin filtros.

Cómo leer un rechazo

Un registro con estadoEnvioFinal: "rechazada" no ha quedado presentado ante la administración. El motivo está en codigoRespuestaAeat y descripcionRespuestaAeat (en TicketBAI, además, en codigoRespuestaTbai y mensajeRespuestaTbai). Son el código y el texto de la propia administración.

Corrige el dato que señalan y reenvía con PUT /v1/{sistema}/subsanar. VeriBai no reintenta por su cuenta un rechazo por datos: el mismo envío obtendría el mismo rechazo, y en TicketBAI cada intento avanza la cadena de huellas del emisor de forma permanente.

Para no sondear en busca de rechazos, suscríbete al evento factura.rechazada de los webhooks: llega con el código de la administración en el cuerpo.