{"openapi":"3.1.0","info":{"title":"Wattle Cloud API","version":"1.0.0","description":"Apps and managed databases on Wattle Cloud. Send an API token (Settings, API tokens) as Authorization: Bearer. A token acts as its maker in one workspace, at most at its scope: read (viewer), deploy (member) or full. Deletes made with a token wait 48 hours and can be undone. Errors come back as { error } with a 4xx status."},"servers":[{"url":"https://app.wattlecloud.com"}],"security":[{"token":[]}],"components":{"securitySchemes":{"token":{"type":"http","scheme":"bearer","description":"An API token, rdx_..."}}},"paths":{"/api/services":{"get":{"tags":["Services"],"summary":"List services","x-role":"viewer","x-scope":"read","parameters":[],"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{"services":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"kind":{"type":"string","enum":["app","db"]},"engine":{"type":"string","description":"A database's engine: postgres, mysql, mariadb, valkey, clickhouse."},"status":{"type":"string","enum":["creating","starting","running","stopped","crashed","failed","deleting"]},"statusDetail":{"type":"string","description":"Why it is in that state; lines after the first are what it printed."},"host":{"type":["string","null"],"description":"An app's web address."},"internal":{"type":"string","description":"Its address on the environment's private network, name.internal:port."},"region":{"type":"string"},"worker":{"type":"boolean"},"cron":{"type":"string"},"repo":{"type":["string","null"]},"branch":{"type":["string","null"]},"deleteAt":{"type":["string","null"],"description":"Deleted with a token: removed at this time unless undeleted."}},"required":["id","name","kind","status"]}}},"required":["services"]}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"post":{"tags":["Services"],"summary":"Create a database or an app","description":"A database: { kind: \"db\", engine }. An app from an image: { kind: \"app\", image, port }. From GitHub: { kind: \"app\", github: { installationId, repoId, branch } }.","x-role":"member","x-scope":"deploy","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"type":"string","enum":["db","app"]},"engine":{"type":"string","description":"postgres, mysql, mariadb, valkey or clickhouse."},"name":{"type":"string","description":"Lowercase letters, digits and dashes; made up when left out."},"region":{"type":"string","description":"bne (Brisbane)."},"image":{"type":"string"},"port":{"type":"integer"},"volumePath":{"type":"string","description":"Where an app keeps files that should last."},"worker":{"type":"boolean","description":"No web address (a Discord bot)."},"cron":{"type":"string","description":"A schedule, in UTC, for a job."},"github":{"type":"object","properties":{"installationId":{"type":"integer"},"repoId":{"type":"integer"},"branch":{"type":"string"},"rootDir":{"type":"string"},"builder":{"type":"string","enum":["railpack","dockerfile"]}}}}}}}},"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"server":{"type":"string"},"build":{"type":["integer","null"]}},"required":["id","name"]}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/services/{id}":{"get":{"tags":["Services"],"summary":"Get a service","description":"A database's connection details (with passwords), or an app's settings.","x-role":"member","x-scope":"deploy","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"patch":{"tags":["Services"],"summary":"Change a service","description":"An app's settings, or a database's public access or sizes.","x-role":"member","x-scope":"deploy","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"port":{"type":"integer"},"memoryMb":{"type":"integer"},"diskMb":{"type":"integer","description":"Databases."},"public":{"type":"boolean","description":"Databases: reachable with wattle db proxy."},"builder":{"type":"string","enum":["railpack","dockerfile"]},"rootDir":{"type":"string"},"dockerfilePath":{"type":"string"},"buildCommand":{"type":"string"},"startCommand":{"type":"string"},"preDeployCommand":{"type":"string"},"healthPath":{"type":"string"},"cronSchedule":{"type":"string"},"watchPaths":{"type":"array","items":{"type":"string"}},"autoDeploy":{"type":"boolean"},"branch":{"type":"string"}}}}}},"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"delete":{"tags":["Services"],"summary":"Delete a service","description":"With its data. Asked with a token, it stops now and is removed after 48 hours unless undeleted; the answer has deleteAt.","x-role":"admin","x-scope":"full","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"confirm":{"type":"string","description":"The service's name."}},"required":["confirm"]}}}},"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"pending":{"type":"boolean"},"deleteAt":{"type":["string","null"]}},"required":["ok"]}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/services/{id}/undelete":{"post":{"tags":["Services"],"summary":"Undo a delete","description":"Within the 48 hours of a delete made with a token.","x-role":"admin","x-scope":"full","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/services/{id}/actions":{"post":{"tags":["Services"],"summary":"Start, stop, restart or redeploy","x-role":"member","x-scope":"deploy","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["start","stop","restart","redeploy"]}},"required":["action"]}}}},"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/services/{id}/variables":{"get":{"tags":["Apps"],"summary":"Get an app's variables","x-role":"member","x-scope":"deploy","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{"own":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"}},"required":["key","value"]}},"shared":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"}},"required":["key","value"]}},"missing":{"type":"array","items":{"type":"string"}},"pending":{"type":"boolean"}},"required":["own","shared"]}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"put":{"tags":["Apps"],"summary":"Replace an app's variables","description":"The whole list, for the app (scope \"service\") or its environment (\"shared\"). They apply at the next deploy.","x-role":"member","x-scope":"deploy","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"scope":{"type":"string","enum":["service","shared"]},"variables":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"}},"required":["key","value"]}}},"required":["variables"]}}}},"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{"added":{"type":"array","items":{"type":"string"}},"changed":{"type":"array","items":{"type":"string"}},"removed":{"type":"array","items":{"type":"string"}}}}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/services/{id}/source":{"post":{"tags":["Apps"],"summary":"Deploy uploaded source","description":"A .tar.gz (50 MB at most) with everything under one top folder; it builds with the app's settings, then deploys. What `wattle up` does.","x-role":"member","x-scope":"deploy","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/gzip":{"schema":{"type":"string","format":"binary"}}}},"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer"},"status":{"type":"string"}},"required":["id"]}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/services/{id}/builds":{"get":{"tags":["Apps"],"summary":"List an app's builds","x-role":"viewer","x-scope":"read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{"builds":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"status":{"type":"string"}}}}},"required":["builds"]}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"post":{"tags":["Apps"],"summary":"Build from a source address","x-role":"member","x-scope":"deploy","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"sourceUrl":{"type":"string","description":"https address of a .tar.gz."},"builder":{"type":"string","enum":["railpack","dockerfile"]},"rootDir":{"type":"string"}},"required":["sourceUrl"]}}}},"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer"},"status":{"type":"string"}},"required":["id"]}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/services/{id}/deploy":{"post":{"tags":["Apps"],"summary":"Deploy the branch's newest commit","description":"Apps from GitHub.","x-role":"member","x-scope":"deploy","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/services/{id}/rollback":{"post":{"tags":["Apps"],"summary":"Roll back to an earlier build","x-role":"member","x-scope":"deploy","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"build":{"type":"integer"}},"required":["build"]}}}},"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/builds/{id}":{"get":{"tags":["Apps"],"summary":"Get a build and its log","x-role":"member","x-scope":"deploy","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer"},"status":{"type":"string","enum":["queued","building","succeeded","failed"]},"error":{"type":["string","null"]},"log":{"type":"string"}},"required":["id","status"]}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/services/{id}/runs":{"post":{"tags":["Apps"],"summary":"Run a command once in an app","description":"In its running container, with its variables and private network, for up to 15 minutes. What `wattle run` does.","x-role":"member","x-scope":"deploy","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"command":{"type":"string"}},"required":["command"]}}}},"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer"},"status":{"type":"string"}},"required":["id"]}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/services/{id}/runs/{rid}":{"get":{"tags":["Apps"],"summary":"Get a command's result","x-role":"member","x-scope":"deploy","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}},{"name":"rid","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer"},"command":{"type":"string"},"status":{"type":"string","enum":["queued","running","done","failed"]},"exitCode":{"type":["integer","null"]},"output":{"type":"string"}},"required":["id","status"]}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/services/{id}/logs":{"get":{"tags":["Observability"],"summary":"Read logs","x-role":"member","x-scope":"deploy","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}},{"name":"q","in":"query","required":false,"schema":{"type":"string"},"description":"A phrase to find (any case)."},{"name":"kind","in":"query","required":false,"schema":{"type":"string","enum":["app","http"]},"description":"http: an app's request log."},{"name":"after","in":"query","required":false,"schema":{"type":"string"},"description":"Lines after this time (ISO 8601), to follow."},{"name":"before","in":"query","required":false,"schema":{"type":"string"},"description":"Lines before this time, for older ones."}],"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{"store":{"type":"boolean"},"lines":{"type":"array","items":{"type":"object","properties":{"t":{"type":"string"},"msg":{"type":"string"}}}},"text":{"type":"string"}},"required":["store"]}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/services/{id}/metrics":{"get":{"tags":["Observability"],"summary":"Read metrics","x-role":"viewer","x-scope":"read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}},{"name":"range","in":"query","required":false,"schema":{"type":"string","enum":["1h","24h","7d","30d"]},"description":"How far back."}],"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/services/{id}/domains":{"get":{"tags":["Domains"],"summary":"List an app's domains","x-role":"viewer","x-scope":"read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{"domains":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"hostname":{"type":"string"},"kind":{"type":"string"},"status":{"type":"string"}}}}},"required":["domains"]}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"post":{"tags":["Domains"],"summary":"Add a custom domain","x-role":"member","x-scope":"deploy","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"hostname":{"type":"string"}},"required":["hostname"]}}}},"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/domains/{id}":{"delete":{"tags":["Domains"],"summary":"Remove a custom domain","x-role":"member","x-scope":"deploy","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/services/{id}/tunnel":{"post":{"tags":["Databases"],"summary":"Open a database tunnel","description":"A ticket for `wattle db proxy`.","x-role":"member","x-scope":"deploy","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"port":{"type":"integer"}}}}}},"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/services/{id}/queries":{"post":{"tags":["Databases"],"summary":"Run a statement","description":"As the read-only account, or (write: true) the owner, logged.","x-role":"member","x-scope":"deploy","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"statement":{"type":"string"},"write":{"type":"boolean"}},"required":["statement"]}}}},"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer"}},"required":["id"]}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/services/{id}/queries/{qid}":{"get":{"tags":["Databases"],"summary":"Get a statement's result","x-role":"member","x-scope":"deploy","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}},{"name":"qid","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"},"output":{"type":"string"},"truncated":{"type":"boolean"},"error":{"type":["string","null"]}},"required":["status"]}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/services/{id}/rotate":{"post":{"tags":["Databases"],"summary":"Rotate a database's passwords","x-role":"admin","x-scope":"full","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/services/{id}/backups":{"get":{"tags":["Backups"],"summary":"List backups and exports","x-role":"member","x-scope":"deploy","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{"exports":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"at":{"type":"integer"},"bytes":{"type":"integer"}}}}}}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"post":{"tags":["Backups"],"summary":"Back up or export now","x-role":"member","x-scope":"deploy","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"export":{"type":"boolean","description":"An export to download, rather than a backup."}}}}}},"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/services/{id}/restore":{"post":{"tags":["Backups"],"summary":"Restore","description":"{ into: \"new\", name?, base? | at? } makes a copy; { into: \"this\", confirm } replaces it, backing it up first.","x-role":"admin","x-scope":"full","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"into":{"type":"string","enum":["new","this"]},"name":{"type":"string"},"confirm":{"type":"string"},"base":{"type":"string"},"at":{"type":"integer","description":"Unix ms."}}}}}},"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/services/{id}/exports/{name}":{"get":{"tags":["Backups"],"summary":"Download an export","x-role":"member","x-scope":"deploy","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Done.","content":{"application/gzip":{"schema":{"type":"string","format":"binary"}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/billing":{"get":{"tags":["Workspace"],"summary":"Plan and what it includes","x-role":"viewer","x-scope":"read","parameters":[],"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/workspace/webhooks":{"get":{"tags":["Workspace"],"summary":"List webhooks","x-role":"admin","x-scope":"full","parameters":[],"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"post":{"tags":["Workspace"],"summary":"Add a webhook","x-role":"admin","x-scope":"full","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"type":"string","enum":["discord","slack","http"]},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string"}}},"required":["kind","url"]}}}},"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/workspace/webhooks/{id}":{"post":{"tags":["Workspace"],"summary":"Send a test delivery","x-role":"admin","x-scope":"full","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"delete":{"tags":["Workspace"],"summary":"Remove a webhook","x-role":"admin","x-scope":"full","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Done.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"4XX":{"description":"Refused: { error } says why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}}}}