Paginación, errores y sondeo
Los sobres de paginación, el contrato uniforme de errores y cómo sondear los cambios sin webhooks
Esta página cubre los contratos transversales compartidos por todos los endpoints: la paginación, la forma uniforme de los errores y — dado que la API no tiene webhooks — cómo sondear los cambios.
Paginación por offset
Casi todos los endpoints de listado usan el mismo contrato de paginación por offset, con dos parámetros de consulta:
| Parámetro | Tipo | Por defecto | Comportamiento |
|---|---|---|---|
limit | integer | 50 | Tamaño de página. Acotado al rango [1, 200] — un valor inferior a 1 se convierte en 1, un valor superior a 200 se convierte en 200. |
offset | integer | 0 | Número de elementos a omitir. Con mínimo de 0 — un valor negativo se convierte en 0. |
Los valores fuera de rango se acotan silenciosamente, nunca se rechazan. ?limit=10000 no devuelve un 400 — se trata como limit=200. El techo máximo es siempre de 200 elementos por llamada.
Una respuesta de listado paginada tiene esta forma:
{
"data": [ ],
"pagination": {
"limit": 50,
"offset": 0,
"total": 342
}
}data— la página de elementos.pagination.limit/pagination.offset— devuelven los valores efectivos (tras el acotado) que se aplicaron.pagination.total— el número total de elementos que coinciden con la consulta, en todas las páginas. Úselo para saber cuándo dejar de paginar.
Para recorrer todas las páginas, incremente offset en limit en cada llamada hasta que offset >= total:
async function listAll(path, apiKey, limit = 200) {
const items = [];
let offset = 0;
let total = Infinity;
while (offset < total) {
const url = new URL(`https://app.kabeen.io/public/v1${path}`);
url.searchParams.set("limit", limit);
url.searchParams.set("offset", offset);
const res = await fetch(url, { headers: { Authorization: `Bearer ${apiKey}` } });
const body = await res.json();
items.push(...body.data);
total = body.pagination.total;
offset += limit;
}
return items;
}Excepción: catálogo de aplicaciones — paginación por número de página
GET /application-catalog busca en el catálogo de referencia global, independiente del espacio de trabajo, que pagina por número de página en lugar de offset/limit:
{
"data": [ ],
"total": 1204,
"page": 1
}Solicite la página siguiente con ?page=2, ?page=3, etc. (base 1, por defecto 1). No hay parámetro de consulta limit para este endpoint — el tamaño de página lo fija el servicio de catálogo. Deténgase cuando data vuelva vacío.
Excepción: registro de auditoría — paginación por cursor
GET /audit-log es de solo anexado y de gran volumen, por lo que usa paginación por keyset (cursor) en lugar de offset, del más reciente al más antiguo:
{
"data": [ ],
"total": 58213,
"nextCursor": "2026-07-21T09:12:03.441Z,3f9c...",
"hasMore": true
}nextCursores un token opaco: trátelo como una caja negra, no lo analice ni lo construya usted mismo.- Devuélvalo como
?cursor=...para obtener la página siguiente. hasMorele indica si debe continuar;nextCursorsolo tiene sentido mientrashasMoreseatrue(es anulable y está ausente en la última página).totalsigue siendo el recuento global que coincide con sus filtros, pero el bucle se dirige conhasMore/nextCursor, no comparando un offset contotal.
Excepción: listados de sub-recursos — arrays sin sobre
Algunos endpoints de sub-recursos devuelven un array JSON plano sin sobre alguno: sin envoltorio data, sin pagination. Representan colecciones pequeñas y acotadas asociadas a un único recurso padre. Ejemplos: GET /applications/{id}/owners, GET /applications/{id}/tags, GET /applications/{id}/teams, GET /applications/{id}/comments, GET /servers/{id}/owners, GET /servers/{id}/tags, GET /networks/{id}/routers, GET /routers/{id}/networks, GET /routers/{id}/tags. Una llamada a uno de ellos devuelve [ ... ] directamente.
Gestión de errores
Toda respuesta de error generada por el propio tratamiento de peticiones de la API — cada 400, 403, 404, 409, 422 y 5xx, en todos los endpoints — tiene la misma forma JSON:
{
"code": "not_found",
"message": "server not found",
"status": 404
}| Campo | Tipo | Descripción |
|---|---|---|
code | string | Identificador de error estable y legible por máquina (p. ej. not_found, insufficient_permission). Seguro para ramificar en código. |
message | string | Explicación legible por humanos. Pensada para logs y depuración, no para sentencias switch. |
status | integer | El código de estado HTTP, reflejado en el cuerpo. |
Códigos de estado
| Estado | Significado | Cuándo ocurre |
|---|---|---|
400 Bad Request | Entrada malformada | Un valor de ruta o de consulta no se puede analizar: un UUID inanalizable, un valor de enum/filtro desconocido, una fecha-hora ISO-8601 inválida o un campo obligatorio ausente o vacío. |
401 Unauthorized | Credenciales ausentes o inválidas | No hay cabecera Authorization/X-Api-Key, o la clave no se reconoce, está revocada o ha expirado. Caso especial: vea más abajo. |
403 Forbidden | No permitido | La clave no lleva el scope de permiso que el endpoint requiere (insufficient_permission), o la clave no está vinculada a ningún espacio de trabajo (forbidden). |
404 Not Found | El recurso no existe en su espacio de trabajo | El id no existe, o pertenece a otro espacio de trabajo. La API nunca distingue ambos casos — los dos devuelven el mismo 404, por lo que una clave no puede usar las respuestas de error para enumerar recursos que no posee. |
409 Conflict | La petición entra en conflicto con el estado actual | Reservado por el contrato de errores; ningún endpoint lo produce actualmente. |
422 Unprocessable Entity | Bien formada pero semánticamente inválida | La petición se analiza correctamente pero viola una regla de negocio — p. ej. intentar cambiar name/os/manufacturer en un servidor reportado por agente (automático). |
5xx | Error inesperado del servidor | No forma parte del flujo de control normal — trátelo como transitorio y reintente con backoff. |
El caso especial del 401
Todos los estados anteriores vuelven con el cuerpo JSON uniforme — excepto el 401. La autenticación se aplica antes de que la petición llegue al propio tratamiento de errores de la API, por lo que un 401 real vuelve como:
- Estado
401, cuerpo vacío (sin JSON — no intente extraercode/message/statusde él). - Una cabecera de respuesta
WWW-Authenticate: Bearer realm="kbine", error="invalid_token".
Si su cliente espera siempre un cuerpo de error JSON, trate el 401 como caso especial (o simplemente ramifique solo por el estado).
400 y 422 se solapan en espíritu pero no en intención: 400 significa que la API ni siquiera pudo analizar lo que envió; 422 significa que se analizó bien pero el significado es inválido dado el estado actual del recurso.
Valores conocidos de code
Podrán añadirse nuevos códigos en futuras versiones — tenga siempre un caso por defecto.
code | status típico | Notas |
|---|---|---|
bad_request | 400 | Valor por defecto genérico para entrada malformada. |
invalid_uuid | 400 | Un valor de ruta o de consulta que debía ser un UUID no pudo analizarse. |
invalid_datetime | 400 | Un valor de fecha-hora no es ISO-8601 válido. |
unauthorized | 401 | Definido por el contrato, pero inalcanzable en la práctica — los fallos reales de autenticación no llevan cuerpo JSON. |
forbidden | 403 | La clave no está vinculada a ningún espacio de trabajo (u otro fallo genérico de autorización). |
insufficient_permission | 403 | La clave está vinculada a un espacio de trabajo pero carece del scope requerido. |
not_found | 404 | El recurso no existe en el espacio de trabajo de la clave. |
conflict | 409 | Reservado — actualmente ningún endpoint lo emite. |
unprocessable_entity | 422 | La petición está bien formada pero es semánticamente inválida para el recurso de destino. |
Buenas prácticas: compruebe primero el estado HTTP, ramifique por code (nunca por message), registre message para el diagnóstico y recurra a status para cualquier código que no reconozca.
Sin webhooks — sondee en su lugar
La API no ofrece webhooks, suscripciones a eventos ni ningún mecanismo de push/callback en esta versión. Es únicamente petición/respuesta (pull). Dos enfoques complementarios cubren casi todos los casos de uso de "detectar un cambio":
1. Sondear el registro de auditoría (lo más parecido a un flujo de cambios)
GET /public/v1/audit-log (scope audit_log:read) es la mejor aproximación a un flujo de eventos que la API tiene hoy. Devuelve los eventos de auditoría del espacio de trabajo, del más reciente al más antiguo, y admite filtrado por rango de fechas (from/to), por actor (actorId), por categoría/acción (category, actions repetible), por recurso (resourceType, resourceId), búsqueda de texto libre (query) y paginación por cursor (limit máx. 200 + cursor).
Sondéelo a intervalos, avanzando con nextCursor mientras hasMore sea true, y persista el último cursor (o el timestamp del último evento procesado) entre ejecuciones:
cursor = load_last_cursor()
loop:
page = GET /audit-log?limit=200&cursor={cursor}
for event in page.data:
handle(event)
if page.nextCursor:
cursor = page.nextCursor
save_cursor(cursor)
if not page.hasMore:
sleep(poll_interval)2. Comparar marcas de tiempo en los recursos
La mayoría de los recursos llevan una marca de tiempo que puede comparar con un sondeo anterior:
| Recurso | Campo |
|---|---|
Aplicaciones (GET /applications/{id}) | updatedAt |
Objetos de datos (GET /data, GET /data/{id}) | updatedAt |
Contratos (GET /contracts) | updatedAt |
Equipos (GET /teams, GET /teams/{id}) | updatedAt |
Anuncios (GET /announcements) | updatedAt |
Aplicaciones descubiertas (GET /discovered-applications) | lastUpdate |
Servidores (GET /servers, GET /servers/{id}) | lastCheckTime |
Es más grueso que el registro de auditoría — le dice que algo cambió, no qué ni quién — pero es simple y funciona incluso para recursos sin categoría de auditoría dedicada.
Sondeo eficiente
- Respete el tope de tamaño de página (200) y solicite la página más grande que su intervalo pueda procesar cómodamente.
- Elija un intervalo razonable. Un intervalo de 1 a 5 minutos es un valor por defecto sensato para la mayoría de las integraciones.
- Persista siempre su cursor/marca de tiempo antes de actuar sobre la página, para que un fallo o un reinicio reanude en lugar de reprocesar u omitir.
- Prefiera el registro de auditoría cuando necesite saber "qué cambió": conoce el actor, la acción y el recurso.