Dispositivos (Alta capacidad)
El registro de dispositivos de los emisores de gran volumen, donde cada dispositivo lleva su propia cadena de encadenado.
Un emisor factura con una sola cadena de encadenado: cada registro encadena con el anterior, en orden. La cadena se escribe en serie, así que limita el ritmo de emisión.
Alta capacidad reparte esa cadena. Cada dispositivo registrado lleva la suya y emite en paralelo con los demás. Es un modo contratado, se decide por emisor y lo habilita el equipo de VeriBai. Si emites desde decenas de TPV con el mismo NIF, habla con el equipo.
Un dispositivo (idMaquina) es un emisor lógico independiente: una caja, el hub de una tienda, un proceso de tu backend. No tiene que ser hardware. Es la clave de su propia cadena, así que un identificador ni se renombra ni se reutiliza.
Estos endpoints pertenecen a la API de gestión: base URL
https://manage-api.veribai.com, misma clavex-api-key. El entorno (Sandbox o LIVE) se detecta automáticamente a partir de la clave.
Modo y fase
Dos campos describen el estado de un emisor. Viajan en la respuesta de estos endpoints y también en el listado y el detalle de clientes:
| Campo | Valores | Qué indica |
|---|---|---|
modoCadena |
unica, por_dispositivo |
Cómo encadena la emisión hoy. Es el valor que se aplica al procesar una factura. |
faseAltaCapacidad |
no_contratada, preparacion, activa |
La fase del contrato. La fija el equipo de VeriBai; no hay endpoint de cliente para cambiarla. |
Los dos valores son compartidos entre Sandbox y LIVE: un emisor no puede estar en por_dispositivo en un entorno y en unica en el otro. El registro de dispositivos, en cambio, es por entorno: lo que des de alta en Sandbox no existe en LIVE hasta que lo copies.
| Fase | Emisión | Qué puedes hacer |
|---|---|---|
no_contratada |
Cadena única. idMaquina es solo metadato. |
Leer. Las escrituras responden 409 SHARDING_NOT_ENABLED. |
preparacion |
Cadena única, sin cambios. El idMaquina de una factura se ignora para el encaminamiento, pero se anota. |
Registrar y dar de baja dispositivos, reservar y liberar series del envío centralizado, y avisar de que estás listo. Nada de esto afecta todavía a tus facturas. |
activa |
Una cadena por dispositivo. idMaquina encamina. |
Registrar y dar de baja dispositivos, reservar series. Liberar una reserva ya no. |
Las escrituras dependen de la fase, no del modo. La fase preparacion permite registrar dispositivos y reservar series antes de que el encaminamiento cambie.
Endpoints
| Método | Endpoint | Descripción |
|---|---|---|
GET |
/v1/clientes/{nif}/dispositivos |
Dispositivos, series reclamadas y estado de Alta capacidad del emisor. |
POST |
/v1/clientes/{nif}/dispositivos |
Registra un dispositivo. |
DELETE |
/v1/clientes/{nif}/dispositivos/{idMaquina} |
Da de baja un dispositivo. |
POST |
/v1/clientes/{nif}/dispositivos/promover |
Copia a LIVE los dispositivos de Sandbox. |
POST |
/v1/clientes/{nif}/dispositivos/solicitar-activacion |
Avisa de que la preparación está hecha. Solo en preparacion. |
POST |
/v1/clientes/{nif}/dispositivos/series-centralizadas |
Reserva series para el envío centralizado. |
DELETE |
/v1/clientes/{nif}/dispositivos/series-centralizadas/{serie} |
Libera una reserva. Solo en preparacion. |
Listar los dispositivos
curl https://manage-api.veribai.com/v1/clientes/B00000000/dispositivos \
-H "x-api-key: TU_CLAVE"
import veribai
client = veribai.Client(api_key="TU_CLAVE", environment="test")
respuesta = client.dispositivos.listar("B00000000")
const respuesta = await fetch("https://manage-api.veribai.com/v1/clientes/B00000000/dispositivos", {
method: "GET",
headers: { "x-api-key": "TU_CLAVE" },
});
if (!respuesta.ok) throw new Error(`VeriBai ${respuesta.status}`);
const datos = await respuesta.json();
{
"entorno": "test",
"nif": "B00000000",
"modoCadena": "por_dispositivo",
"faseAltaCapacidad": "activa",
"dispositivos": [
{
"idMaquina": "CAJA-BILBAO-01",
"etiqueta": "Caja 1 · Bilbao",
"creadoEn": "2026-09-01T10:00:00Z",
"activo": true,
"numeroInstalacion": "20260901T101500Z_B00000000_CAJA-BILBAO-01"
},
{
"idMaquina": "CAJA-VIEJA",
"etiqueta": "",
"creadoEn": "2026-08-01T09:00:00Z",
"activo": false,
"dadoDeBajaEn": "2026-09-10T08:00:00Z"
}
],
"series": [
{ "serie": "A2026", "idMaquinaPropietaria": "CAJA-BILBAO-01", "reclamadaEn": "2026-09-01T11:00:00Z" },
{ "serie": "FC2026", "idMaquinaPropietaria": "__SHARD0__", "reclamadaEn": "2026-08-01T09:00:00Z" }
],
"idsMaquinaVistos": []
}
| Campo | Notas |
|---|---|
entorno |
test o live, derivado de la clave. |
dispositivos[].etiqueta |
Nombre legible que le diste al registrarlo. Cadena vacía si no le diste ninguno. |
dispositivos[].activo |
false es un dispositivo dado de baja. Los dados de baja también se listan: sus series y su cadena siguen existiendo. |
dispositivos[].numeroInstalacion |
Solo VeriFactu: la instalación que la AEAT asocia al dispositivo. Se asigna en su primera factura y no se incluye hasta entonces. |
dispositivos[].dadoDeBajaEn / reactivadoEn |
Presentes solo cuando aplican. |
series[] |
Las series ya reclamadas, con su dueño y la fecha de la reclamación. |
series[].idMaquinaPropietaria |
El dispositivo dueño de la serie, o el literal __SHARD0__: el envío centralizado. |
idsMaquinaVistos |
Los idMaquina que tu integración envió durante la ventana de preparación. |
observandoDesde |
Desde cuándo se anotan esos idMaquina. Solo en fase preparacion. |
activacionSolicitadaEn |
Cuándo avisaste de que estabas listo. Ausente hasta que lo hagas. |
Responde 200 en todos los casos. Un emisor sin Alta capacidad devuelve faseAltaCapacidad: "no_contratada" con las listas vacías.
Los campos sin valor se omiten; no llegan como null ni como cadena vacía. Un dispositivo sin numeroInstalacion todavía no ha facturado.
Registrar un dispositivo
curl -X POST https://manage-api.veribai.com/v1/clientes/B00000000/dispositivos \
-H "x-api-key: TU_CLAVE" \
-H "Content-Type: application/json" \
-d '{
"idMaquina": "CAJA-BILBAO-02",
"etiqueta": "Caja 2"
}'
import veribai
client = veribai.Client(api_key="TU_CLAVE", environment="test")
respuesta = client.dispositivos.registrar(
"B00000000",
"CAJA-BILBAO-02",
etiqueta="Caja 2",
)
const respuesta = await fetch("https://manage-api.veribai.com/v1/clientes/B00000000/dispositivos", {
method: "POST",
headers: {
"x-api-key": "TU_CLAVE",
"Content-Type": "application/json",
},
body: JSON.stringify({
"idMaquina": "CAJA-BILBAO-02",
"etiqueta": "Caja 2"
}),
});
if (!respuesta.ok) throw new Error(`VeriBai ${respuesta.status}`);
const datos = await respuesta.json();
{
"message": "Dispositivo registrado",
"entorno": "test",
"dispositivo": {
"idMaquina": "CAJA-BILBAO-02",
"etiqueta": "Caja 2",
"creadoEn": "2026-09-15T09:00:00Z",
"activo": true
}
}
| Campo | Notas |
|---|---|
idMaquina |
Obligatorio. Máximo 64 caracteres, A-Za-z0-9._-. Es la misma regla que en los endpoints de emisión. |
etiqueta |
Opcional, máximo 120 caracteres. Solo para que lo reconozcas en el listado. |
201 Created con el dispositivo en el sobre { "message", "entorno", "dispositivo" }.
Dos respuestas posibles:
- Un identificador activo que ya existe responde
409 MACHINE_ALREADY_REGISTERED. - Un identificador dado de baja se reactiva:
200conreactivado: true. Conserva su instalación y su cadena, que continúa desde el último registro.
Un identificador no se reutiliza para otro dispositivo. Si sustituyes el dispositivo físico, registra un identificador nuevo con una serie nueva. La cadena del antiguo se conserva y sus series siguen asignadas a él.
Dar de baja un dispositivo
curl -X DELETE https://manage-api.veribai.com/v1/clientes/B00000000/dispositivos/CAJA-VIEJA \
-H "x-api-key: TU_CLAVE"
import veribai
client = veribai.Client(api_key="TU_CLAVE", environment="test")
respuesta = client.dispositivos.dar_de_baja("B00000000", "CAJA-VIEJA")
const respuesta = await fetch("https://manage-api.veribai.com/v1/clientes/B00000000/dispositivos/CAJA-VIEJA", {
method: "DELETE",
headers: { "x-api-key": "TU_CLAVE" },
});
if (!respuesta.ok) throw new Error(`VeriBai ${respuesta.status}`);
const datos = await respuesta.json();
{
"message": "Dispositivo dado de baja",
"entorno": "test",
"idMaquina": "CAJA-VIEJA",
"dadoDeBajaEn": "2026-09-10T08:00:00Z"
}
La baja marca el dispositivo; no borra nada. El historial firmado, las series reclamadas y la instalación ante la AEAT se conservan. Las facturas nuevas dejan de encaminarse a ese dispositivo: con el emisor en fase activa, una factura con ese idMaquina responde 409 MACHINE_NOT_REGISTERED.
La llamada es idempotente: repetirla responde 200 conservando el dadoDeBajaEn original. Un identificador que no existe en ese entorno responde 404 MACHINE_NOT_FOUND.
Las series se reclaman una sola vez
La regla que condiciona la preparación:
En fase activa, la primera factura emitida sobre una serie asigna esa serie a su escritor de forma permanente. La asignación no se transfiere, no se libera y no se reasigna. Las anulaciones no reclaman series.
Hay dos clases de escritor:
- Un dispositivo, identificado por su
idMaquina. - El envío centralizado: las facturas que se envían sin
idMaquina. Son válidas en cualquier modo y viajan por la cadena original del emisor. En el listado de series aparece como__SHARD0__.
Dos consecuencias:
- Asigna series nuevas a los dispositivos. Un dispositivo no reserva: reclama la serie con su primera factura. Si la serie ya pertenece a otro escritor, la factura responde
409 SERIE_OWNED_BY_OTHER_MACHINE; emite con otra serie. - Reserva las series del envío centralizado antes de la activación. Las reclamaciones empiezan con la activación, así que las series que el envío central ya usa están sin reclamar ese día. Si un dispositivo emite antes sobre una de ellas, la serie pasa a ser suya y las facturas del envío central sobre esa serie se rechazan.
Reservar las series del envío centralizado
Es la única reclamación previa que existe: asigna cada serie a __SHARD0__ antes de que un dispositivo pueda reclamarla.
curl -X POST https://manage-api.veribai.com/v1/clientes/B00000000/dispositivos/series-centralizadas \
-H "x-api-key: TU_CLAVE" \
-H "Content-Type: application/json" \
-d '{
"series": [
"A2026",
"FC"
]
}'
import veribai
client = veribai.Client(api_key="TU_CLAVE", environment="test")
respuesta = client.dispositivos.reservar_series_centralizadas(
"B00000000",
["A2026", "FC"],
)
const respuesta = await fetch("https://manage-api.veribai.com/v1/clientes/B00000000/dispositivos/series-centralizadas", {
method: "POST",
headers: {
"x-api-key": "TU_CLAVE",
"Content-Type": "application/json",
},
body: JSON.stringify({
"series": [
"A2026",
"FC"
]
}),
});
if (!respuesta.ok) throw new Error(`VeriBai ${respuesta.status}`);
const datos = await respuesta.json();
{
"message": "Series reservadas para el envío centralizado",
"entorno": "test",
"reservadas": ["A2026"],
"omitidas": [ { "serie": "FC", "motivo": "propiedad_de_dispositivo", "idMaquina": "CAJA-BILBAO-01" } ]
}
De 1 a 100 series por llamada, cada una de hasta 60 caracteres ASCII imprimibles y sin espacio inicial. La serie vacía no se puede reservar: la reclama su primer uso.
La llamada es idempotente. reservadas incluye tanto las que se reservan ahora como las que ya eran del envío centralizado. omitidas lista las que no se tocaron, con un único motivo posible, propiedad_de_dispositivo: un dispositivo ya la tiene, y la reserva no sobrescribe al propietario. Se admite en preparacion y en activa.
Reserva las series que el envío central usa y va a seguir usando. Una serie sin reservar puede reclamarla un dispositivo después de la activación, y la reserva ya no la recupera.
Liberar una reserva
curl -X DELETE https://manage-api.veribai.com/v1/clientes/B00000000/dispositivos/series-centralizadas/A%2F2026 \
-H "x-api-key: TU_CLAVE"
import veribai
client = veribai.Client(api_key="TU_CLAVE", environment="test")
respuesta = client.dispositivos.liberar_serie_centralizada(
"B00000000",
"A/2026",
)
const respuesta = await fetch("https://manage-api.veribai.com/v1/clientes/B00000000/dispositivos/series-centralizadas/A%2F2026", {
method: "DELETE",
headers: { "x-api-key": "TU_CLAVE" },
});
if (!respuesta.ok) throw new Error(`VeriBai ${respuesta.status}`);
const datos = await respuesta.json();
{
"message": "Reserva liberada",
"entorno": "test",
"serie": "A/2026"
}
Solo en fase preparacion. En fase activa responde 409 RESERVATION_LOCKED, de forma permanente: el envío central puede haber encadenado ya sobre esa serie.
La serie viaja en la ruta, así que hay que codificarla (A/2026 se escribe A%2F2026). Una serie sin reserva responde 404 SERIE_NOT_RESERVED; una que pertenece a un dispositivo, 409 SERIE_OWNED_BY_OTHER_MACHINE.
Promover de Sandbox a LIVE
Los registros de dispositivos son por entorno. Este endpoint copia a LIVE los dispositivos registrados en Sandbox:
curl -X POST https://manage-api.veribai.com/v1/clientes/B00000000/dispositivos/promover \
-H "x-api-key: TU_CLAVE" \
-H "Content-Type: application/json" \
-d '{
"idsMaquina": [
"CAJA-BILBAO-01",
"CAJA-BILBAO-02"
]
}'
import veribai
client = veribai.Client(api_key="TU_CLAVE", environment="test")
respuesta = client.dispositivos.promover(
"B00000000",
["CAJA-BILBAO-01", "CAJA-BILBAO-02"],
)
const respuesta = await fetch("https://manage-api.veribai.com/v1/clientes/B00000000/dispositivos/promover", {
method: "POST",
headers: {
"x-api-key": "TU_CLAVE",
"Content-Type": "application/json",
},
body: JSON.stringify({
"idsMaquina": [
"CAJA-BILBAO-01",
"CAJA-BILBAO-02"
]
}),
});
if (!respuesta.ok) throw new Error(`VeriBai ${respuesta.status}`);
const datos = await respuesta.json();
{
"message": "Dispositivos copiados a LIVE",
"origen": "test",
"entorno": "live",
"creados": ["CAJA-BILBAO-01"],
"omitidos": [
{ "idMaquina": "CAJA-VIEJA", "motivo": "dado_de_baja" },
{ "idMaquina": "CAJA-BILBAO-02", "motivo": "ya_registrado" }
]
}
El cuerpo es opcional: sin idsMaquina se copian todos los dispositivos activos de Sandbox. Funciona con la clave de cualquiera de los dos entornos, y es idempotente.
Se copian el identificador y la etiqueta. El numeroInstalacion y las series son identidades propias de cada entorno: LIVE asigna la instalación en la primera factura del dispositivo y las series se reclaman con facturas de LIVE. El emisor tiene que existir ya en LIVE, o la respuesta es 409 CLIENT_NOT_IN_LIVE.
omitidos[].motivo toma tres valores: no_existe (no es un dispositivo de Sandbox), dado_de_baja (está de baja en Sandbox) y ya_registrado (ya está en LIVE). Un dispositivo dado de baja en LIVE también se informa como ya_registrado, y la promoción no lo reactiva.
La ventana de preparación
Mientras el emisor está en preparacion, el idMaquina de una factura se sigue ignorando para el encaminamiento, pero queda anotado. GET …/dispositivos los devuelve en idsMaquinaVistos:
{
"faseAltaCapacidad": "preparacion",
"observandoDesde": "2026-09-14T08:00:00Z",
"idsMaquinaVistos": [
{ "idMaquina": "MAIN", "primeraVez": "2026-09-14T09:12:00Z", "ultimaVez": "2026-09-15T07:40:00Z", "registrado": false }
]
}
Si tu integración ya envía un idMaquina sin registrar, a partir de la activación cada factura con ese identificador responde 409 MACHINE_NOT_REGISTERED. La lista muestra cuáles son. Regístralos con el mismo identificador o retira el campo del envío.
Una lista vacía no confirma que no envías idMaquina. Las anotaciones empiezan en observandoDesde, el instante en que se abrió la ventana, y pueden tardar en aparecer. Lee la lista junto con observandoDesde. Fuera de la fase preparacion, observandoDesde no se incluye.
Avisar de que estás listo
La activación la hace el equipo de VeriBai. Cuando hayas registrado los dispositivos y reservado las series, envía el aviso con este endpoint. Queda registrado como uno de los requisitos que el equipo comprueba antes de activar.
curl -X POST https://manage-api.veribai.com/v1/clientes/B00000000/dispositivos/solicitar-activacion \
-H "x-api-key: TU_CLAVE"
import veribai
client = veribai.Client(api_key="TU_CLAVE", environment="test")
respuesta = client.dispositivos.solicitar_activacion("B00000000")
const respuesta = await fetch("https://manage-api.veribai.com/v1/clientes/B00000000/dispositivos/solicitar-activacion", {
method: "POST",
headers: { "x-api-key": "TU_CLAVE" },
});
if (!respuesta.ok) throw new Error(`VeriBai ${respuesta.status}`);
const datos = await respuesta.json();
{
"message": "Aviso enviado. Revisaremos los requisitos y activaremos Alta capacidad.",
"entorno": "test",
"solicitadoEn": "2026-09-15T09:30:00Z"
}
Sin cuerpo. Solo en fase preparacion: fuera de ella responde 409 NOT_IN_PREPARATION. Es idempotente: las repeticiones no modifican la solicitud original. A partir de entonces GET …/dispositivos incluye activacionSolicitadaEn.
El aviso no activa nada por sí mismo. La fase sigue en preparacion hasta que el equipo la pasa a activa.
Antes de la activación
- Elige identificadores definitivos. Un
idMaquinaes la clave de una cadena: no se renombra. - Reserva las series del envío centralizado y asigna series nuevas a los dispositivos.
- Registra o retira los
idMaquinaque ya envías, según lo que muestreidsMaquinaVistos. - Avisa cuando esté todo, con
solicitar-activacion. Es uno de los requisitos que el equipo comprueba antes de activar. - TicketBAI: el documento firmado declara el dispositivo con los últimos 30 caracteres del
idMaquina(el largo que fija la especificación). Dos dispositivos que compartan esos 30 caracteres finales se declaran igual ante la hacienda foral. Mantén los identificadores de TicketBAI en 30 caracteres o distínguelos por el final. - Araba marca la primera factura de cada dispositivo nuevo con el aviso
009 Posible error de encadenamiento. La factura se acepta y el aviso no requiere ninguna acción. Bizkaia y Gipuzkoa no lo emiten.
Errores al emitir en fase activa
Estos tres códigos aparecen en los endpoints de emisión, no aquí, y solo en cuentas con Alta capacidad activa:
| Código | Cuándo |
|---|---|
409 MACHINE_NOT_REGISTERED |
El idMaquina enviado no está registrado (o está de baja) para ese emisor en ese entorno. |
409 SERIE_OWNED_BY_OTHER_MACHINE |
La serie del alta pertenece de forma permanente a otro escritor. Usa otra serie para ese dispositivo. |
503 SHARDING_UNAVAILABLE |
La configuración no pudo comprobarse en ese momento. Reintentable: la petición no se encamina sin verificar la configuración. |
Errores de estos endpoints
| Código | Cuándo |
|---|---|
400 VALIDATION_ERROR |
idMaquina, etiqueta o series fuera de formato. La respuesta nombra el campo. |
403 FORBIDDEN |
El emisor no es de tu cuenta. |
404 NOT_FOUND |
El emisor no existe bajo tu cuenta en el entorno de la clave. |
404 MACHINE_NOT_FOUND |
La baja apunta a un identificador sin registro en ese entorno. |
404 SERIE_NOT_RESERVED |
La serie no tiene reserva del envío centralizado. |
409 MACHINE_ALREADY_REGISTERED |
Ya hay un dispositivo activo con ese identificador. |
409 SHARDING_NOT_ENABLED |
El emisor está en no_contratada: las escrituras no se admiten. Habla con el equipo. |
409 NOT_IN_PREPARATION |
solicitar-activacion fuera de la fase preparacion. |
409 CLIENT_NOT_IN_LIVE |
promover: el emisor todavía no existe en LIVE. |
409 RESERVATION_LOCKED |
La liberación de una reserva con el emisor ya en fase activa. Es definitivo. |
409 SERIE_OWNED_BY_OTHER_MACHINE |
La serie que intentas liberar pertenece a un dispositivo. |
500 DATABASE_ERROR |
Falló una lectura o escritura de datos. Reintentable. |
503 ENVIRONMENT_NOT_AVAILABLE |
El entorno LIVE no está disponible para esa operación. |