{"openapi":"3.1.0","info":{"title":"HTX Cloud Portal API","version":"2.0.0","description":"Create and manage HTX Cloud services with an API token. Create a token at Account → API tokens and send it as `Authorization: Bearer <token>`.\n\n**Permissions** — choose any combination of `read`, `create`, `update`, and `delete` for databases, functions, proxies, cloud, apps, registry, web security, and notifications. Out-of-permission calls return `403`. Account/token administration, operator/reseller APIs, and payment mutations are excluded.\n\n**Rate limit** — requests are limited per token (default 120/min). Exceeding it returns `429` with a `Retry-After` header."},"servers":[{"url":"https://portal.vinacis.com"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"API token (htx_…). Its service/action permission matrix is fixed at creation and preserved on rotation."}},"schemas":{"Database":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"engine":{"type":"string","examples":["postgresql","mysql","mongodb","clickhouse"]},"sku":{"type":"string"},"status":{"type":"string"},"cluster_target":{"type":"string"}}},"CreateDatabase":{"type":"object","required":["name","engine","sku"],"properties":{"name":{"type":"string"},"engine":{"type":"string","enum":["postgresql","mysql","mongodb","clickhouse"]},"sku":{"type":"string","description":"e.g. db-s-1vcpu-2gb, ch-s-1vcpu-2gb"}}},"FunctionImage":{"type":"string","minLength":3,"description":"Container image reference. A tag or @sha256: digest is accepted for any registry; a digest is recommended for CD. Private registry.fn.vinacis.com/<tenant>/... images require Registry credentials, and PATCH attaches the pull secret to an existing function.","examples":["nginx:1.27","ghcr.io/example/api@sha256:0123456789abcdef","registry.fn.vinacis.com/d31bf6ce/api:release"]},"FunctionVisibility":{"type":"string","enum":["public","cluster-local"],"default":"public","description":"cluster-local means the function is not reachable from the internet. It is not tenant isolation because functions share a Kubernetes namespace."},"CreateFunction":{"type":"object","required":["name","image","sku"],"properties":{"name":{"type":"string"},"image":{"$ref":"#/components/schemas/FunctionImage"},"sku":{"type":"string"},"env":{"type":"object","additionalProperties":{"type":"string"}},"visibility":{"$ref":"#/components/schemas/FunctionVisibility"},"runtimeProfile":{"type":"string","enum":["serverless","always-on-singleton"],"default":"serverless"}}},"PatchFunction":{"type":"object","minProperties":1,"additionalProperties":false,"properties":{"image":{"$ref":"#/components/schemas/FunctionImage"},"env":{"type":"object","maxProperties":64,"propertyNames":{"pattern":"^[A-Z_][A-Z0-9_]{0,63}$"},"additionalProperties":{"type":"string","maxLength":4096}},"visibility":{"$ref":"#/components/schemas/FunctionVisibility"},"restoreLatestTraffic":{"type":"boolean"},"sku":{"type":"string"},"runtimeProfile":{"type":"string","enum":["serverless","always-on-singleton"]}}},"Error":{"type":"object","properties":{"error":{"type":"string"}}}}},"paths":{"/api/portal/databases":{"get":{"summary":"List databases","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Database"}}}}},"401":{"description":"Unauthorized"}}},"post":{"summary":"Create a database","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDatabase"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Database"}}}},"401":{"description":"Unauthorized"},"402":{"description":"Plan quota reached"},"403":{"description":"Token permission matrix does not permit this service/action"},"429":{"description":"Rate limit exceeded (see Retry-After)"}}}},"/api/portal/databases/{name}":{"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"get":{"summary":"Get a database","responses":{"200":{"description":"OK"},"404":{"description":"Not found"}}},"delete":{"summary":"Delete a database","responses":{"200":{"description":"Deleted"},"404":{"description":"Not found"}}}},"/api/portal/databases/{name}/backups":{"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"get":{"summary":"List backups / snapshots","responses":{"200":{"description":"OK"}}},"post":{"summary":"Trigger an on-demand backup","responses":{"201":{"description":"Started"}}}},"/api/portal/functions":{"get":{"summary":"List serverless functions","responses":{"200":{"description":"OK"}}},"post":{"summary":"Create a serverless function","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFunction"}}}},"responses":{"201":{"description":"Created"},"400":{"description":"Invalid request"},"401":{"description":"Unauthorized"},"403":{"description":"Token permission matrix does not permit functions:create"},"409":{"description":"Name or cluster-capacity conflict"}}}},"/api/portal/functions/{name}":{"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"get":{"summary":"Get a serverless function","responses":{"200":{"description":"OK, including live image and visibility-derived URLs"},"404":{"description":"Not found"}}},"patch":{"summary":"Update a serverless function in one rollout","description":"Updates image, env, visibility, and/or latest-traffic mode with optimistic concurrency. cluster-local removes public ingress but is not tenant isolation.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchFunction"}}}},"responses":{"200":{"description":"Update accepted; inspect ready and rolloutPending"},"400":{"description":"Invalid request"},"401":{"description":"Unauthorized"},"403":{"description":"Token permission matrix does not permit functions:update"},"404":{"description":"Not found"},"409":{"description":"Unsafe state, missing credentials, or concurrent update"}}}},"/api/portal/proxies":{"get":{"summary":"List SOCKS5 proxies","responses":{"200":{"description":"OK"}}}}},"x-htx-service-permissions":{"services":["databases","functions","proxies","cloud","apps","registry","security","notifications"],"actions":["read","create","update","delete"],"protectedRouteFamilies":["account","admin","reseller","billing mutations"]}}