Saltar al contenido

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:

sh
curl -H "Authorization: Bearer $ZENAVO_API_KEY" https://zenavo.app/api/auth/me

El 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_…"}. Consulta GET /api/jobs/{id} para obtener status (queued, running, succeeded, failed), log y el progress de cada servicio.

Endpoints ​

Cuenta ​

MétodoRuta
GET/api/auth/meEl usuario con sesión iniciada, sus equipos y email_verified
PATCH/api/accountNombre y avatar
GET POST/api/account/tokensLista 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/sessionsSesiones iniciadas
POST/api/auth/2faSegundo paso del inicio de sesión: {code} después de que /api/auth/login respondiera {two_factor: true}
POST/api/account/verifyConfirma que eres tú: {code} con dos pasos, si no {password}. Ninguna acción lo requiere por ahora
POST/api/auth/verify-emailAbre un enlace de verificación de correo: {token} (funciona una vez, durante 24 horas)
POST/api/account/email/verifyEnvía un enlace de verificación nuevo; {verified: true} si la dirección ya está verificada
GET/api/account/2faEstado 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-codesGestiona la autenticación en dos pasos (solo sesión de navegador)
POST/api/auth/2fa/passkey/options y luego /api/auth/2fa/passkeySegundo 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/passkeysTus llaves de acceso: nombre, AAGUID, sincronizada, creación, último uso
POST/api/account/passkeys/options y luego /api/account/passkeysRegistra 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/codeInicia 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/tokenConsulta 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étodoRuta
GET POST/api/teamsLista o crea equipos
PATCH/api/teams/{tid}Renombrar
GET/api/teams/{tid}/membersMiembros
PATCH DELETE/api/teams/{tid}/members/{uid}Cambia un rol, quita a un miembro
GET POST/api/teams/{tid}/invitesInvitaciones
GET/api/teams/{tid}/audit?before=&limit=Registro de auditoría (administradores)
GET POST/api/teams/{tid}/notification-channelsCanales 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}/testEnvía un mensaje de prueba ahora
POST/api/teams/{tid}/notification-channels/{cid}/secretEl nuevo secreto de firma de un webhook (se devuelve una vez)
GET POST/api/teams/{tid}/log-drainsReenví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}/testEnvía una línea de prueba ahora
POST/api/teams/{tid}/log-drains/{did}/secretEl nuevo secreto de firma de un reenvío HTTP (se devuelve una vez)

Uso y facturación ​

MétodoRuta
GET/api/teams/{tid}/usage?period=YYYY-MMLí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}/transferTransferencia 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}/billingPlan, estado de la suscripción, puestos y el catálogo de planes (administradores)
GET/api/teams/{tid}/billing/invoicesFacturas recientes con los enlaces a la página de Stripe y al PDF (administradores)
GET/api/teams/{tid}/server-billingServidores comprados con su mes pagado y su renovación automática, y los cargos recientes de servidores (administradores)
POST/api/teams/{tid}/billing/checkoutInicia una suscripción: {plan} → {url} de Stripe Checkout (propietarios)
POST/api/teams/{tid}/billing/changeCambia una suscripción a otro plan: {plan} (propietarios)
POST/api/teams/{tid}/billing/cancel /resumeCancela al final del periodo, o lo deshace (propietarios)
PUT/api/teams/{tid}/billing/spend-limitDefine 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étodoRuta
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}/bootstrapVuelve 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}/billingLa 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}/renewPaga 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}/systemComponentes de la plataforma
GET/api/servers/{sid}/portsComprobaciones de puertos
GET/api/servers/{sid}/jobsConfiguración y otras tareas
POST/api/servers/{sid}/deleteQuitar: {confirmation: "<server name>"}
POST/api/servers/{sid}/reinstallDesinstala 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-osReinstala 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}/agentPasa al agente un servidor añadido antes de que existiera (administradores)
POST/api/servers/{sid}/agent/updateActualiza el agente (administradores; devuelve una tarea)
GET/api/servers/{sid}/firewallEl 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/rulesAñ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/applyVuelve a aplicar las reglas en el proveedor tras un fallo (administradores)

Proyectos ​

MétodoRuta
GET/api/projects?team_id=Lista los proyectos
POST/api/projectsCrear: {team_id, server_id, manifest}
GET/api/projects/{pid}Resumen, servicios y estado
GET/api/projects/{pid}/manifestLa configuración completa, con variables
PUT/api/projects/{pid}Guarda la configuración: {manifest, server_id, revision}
POST/api/projects/{pid}/deployDespliega 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 /stopReinicia o detiene todos los servicios
GET/api/projects/{pid}/runtimeEstado en vivo de cada servicio
POST/api/projects/{pid}/clone /transferClonar: {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}/moveEl último traslado a otro servidor
GET/api/projects/{pid}/move/preflight?server_id=Comprueba un traslado sin cambiar nada (administradores)
POST/api/projects/{pid}/moveTraslada 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/rollbackElimina la copia antigua, o vuelve al servidor antiguo (administradores; devuelve una tarea)
POST/api/projects/{pid}/delete{confirmation: "<project name>"}
PUT/api/projects/{pid}/checklistLa 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étodoRuta
POST/api/projects/{pid}/services/{service}/deployDespliega 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}/execEjecuta un comando en el contenedor
GET/api/projects/{pid}/services/{service}/terminalWebSocket: 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/mkdirCrea una carpeta: {"path"}
POST/api/projects/{pid}/services/{service}/files/renameRenombra en el sitio: {"path", "name"}
POST/api/projects/{pid}/services/{service}/files/deleteElimina 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}/streamRegistros 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}/domainsGenera 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étodoRuta
GET…/database/tablesPostgreSQL, 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/queryEjecuta una sentencia SQL: {sql, allow_writes, max_rows}. Solo lectura salvo allow_writes (administradores)
POST…/database/keysRedis, Valkey: lista claves con SCAN: {db, match, cursor, count}
POST…/database/keyEl valor de una clave: {db, key, cursor, count}, o key_b64 para claves que no son UTF-8
POST…/database/commandEjecuta un comando: {db, command, allow_writes}. Solo comandos de lectura salvo allow_writes (administradores)
GET…/database/collectionsMongoDB: bases de datos y colecciones, con el número estimado de documentos
POST…/database/findBusca documentos: {db, collection, filter, sort, projection, skip, limit}, todos cadenas JSON; 50 por página por defecto
POST…/database/documentCambia un documento: {op, db, collection, filter, update, document}, con op insert, update o delete (administradores)

Copias de seguridad ​

MétodoRuta
GET/api/backups?project_id=Lista las copias de seguridad
POST/api/projects/{pid}/backupsCopia de seguridad de un servicio: {service, engine}
GET/api/backups/{bid}/downloadDescarga
POST/api/backups/{bid}/restoreRestaura: {confirmation}, como se escribe en la consola

Git y GitHub ​

MétodoRuta
POST/api/git/refsRamas y etiquetas de un repositorio público: {url}
POST/api/git/planVista 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étodoRuta
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étodoRuta
GET/api/templatesLa galería de plantillas (pública, sin clave)
GET/api/templates/{code}Una plantilla y su manifiesto (pública)
POST/api/imports/previewRevisa 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}/commitCrea el proyecto: {server_id}

Ejemplo: desplegar y esperar ​

sh
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

Sin relación con Zeabur.