API REST
La consola está construida sobre una API REST JSON en https://zenavo.app/api. Todo lo que puedes hacer en la consola, puedes hacerlo con ella.
Autenticación
Envía una clave de API como token bearer:
curl -H "Authorization: Bearer $ZENAVO_API_KEY" https://zenavo.app/api/auth/meEl permiso y el límite de proyectos de una clave deciden a qué endpoints puede llamar; los demás responden 403. La facturación, las compras, los miembros del equipo y /api/account/* son solo para sesiones de navegador.
Las peticiones desde una sesión de navegador también necesitan la cabecera X-Zenavo-Request: 1 en todo lo que no sea GET; las peticiones con token bearer no.
Las peticiones solo necesitan una sesión válida o una clave de API con el rol adecuado; ninguna acción pide una segunda confirmación. Las cuentas que no han verificado su correo reciben 403 con {"email_unverified": true} en cambios fuera de su propia cuenta.
Convenciones
- Los cuerpos son JSON. Las horas son segundos Unix.
- Los ID llevan prefijo:
team_,srv_,proj_,job_,bak_,tok_. - Los errores devuelven un código de estado y
{"detail": …}. Los errores de validación (422) listan cada campo. - Las operaciones largas (desplegar, reiniciar, copia de seguridad) devuelven una tarea:
{"id": "job_…"}. ConsultaGET /api/jobs/{id}para obtenerstatus(queued,running,succeeded,failed),logy elprogressde cada servicio.
Endpoints
Cuenta
| Método | Ruta | |
|---|---|---|
GET | /api/auth/me | El usuario con sesión iniciada, sus equipos y email_verified |
PATCH | /api/account | Nombre y avatar |
GET POST | /api/account/tokens | Lista o crea claves de API (solo sesión de navegador). Crea con {name, days, scope, project_ids}: scope es read, deploy o full (por defecto); project_ids limita la clave a esos proyectos (omítelo o usa null para todos) |
DELETE | /api/account/tokens/{id} | Elimina una clave de API |
GET | /api/account/sessions | Sesiones iniciadas |
POST | /api/auth/2fa | Segundo paso del inicio de sesión: {code} después de que /api/auth/login respondiera {two_factor: true} |
POST | /api/account/verify | Confirma que eres tú: {code} con dos pasos, si no {password}. Ninguna acción lo requiere por ahora |
POST | /api/auth/verify-email | Abre un enlace de verificación de correo: {token} (funciona una vez, durante 24 horas) |
POST | /api/account/email/verify | Envía un enlace de verificación nuevo; {verified: true} si la dirección ya está verificada |
GET | /api/account/2fa | Estado de dos pasos: enabled (aplicación o llave de acceso), totp, códigos de recuperación restantes, número de llaves de acceso |
POST | /api/account/2fa/setup /enable /disable /recovery-codes | Gestiona la autenticación en dos pasos (solo sesión de navegador) |
POST | /api/auth/2fa/passkey/options y luego /api/auth/2fa/passkey | Segundo paso del inicio de sesión con una llave de acceso: opciones de petición WebAuthn ({options: null} sin llaves de acceso) y si la cuenta tiene aplicación de autenticación (totp), y luego {credential} |
GET | /api/account/passkeys | Tus llaves de acceso: nombre, AAGUID, sincronizada, creación, último uso |
POST | /api/account/passkeys/options y luego /api/account/passkeys | Registra una llave de acceso: opciones de creación, y luego {name, credential} (el primer segundo factor de la cuenta también devuelve recovery_codes) |
DELETE | /api/account/passkeys/{id} | Quita una llave de acceso (quitar el último segundo factor desactiva la autenticación en dos pasos) |
GET | /api/account/audit?before=&limit= | Tu propia actividad de seguridad |
POST | /api/auth/device/code | Inicia un inicio de sesión de la CLI (flujo de dispositivo): {client_name, scope, client} → {device_code, user_code, verification_uri, expires_in, interval}. scope es full por defecto; client es cli (por defecto) o mcp, y llama a la clave "CLI on …" o "MCP on …" |
POST | /api/auth/device/token | Consulta con {device_code}: 202 mientras espera, 429 si se consulta más rápido que interval, 200 {token, scope, project_ids} una vez aprobado (quien aprueba puede haber reducido el alcance) |
Equipos
| Método | Ruta | |
|---|---|---|
GET POST | /api/teams | Lista o crea equipos |
PATCH | /api/teams/{tid} | Renombrar |
GET | /api/teams/{tid}/members | Miembros |
PATCH DELETE | /api/teams/{tid}/members/{uid} | Cambia un rol, quita a un miembro |
GET POST | /api/teams/{tid}/invites | Invitaciones |
GET | /api/teams/{tid}/audit?before=&limit= | Registro de auditoría (administradores) |
GET POST | /api/teams/{tid}/notification-channels | Canales de notificación (administradores). Crea con {kind, name, events, url} (Slack, Discord, webhook) o {kind: "telegram", name, events, bot_token, chat_id}; el secret de un webhook se devuelve una vez |
PATCH DELETE | /api/teams/{tid}/notification-channels/{cid} | Cambia name, events, enabled o el destino; elimina |
POST | /api/teams/{tid}/notification-channels/{cid}/test | Envía un mensaje de prueba ahora |
POST | /api/teams/{tid}/notification-channels/{cid}/secret | El nuevo secreto de firma de un webhook (se devuelve una vez) |
GET POST | /api/teams/{tid}/log-drains | Reenvíos de registros (administradores; crear necesita el plan Pro o superior). Crea con {kind: "http", name, url}, {kind: "datadog", name, site, api_key} o {kind: "loki", name, url, username, password, tenant_id}, más projects (vacío para todos); el secret de un reenvío HTTP se devuelve una vez |
PATCH DELETE | /api/teams/{tid}/log-drains/{did} | Cambia name, projects, enabled o el destino; elimina |
POST | /api/teams/{tid}/log-drains/{did}/test | Envía una línea de prueba ahora |
POST | /api/teams/{tid}/log-drains/{did}/secret | El nuevo secreto de firma de un reenvío HTTP (se devuelve una vez) |
Uso y facturación
| Método | Ruta | |
|---|---|---|
GET | /api/teams/{tid}/usage?period=YYYY-MM | Límites del plan, servidores, miembros, minutos de compilación por proyecto, servicio y día |
GET | /api/teams/{tid}/usage/builds.csv?period= | Todas las compilaciones medidas de un mes, en CSV |
GET | /api/teams/{tid}/transfer | Transferencia de imágenes de este periodo: bytes, included_bytes, overage_cents, limit_cents, blocked, percent y el inicio y el final del periodo |
GET | /api/teams/{tid}/billing | Plan, estado de la suscripción, puestos y el catálogo de planes (administradores) |
GET | /api/teams/{tid}/billing/invoices | Facturas recientes con los enlaces a la página de Stripe y al PDF (administradores) |
GET | /api/teams/{tid}/server-billing | Servidores comprados con su mes pagado y su renovación automática, y los cargos recientes de servidores (administradores) |
POST | /api/teams/{tid}/billing/checkout | Inicia una suscripción: {plan} → {url} de Stripe Checkout (propietarios) |
POST | /api/teams/{tid}/billing/change | Cambia una suscripción a otro plan: {plan} (propietarios) |
POST | /api/teams/{tid}/billing/cancel /resume | Cancela al final del periodo, o lo deshace (propietarios) |
PUT | /api/teams/{tid}/billing/spend-limit | Define el límite de gasto: {cents}, de 0 a 1.000.000 (propietarios) |
POST | /api/teams/{tid}/billing/portal | {url} del portal de clientes de Stripe para tarjetas, datos de facturación y facturas (propietarios) |
Servidores
| Método | Ruta | |
|---|---|---|
GET | /api/servers?team_id= | Lista los servidores |
POST | /api/servers?team_id= | Añade un servidor: {name, host, port, user, password | private_key} |
GET PATCH | /api/servers/{sid} | Leer, renombrar |
POST | /api/servers/{sid}/bootstrap | Vuelve a ejecutar la configuración: {password | private_key}, o un cuerpo vacío durante el día siguiente a una configuración fallida para reutilizar las credenciales guardadas |
PATCH | /api/servers/{sid}/billing | La renovación automática de un servidor comprado: {auto_renew} (administradores). GET /api/servers/{sid} la muestra en bought, con paid_until |
POST | /api/servers/{sid}/renew | Paga ahora el mes siguiente de un servidor comprado (administradores) |
GET | /api/servers/{sid}/usage?hours= | Historial de CPU, memoria y disco |
GET | /api/servers/{sid}/system | Componentes de la plataforma |
GET | /api/servers/{sid}/ports | Comprobaciones de puertos |
GET | /api/servers/{sid}/jobs | Configuración y otras tareas |
POST | /api/servers/{sid}/delete | Quitar: {confirmation: "<server name>"} |
POST | /api/servers/{sid}/reinstall | Desinstala K3s y configura el servidor desde cero: {confirmation: "<server name>", password | private_key} (administradores; el servidor no debe tener proyectos). Responde con el servidor, cuya configuración queda en cola |
POST | /api/servers/{sid}/reinstall-os | Reinstala el sistema operativo de un servidor comprado y lo configura desde cero, sin credenciales SSH; se envía por correo una nueva contraseña de inicio de sesión a quien lo pide: {confirmation: "<server name>"} (administradores; el servidor no debe tener proyectos y su bought.reinstall_os debe ser true). Responde con el servidor, cuya reinstalación queda en cola |
GET | /api/servers/{sid}/ssh-logins?hours=&failed= | Historial de accesos SSH (miembros) |
POST | /api/servers/{sid}/agent | Pasa al agente un servidor añadido antes de que existiera (administradores) |
POST | /api/servers/{sid}/agent/update | Actualiza el agente (administradores; devuelve una tarea) |
GET | /api/servers/{sid}/firewall | El cortafuegos del servidor (miembros). En un servidor comprado: las reglas fijas de Zenavo (web, HTTPS, ping, el SSH de la configuración) y las del equipo, aplicadas en el proveedor; en un servidor que conectaste tú: los puertos que hay que abrir allí |
POST | /api/servers/{sid}/firewall/rules | Añade una regla de permiso a un servidor comprado: {protocol, ports, sources, note}, con protocol tcp, udp o icmp, ports como 8080 o 30000-30100, y sources una lista de direcciones o rangos CIDR (administradores; hasta 30 reglas) |
PUT DELETE | /api/servers/{sid}/firewall/rules/{rid} | Cambia una regla (mismo cuerpo) o la quita (administradores) |
POST | /api/servers/{sid}/firewall/apply | Vuelve a aplicar las reglas en el proveedor tras un fallo (administradores) |
Proyectos
| Método | Ruta | |
|---|---|---|
GET | /api/projects?team_id= | Lista los proyectos |
POST | /api/projects | Crear: {team_id, server_id, manifest} |
GET | /api/projects/{pid} | Resumen, servicios y estado |
GET | /api/projects/{pid}/manifest | La configuración completa, con variables |
PUT | /api/projects/{pid} | Guarda la configuración: {manifest, server_id, revision} |
POST | /api/projects/{pid}/deploy | Despliega todos los servicios (devuelve una tarea) |
POST | /api/projects/{pid}/rollback | {target_job} |
POST | /api/projects/{pid}/services/{service}/rollback | {target_job}: devuelve un servicio a ese despliegue; los demás quedan como están desplegados (devuelve una tarea) |
POST | /api/projects/{pid}/restart /stop | Reinicia o detiene todos los servicios |
GET | /api/projects/{pid}/runtime | Estado en vivo de cada servicio |
POST | /api/projects/{pid}/clone /transfer | Clonar: {name, team_id, server_id}; o trasladar un proyecto nunca desplegado a otro equipo: {team_id, server_id} (administradores de ambos equipos) |
GET | /api/projects/{pid}/move | El último traslado a otro servidor |
GET | /api/projects/{pid}/move/preflight?server_id= | Comprueba un traslado sin cambiar nada (administradores) |
POST | /api/projects/{pid}/move | Traslada el proyecto, con sus datos, a otro servidor del equipo: {server_id, confirmation: "<project name>"} (administradores; devuelve una tarea) |
POST | /api/projects/{pid}/move/finish /move/rollback | Elimina la copia antigua, o vuelve al servidor antiguo (administradores; devuelve una tarea) |
POST | /api/projects/{pid}/delete | {confirmation: "<project name>"} |
PUT | /api/projects/{pid}/checklist | La lista de migración: {source_backup, data_restored, application_verified, dns_switched, rollback_ready}, cada uno true o false (miembros) |
Servicios
{service} es el ID del servicio; su nombre actual también sirve. En la configuración de un proyecto, cada servicio tiene un id y un hostname opcional; omite ambos en un servicio nuevo y Zenavo le da un id svc-… aleatorio y un nombre de host a partir del nombre.
| Método | Ruta | |
|---|---|---|
POST | /api/projects/{pid}/services/{service}/deploy | Despliega solo este servicio; los demás quedan como están desplegados (devuelve una tarea) |
PUT | /api/projects/{pid}/services/{service}/source?create=&port=&deploy=&acknowledge_init= | Sube un tar comprimido con gzip de un directorio (hasta 500 MB) como origen del servicio y lo despliega (devuelve {upload_id, service, created, job}); create=1 añade un servicio que falte, deploy=0 solo lo guarda |
POST | /api/projects/{pid}/services/{service}/restart /suspend /resume | |
POST | /api/projects/{pid}/services/{service}/exec | Ejecuta un comando en el contenedor |
GET | /api/projects/{pid}/services/{service}/terminal | WebSocket: un terminal interactivo (miembros). Los frames binarios llevan la entrada y la salida; los de texto llevan JSON: {"type":"resize","cols","rows"} desde el cliente, {"type":"exit","code"} o {"type":"error","message"} desde el servidor. Los navegadores deben enviar el Origin de la consola |
GET | /api/projects/{pid}/services/{service}/files?path= | Lista una carpeta del contenedor (miembros) |
GET | /api/projects/{pid}/services/{service}/files/content?path= | Los primeros 256 KB de un archivo, como texto |
GET | /api/projects/{pid}/services/{service}/files/download?path= | Descarga un archivo (hasta 500 MB) |
PUT | /api/projects/{pid}/services/{service}/files/upload?dir=&name= | Sube el cuerpo tal cual (hasta 50 MB, Content-Length obligatorio); &overwrite=1 sustituye |
POST | /api/projects/{pid}/services/{service}/files/mkdir | Crea una carpeta: {"path"} |
POST | /api/projects/{pid}/services/{service}/files/rename | Renombra en el sitio: {"path", "name"} |
POST | /api/projects/{pid}/services/{service}/files/delete | Elimina un archivo o una carpeta: {"path"} |
GET | /api/projects/{pid}/services/{service}/usage?hours= | Historial de CPU y memoria |
GET | /api/projects/{pid}/logs/{service} | Registros de ejecución recientes |
GET | /api/projects/{pid}/logs/{service}/stream | Registros de ejecución en directo (Server-Sent Events) |
GET | /api/projects/{pid}/logs/{service}/history?from=&to=&q=&cursor=&limit= | Registros de ejecución guardados, del más antiguo al más reciente: las líneas que contienen q (sin distinguir mayúsculas) entre from y to (RFC 3339 o segundos Unix; por defecto la última hora). Pasa next como cursor para obtener más |
GET | /api/projects/{pid}/logs/{service}/download?from=&to=&q= | Registros de ejecución guardados de un intervalo como archivo de texto |
POST | /api/projects/{pid}/services/{service}/domains | Genera un dominio |
DELETE | /api/projects/{pid}/domains/{domain} | Quita un dominio generado |
Vista de base de datos
La vista de base de datos de un servicio de base de datos. Los miembros pueden leer; escribir necesita un administrador del equipo. Cada petición se ejecuta dentro del contenedor de la base de datos y queda en el registro de auditoría sin su SQL, claves, valores ni documentos. … es /api/projects/{pid}/services/{service}.
| Método | Ruta | |
|---|---|---|
GET | …/database/tables | PostgreSQL, MySQL, MariaDB: tablas y vistas, con filas estimadas y columnas |
GET | …/database/rows?schema=&table=&offset=&limit= | Una página de filas de una tabla |
POST | …/database/query | Ejecuta una sentencia SQL: {sql, allow_writes, max_rows}. Solo lectura salvo allow_writes (administradores) |
POST | …/database/keys | Redis, Valkey: lista claves con SCAN: {db, match, cursor, count} |
POST | …/database/key | El valor de una clave: {db, key, cursor, count}, o key_b64 para claves que no son UTF-8 |
POST | …/database/command | Ejecuta un comando: {db, command, allow_writes}. Solo comandos de lectura salvo allow_writes (administradores) |
GET | …/database/collections | MongoDB: bases de datos y colecciones, con el número estimado de documentos |
POST | …/database/find | Busca documentos: {db, collection, filter, sort, projection, skip, limit}, todos cadenas JSON; 50 por página por defecto |
POST | …/database/document | Cambia un documento: {op, db, collection, filter, update, document}, con op insert, update o delete (administradores) |
Copias de seguridad
| Método | Ruta | |
|---|---|---|
GET | /api/backups?project_id= | Lista las copias de seguridad |
POST | /api/projects/{pid}/backups | Copia de seguridad de un servicio: {service, engine} |
GET | /api/backups/{bid}/download | Descarga |
POST | /api/backups/{bid}/restore | Restaura: {confirmation}, como se escribe en la consola |
Git y GitHub
| Método | Ruta | |
|---|---|---|
POST | /api/git/refs | Ramas y etiquetas de un repositorio público: {url} |
POST | /api/git/plan | Vista previa del plan de compilación: {team_id, url, ref, context} |
GET | /api/github/repos?team_id= | Repositorios que pueden leer las instalaciones de GitHub del equipo |
GET | /api/github/branches?team_id=&repo= | Ramas de uno de ellos |
Imágenes y dominios
| Método | Ruta | |
|---|---|---|
GET | /api/images/ports?team_id=&image= | Los puertos que una imagen pública declara con EXPOSE, leídos de su registro sin descargarla |
GET | /api/images/mirror?image= | Si la réplica de Zenavo para China continental tiene una imagen, y su dirección allí |
GET | /api/images/mirror/search?q= | Imágenes de la réplica cuyo nombre contiene q |
GET | /api/domains/available?name= | Si un nombre de dominio generado está libre: {available, reason} |
Plantillas
| Método | Ruta | |
|---|---|---|
GET | /api/templates | La galería de plantillas (pública, sin clave) |
GET | /api/templates/{code} | Una plantilla y su manifiesto (pública) |
POST | /api/imports/preview | Revisa una plantilla de Zeabur o una exportación de Zenavo: {text, team_id, variables}, o una plantilla de la galería: {template, team_id, variables} |
POST | /api/imports/{did}/commit | Crea el proyecto: {server_id} |
Ejemplo: desplegar y esperar
JOB=$(curl -s -X POST -H "Authorization: Bearer $ZENAVO_API_KEY" \
-H "Content-Type: application/json" -d '{}' \
https://zenavo.app/api/projects/$PROJECT/deploy | jq -r .id)
until curl -s -H "Authorization: Bearer $ZENAVO_API_KEY" https://zenavo.app/api/jobs/$JOB \
| jq -e '.status == "succeeded" or .status == "failed"' >/dev/null; do sleep 5; done