Zaroz
Tokens de API

Crea y usa un token de API con permisos granulares

Los tokens de API permiten que scripts, bots y tareas de CI llamen a la API de Zaroz Cloud sin guardar la contraseña de tu cuenta ni tu sesión del navegador. Todo token deniega por defecto: tú eliges los permisos exactos que tiene y, en un token personal, los recursos exactos que puede tocar.

La lista completa de rutas está en la referencia de la API. Las convenciones que comparten todas las llamadas (URL base, errores, paginación) están en Usar la API de Zaroz.

Token personal o de organización

Token personalToken de organización
Actúa comoTúLa organización
Alcance de cada permisoGlobal, un tipo de recurso, un recurso, o un recurso y sus descendientesToda la organización
Se revoca cuandoLo revocas o pierdes el accesoLo revocas; con membership_tied también cuando su creador se va
Se crea enPOST /me/api-tokensPOST /organizations/{id}/api-tokens (requiere Owner o Admin)
Ideal paraUn script, un servicio, mínimo privilegioEl CI de un equipo o un bot que debe sobrevivir a una persona

Un token nunca tiene más autoridad que la persona u organización que lo creó, y se comprueba de nuevo en cada petición: si pierdes un rol, tus tokens lo pierden también.

Antes de crear uno

Trata el token como una contraseña. Quien lo consiga puede hacer todo lo que permite hasta que caduque o lo revoques.

  • Crea un token distinto para cada integración.
  • Concede solo los permisos imprescindibles.
  • Prefiere un servicio concreto a todos (solo tokens personales).
  • Añade expires_at siempre que la integración pueda rotar el secreto.
  • Nunca pongas un token en JavaScript de frontend, una app móvil, un repositorio público o un mensaje de soporte.

Consulta qué puedes conceder

Solo puedes conceder lo que tú mismo tienes. Pregúntaselo a la API para no fijar una lista a mano:

curl

TerminalCode
curl -sS "$ZAROZ_API/me/api-token-capabilities" -H "$AUTH" # {"data":[{"capability":"services.read","admin_only":false}, ...]} # para un token de organización curl -sS "$ZAROZ_API/organizations/$ORG/api-tokens/grantable-capabilities" -H "$AUTH" # {"data":[{"capability":"services.read"}, ...], "max_lifetime_days": null}

TypeScript

TypeScriptCode
import { call } from "./zaroz"; const mine = await call("GET", "/me/api-token-capabilities"); console.log(mine.body.data.filter((c: any) => !c.admin_only).map((c: any) => c.capability));

Python

Code
from zaroz import call _, mine = call("GET", "/me/api-token-capabilities") print([c["capability"] for c in mine["data"] if not c["admin_only"]])

Los permisos que importan a la mayoría de clientes:

PermisoPermite
services.readToda lectura de un servicio que no sea secreta
services.powerIniciar, detener, reiniciar
services.deployFijar el origen y crear despliegues, nada más
services.manageCambiar el servicio (entorno, copias, archivos, cortafuegos...) y todo lo de services.deploy
services.consoleFlujo de logs, consola de comandos, SSH
services.files.readExplorar y descargar archivos
services.external_access.manageAbrir y cerrar puertos públicos
services.schedules.manageTareas programadas
services.databases.readExplorar bases de datos conectadas
organizations.read, projects.readLeer organizaciones y proyectos
organizations.members.manageAñadir, quitar y cambiar roles de miembros
organizations.manage, projects.manageRenombrar una organización; crear, renombrar y borrar proyectos
organizations.purchaseComprar una suscripción facturada a la organización
organizations.wallet.spend, organizations.wallet.top_upPagar facturas con el monedero; añadir fondos
subscriptions.renewal.manage, subscriptions.cancel, subscriptions.resize.manageActivar o desactivar la renovación; terminar una suscripción; redimensionar o cambiar de plan
organizations.api_tokens.manageCrear, listar y revocar los tokens de la organización
wallet.readLeer saldos y movimientos del monedero

No hay comodines: services.read no implica services.power.

Crea un token personal

Crearlo requiere una sesión iniciada (la consola o las rutas de login), no otro token de API. El secreto se devuelve una sola vez, con la forma zaroz_pat_<id>_<secreto>; solo se guarda un resumen.

Cada concesión es un permiso más un alcance:

kind del alcanceAlcanza
globalEl permiso allí donde lo tengas
resource_typeTodos los recursos de un tipo (resource_type)
resourceExactamente un recurso (resource_type, resource_id)
resource_treeUn recurso y lo que desciende de él (resource_type, root_resource_id)

curl

TerminalCode
curl -sS -X POST "$ZAROZ_API/me/api-tokens" -H "$AUTH" -H "Content-Type: application/json" -d '{ "name": "status-bot", "expires_at": "2027-01-01T00:00:00Z", "authorization": {"grants": [ {"capability": "services.read", "scope": {"kind": "resource", "resource_type": "service_instance", "resource_id": "<uuid de la instancia de servicio>"}} ]} }' # {"token": {"id": "...", "name": "status-bot", "capabilities": [...]}, "secret": "zaroz_pat_..."} se muestra una vez

TypeScript

TypeScriptCode
import { call } from "./zaroz"; const created = await call("POST", "/me/api-tokens", { name: "status-bot", expires_at: "2027-01-01T00:00:00Z", authorization: { grants: [ { capability: "services.read", scope: { kind: "resource", resource_type: "service_instance", resource_id: SERVICE }, }, ], }, }); console.log(created.body.secret); // guárdalo ahora; no se puede volver a mostrar

Python

Code
from zaroz import call _, created = call("POST", "/me/api-tokens", { "name": "status-bot", "expires_at": "2027-01-01T00:00:00Z", "authorization": {"grants": [{ "capability": "services.read", "scope": {"kind": "resource", "resource_type": "service_instance", "resource_id": SERVICE}, }]}, }) print(created["secret"]) # guárdalo ahora; no se puede volver a mostrar

Una lista grants vacía crea un token que se autentica y no puede hacer nada. La ausencia deniega.

Crea un token de organización

Quien lo crea debe ser Owner o Admin de la organización. Los permisos se aplican a toda la organización.

curl

TerminalCode
curl -sS -X POST "$ZAROZ_API/organizations/$ORG/api-tokens" -H "$AUTH" -H "Content-Type: application/json" -d '{ "name": "ci-deploy", "lifetime": "organization_owned", "expires_at": "2027-01-01T00:00:00Z", "capabilities": ["services.deploy", "services.read"] }'

TypeScript

TypeScriptCode
const org = await call("POST", `/organizations/${ORG}/api-tokens`, { name: "ci-deploy", lifetime: "organization_owned", // sobrevive a la persona que lo creó expires_at: "2027-01-01T00:00:00Z", capabilities: ["services.deploy", "services.read"], });

Python

Code
_, org_token = call("POST", f"/organizations/{ORG}/api-tokens", { "name": "ci-deploy", "lifetime": "organization_owned", "expires_at": "2027-01-01T00:00:00Z", "capabilities": ["services.deploy", "services.read"], })

lifetime es membership_tied por defecto: el token se revoca solo cuando su creador deja la organización. Usa organization_owned si debe seguir funcionando después. Los permisos exclusivos de personal de Zaroz no pueden ir nunca en un token de organización.

Úsalo

Envíalo como token bearer. Esto lee un servicio con un token que solo tiene services.read:

curl

TerminalCode
curl -sS "$ZAROZ_API/service-instances/$SERVICE" -H "Authorization: Bearer zaroz_pat_..." curl -sS "$ZAROZ_API/service-instances/$SERVICE/resources" -H "Authorization: Bearer zaroz_pat_..."

TypeScript

TypeScriptCode
// status.mjs: ZAROZ_TOKEN debe ser el token del bot; Node 20+ const API = "https://kernel.zaroz.cloud/api/v2"; const res = await fetch(`${API}/service-instances/${process.env.SERVICE}/resources`, { headers: { Authorization: `Bearer ${process.env.ZAROZ_TOKEN}` }, }); if (!res.ok) throw new Error(`${res.status} ${(await res.json()).code}`); const { data } = await res.json(); for (const r of data) console.log(r.kind, r.role, r.status);

Python

Code
import os, requests API = "https://kernel.zaroz.cloud/api/v2" r = requests.get( f"{API}/service-instances/{os.environ['SERVICE']}/resources", headers={"Authorization": "Bearer " + os.environ["ZAROZ_TOKEN"]}, timeout=20, ) r.raise_for_status() for res in r.json()["data"]: print(res["kind"], res["role"], res["status"])

Prueba una ruta que el token no tiene (por ejemplo POST /service-instances/{id}/restart con el token anterior): responde 403. Ese es el objetivo.

No llames a la API directamente desde una web pública

El código del navegador lo ve cualquier visitante. Guarda el token en un servidor, un bot, el almacén de secretos del CI u otro backend de confianza.

Errores que debes manejar

  • 401: el token falta, está mal formado, caducó o fue revocado.
  • 403: el token, su propietario o ambos carecen del permiso para ese recurso.
  • 404: el id es incorrecto, o el recurso está deliberadamente oculto para esta identidad.
  • 429: espera Retry-After y reintenta.
  • 5xx: temporal; reintenta un número limitado de veces y registra la respuesta sin registrar el token.

Listar, rotar y revocar

curl

TerminalCode
curl -sS "$ZAROZ_API/me/api-tokens" -H "$AUTH" # tus tokens vivos: nunca el secreto curl -sS "$ZAROZ_API/organizations/$ORG/api-tokens" -H "$AUTH" # los tokens de la organización curl -sS -X DELETE "$ZAROZ_API/me/api-tokens/$TOKEN_ID" -H "$AUTH" # 204 curl -sS -X DELETE "$ZAROZ_API/organizations/$ORG/api-tokens/$TOKEN_ID" -H "$AUTH" # 204

TypeScript

TypeScriptCode
const live = await call("GET", "/me/api-tokens"); for (const t of live.body.data) console.log(t.name, t.last_used_at); await call("DELETE", `/me/api-tokens/${TOKEN_ID}`);

Python

Code
_, live = call("GET", "/me/api-tokens") for t in live["data"]: print(t["name"], t["last_used_at"]) call("DELETE", f"/me/api-tokens/{TOKEN_ID}")

Para rotar: crea el reemplazo, actualiza la integración, comprueba que funciona y revoca el antiguo. Revocar es inmediato, y revocar un token personal requiere una sesión iniciada, no un token. Las acciones hechas con un token aparecen en la actividad de la organización con el nombre del token, así que ponles nombres que reconozcas durante un incidente.

Siguiente

Last modified on