{"openapi":"3.0.3","info":{"title":"Otwa Cloud API","version":"1.9.0","description":"REST API for Otwa Cloud — deploy and manage cloud servers, retrieve credentials, attach add-ons with prorated billing, and automate your infrastructure programmatically. Every endpoint is scope-gated via API-key permissions (`account:read`, `servers:read`, `servers:write`, `servers:destroy`, `billing:read`).","contact":{"name":"Otwa Cloud Support","email":"support@otwa.cloud","url":"https://otwa.cloud/support"},"license":{"name":"Proprietary"}},"servers":[{"url":"https://otwa.cloud/api/v1","description":"Production"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"API Key (otwa_...)","description":"Generate an API key from your dashboard at https://otwa.cloud/dashboard/api"}},"schemas":{"Error":{"type":"object","properties":{"message":{"type":"string","example":"Unauthorized"},"statusCode":{"type":"integer","example":401}}},"Account":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"email":{"type":"string","format":"email"},"firstName":{"type":"string"},"lastName":{"type":"string"},"balance":{"type":"string","example":"50.00"},"tier":{"type":"string","enum":["standard","pro","enterprise"]},"createdAt":{"type":"string","format":"date-time"}}},"Product":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"vcpu":{"type":"integer","example":2},"ramMb":{"type":"integer","example":4096},"diskGb":{"type":"integer","example":50},"bandwidthGb":{"type":"integer","example":1000},"monthlyPrice":{"type":"string","example":"10.00"},"region":{"type":"string","example":"OTWA IST"}}},"IpEntry":{"type":"object","properties":{"ip":{"type":"string","example":"198.51.100.11"},"gateway":{"type":"string","example":"198.51.100.1"},"netmask":{"type":"string","example":"255.255.255.0"},"cidr":{"type":"string","example":"198.51.100.0/24"},"region":{"type":"string","example":"OTWA IST"}}},"ServerSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"label":{"type":"string","example":"prod-1"},"hostname":{"type":"string","example":"srv1.example.com"},"status":{"type":"string","enum":["pending","provisioning","running","stopped","suspended","terminated"]},"ipAddress":{"type":"string","example":"198.51.100.10","nullable":true},"os":{"type":"string","example":"ubuntu-24.04"},"region":{"type":"string","example":"OTWA IST"},"vcpu":{"type":"integer","example":2},"ramMb":{"type":"integer","example":4096},"diskGb":{"type":"integer","example":50},"bandwidthGb":{"type":"integer","example":1000},"additionalIps":{"type":"array","items":{"$ref":"#/components/schemas/IpEntry"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"ServerDetail":{"allOf":[{"$ref":"#/components/schemas/ServerSummary"},{"type":"object","properties":{"networking":{"type":"object","properties":{"primaryIp":{"type":"string","nullable":true,"example":"198.51.100.10"},"gateway":{"type":"string","nullable":true,"example":"198.51.100.1"},"additionalIps":{"type":"array","items":{"$ref":"#/components/schemas/IpEntry"}}}},"specs":{"type":"object","properties":{"vcpu":{"type":"integer"},"ramMb":{"type":"integer"},"ramGb":{"type":"integer"},"diskGb":{"type":"integer"},"bandwidthGb":{"type":"integer"},"os":{"type":"string"},"region":{"type":"string"}}}}}]},"Credentials":{"type":"object","properties":{"username":{"type":"string","example":"root"},"password":{"type":"string","example":"G7xK2mP9qR3s"},"ip":{"type":"string","example":"198.51.100.10","nullable":true},"sshCommand":{"type":"string","example":"ssh root@198.51.100.10","nullable":true}}},"ServerStats":{"type":"object","nullable":true,"properties":{"cpu":{"type":"number","example":12.4,"description":"CPU usage %"},"memoryUsed":{"type":"integer","example":1024,"description":"Used RAM in MB"},"memoryTotal":{"type":"integer","example":4096,"description":"Total RAM in MB"},"diskUsed":{"type":"integer","example":10,"description":"Used disk in GB"},"diskTotal":{"type":"integer","example":50,"description":"Total disk in GB"}}},"DeployServerRequest":{"type":"object","required":["productId","os","osTemplate"],"properties":{"productId":{"type":"string","format":"uuid","description":"Plan ID from GET /products"},"os":{"type":"string","example":"ubuntu-24.04","description":"OS slug from GET /os-templates"},"osTemplate":{"type":"string","example":"a45a4d5e-bcdf-4c5a-9b51-ec213c26b881","description":"Internal template identifier — pass the value returned by /products or /os-templates"},"label":{"type":"string","example":"prod-1","description":"Optional display name; auto-generated if omitted"},"hostname":{"type":"string","example":"srv1.example.com","description":"Optional DNS-safe hostname; auto-generated if omitted"},"region":{"type":"string","example":"tr-ist-01","description":"Optional region slug from GET /regions; falls back to default pool"},"addons":{"type":"array","items":{"type":"string"},"example":["vpk7wjf5"],"description":"Optional list of add-on IDs (one per type)"}}},"Region":{"type":"object","properties":{"slug":{"type":"string","example":"tr-ist-01"},"name":{"type":"string","example":"Istanbul, Turkey"}}},"OsTemplate":{"type":"object","properties":{"id":{"type":"string","example":"ubuntu-24.04","description":"Pass this value as the os field on POST /servers"},"family":{"type":"string","example":"Ubuntu"},"label":{"type":"string","example":"Ubuntu 24.04 LTS"},"icon":{"type":"string","nullable":true},"color":{"type":"string","example":"#E95420","nullable":true}}},"ResellerState":{"type":"object","nullable":true,"properties":{"isReseller":{"type":"boolean","example":true},"brandSlug":{"type":"string","example":"acme-hosting","nullable":true},"brandName":{"type":"string","example":"Acme Hosting","nullable":true},"marginPercent":{"type":"number","example":20,"nullable":true},"status":{"type":"string","enum":["active","pending","disabled"],"nullable":true}}},"SsoLinkRequest":{"type":"object","properties":{"next":{"type":"string","example":"/dashboard/billing","description":"Optional path to land on after login. Must start with /. Defaults to /dashboard."}}},"SsoLinkResponse":{"type":"object","properties":{"url":{"type":"string","example":"https://otwa.cloud/sso?t=eyJhbGc...&next=%2Fdashboard"},"expiresIn":{"type":"integer","example":300,"description":"Seconds until the mint token expires"}}},"PasswordResetResponse":{"type":"object","properties":{"success":{"type":"boolean","example":true},"password":{"type":"string","example":"nL4kT8wQ2zF7mR9v","nullable":true,"description":"New admin password on success; omitted on failure"},"message":{"type":"string","example":"Guest agent is not responding","nullable":true,"description":"Failure reason when success is false"}}},"MessageResponse":{"type":"object","properties":{"message":{"type":"string"}}},"ReinstallRequest":{"type":"object","required":["os","osTemplate"],"properties":{"os":{"type":"string","maxLength":64,"example":"ubuntu","description":"Short OS family (\"ubuntu\", \"debian\", \"almalinux\", \"centos\", \"rocky\"…)."},"osTemplate":{"type":"string","maxLength":128,"example":"ubuntu-24.04","description":"Full OS template id from GET /os-templates (e.g. \"ubuntu-24.04\", \"debian-13\", \"almalinux-10.1\")."}}},"Addon":{"type":"object","properties":{"id":{"type":"string","example":"vpk7wjf5"},"name":{"type":"string","example":"8 Additional IPs"},"description":{"type":"string"},"type":{"type":"string","enum":["extra-ip","ip-class","backup"]},"price":{"type":"number","example":32},"proratedPrice":{"type":"number","example":15.47,"description":"Amount charged now based on days left in cycle"},"owned":{"type":"boolean","description":"true if the server already has an active add-on of the same type"}}},"AddonCatalog":{"type":"object","properties":{"addons":{"type":"array","items":{"$ref":"#/components/schemas/Addon"}},"daysRemaining":{"type":"integer","example":14},"billingCycleEnd":{"type":"string","format":"date-time","nullable":true}}},"PurchaseAddonRequest":{"type":"object","required":["addonId"],"properties":{"addonId":{"type":"string","example":"vpk7wjf5"}}},"PurchaseAddonResponse":{"type":"object","properties":{"addon":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"price":{"type":"number"}}},"prorated":{"type":"number","example":15.47,"description":"Amount charged to wallet"},"daysRemaining":{"type":"integer","example":14},"newMonthlyPrice":{"type":"number","example":41.99,"description":"Server monthly price after this add-on is included"},"allocatedIpCount":{"type":"integer","example":8,"description":"Zero for backup add-ons"}}}},"responses":{"Unauthorized":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"API key lacks required permission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/account":{"get":{"operationId":"getAccount","summary":"Get account info","description":"Returns the authenticated account details including balance and tier.","tags":["Account"],"security":[{"bearerAuth":["account:read"]}],"responses":{"200":{"description":"Account details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Account"}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/products":{"get":{"operationId":"listProducts","summary":"List available plans","description":"Returns all available server plans with pricing and specs. Reseller accounts see wholesale pricing.","tags":["Catalog"],"security":[{"bearerAuth":["account:read"]}],"responses":{"200":{"description":"List of plans","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Product"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/regions":{"get":{"operationId":"listRegions","summary":"List regions","description":"Flat list of every deployment region currently offered, with display-ready names. Use `slug` as the `region` value on POST /servers.","tags":["Catalog"],"security":[{"bearerAuth":["account:read"]}],"responses":{"200":{"description":"List of regions","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Region"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/os-templates":{"get":{"operationId":"listOsTemplates","summary":"List OS templates","description":"Every live operating system available at deploy time, flattened per version. The returned `id` is the value to pass as `os` on POST /servers. Credentials are stripped.","tags":["Catalog"],"security":[{"bearerAuth":["account:read"]}],"responses":{"200":{"description":"List of OS templates","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OsTemplate"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/reseller":{"get":{"operationId":"getResellerState","summary":"Get reseller state","description":"Returns the reseller state of the authenticated account — partner status, brand configuration, and margin settings. Null for non-reseller accounts.","tags":["Account"],"security":[{"bearerAuth":["account:read"]}],"responses":{"200":{"description":"Reseller state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResellerState"}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/sso":{"post":{"operationId":"issueAccountSso","summary":"Issue dashboard SSO link","description":"Mints a short-lived login URL that signs the authenticated user straight into their dashboard. Valid for 5 minutes, single-use.","tags":["Account"],"security":[{"bearerAuth":["account:read"]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SsoLinkRequest"}}}},"responses":{"200":{"description":"SSO link","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SsoLinkResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/servers":{"get":{"operationId":"listServers","summary":"List servers","description":"Returns all servers in your account, each enriched with primary IP and all additional IPs.","tags":["Servers"],"security":[{"bearerAuth":["servers:read"]}],"responses":{"200":{"description":"List of servers","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ServerSummary"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}},"post":{"operationId":"deployServer","summary":"Deploy server","description":"Provisions a new server. Deducts monthly cost from wallet balance. Server is ready in ~60 seconds. Pass an Idempotency-Key header to safely retry — repeat requests with the same key within 24h return the cached response instead of creating a duplicate server.","tags":["Servers"],"security":[{"bearerAuth":["servers:write"]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","example":"5fb2c8e1-1e79-4d93-9a1a-1f6a4e0c0b1e"},"description":"Opaque key (any unique string). Cached for 24 hours."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeployServerRequest"}}}},"responses":{"201":{"description":"Server created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerSummary"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"Insufficient wallet balance"}}}},"/servers/{id}":{"get":{"operationId":"getServer","summary":"Get server","description":"Returns full server details including structured networking and specs blocks.","tags":["Servers"],"security":[{"bearerAuth":["servers:read"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Server detail","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerDetail"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"operationId":"terminateServer","summary":"Terminate server","description":"Permanently destroys the server, powers it off, and releases all IPs (including class blocks). Irreversible. Requires the `servers:destroy` scope — NOT included in the default scope set; opt in when creating the key.","tags":["Servers"],"security":[{"bearerAuth":["servers:destroy"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Termination queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/servers/{id}/credentials":{"get":{"operationId":"getServerCredentials","summary":"Get SSH credentials","description":"Returns the root username, password, primary IP, and a ready-to-use SSH command.","tags":["Servers"],"security":[{"bearerAuth":["servers:read"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Server credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Credentials"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/servers/{id}/stats":{"get":{"operationId":"getServerStats","summary":"Live resource stats","description":"Returns real-time CPU, RAM, and disk usage. Only available when server status is running.","tags":["Servers"],"security":[{"bearerAuth":["servers:read"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Live stats or null if unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerStats"}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/servers/{id}/label":{"patch":{"operationId":"renameServer","summary":"Rename server","description":"Updates the display label of a server.","tags":["Servers"],"security":[{"bearerAuth":["servers:write"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["label"],"properties":{"label":{"type":"string"}}}}}},"responses":{"200":{"description":"Updated server","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerSummary"}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/servers/{id}/addons":{"get":{"operationId":"listServerAddons","summary":"List available add-ons","description":"Returns every add-on offered by the server's plan, each annotated with the prorated price to charge now and whether an add-on of that type is already active on this server. Only one add-on per type (extra-ip, ip-class, backup) can be active at a time.","tags":["Servers"],"security":[{"bearerAuth":["servers:read"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Add-on catalog for the server","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddonCatalog"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"post":{"operationId":"purchaseServerAddon","summary":"Purchase add-on","description":"Attaches an add-on to an already-running server. Charges the prorated amount to your wallet immediately and bumps the server's monthly price so the full add-on cost is included in future renewals. For extra-ip and ip-class add-ons the new IPs are routed to the VM at the network layer — you must configure them inside the guest OS yourself. Fails with 400 if an add-on of the same type is already active or if wallet balance is insufficient.","tags":["Servers"],"security":[{"bearerAuth":["servers:write"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseAddonRequest"}}}},"responses":{"200":{"description":"Add-on attached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseAddonResponse"}}}},"400":{"description":"Duplicate type, insufficient balance, or allocation failed (wallet refunded)"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/servers/{id}/power/{action}":{"post":{"operationId":"serverPowerAction","summary":"Power action","description":"Perform a power action on a server: start, stop, or reboot.","tags":["Servers"],"security":[{"bearerAuth":["servers:write"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"action","in":"path","required":true,"schema":{"type":"string","enum":["start","stop","reboot"]}}],"responses":{"200":{"description":"Action queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/servers/{id}/reinstall":{"post":{"operationId":"reinstallServer","summary":"Reinstall server","description":"Wipes the server disk and rebuilds it from a new OS template. All data is lost. Preserves server ID, label, region, primary IPv4, and attached add-ons. Billing and subscription are unchanged. A fresh root/administrator password is generated and surfaced via GET /servers/{id}/credentials after the reinstall completes. Requires `servers:destroy` — same scope as termination, because the blast radius on the customer side is equivalent. Accepts an optional `Idempotency-Key` header; replaying the same key within 24 h returns the cached response instead of queuing a second reinstall.","tags":["Servers"],"security":[{"bearerAuth":["servers:destroy"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":128},"description":"Replay-safe key scoped to (user, server). Retries within 24 h return the cached response."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReinstallRequest"}}}},"responses":{"200":{"description":"Reinstall queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"400":{"description":"Server not provisioned yet, or unknown OS template"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Server is suspended by administrator"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/servers/{id}/password-reset":{"post":{"operationId":"resetServerPassword","summary":"Rotate root password","description":"Generates a fresh admin password (root on Linux, Administrator on Windows) and returns it. Applied live through the guest agent — no reboot required. Only available on running servers with the agent installed.","tags":["Servers"],"security":[{"bearerAuth":["servers:write"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Password rotated (check success field for outcome)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordResetResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/servers/{id}/sso":{"post":{"operationId":"issueServerSso","summary":"Issue server-scoped SSO link","description":"Mints a 5-minute single-use login URL that drops the authenticated user directly on this server's detail page. Useful for deep-linking from a billing portal or helpdesk into a specific server.","tags":["Servers"],"security":[{"bearerAuth":["servers:read"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SsoLinkRequest"}}}},"responses":{"200":{"description":"SSO link","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SsoLinkResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}}},"tags":[{"name":"Account","description":"Profile, balance, reseller state, and SSO links"},{"name":"Catalog","description":"Plans, regions, and OS templates"},{"name":"Servers","description":"Deploy, manage, and monitor cloud servers"}]}