{"openapi":"3.1.0","info":{"title":"Skyversal API","version":"1.0","description":"Skyversal platform API \u2014 the same surface the Skyversal CLI talks to.\n\n**Base URL:** `https:\/\/skyversal.com\/api\/cli`\n\n## Authentication\n\nThe API uses OAuth 2.0 Device Authorization Grant (RFC 8628):\n\n1. `POST \/device\/code` \u2192 returns `device_code` + `user_code`\n2. User opens `verification_uri` and approves the code\n3. `POST \/device\/token` \u2192 returns a Bearer `access_token`\n4. Send `Authorization: Bearer <token>` on every authenticated call\n\n## Authorization (project roles)\n\nEvery project-scoped endpoint resolves `{projectKey}` as numeric id **or**\nslug, then checks the caller's role: owner > manager > developer > viewer.\n\n- **Viewer (level 0)** \u2014 read endpoints (GET)\n- **Manager (level 2)** \u2014 write endpoints (POST\/PUT\/PATCH\/DELETE)\n- No access \u2192 **404** (resource existence is not leaked)\n\n## Rate limits\n\n| Bucket | Limit |\n|---|---|\n| Read (auth'd GET) | 60 req\/min |\n| Write (auth'd POST\/PUT\/PATCH\/DELETE) | 10 req\/min |\n| Source upload | 20 req\/min |\n| Device code \/ token polling | 10 \/ 30 req\/min |\n| Public health & apps | 60 req\/min |\n","contact":{"name":"Skyversal","url":"https:\/\/skyversal.com","email":"destek@skyversal.com"},"license":{"name":"Proprietary"}},"servers":[{"url":"https:\/\/skyversal.com\/api\/cli","description":"Production"}],"tags":[{"name":"Authentication","description":"OAuth 2.0 Device Authorization Grant (RFC 8628)"},{"name":"Ecosystem","description":"Platform health, diagnostics and the app catalog"},{"name":"Projects","description":"Project lifecycle \u2014 create, inspect, restart"},{"name":"Deployments","description":"Deploy, monitor, roll back, cancel"},{"name":"Environment","description":"Environment variables (take effect on next deploy)"},{"name":"Logs & Metrics","description":"Runtime logs, access logs and resource metrics"},{"name":"Domains","description":"Custom domain attach, verify, detach \u2014 SSL state included"},{"name":"Backups","description":"Database backups \u2014 take, download, restore, schedule"},{"name":"Cron","description":"Scheduled jobs inside the project container"},{"name":"Scale","description":"Per-project resource limits within plan + add-on ceiling"},{"name":"Mail","description":"Managed outbound mail \u2014 SASL, DKIM, quotas, send log"},{"name":"Databases","description":"Account-scoped databases and credentials"},{"name":"Redis","description":"Account-scoped Redis instances"}],"paths":{"\/device\/code":{"post":{"tags":["Authentication"],"summary":"Request a device code","description":"Starts the device authorization flow. Returns the codes the user must approve in the browser.","security":[],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["client_id"],"properties":{"client_id":{"type":"string","enum":["skyversal_cli","flyway_cli"],"description":"`flyway_cli` kept for older CLI versions."}}}}}},"responses":{"200":{"description":"Device code issued","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/DeviceCodeResponse"}}}},"422":{"$ref":"#\/components\/responses\/ValidationError"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/device\/token":{"post":{"tags":["Authentication"],"summary":"Poll for an access token","description":"Poll until the user approves (`authorization_pending`) or the code expires. On approval returns a Bearer token.","security":[],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["client_id","grant_type","device_code"],"properties":{"client_id":{"type":"string","enum":["skyversal_cli","flyway_cli"]},"grant_type":{"type":"string","enum":["urn:ietf:params:oauth:grant-type:device_code"]},"device_code":{"type":"string","minLength":40,"maxLength":40}}}}}},"responses":{"200":{"description":"Access token issued","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/TokenResponse"}}}},"400":{"description":"OAuth error \u2014 `invalid_grant`, `expired_token`, `authorization_pending` or `access_denied`","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/OAuthError"}}}},"422":{"$ref":"#\/components\/responses\/ValidationError"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/whoami":{"get":{"tags":["Authentication"],"summary":"Current token owner","responses":{"200":{"description":"Identity of the token owner","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/WhoamiResponse"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/health":{"get":{"tags":["Ecosystem"],"summary":"Platform health","security":[],"responses":{"200":{"description":"Platform status","content":{"application\/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"operational"},"score":{"type":"integer","example":100}}}}}},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/apps":{"get":{"tags":["Ecosystem"],"summary":"Installable app catalog","security":[],"responses":{"200":{"description":"Catalog of one-click apps","content":{"application\/json":{"schema":{"type":"object","properties":{"apps":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","example":"wordpress"},"name":{"type":"string","example":"WordPress"},"description":{"type":"string"}}}}}}}}},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/apps\/install":{"post":{"tags":["Ecosystem"],"summary":"Install an app","description":"Requires **manager** role.","requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["app_id"],"properties":{"app_id":{"type":"string","example":"wordpress"}}}}}},"responses":{"200":{"description":"Install started","content":{"application\/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"installing"},"app_id":{"type":"string"}}}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"422":{"$ref":"#\/components\/responses\/ValidationError"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/doctor":{"get":{"tags":["Ecosystem"],"summary":"Connectivity self-check","responses":{"200":{"description":"Diagnostic booleans for CLI \u2194 platform connectivity","content":{"application\/json":{"schema":{"type":"object","properties":{"internet":{"type":"boolean"},"api":{"type":"boolean"},"docker":{"type":"boolean"},"dns":{"type":"boolean"},"region":{"type":"string","example":"TR-1"},"cli_version":{"type":"string"}}}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects":{"get":{"tags":["Projects"],"summary":"List accessible projects","description":"Owned + shared projects, sorted by name. **Viewer** role is enough.","responses":{"200":{"description":"Project list","content":{"application\/json":{"schema":{"type":"object","properties":{"projects":{"type":"array","items":{"$ref":"#\/components\/schemas\/ProjectSummary"}}}}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"429":{"$ref":"#\/components\/responses\/Throttled"}}},"post":{"tags":["Projects"],"summary":"Create a project","description":"Requires **manager** role. A verified e-mail address is required first (403 otherwise).","requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","maxLength":255},"framework":{"type":"string","maxLength":50,"example":"laravel"},"port":{"type":"integer","minimum":1,"maximum":65535}}}}}},"responses":{"201":{"description":"Project created","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/ProjectSummary"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"403":{"$ref":"#\/components\/responses\/Forbidden"},"422":{"$ref":"#\/components\/responses\/ValidationError"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}":{"get":{"tags":["Projects"],"summary":"Project detail","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"responses":{"200":{"description":"Project detail","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/ProjectSummary"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/deploy":{"post":{"tags":["Deployments"],"summary":"Trigger a deploy","description":"Requires **manager** role. Rebuilds from the linked source (GitHub\/template) or the last uploaded artifact.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"responses":{"200":{"description":"Deploy queued","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/DeploymentSummary"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/deploys":{"get":{"tags":["Deployments"],"summary":"Deployment history","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"responses":{"200":{"description":"Recent deployments","content":{"application\/json":{"schema":{"type":"object","properties":{"deployments":{"type":"array","items":{"$ref":"#\/components\/schemas\/DeploymentSummary"}}}}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/deploys\/{deploymentId}":{"get":{"tags":["Deployments"],"summary":"Deployment status","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"},{"$ref":"#\/components\/parameters\/DeploymentId"}],"responses":{"200":{"description":"Deployment status","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/DeploymentSummary"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/deploys\/{deploymentId}\/logs":{"get":{"tags":["Deployments"],"summary":"Deployment build log","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"},{"$ref":"#\/components\/parameters\/DeploymentId"}],"responses":{"200":{"description":"Build log lines","content":{"application\/json":{"schema":{"type":"object","properties":{"logs":{"type":"string"}}}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/deploys\/{deploymentId}\/rollback":{"post":{"tags":["Deployments"],"summary":"Roll back to a deployment","description":"Requires **manager** role. Re-points traffic to a previous image \u2014 no rebuild.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"},{"$ref":"#\/components\/parameters\/DeploymentId"}],"responses":{"200":{"description":"Rollback started","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/DeploymentSummary"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/deploys\/{deploymentId}\/cancel":{"post":{"tags":["Deployments"],"summary":"Cancel a deployment","description":"Requires **manager** role. Queued deploys cancel immediately; running builds stop at the next checkpoint.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"},{"$ref":"#\/components\/parameters\/DeploymentId"}],"responses":{"200":{"description":"Cancel requested","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/DeploymentSummary"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/env":{"get":{"tags":["Environment"],"summary":"List environment variables","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"responses":{"200":{"description":"Environment variables","content":{"application\/json":{"schema":{"type":"object","properties":{"vars":{"type":"object","additionalProperties":{"type":"string"}}}}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}},"post":{"tags":["Environment"],"summary":"Set environment variables","description":"Requires **manager** role. Keys must match `^[A-Za-z_][A-Za-z0-9_]*$`.\nChanges take effect on the next deploy.\n","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["vars"],"properties":{"vars":{"type":"object","minProperties":1,"additionalProperties":{"type":"string","maxLength":20000,"nullable":true},"example":{"APP_ENV":"production","DB_PORT":"5432"}}}}}}},"responses":{"200":{"description":"Variables stored","content":{"application\/json":{"schema":{"type":"object","properties":{"updated":{"type":"boolean","example":true}}}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"422":{"$ref":"#\/components\/responses\/ValidationError"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/env\/{envKey}":{"delete":{"tags":["Environment"],"summary":"Delete an environment variable","description":"Requires **manager** role.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"},{"$ref":"#\/components\/parameters\/EnvKey"}],"responses":{"200":{"description":"Variable removed","content":{"application\/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean","example":true}}}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/upload":{"post":{"tags":["Deployments"],"summary":"Upload source artifact","description":"Requires **manager** role. Multipart upload of a `.tar.gz` source\nsnapshot (max **100 MB**). Pass `deploy=true` to deploy right after.\n","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"requestBody":{"required":true,"content":{"multipart\/form-data":{"schema":{"type":"object","required":["source"],"properties":{"source":{"type":"string","format":"binary","description":".tar.gz \/ .tgz archive (max 100 MB)"},"deploy":{"type":"string","enum":["true","false","1","0"],"description":"Deploy after upload"}}}}}},"responses":{"200":{"description":"Source uploaded (deploy queued if requested)"},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"422":{"$ref":"#\/components\/responses\/ValidationError"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/restart":{"post":{"tags":["Projects"],"summary":"Restart the process","description":"Requires **manager** role. Restarts the running container \u2014 no rebuild.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"responses":{"200":{"description":"Restart issued"},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/logs\/runtime":{"get":{"tags":["Logs & Metrics"],"summary":"Runtime logs","description":"Live application logs from the running container.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"responses":{"200":{"description":"Runtime log lines","content":{"application\/json":{"schema":{"type":"object","properties":{"logs":{"type":"string"}}}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/logs\/access":{"get":{"tags":["Logs & Metrics"],"summary":"Access log summary","description":"IP \/ user-agent \/ path breakdown for the project's own domains.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"responses":{"200":{"description":"Aggregated access stats","content":{"application\/json":{"schema":{"type":"object"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/metrics":{"get":{"tags":["Logs & Metrics"],"summary":"Resource metrics","description":"CPU \/ RAM \/ disk usage of the running container.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"responses":{"200":{"description":"Current metrics","content":{"application\/json":{"schema":{"type":"object"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/domains":{"get":{"tags":["Domains"],"summary":"List domains","description":"Domains attached to the project, with SSL state.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"responses":{"200":{"description":"Domain list","content":{"application\/json":{"schema":{"type":"object","properties":{"domains":{"type":"array","items":{"$ref":"#\/components\/schemas\/Domain"}}}}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}},"post":{"tags":["Domains"],"summary":"Attach a domain","description":"Requires **manager** role. Platform subdomains (`*.skyversal.xyz` etc.)\ncannot be attached \u2014 they are auto-assigned. SSL starts as `pending`;\ncall `verify` once DNS has propagated.\n","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["domain"],"properties":{"domain":{"type":"string","maxLength":255,"example":"www.example.com"}}}}}},"responses":{"200":{"description":"Domain attached","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Domain"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"409":{"description":"Domain already attached to this or another project","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"422":{"$ref":"#\/components\/responses\/ValidationError"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/domains\/{hostOrId}":{"delete":{"tags":["Domains"],"summary":"Detach a domain","description":"Requires **manager** role.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"},{"$ref":"#\/components\/parameters\/HostOrId"}],"responses":{"200":{"description":"Domain detached"},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/domains\/{hostOrId}\/verify":{"post":{"tags":["Domains"],"summary":"Verify domain (DNS + TLS)","description":"Requires **manager** role. Runs a real TLS handshake; writes `active` only when the certificate is live \u2014 never fabricated.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"},{"$ref":"#\/components\/parameters\/HostOrId"}],"responses":{"200":{"description":"Verification result","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Domain"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/backups":{"get":{"tags":["Backups"],"summary":"List backups","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"responses":{"200":{"description":"Backup list","content":{"application\/json":{"schema":{"type":"object","properties":{"backups":{"type":"array","items":{"$ref":"#\/components\/schemas\/Backup"}}}}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}},"post":{"tags":["Backups"],"summary":"Take a backup now","description":"Requires **manager** role. Runs asynchronously.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"responses":{"200":{"description":"Backup started"},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/backups\/{id}":{"delete":{"tags":["Backups"],"summary":"Delete a backup","description":"Requires **manager** role.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"},{"$ref":"#\/components\/parameters\/BackupId"}],"responses":{"200":{"description":"Backup deleted"},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/backups\/{id}\/download":{"get":{"tags":["Backups"],"summary":"Download a backup","description":"Returns the backup file for download. **Viewer** role is enough.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"},{"$ref":"#\/components\/parameters\/BackupId"}],"responses":{"200":{"description":"Backup file","content":{"application\/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/backups\/{id}\/restore":{"post":{"tags":["Backups"],"summary":"Restore a backup","description":"Requires **manager** role. **Destructive** \u2014 overwrites the live\ndatabase. Body `confirm` must exactly equal the backup's `filename`.\n","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"},{"$ref":"#\/components\/parameters\/BackupId"}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["confirm"],"properties":{"confirm":{"type":"string","description":"Must equal the backup filename exactly."}}}}}},"responses":{"200":{"description":"Restore started"},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"422":{"$ref":"#\/components\/responses\/ValidationError"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/backup-schedule":{"get":{"tags":["Backups"],"summary":"Read backup schedule","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"responses":{"200":{"description":"Current schedule","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/BackupSchedule"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}},"put":{"tags":["Backups"],"summary":"Set backup schedule","description":"Requires **manager** role. Retention derives from interval (hourly \u2192 3d, 3-hourly \u2192 7d, daily \u2192 30d). The newest successful backup is never pruned.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","properties":{"every_hours":{"type":"integer","description":"Interval in hours (e.g. 1, 3, 24). Omit to disable."}}}}}},"responses":{"200":{"description":"Schedule updated","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/BackupSchedule"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"422":{"$ref":"#\/components\/responses\/ValidationError"},"429":{"$ref":"#\/components\/responses\/Throttled"}}},"patch":{"tags":["Backups"],"summary":"Set backup schedule (PATCH)","description":"Same as PUT \u2014 both verbs accepted.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","properties":{"every_hours":{"type":"integer"}}}}}},"responses":{"200":{"description":"Schedule updated","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/BackupSchedule"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"422":{"$ref":"#\/components\/responses\/ValidationError"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/scale":{"get":{"tags":["Scale"],"summary":"Current resource limits","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"responses":{"200":{"description":"Effective limits + ceiling","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Scale"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}},"patch":{"tags":["Scale"],"summary":"Set resource limits","description":"Requires **manager** role. Values must stay **under** the plan +\nadd-on ceiling \u2014 raising the ceiling is billing's job; the API\nreturns **422** instead of silently clipping.\n","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","properties":{"ram_mb":{"type":"integer","minimum":128},"cpu_mcpu":{"type":"integer","minimum":100},"disk_mb":{"type":"integer","minimum":1024}}}}}},"responses":{"200":{"description":"Limits updated","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Scale"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"422":{"description":"Below minimum or above ceiling","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/cron":{"get":{"tags":["Cron"],"summary":"List scheduled jobs","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"responses":{"200":{"description":"Job list","content":{"application\/json":{"schema":{"type":"object","properties":{"jobs":{"type":"array","items":{"$ref":"#\/components\/schemas\/CronJob"}}}}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}},"post":{"tags":["Cron"],"summary":"Create a scheduled job","description":"Requires **manager** role. `cron_expression` is validated up front \u2014 an invalid expression is rejected instead of silently never running.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["name","command","cron_expression"],"properties":{"name":{"type":"string","maxLength":120},"command":{"type":"string","maxLength":8000},"cron_expression":{"type":"string","maxLength":120,"example":"0 3 * * *"},"timezone":{"type":"string","maxLength":64,"example":"Europe\/Istanbul"},"timeout_seconds":{"type":"integer","minimum":5,"maximum":3600,"default":300}}}}}},"responses":{"200":{"description":"Job created","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/CronJob"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"422":{"description":"Invalid cron expression or fields","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/cron\/{id}":{"delete":{"tags":["Cron"],"summary":"Delete a job","description":"Requires **manager** role.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"},{"$ref":"#\/components\/parameters\/CronId"}],"responses":{"200":{"description":"Job deleted"},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/cron\/{id}\/toggle":{"post":{"tags":["Cron"],"summary":"Enable \/ disable a job","description":"Requires **manager** role.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"},{"$ref":"#\/components\/parameters\/CronId"}],"responses":{"200":{"description":"New state","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/CronJob"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/cron\/{id}\/run":{"post":{"tags":["Cron"],"summary":"Run a job now","description":"Requires **manager** role. Fires the command immediately, outside the schedule.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"},{"$ref":"#\/components\/parameters\/CronId"}],"responses":{"200":{"description":"Job triggered"},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/mail":{"get":{"tags":["Mail"],"summary":"Mail service status","description":"Enabled state, sender domain, quota usage, DNS health.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"responses":{"200":{"description":"Mail status","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/MailStatus"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}},"put":{"tags":["Mail"],"summary":"Configure mail service","description":"Requires **manager** role. Set sender domain or toggle the service. Your MX records are never touched \u2014 inbound mail keeps working.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"requestBody":{"content":{"application\/json":{"schema":{"type":"object","properties":{"domain":{"type":"string","nullable":true,"description":"Sender domain; `null` returns to the platform subdomain."},"enabled":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Updated mail status + DNS records to publish","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/MailStatus"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"422":{"$ref":"#\/components\/responses\/ValidationError"},"429":{"$ref":"#\/components\/responses\/Throttled"}}},"patch":{"tags":["Mail"],"summary":"Configure mail service (PATCH)","description":"Same as PUT \u2014 both verbs accepted.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"requestBody":{"content":{"application\/json":{"schema":{"type":"object","properties":{"domain":{"type":"string","nullable":true},"enabled":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Updated mail status","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/MailStatus"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"422":{"$ref":"#\/components\/responses\/ValidationError"},"429":{"$ref":"#\/components\/responses\/Throttled"}}},"delete":{"tags":["Mail"],"summary":"Disable mail service","description":"Requires **manager** role. DNS records are kept \u2014 re-enable any time.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"responses":{"200":{"description":"Mail disabled"},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/mail\/relay":{"get":{"tags":["Mail"],"summary":"Relay (SMTP) credentials info","description":"Shared-relay SASL endpoint + username for direct SMTP use.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"responses":{"200":{"description":"Relay connection info","content":{"application\/json":{"schema":{"type":"object"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/mail\/dns":{"get":{"tags":["Mail"],"summary":"DNS records to publish","description":"SPF + DKIM records needed for the sender domain.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"responses":{"200":{"description":"DNS record list","content":{"application\/json":{"schema":{"type":"object"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/mail\/verify":{"get":{"tags":["Mail"],"summary":"Verify mail DNS","description":"Live check whether published DNS matches expected records.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"responses":{"200":{"description":"Per-record verification result","content":{"application\/json":{"schema":{"type":"object"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/mail\/usage":{"get":{"tags":["Mail"],"summary":"Quota usage + trend","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"responses":{"200":{"description":"Daily quota + recent trend","content":{"application\/json":{"schema":{"type":"object"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/mail\/log":{"get":{"tags":["Mail"],"summary":"Send history","description":"Message-ID based send log for the project.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"responses":{"200":{"description":"Send log entries","content":{"application\/json":{"schema":{"type":"object"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/mail\/test":{"post":{"tags":["Mail"],"summary":"Send a test e-mail","description":"Requires **manager** role. Fails cleanly when the service is off \u2014 enable first.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["to"],"properties":{"to":{"type":"string","format":"email","maxLength":190}}}}}},"responses":{"200":{"description":"Send result (`sent:false` with reason when disabled)","content":{"application\/json":{"schema":{"type":"object","properties":{"sent":{"type":"boolean"},"error":{"type":"string","nullable":true}}}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"422":{"$ref":"#\/components\/responses\/ValidationError"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/projects\/{projectKey}\/mail\/provision":{"post":{"tags":["Mail"],"summary":"Provision \/ repair mail service","description":"Requires **manager** role. (Re)creates SASL + DKIM for the project \u2014 idempotent repair path.","parameters":[{"$ref":"#\/components\/parameters\/ProjectKey"}],"responses":{"200":{"description":"Provisioned","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/MailStatus"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/databases":{"get":{"tags":["Databases"],"summary":"List databases","description":"Account-scoped databases owned by the token user.","responses":{"200":{"description":"Database list","content":{"application\/json":{"schema":{"type":"object","properties":{"databases":{"type":"array","items":{"$ref":"#\/components\/schemas\/TenantDatabase"}}}}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"429":{"$ref":"#\/components\/responses\/Throttled"}}},"post":{"tags":["Databases"],"summary":"Create a database","description":"Requires **manager**-equivalent account scope.","requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["engine"],"properties":{"engine":{"type":"string","example":"postgresql"},"name":{"type":"string"}}}}}},"responses":{"200":{"description":"Database created","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/TenantDatabase"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"422":{"$ref":"#\/components\/responses\/ValidationError"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/databases\/{id}":{"delete":{"tags":["Databases"],"summary":"Delete a database","parameters":[{"$ref":"#\/components\/parameters\/DbId"}],"responses":{"200":{"description":"Database deleted"},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/databases\/{id}\/credential":{"get":{"tags":["Databases"],"summary":"Database credentials","description":"Connection credentials. Accepts `project_303`, `standalone_8` or a bare `8` as id.","parameters":[{"$ref":"#\/components\/parameters\/DbId"}],"responses":{"200":{"description":"Host, port, name, user, password","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/DbCredential"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/redis":{"get":{"tags":["Redis"],"summary":"List Redis instances","description":"Account-scoped Redis instances owned by the token user.","responses":{"200":{"description":"Redis list","content":{"application\/json":{"schema":{"type":"object","properties":{"redis":{"type":"array","items":{"$ref":"#\/components\/schemas\/RedisInstance"}}}}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"429":{"$ref":"#\/components\/responses\/Throttled"}}},"post":{"tags":["Redis"],"summary":"Create a Redis instance","responses":{"200":{"description":"Redis created","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/RedisInstance"}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"422":{"$ref":"#\/components\/responses\/ValidationError"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}},"\/redis\/{id}":{"delete":{"tags":["Redis"],"summary":"Delete a Redis instance","parameters":[{"$ref":"#\/components\/parameters\/DbId"}],"responses":{"200":{"description":"Redis deleted"},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/Throttled"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Access token from the device authorization flow (`POST \/device\/token`)."}},"parameters":{"ProjectKey":{"name":"projectKey","in":"path","required":true,"description":"Project numeric **id** or **slug**.","schema":{"type":"string"}},"DeploymentId":{"name":"deploymentId","in":"path","required":true,"schema":{"type":"integer"}},"EnvKey":{"name":"envKey","in":"path","required":true,"schema":{"type":"string"}},"HostOrId":{"name":"hostOrId","in":"path","required":true,"description":"Domain host (`www.example.com`) or numeric id.","schema":{"type":"string"}},"BackupId":{"name":"id","in":"path","required":true,"schema":{"type":"integer"}},"CronId":{"name":"id","in":"path","required":true,"schema":{"type":"integer"}},"DbId":{"name":"id","in":"path","required":true,"description":"`project_303`, `standalone_8` or bare numeric id.","schema":{"type":"string"}}},"responses":{"Unauthorized":{"description":"Missing, invalid or expired bearer token","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"},"example":{"message":"Unauthenticated."}}}},"Forbidden":{"description":"Authenticated but not allowed (e.g. unverified e-mail)","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"NotFound":{"description":"Resource not found **or** no access (existence is not leaked)","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"ValidationError":{"description":"Request validation failed (Laravel `errors` map)","content":{"application\/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}}}}}},"Throttled":{"description":"Rate limit exceeded \u2014 see the bucket table above","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"},"example":{"message":"Too Many Attempts."}}}}},"schemas":{"Error":{"type":"object","properties":{"message":{"type":"string"},"error":{"type":"string"}}},"OAuthError":{"type":"object","properties":{"error":{"type":"string","enum":["invalid_grant","expired_token","authorization_pending","access_denied"]}}},"DeviceCodeResponse":{"type":"object","properties":{"device_code":{"type":"string"},"user_code":{"type":"string","example":"ABCD-1234"},"verification_uri":{"type":"string","format":"uri","example":"https:\/\/skyversal.com\/device"},"expires_in":{"type":"integer","example":900},"interval":{"type":"integer","example":5,"description":"Poll interval (seconds)"}}},"TokenResponse":{"type":"object","properties":{"access_token":{"type":"string"},"token_type":{"type":"string","example":"Bearer"},"user":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string","format":"email"}}},"workspace":{"type":"object","properties":{"name":{"type":"string"}}},"plan":{"type":"string","example":"Developer"},"region":{"type":"string","example":"TR-1"}}},"WhoamiResponse":{"type":"object","properties":{"user":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string","format":"email"}}},"workspace":{"type":"object","properties":{"name":{"type":"string"}}},"plan":{"type":"string","example":"Developer"},"region":{"type":"string","example":"TR-1"},"cli_version":{"type":"string"},"api_version":{"type":"string","example":"v1"},"authenticated":{"type":"boolean","example":true}}},"ProjectSummary":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"slug":{"type":"string"},"status":{"type":"string"}}},"DeploymentSummary":{"type":"object","properties":{"id":{"type":"integer"},"status":{"type":"string","enum":["queued","running","live","failed","cancelled","superseded"]},"created_at":{"type":"string","format":"date-time"}}},"Domain":{"type":"object","properties":{"id":{"type":"integer"},"domain":{"type":"string","example":"www.example.com"},"status":{"type":"string","enum":["pending","active","failed"],"description":"SSL\/provisioning state \u2014 written by real TLS verification only."}}},"Backup":{"type":"object","properties":{"id":{"type":"integer"},"filename":{"type":"string"},"size_bytes":{"type":"integer"},"created_at":{"type":"string","format":"date-time"}}},"BackupSchedule":{"type":"object","properties":{"enabled":{"type":"boolean"},"every_hours":{"type":"integer"},"retention_days":{"type":"integer"}}},"CronJob":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"command":{"type":"string"},"cron_expression":{"type":"string"},"timezone":{"type":"string"},"enabled":{"type":"boolean"}}},"Scale":{"type":"object","properties":{"ram_mb":{"type":"integer"},"cpu_mcpu":{"type":"integer"},"disk_mb":{"type":"integer"},"ceiling":{"type":"object","properties":{"ram_mb":{"type":"integer"},"cpu_mcpu":{"type":"integer"},"disk_mb":{"type":"integer"}}}}},"MailStatus":{"type":"object","properties":{"enabled":{"type":"boolean"},"domain":{"type":"string","nullable":true},"quota_daily":{"type":"integer","example":200},"dns":{"type":"object"}}},"TenantDatabase":{"type":"object","properties":{"id":{"type":"string","example":"project_303"},"engine":{"type":"string","example":"postgresql"},"name":{"type":"string"}}},"DbCredential":{"type":"object","properties":{"host":{"type":"string"},"port":{"type":"integer"},"database":{"type":"string"},"username":{"type":"string"},"password":{"type":"string"}}},"RedisInstance":{"type":"object","properties":{"id":{"type":"string"},"host":{"type":"string"},"port":{"type":"integer"}}}}}}