Referencia de API
URL base: https://api.cynchro.cloud (tu ROOT_DOMAIN). Todas las rutas /apps, /deploy, /secrets, /admin requieren el header Authorization: Bearer <accessToken>.
Auth
| Método | Path | Body | Notas |
|---|---|---|---|
| POST | /auth/register | {email, password, name?} | Abierto; crea usuario + org personal. Devuelve tokens. |
| POST | /auth/login | {email, password} | Devuelve {user, accessToken, refreshToken, orgId}. |
| POST | /auth/refresh | {refreshToken} | Rota tokens (el refresh viejo se revoca). |
| POST | /auth/logout | — | Revoca todos tus refresh tokens. |
| GET | /auth/me | — | {user, orgId}. |
| POST | /auth/change-password | {currentPassword, newPassword} | Revoca otras sesiones; mantiene esta. |
- Access token: JWT, TTL ~15 min. Va en
Authorization: Bearer. - Refresh token: opaco, ~30 días. Se canjea en
/auth/refreshpor tokens nuevos. /authtiene rate limit (30 requests / 5 min por IP).
Deploy
POST /deploy — crea o actualiza (idempotente por name) y encola un deployment.
{
"name": "myapp", // requerido, DNS-safe, único por org
// uno de:
"image": "acme/api:1.0", // imagen prearmada
"repo": "https://github.com/acme/api", // build desde el código
"services": [ /* … */ ], // multi-servicio (ver Apps multi-servicio)
"branch": "main", // builds de repo
"dockerfile": "Dockerfile", // ruta relativa, sin "..", sin "/" inicial
"context": ".",
"repoToken": "ghp_…", // PAT para repos privados (cifrado)
"port": 80, // 1–65535
"domain": "www.x.com", // hostname válido; default <name>.<ROOT_DOMAIN>
"cpu": 1, // cores, máx 8
"memory": 512, // MB, 64–16384
"env": { "K": "V" }, // env inline (cifrado at-rest)
"envFrom": ["secret-name"], // inyecta claves de un secret como env vars
"secretFiles": ["secret-name"] // monta claves de un secret en /run/secrets/<KEY>
}
Los campos de texto opcionales se pueden omitir o mandar como
""— el string vacío se trata como “no provisto”.
Respuesta 202:
{ "appId":"…","deploymentId":"…","domain":"…","status":"queued","url":"https://…",
"webhook": { "url":"…","secret":"…","contentType":"application/json","events":["push"] } }
(webhook solo para apps de repo.)
Apps
| Método | Path | Notas |
|---|---|---|
| GET | /apps | Lista las apps de tu org. |
| GET | /apps/:id | App + webhook + últimos 20 deployments. |
| POST | /apps/:id/restart | Recrea contenedor(es). |
| POST | /apps/:id/stop | Para contenedor(es). |
| DELETE | /apps/:id | Borra app, contenedores, red, imágenes. |
| GET | /apps/:id/logs?tail=N | Snapshot de logs (N ≤ 2000, default 200). |
| WS | /apps/:id/logs/stream?token=<access> | Stream de logs en vivo. |
Secrets
Los valores nunca se devuelven — las respuestas listan solo nombres de claves.
| Método | Path | Body / Query | Notas |
|---|---|---|---|
| POST | /secrets | {name, data:{...}, appId?} | Crear/reemplazar. appId lo scopea a una app. |
| GET | /secrets | — | Lista (nombres + claves + scope). |
| GET | /secrets/:name | ?appId= | Metadata + nombres de claves. |
| DELETE | /secrets/:name | ?appId= | Borrar (omitir appId para compartido). |
Webhooks
| Método | Path | Notas |
|---|---|---|
| POST | /webhooks/github/:appId | Push de GitHub. Sin JWT; se verifica por HMAC X-Hub-Signature-256 con el secret del webhook de la app. Los pushes a la rama configurada disparan un rebuild. |
Admin (solo operadores)
Gateado por la allowlist ADMIN_EMAILS (un login normal no alcanza).
| Método | Path | Notas |
|---|---|---|
| GET | /admin/seal-status | { sealed }. |
| POST | /admin/unseal | {key, previous?} (64 hex) — carga la llave maestra en memoria. |
| POST | /admin/seal | Saca la llave de memoria. |
Errores
Forma JSON: { "error": "<code>", … }. Los errores de validación incluyen detalle por campo:
{ "error":"validation", "details": { "fieldErrors": { "name": ["…"] } } }
Códigos comunes: validation, invalid_credentials, missing_bearer_token, invalid_or_expired_token, quota_exceeded, unknown_secret, sealed, app_not_found, rate_limited. Ver Solución de problemas.