Clientes (emisores)
Alta y gestión de los emisores en cuyo nombre factura tu software.
En VeriBai, cada empresa o autónomo para quien tu software emite facturas es un cliente de tu cuenta: el emisor. Todo emisor debe estar dado de alta antes de emitir su primera factura: una petición con un emisor.nif no registrado bajo tu cuenta se rechaza con 403.
Estos endpoints pertenecen a la API de gestión: base URL
https://manage-api.veribai.com, misma clavex-api-key. El entorno (TEST o LIVE) se detecta automáticamente a partir de la clave.
Endpoints
| Método | Endpoint | Descripción |
|---|---|---|
GET |
/v1/clientes |
Listado y plazas del plan. ?incluirEliminados=true incluye los eliminados. |
POST |
/v1/clientes/crear |
Alta de un cliente, incluido el de tu propia empresa. |
GET |
/v1/clientes/{nif} |
Detalle. |
PATCH |
/v1/clientes/{nif} |
Modificación parcial. |
PATCH |
/v1/clientes/{nif}/estado |
Activar o desactivar. |
La eliminación y restauración de clientes se gestionan desde el panel.
Crear un cliente
curl -X POST https://manage-api.veribai.com/v1/clientes/crear \
-H "x-api-key: TU_CLAVE" \
-H "Content-Type: application/json" \
-d '{
"nif": "B98765432",
"nombre": "Restaurant La Buena Mesa SL",
"email": "contacto@restaurant.com",
"tipoUsuario": "empresa",
"hacienda": "verifactu",
"direccion": {
"calle": "Gran Via",
"numero": "1",
"codigoPostal": "08001",
"ciudad": "Barcelona",
"provincia": "Barcelona"
},
"representante": {
"nombre": "Juan Pérez García",
"nif": "12345678A",
"direccion": {
"calle": "Gran Via",
"numero": "1",
"codigoPostal": "08001",
"ciudad": "Barcelona",
"provincia": "Barcelona"
}
}
}'
import veribai
client = veribai.Client(api_key="TU_CLAVE", environment="test")
respuesta = client.clientes.crear({
"nif": "B98765432",
"nombre": "Restaurant La Buena Mesa SL",
"email": "contacto@restaurant.com",
"tipoUsuario": "empresa",
"hacienda": "verifactu",
"direccion": {
"calle": "Gran Via",
"numero": "1",
"codigoPostal": "08001",
"ciudad": "Barcelona",
"provincia": "Barcelona",
},
"representante": {
"nombre": "Juan Pérez García",
"nif": "12345678A",
"direccion": {
"calle": "Gran Via",
"numero": "1",
"codigoPostal": "08001",
"ciudad": "Barcelona",
"provincia": "Barcelona",
},
},
})
const respuesta = await fetch("https://manage-api.veribai.com/v1/clientes/crear", {
method: "POST",
headers: {
"x-api-key": "TU_CLAVE",
"Content-Type": "application/json",
},
body: JSON.stringify({
"nif": "B98765432",
"nombre": "Restaurant La Buena Mesa SL",
"email": "contacto@restaurant.com",
"tipoUsuario": "empresa",
"hacienda": "verifactu",
"direccion": {
"calle": "Gran Via",
"numero": "1",
"codigoPostal": "08001",
"ciudad": "Barcelona",
"provincia": "Barcelona"
},
"representante": {
"nombre": "Juan Pérez García",
"nif": "12345678A",
"direccion": {
"calle": "Gran Via",
"numero": "1",
"codigoPostal": "08001",
"ciudad": "Barcelona",
"provincia": "Barcelona"
}
}
}),
});
if (!respuesta.ok) throw new Error(`VeriBai ${respuesta.status}`);
const datos = await respuesta.json();
201 Created devuelve el cliente completo en el sobre { "cliente": { … } }. El alta valida el formato del NIF, su unicidad global y el límite de clientes de tu plan. Las tres comprobaciones son atómicas.
Campos
| Campo | Notas |
|---|---|
nif |
NIF del emisor. Único en toda la plataforma. |
nombre |
Razón social o nombre. Se contrasta contra el censo de la AEAT de forma informativa. Ver Validación de NIF. |
tipoUsuario |
autonomo o empresa. |
hacienda |
verifactu, tbai-araba, tbai-bizkaia o tbai-gipuzkoa. Determina el documento de representación y las reglas aplicables. Inmutable tras el alta. |
direccion |
calle, numero, codigoPostal, ciudad, provincia. |
representante |
Obligatorio para empresa (nombre, NIF y dirección del representante legal); no se admite para autonomo. |
epigrafeIAE |
Obligatorio para autonomo con hacienda=tbai-bizkaia: el epígrafe IAE del Modelo 140 de Batuz, validado contra el catálogo oficial. No aplica en el resto de casos. |
Campos derivados en las respuestas
| Campo | Notas |
|---|---|
estado |
activo, inactivo o eliminado. |
estadoRepresentacion |
Estado del documento de representación. Ver Representación. |
validacionCensal |
Veredicto del censo AEAT para (NIF, nombre): estado, nombreCenso, validadoEn. Informativo, nunca bloqueante. |
creadoEn / ultimaModificacion |
Fechas de alta y de última modificación, en ISO 8601. |
clientePropio |
Presente (true) cuando el NIF del emisor coincide con el de tu propia cuenta. Ver Tu propia empresa como emisor. Se deriva del NIF: no se pide en el alta ni se puede fijar a mano. |
portalHabilitado |
Si ese emisor puede entrar al portal de clientes. En el detalle, el alta y el PATCH viene siempre, false mientras no lo actives; el listado no lo incluye. |
modoCadena |
unica o por_dispositivo. Cómo encadena hoy la emisión de ese emisor. Ver Alta capacidad. En el listado y en el detalle. |
faseAltaCapacidad |
no_contratada, preparacion o activa. La fase de contratación de Alta capacidad. En el listado y en el detalle. |
Tu propia empresa como emisor
Si facturas en tu propio nombre, tu empresa se da de alta como un emisor más: POST /v1/clientes/crear con tu propio NIF y los mismos campos que cualquier otro cliente. No hay endpoint aparte, y ese emisor ocupa una plaza de tu plan como los demás.
La respuesta lo marca con clientePropio: true. Ese campo se deriva de comparar el NIF del alta con el de tu cuenta, así que no puedes pedirlo ni quitarlo.
Dos respuestas son distintas de las del alta de un tercero, porque tu propio NIF ya lo conoces y no hay nada que averiguar:
| Situación | Con tu propio NIF | Con el NIF de un tercero |
|---|---|---|
| El emisor ya existe bajo tu cuenta | 200 con la ficha existente (repetición idempotente) |
400 VALIDATION_ERROR, genérico |
| El NIF está registrado en otra cuenta | 409 SELF_CLIENT_CONFLICT |
400 VALIDATION_ERROR, genérico |
El alta de un tercero no distingue entre esos dos casos a propósito. Si lo hiciera, bastaría con recorrer NIFs para saber cuáles están registrados en VeriBai y bajo qué cuenta. Con tu propio NIF no se cruza esa frontera, y por eso la respuesta puede ser explícita.
El 200 depende de que la ficha sea tuya, no solo de que el NIF coincida: una ficha con tu NIF que pertenezca a otra cuenta nunca se devuelve.
Confirma la hacienda antes de enviar el alta. Es inmutable, y tu propio emisor tampoco puede desactivarse ni eliminarse (409 SELF_CLIENT_PROTECTED): solo se factura para un emisor activo, así que desactivar el tuyo te dejaría sin poder emitir para ti mismo. Entre ambas reglas, una hacienda equivocada no se corrige por API.
representante sigue siendo obligatorio para tipoUsuario: empresa, también en el alta de tu propia empresa: nombre, NIF y dirección completa. Son los datos que necesita el documento de representación en el paso siguiente. Un autónomo que se da de alta a sí mismo no lo lleva.
Listado y plazas del plan
GET /v1/clientes devuelve tus emisores paginados y, con ellos, el consumo del plan en el entorno de la clave.
curl https://manage-api.veribai.com/v1/clientes \
-H "x-api-key: TU_CLAVE"
import veribai
client = veribai.Client(api_key="TU_CLAVE", environment="test")
respuesta = client.clientes.listar()
print(respuesta["clientesUsados"], respuesta["limitePlan"])
const respuesta = await fetch("https://manage-api.veribai.com/v1/clientes", {
method: "GET",
headers: { "x-api-key": "TU_CLAVE" },
});
if (!respuesta.ok) throw new Error(`VeriBai ${respuesta.status}`);
const datos = await respuesta.json();
| Parámetro | Notas |
|---|---|
limite |
Opcional, por defecto 20, máximo 100. Un valor mayor se recorta al máximo, y uno inválido cae al valor por defecto. Este listado no rechaza el parámetro. |
cursor |
Opcional. El proximaPagina de la página anterior. Ver Paginación. |
incluirEliminados |
Opcional. true añade los emisores eliminados que siguen dentro de su ventana de restauración de 30 días. |
{
"entorno": "test",
"clientes": [
{
"nif": "B98765432",
"nombre": "Restaurant La Buena Mesa SL",
"hacienda": "verifactu",
"estado": "activo",
"estadoRepresentacion": "pendiente",
"estadoCenso": "identificado"
}
],
"total": 1,
"limitePlan": 25,
"clientesUsados": 18,
"clientesDisponibles": 7
}
| Campo | Notas |
|---|---|
entorno |
test o live, derivado de la clave. |
total |
Emisores en esta página, no en tu cuenta. |
limitePlan |
El límite de emisores de tu cuenta en este entorno. |
clientesUsados |
Emisores activos que cuentan contra ese límite. Un cliente inactivo o eliminado no cuenta. |
clientesDisponibles |
limitePlan - clientesUsados, con suelo en 0. Una bajada de plan puede dejar la cuenta por encima del límite nuevo. |
estadoCenso |
Veredicto del censo AEAT, en una palabra. Es la versión compacta de validacionCensal, que solo viaja en el detalle y en el alta. |
fechaEliminacion |
Solo en las filas eliminadas, con ?incluirEliminados=true. |
proximaPagina |
Cursor de la página siguiente. Ausente en la última página. |
total cuenta filas de la página, no emisores de la cuenta: con limite=20 y 137 emisores dados de alta, total es 20. Para saber cuántos emisores tiene la cuenta, el campo es clientesUsados.
Ausente no es cero
limitePlan, clientesUsados y clientesDisponibles aparecen los tres o ninguno. Se omiten cuando la cuenta no tiene límite configurado, y también si su lectura falla. El listado sigue respondiendo 200 con clientes intacto, porque un contador no debe tumbar el listado.
Su ausencia significa «límite desconocido», nunca «cero». Leer un campo ausente como 0 le diría a tu cliente que no le quedan plazas cuando puede tenerlas todas: si los tres campos no vienen, oculta la interfaz de plazas en lugar de bloquear el alta. La comprobación que manda es la del alta, y es atómica.
No deduzcas el límite a partir del plan contratado. El límite se ajusta por cuenta, y hay cuentas cuyo valor no coincide con ninguna tabla de planes. Este endpoint es la única fuente fiable.
Los límites y los contadores son por entorno: TEST y LIVE llevan cada uno el suyo. Los tres campos describen el entorno que resolvió la petición (el de la clave, el que devuelve entorno), no la cuenta en conjunto.
clientesUsados no se reconstruye contando filas. Cuenta emisores activos, y se mueve con el estado de cada uno:
| Acción | Efecto en clientesUsados |
|---|---|
| Alta de un emisor | +1, incluido el de tu propia empresa |
Desactivar (PATCH …/estado → inactivo) |
−1 |
Reactivar (PATCH …/estado → activo) |
+1, y puede responder 409 CLIENT_LIMIT_REACHED si la cuenta está llena |
| Eliminar desde el panel | −1, solo si el emisor estaba activo |
| Restaurar desde el panel | +1, y puede responder 409 CLIENT_LIMIT_REACHED |
De ahí dos consecuencias que conviene prever: clientesUsados queda por debajo del número de filas de clientes en cuanto la cuenta tiene emisores inactivos, y reactivar o restaurar un emisor que ya existe puede rechazarse: la plaza tiene que estar libre otra vez.
Una cuenta con limitePlan: 10, ocho emisores dados de alta y uno de ellos inactivo responde clientesUsados: 7 y clientesDisponibles: 3. Contar las ocho filas diría «8 de 10» y escondería una de las tres plazas libres.
El emisor propio estuvo exento del límite hasta el 10 de septiembre de 2026. Si tu cuenta es anterior a esa fecha, su clientesUsados puede seguir una unidad por debajo de sus emisores activos.
Alta capacidad en las fichas
modoCadena y faseAltaCapacidad acompañan a cada emisor, tanto en las filas del listado como en el detalle. Son la vía para saber, sin llamar a otro endpoint, si un emisor tiene Alta capacidad contratada y en qué fase está.
Los dos campos se omiten si su lectura falla. Un campo ausente se lee como el valor por reposo: unica para modoCadena y no_contratada para faseAltaCapacidad. El listado nunca falla por ellos.
Ambos valores son de la cuenta del emisor, no del entorno: TEST y LIVE responden lo mismo.
Modificar y desactivar
PATCH /v1/clientes/{nif} acepta cambios parciales de nombre, direccion, representante, epigrafeIAE y portalHabilitado. Un cuerpo sin ninguno de esos cinco campos responde 400 VALIDATION_ERROR. hacienda no está entre ellos y se ignora: si un emisor cambia de hacienda, es un alta nueva.
portalHabilitado es estrictamente booleano: "true", 1 o "" se rechazan, porque el campo abre un acceso y una petición que pretende cerrarlo no puede leerse como que lo abre. Activarlo es solo la mitad del interruptor: el portal de clientes se habilita también por cuenta, y esa mitad la activa el equipo de VeriBai.
Mientras hay una firma de representación en curso, los campos que alimentan el documento (nombre, direccion, representante) quedan bloqueados: la respuesta es 409 SIGNING_IN_PROGRESS con camposBloqueados. Cancela la firma con DELETE …/representacion/firma-en-curso o complétala, y vuelve a intentarlo.
PATCH /v1/clientes/{nif}/estado alterna entre activo e inactivo con cuerpo { "estado": "inactivo" }. Un cliente inactivo no consume plaza de tu plan: al desactivarlo sale de clientesUsados, y volver a activarlo vuelve a pasar por el límite. Si la cuenta está llena, la reactivación responde 409 CLIENT_LIMIT_REACHED.
La respuesta lleva message, nif, estado y entorno. Cuando el emisor ya estaba en el estado pedido, la llamada sigue siendo un 200 correcto pero entorno no viene: no leas ese campo sin comprobarlo.
Un emisor eliminado no se reactiva por aquí: responde 409 CLIENT_DELETED. Se restaura desde el panel, que es lo único que comprueba la ventana de 30 días.
Errores
| Código | Cuándo |
|---|---|
400 VALIDATION_ERROR |
Formato de NIF inválido, campos obligatorios ausentes o epigrafeIAE fuera del catálogo. |
409 CLIENT_LIMIT_REACHED |
El alta (o la reactivación de un cliente inactivo) superaría el límite de clientes de tu plan. |
409 SELF_CLIENT_CONFLICT |
El alta lleva tu propio NIF y ya está registrado en otra cuenta. Escríbenos. |
409 SELF_CLIENT_PROTECTED |
El emisor que es tu propia empresa no puede desactivarse ni eliminarse. |
409 SIGNING_IN_PROGRESS |
Hay una firma de representación en curso y el PATCH toca los datos del documento. camposBloqueados los enumera. |
409 CLIENT_DELETED |
PATCH …/estado sobre un emisor eliminado. Restáuralo desde el panel. |
409 CLIENT_STATUS_INVALID |
El estado guardado del emisor no admite el cambio. Escríbenos. |
404 NOT_FOUND |
El NIF no existe bajo tu cuenta. |