VeriBaiDocs
Acceder

Cliente de Python

El paquete oficial de Python. Entornos explícitos, reintentos seguros, espera de la respuesta de la administración y errores tipados.

El cliente de Python es la manera más sencilla de implementar e integrar la API de VeriBai en tu sistema. Todo comienza con la instalación del proyecto mediante pip:

pip install veribai

Requiere Python 3.10 o superior. Una sola dependencia en tiempo de ejecución, requests.

La llamada concreta de cada endpoint está en la Referencia API, en la pestaña Python de cada ejemplo. Esta página cubre lo común a todas las llamadas.

Qué añade sobre requests

  • Reintentos que distinguen un rechazo de un resultado desconocido. Ver Reintentos.
  • Importes y fechas en el formato que exige la API. Pasa Decimal y date; la conversión se hace al serializar. float se rechaza: la representación binaria puede desviar una cuota un céntimo. Un importe con más decimales de los que admite el formato levanta una excepción; el cliente no redondea.
  • Verificación de webhooks sobre los bytes crudos de la petición, antes de parsear el JSON.
  • Errores tipados, cada uno con el code de la API.

No replica las reglas de validación fiscal. Las fijan cuatro administraciones y cambian; una copia local quedaría desactualizada y rechazaría facturas que la API acepta. El cliente comprueba el formato; la validación fiscal la hace la API.

Entornos

Pasa el entorno de forma explícita:

import veribai

client = veribai.Client(api_key="...", environment="test")   # sandbox
client = veribai.Client(api_key="...", environment="live")   # producción

El entorno se resuelve en este orden:

  1. el argumento environment=
  2. la variable VERIBAI_ENVIRONMENT
  3. test

El argumento gana a la variable. Un constructor sin environment= usa Sandbox solo mientras VERIBAI_ENVIRONMENT no esté definida. Si el despliegue define VERIBAI_ENVIRONMENT=live, ese constructor emite en producción.

entorno indica el entorno activo; origen_entorno, de dónde salió:

client = veribai.Client(api_key="...", environment="live")

print(client.entorno)          # 'live'
print(client.origen_entorno)   # 'the environment= argument'

origen_entorno devuelve una de tres cadenas: the environment= argument, the VERIBAI_ENVIRONMENT environment variable o the default. Registra las dos en el log de arranque: distinguen un despliegue que eligió producción de otro que la heredó de la variable.

Una cadena vacía en environment= falla en el constructor, no en la primera llamada:

# ValueError: unknown environment ''
veribai.Client(api_key="...", environment=os.environ.get("MI_ENTORNO", ""))

Un entorno equivocado no es una factura equivocada

Las claves se emiten por entorno. Una clave Sandbox contra api.veribai.com, o una clave LIVE contra sandbox.veribai.com, recibe de la pasarela un 403 sin campo code; el cliente lo lanza como AuthenticationError. Un environment= equivocado produce un error de autenticación, no un registro en el entorno equivocado.

client.cuenta.obtener() devuelve el entorno real: la API lo deduce de la clave, no del argumento. Es la comprobación adecuada para el arranque de la aplicación.

La API de gestión no recibe parámetro de entorno: lo resuelve a partir de la clave.

Sin api_key, el cliente lee VERIBAI_API_KEY. Detalle de claves y rotación en Autenticación.

Reintentos

Un 429, o un 503 que la API documenta como seguro, indica que el servidor rechazó la petición antes de procesarla; reintentar es seguro. Una conexión caída no indica nada: la factura puede estar ya firmada y encolada.

Los errores de red se reintentan solo en las rutas idempotentes por identidad. El alta lo es por serie, número y fecha de expedición: un reenvío idéntico devuelve el registro original. Esa identidad la fija el servidor, así que el cliente no añade ninguna clave de idempotencia propia. Si importeTotal cambia entre dos intentos, la respuesta es 409 INVOICE_IDENTITY_CONFLICT: es otra factura con el mismo número.

En las rutas donde un duplicado crearía un segundo objeto, como crear un webhook o registrar un dispositivo, un error de red no se reintenta.

La política se ajusta en el constructor:

client = veribai.Client(
    api_key="...",
    environment="test",
    retry=veribai.RetryPolicy(max_attempts=4, backoff_base=0.5, backoff_max=20.0),
    timeout=30.0,
)

max_attempts=1 desactiva los reintentos. Se respeta Retry-After cuando el servidor lo envía. El jitter está activo, así que varios procesos en paralelo no reintentan en el mismo segundo tras un 429.

Qué devuelve cada código y cuál es reintentable, en Errores. Los límites y la cuota, en Límites.

Del 200 al veredicto

Un 200 de crear significa aceptada, no presentada. VeriBai ha recibido y validado la factura y, en TicketBAI, la ha firmado. La administración responde después.

Son dos momentos:

respuesta = client.verifactu.crear(factura)

verdicto = client.facturas.esperar_verdicto(
    respuesta["idFactura"], nif_emisor="B00000000", con_detalle=True
)

if verdicto.registrada:
    ...
elif verdicto.rechazada:
    # No presentada: corrige y subsana.
    ...
elif verdicto.requiere_subsanacion:
    # Presentada con errores: requiere subsanación.
    ...

esperar_verdicto sondea hasta que hay respuesta. Con con_detalle, el sondeo final añade una llamada para incluir en verdicto.detalle el código y la descripción de la administración en caso de rechazo.

aceptada_con_errores es un estado final. El registro está presentado y solo cambia con una subsanación. Ver Rectificar y anular.

Si se agota el plazo, el cliente lanza VerdictTimeout: la administración no ha respondido todavía. ultimo_estado guarda la última lectura para reanudar el sondeo.

Cada sondeo consume una llamada de la cuota mensual de la clave. Con volumen, un webhook sustituye al sondeo.

Errores

Cada fallo documentado tiene su excepción, y todas llevan el code de la API.

try:
    client.verifactu.crear(factura)
except veribai.IdentityConflictError as exc:
    # misma serie, número y fecha, distinto importe: es otra factura
    print(exc.code, exc.errors)
except veribai.ValidationError as exc:
    print(exc.errors)          # cadenas por campo, incluidos códigos de la AEAT
except veribai.RateLimitError as exc:
    print(exc.retry_after)     # ya se reintentó; esto es al agotar la política
except veribai.APIError as exc:
    print(exc.status, exc.code, exc.request_id)

Ramifica por code, no por el estado HTTP. Un 409 es un conflicto de identidad en crear y un límite de plan en clientes/crear.

exc.errors es texto legible, a veces con códigos de reglas de la AEAT. Sirve para mostrarlo o registrarlo, no para parsearlo.

exc.request_id es el identificador de la pasarela. Inclúyelo al escribir a soporte.

Un 403 con el cuerpo {"message": "Forbidden"} y sin campo code lo emite la pasarela, no la API: clave ausente, desconocida, deshabilitada o de otro entorno. El cliente lo lanza como AuthenticationError.

Todas heredan de veribai.VeriBaiError. La tabla completa de códigos, en Errores.

Paginación

listar devuelve una página. iterar las recorre todas:

for factura in client.facturas.iterar("B00000000", estado="registrada"):
    print(factura["numSerieFactura"])

Una Pagina se itera, tiene len() y expone hay_mas y proxima_pagina. El cursor es opaco y va ligado a tu cuenta: devuélvelo sin modificarlo. Un cursor alterado o truncado responde 400 INVALID_CURSOR; vuelve a empezar por la primera página.

total_en_pagina cuenta las filas de esa página, no el total del emisor.

Cada página es una llamada contra la cuota de la clave. iterar acepta max_paginas como límite.

Webhooks

El HMAC se calcula sobre los bytes crudos de la petición. Si tu framework parsea el JSON antes de entregarte el cuerpo, la firma ya no se puede verificar. veribai.webhooks.parse_entrega verifica primero, parsea después y devuelve el identificador de entrega con el que deduplicar.

El receptor completo, con la verificación y el tratamiento de duplicados, en Webhooks.

El QR

client.facturas.qr() devuelve los bytes del PNG.

guardar_qr hace lo mismo y lo escribe en la ruta que le des.

Integrar con un agente de IA

Las skills oficiales de VeriBai guían a Claude Code, Codex, Cursor, Copilot, Gemini CLI y otros agentes durante la integración. Están en el repositorio VeriBai/veribai-skills.

En Claude Code:

/plugin marketplace add VeriBai/veribai-skills
/plugin install veribai@veribai

La instalación en otros agentes está en el README del repositorio.