VeriBaiDocs
Acceder

Errores

El formato de error, la tabla de códigos y qué reintentar.

Todos los errores de la API comparten el mismo cuerpo:

{
  "code": "VALIDATION_ERROR",
  "message": "Error de validación",
  "errors": [ "cabecera.numero: obligatorio" ]
}
  • code: código estable, pensado para tratarse por programa. Bifurca tu lógica sobre él: no sobre message, y no sobre el estado HTTP. El code identifica la condición concreta; el estado que la acompaña puede afinarse.
  • message: descripción legible, en castellano.
  • errors aparece en los errores de validación de cuerpo, con la lista de violaciones campo a campo, y en INVOICE_IDENTITY_CONFLICT, con los campos que difieren.
  • campo: en los errores de parámetros de consulta, el parámetro concreto que los provocó (numero, fechaExpedicion, serie).

Un 500 no incluye el texto de la excepción interna: el cuerpo se limita a code y message, con una frase fija. Diagnostica por code; si necesitas más contexto, escríbenos indicando la hora de la petición.

Los rechazos que emite la pasarela antes de alcanzar la API no siguen ese formato. Un caso concreto: en los segundos posteriores a rotar una clave, la petición con la clave nueva recibe 403 con el cuerpo {"message": "Forbidden"} y sin code.

Códigos

HTTP code Cuándo
400 VALIDATION_ERROR El cuerpo no supera la validación. errors enumera los campos. Incluye un bloque rectificativa inválido o que no concuerda con el tipoFactura (R1 a R5), por ejemplo una fecha mal formada en las facturas rectificadas: la entrada nombra el campo, como rectificativa.facturasRectificadas[i].fechaExpedicion.
400 INVALID_JSON El cuerpo no es JSON válido.
400 MISSING_PARAMETER / INVALID_PARAMETER Parámetro ausente o inválido: de consulta (nifEmisor, limite) o de ruta (un idRegistro que no se reconoce).
400 INVALID_DATE_FORMAT GET /v1/facturas: fechaInicio o fechaFin no está en ISO 8601 (2026-01-01T00:00:00Z).
400 INVALID_PROVINCE TicketBAI: provincia ausente o distinta de araba, bizkaia, gipuzkoa.
400 PROVINCE_MISMATCH TicketBAI: provincia no coincide con la hacienda registrada del emisor.
400 NOT_APPLICABLE_FOR_TAX_AGENCY Representación no disponible para esa hacienda. Hoy las cuatro tienen documento gestionado, así que en la práctica solo aparece si la hacienda del emisor falta o no se reconoce.
400 MISSING_FIELDS Faltan campos obligatorios en el cuerpo. El message los enumera.
400 INVALID_FIELD_TYPE Los campos están presentes pero con el tipo JSON equivocado (p. ej. nifEmisor como objeto). errors nombra cada uno. TicketBAI anular.
400 BAD_REQUEST El cuerpo no es un objeto JSON: un array, cadena, número o null se rechaza aquí y no más adelante. También cubre un idMaquina con formato inválido.
400 TAX_SYSTEM_MISMATCH El emisor está registrado para el otro sistema fiscal: usa el endpoint que corresponde a su hacienda.
400 R3_TIMING_ERROR Una rectificativa R3 (art. 80.4 LIVA) con menos de seis meses desde la factura que rectifica. Los dos sistemas. details incluye fechaOriginal, fechaRectificativa y mesesTranscurridos. Ver Rectificar y anular.
400 MISSING_BODY VeriFactu anular: la petición no lleva cuerpo.
400 INVALID_BODY / INVALID_PDF / INVALID_CERT / CERT_ERROR / SIGNING_ERROR Representación: cuerpo que no se decodifica, PDF o certificado que no son base64, certificado que no carga, o firma que falla.
400 SIGNATURE_* / NO_GENERATION_RECORD verificar rechazó el documento firmado. El código nombra la causa y detallesVerificacion trae el diagnóstico. Tabla completa.
401 UNAUTHORIZED Clave API ausente, inválida o revocada.
402 PAYMENT_REQUIRED Facturación de la cuenta suspendida. Solo endpoints de escritura.
403 FORBIDDEN El recurso pertenece a otra cuenta, o tu plan no incluye ese entorno.
403 UNAUTHORIZED_NIF El NIF emisor no está dado de alta bajo tu cuenta.
403 REPRESENTATION_PENDING Solo LIVE: el emisor no tiene la representación firmada.
404 NOT_FOUND El recurso no existe. En /v1/facturas/buscar, que ninguna factura de ese emisor tiene esa identidad.
404 NOT_UPLOADED_YET Declaración Responsable: todavía no hay documento publicado.
404 INVOICE_NOT_FOUND TicketBAI anular: no existe la factura original para ese emisor.
409 INVOICE_NOT_FOUND VeriFactu anular: enviaste sinRegistroPrevio: false pero la factura no consta. Si no se reportó, usa true.
409 INVOICE_EXISTS VeriFactu anular: el caso inverso. Enviaste sinRegistroPrevio: true pero la factura sí consta.
409 INVOICE_HAS_RECTIFICATIVAS anular, los dos sistemas: la factura tiene rectificativas. Anula primero las rectificativas. details identifica la factura (idFactura, numSerieFactura, serie, numero, fechaExpedicion) y lista sus rectificativas.
409 DUPLICATE_INVOICE Caso límite: el registro duplicado existe pero sus datos no pudieron recuperarse. El duplicado normal responde 200 (repetición idempotente), no 409.
409 INVOICE_IDENTITY_CONFLICT crear, los dos sistemas: ya consta una factura con el mismo emisor, serie, número y fecha de expedición, pero con otro importeTotal o tipoFactura. Aplica tanto si esa factura está registrada como si sigue en proceso o fue rechazada. errors enumera los campos que difieren (p. ej. importeTotal: enviado 121.00 / registrado 100.00) y numSerieFactura identifica la factura existente. No es reintentable.
409 CLIENT_LIMIT_REACHED El alta de un cliente (o la reactivación de uno inactivo) superaría el límite de emisores del plan. La petición es correcta; el límite es de la cuenta.
409 SELF_CLIENT_PROTECTED El emisor que es tu propia empresa no puede desactivarse ni eliminarse: solo se factura para un emisor activo.
409 SELF_CLIENT_CONFLICT El alta lleva tu propio NIF y ya está registrado en otra cuenta. Con el NIF de un tercero el mismo caso responde 400 VALIDATION_ERROR.
409 SIGNING_IN_PROGRESS PATCH de un cliente mientras hay una firma de representación en curso, sobre campos que alimentan el documento. camposBloqueados los enumera.
409 CLIENT_DELETED PATCH /v1/clientes/{nif}/estado sobre un emisor eliminado. Se restaura desde el panel, que aplica la ventana de 30 días.
409 CLIENT_STATUS_INVALID El estado guardado del emisor no admite ese cambio. Escríbenos.
409 INVOICE_SIGNING_IN_FLIGHT TicketBAI crear: una petición idéntica anterior aún está completando su firma. Reintenta en unos segundos. El reintento devuelve la identidad ya almacenada. Si persiste después de un RECORD_PERSIST_ERROR o de un XML_PERSIST_ERROR en crear, escríbenos: el documento firmado no llegó a guardarse.
410 GONE El recurso fue eliminado y ha pasado su ventana de restauración de 30 días.
429 Ninguno Límite de peticiones o cuota mensual superados. Ver Límites.
500 INTERNAL_ERROR Fallo inesperado. Reintenta; si persiste, escríbenos.
500 AWS_SERVICE_ERROR Falló una dependencia de infraestructura. Reintentable: se devuelve antes de firmar, encadenar o persistir nada.
500 QR_GENERATION_ERROR VeriFactu crear: no se pudo generar el QR obligatorio. No se registró nada: puedes reintentar.
500 ENQUEUE_ERROR crear/anular: el registro no pudo encolarse para su procesamiento. La petición se deshace por completo, así que el reintento arranca limpio.
500 RECORD_PERSIST_ERROR TicketBAI crear/anular: la factura quedó firmada y encadenada, pero su registro de remisión no pudo escribirse, así que no se envió a la hacienda foral. Reintenta la misma petición: recupera la factura ya firmada (mismo identificador, mismo QR, sin volver a firmarla) y la envía, respondiendo 200 con yaExistente: true.
500 PROPAGATION_INCOMPLETE PATCH de un webhook: el webhook se actualizó, pero algún emisor vinculado conserva los valores anteriores. Repite el mismo PATCH. Es idempotente.
500 XML_PERSIST_ERROR TicketBAI: no se pudo guardar un XML tras tres intentos. El message distingue dos casos. Si dice que no se ha enviado nada (subsanar y la anulación corregida), nada se firmó ni se encoló y el reintento es seguro. En crear («Error al guardar el XML firmado de TicketBAI») la factura sí quedó firmada y encadenada, y el reintento responde 409 INVOICE_SIGNING_IN_FLIGHT de forma indefinida: no reintentes; escríbenos.
500 DATABASE_ERROR Falló una operación de base de datos en los endpoints de consulta o de gestión. Reintentable.
500 AUTH_ERROR VeriFactu anular: falló el propio paso de autorización. No es una denegación. Reintentable.
500 CF_NOT_CONFIGURED Declaración Responsable: fallo de configuración del servidor, no de tu petición. Escríbenos.
503 AEAT_UNAVAILABLE El censo de la AEAT no se pudo alcanzar (validación de NIF). El cuerpo puede incluir las entradas que sí pudieron resolverse. Un NIF que la AEAT omite en su respuesta no es este error: vuelve en un 200 como no_procesado.
503 CHAIN_CONTENTION TicketBAI: la cadena de huellas del emisor estaba siendo escrita simultáneamente. No se firmó ni registró nada: reintenta la misma petición.

Qué reintentar

El cliente de Python aplica esta política por ti, incluida la distinción entre un rechazo del servidor y una conexión caída. Lo que sigue es lo que hay que implementar si llamas a la API directamente.

  • 429: espera y reintenta con retroceso exponencial. Si es por cuota mensual, el reintento inmediato no ayuda.
  • 500 y 503: reintentables. La creación de facturas es idempotente por (emisor, serie, número, fecha): reintentar un alta cuya primera petición sí llegó devuelve 200 con el registro ya existente y yaExistente: true, no un duplicado.
  • RECORD_PERSIST_ERROR: reintenta la misma petición. La factura está firmada pero no remitida, y el reintento la recupera y la envía sin volver a firmarla: no se duplica ni avanza la cadena de huellas. Es el único 500 cuyo reintento responde 200 con yaExistente: true.
  • AWS_SERVICE_ERROR, ENQUEUE_ERROR, QR_GENERATION_ERROR y CHAIN_CONTENTION: reintentables. Los cuatro se devuelven antes de firmar y encadenar, o deshacen por completo lo escrito: no dejan a medias ningún registro ni consumen posición en la cadena de huellas.
  • DATABASE_ERROR y AUTH_ERROR: reintentables.
  • XML_PERSIST_ERROR: depende del message. Si dice que no se ha enviado nada, reintenta. Si es el de crear, no: la factura está firmada y encadenada, y hace falta intervención de soporte.
  • INVOICE_IDENTITY_CONFLICT: no reintentes nunca. Con ese número ya consta otra factura. Una factura distinta necesita otro número; para cambiar una ya registrada, usa la subsanación o una rectificativa. Un reenvío idéntico sí responde 200 con el registro original.
  • Resto de 4xx: el reintento sin corregir la petición obtiene la misma respuesta. Corrige y reenvía.

Cuentas multi-TPV

Los emisores con Alta capacidad contratada, donde cada dispositivo lleva su propia cadena de encadenado, tienen códigos propios. Las cuentas estándar no los reciben.

En los endpoints de emisión, y solo con el modo ya activo:

HTTP code Cuándo
409 MACHINE_NOT_REGISTERED El idMaquina enviado no está registrado para ese emisor, o está de baja.
409 SERIE_OWNED_BY_OTHER_MACHINE La serie del alta pertenece de forma permanente a otro dispositivo o al envío centralizado. Usa otra serie.
503 SHARDING_UNAVAILABLE La configuración no pudo comprobarse. Reintentable: la petición no se encamina sin verificar la configuración.

En los endpoints de gestión de dispositivos: 409 SHARDING_NOT_ENABLED, 409 MACHINE_ALREADY_REGISTERED, 404 MACHINE_NOT_FOUND, 409 CLIENT_NOT_IN_LIVE, 404 SERIE_NOT_RESERVED, 409 RESERVATION_LOCKED, 409 NOT_IN_PREPARATION y 503 ENVIRONMENT_NOT_AVAILABLE.

Si tu volumen lo requiere, habla con el equipo.