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 solo en errores de validación de cuerpo: la lista de violaciones, campo a campo.
  • campo: en los errores de parámetros de consulta, el parámetro concreto que los provocó (numero, fechaExpedicion, serie).

Un 500 nunca lleva 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. El caso que conviene conocer: 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.
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_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 RECTIFICATIVA_ERROR El bloque rectificativa es inválido o no concuerda con el tipoFactura (R1–R5).
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 DATE_FORMAT_ERROR VeriFactu crear, solo en R3: una de las fechas que compara la regla de los seis meses no pudo interpretarse (DD-MM-YYYY). En TicketBAI el mismo caso responde RECTIFICATIVA_ERROR.
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 nunca se reportó, usa true.
409 INVOICE_EXISTS VeriFactu anular: el caso inverso. Enviaste sinRegistroPrevio: true pero la factura sí consta.
409 ALREADY_CANCELLED VeriFactu anular: ya existe una anulación para esa factura. Una anulación que se registró pero nunca llegó a procesarse no cuenta: el reintento la reencola. (En TicketBAI la repetición responde 200 con yaAnulada: true.)
409 (VeriFactu) / 400 (TicketBAI) INVOICE_HAS_RECTIFICATIVAS No puedes anular una factura que tiene rectificativas: anula primero las rectificativas. El cuerpo incluye details.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), nunca 409.
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: es el estado de la cuenta el que choca con ella.
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, a propósito.
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 es lo único que comprueba 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. Siempre es seguro reintentar: 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. Lee el message. 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 indefinidamente: no insistas, 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 en crudo.

  • 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, 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 sin reservas. 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.
  • Resto de 4xx: no reintentes sin corregir la petición, porque el resultado será el mismo.

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 ven nunca.

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 nunca se encamina sin verificar.

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.