API Reference
Base URL: https://api.cynchro.cloud (your ROOT_DOMAIN). All /apps, /deploy, /secrets, /admin routes require a Authorization: Bearer <accessToken> header.
Auth
| Method | Path | Body | Notes |
|---|---|---|---|
| POST | /auth/register | {email, password, name?} | Open; creates a user + personal org. Returns tokens. |
| POST | /auth/login | {email, password} | Returns {user, accessToken, refreshToken, orgId}. |
| POST | /auth/refresh | {refreshToken} | Rotates tokens (old refresh token is revoked). |
| POST | /auth/logout | — | Revokes all your refresh tokens. |
| GET | /auth/me | — | {user, orgId}. |
| POST | /auth/change-password | {currentPassword, newPassword} | Revokes other sessions; keeps this one. |
- Access token: JWT, ~15 min TTL. Send as
Authorization: Bearer. - Refresh token: opaque, ~30 days. Exchange at
/auth/refreshfor new tokens. /authis rate-limited (30 requests / 5 min per IP).
Deploy
POST /deploy — create or update (idempotent on name) and queue a deployment.
{
"name": "myapp", // required, DNS-safe, unique per org
// one of:
"image": "acme/api:1.0", // prebuilt image
"repo": "https://github.com/acme/api", // build from source
"services": [ /* … */ ], // multi-service (see Multi-Service Apps)
"branch": "main", // repo builds
"dockerfile": "Dockerfile", // relative path, no "..", no leading "/"
"context": ".",
"repoToken": "ghp_…", // PAT for private repos (encrypted)
"port": 80, // 1–65535
"domain": "www.x.com", // valid hostname; default <name>.<ROOT_DOMAIN>
"cpu": 1, // cores, max 8
"memory": 512, // MB, 64–16384
"env": { "K": "V" }, // inline env (encrypted at rest)
"envFrom": ["secret-name"], // inject secret keys as env vars
"secretFiles": ["secret-name"] // mount secret keys at /run/secrets/<KEY>
}
Blank optional text fields may be omitted or sent as
""— empty strings are treated as “not provided”.
Response 202:
{ "appId":"…","deploymentId":"…","domain":"…","status":"queued","url":"https://…",
"webhook": { "url":"…","secret":"…","contentType":"application/json","events":["push"] } }
(webhook only for repo apps.)
Apps
| Method | Path | Notes |
|---|---|---|
| GET | /apps | List your org’s apps. |
| GET | /apps/:id | App + webhook + last 20 deployments. |
| POST | /apps/:id/restart | Recreate container(s). |
| POST | /apps/:id/stop | Stop container(s). |
| DELETE | /apps/:id | Remove app, containers, network, images. |
| GET | /apps/:id/logs?tail=N | Snapshot logs (N ≤ 2000, default 200). |
| WS | /apps/:id/logs/stream?token=<access> | Live log stream. |
Secrets
Values are never returned — responses list key names only.
| Method | Path | Body / Query | Notes |
|---|---|---|---|
| POST | /secrets | {name, data:{...}, appId?} | Create/replace. appId scopes to one app. |
| GET | /secrets | — | List (names + key names + scope). |
| GET | /secrets/:name | ?appId= | Metadata + key names. |
| DELETE | /secrets/:name | ?appId= | Delete (omit appId for shared). |
Webhooks
| Method | Path | Notes |
|---|---|---|
| POST | /webhooks/github/:appId | GitHub push events. No JWT; verified by HMAC X-Hub-Signature-256 using the app’s webhook secret. Pushes to the configured branch trigger a rebuild. |
Admin (operators only)
Gated by the ADMIN_EMAILS allowlist (a normal login is not enough).
| Method | Path | Notes |
|---|---|---|
| GET | /admin/seal-status | { sealed }. |
| POST | /admin/unseal | {key, previous?} (64 hex chars) — load the master key into memory. |
| POST | /admin/seal | Drop the key from memory. |
Errors
JSON shape: { "error": "<code>", … }. Validation errors include field details:
{ "error":"validation", "details": { "fieldErrors": { "name": ["…"] } } }
Common codes: validation, invalid_credentials, missing_bearer_token, invalid_or_expired_token, quota_exceeded, unknown_secret, sealed, app_not_found, rate_limited. See Troubleshooting.