{"openapi":"3.0.3","info":{"title":"@faablecloud/api","description":"Faable Cloud Deploy Service","version":"3.0.0","license":{"name":"private","url":"https://www.faable.com/legal/privacy-policy"},"termsOfService":"https://www.faable.com/legal/terms"},"components":{"headers":{"":{"description":"Faable Team"}},"securitySchemes":{"faable_user":{"type":"oauth2","description":"Interactive user login — the flow the dashboard uses. Authorization Code with PKCE (S256); the auth server accepts public clients, so no client secret is involved. To use the Authorize button here, the client you enter must have `<this-host>/docs/oauth2-redirect.html` registered as a redirect URI.","flows":{"authorizationCode":{"authorizationUrl":"https://faable.auth.faable.link/authorize","tokenUrl":"https://faable.auth.faable.link/oauth/token","refreshUrl":"https://faable.auth.faable.link/oauth/token","scopes":{"openid":"Sign in and read the subject claim","profile":"Basic profile of the signed-in user","email":"Email address of the signed-in user","offline_access":"Issue a refresh token"}}}},"faable_cli":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Token issued by `faable login`, which runs the OAuth 2.0 device authorization grant (RFC 8628: `POST /oauth/device/code`, then `POST /oauth/token` with `grant_type=urn:ietf:params:oauth:grant-type:device_code`). OpenAPI has no device flow object, so paste the access token directly."},"faable_m2m":{"type":"oauth2","description":"Machine-to-machine identity (service accounts, the SDKs). The token carries no user, so endpoints scoped to a user reject it.","flows":{"clientCredentials":{"tokenUrl":"https://faable.auth.faable.link/oauth/token","scopes":{}}}},"faable_machine":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Token obtained by exchanging a GitHub Actions OIDC token (RFC 8693 token exchange) — how a workflow deploys without a stored secret. The remote builder also authenticates as a machine, with an opaque `fbt_…` build token minted per deployment and scoped to its app; those are issued by the platform and cannot be requested here."},"apikey_basic":{"type":"http","scheme":"basic","description":"API key as HTTP Basic: the key is the username, the password is empty (`Authorization: Basic base64(\"<key>:\")`). Preferred over the query parameter — it keeps the key out of URLs, logs and referrers."},"apikey":{"type":"apiKey","in":"query","name":"apikey","description":"API key as a query parameter. Accepted, but it travels in the URL, so prefer `apikey_basic`."}},"schemas":{"ProjectLimits":{"type":"object","required":["can_create","reason","message","free_projects","max_free_projects","upgradable_projects","new_account_window_ends_at","resolved"],"properties":{"can_create":{"type":"boolean"},"reason":{"anyOf":[{"type":"string","enum":["free_project_limit"]},{"type":"string","enum":["new_account_window"]},{"type":"null"}]},"message":{"anyOf":[{"type":"string"},{"type":"null"}]},"free_projects":{"anyOf":[{"type":"integer"},{"type":"null"}]},"max_free_projects":{"anyOf":[{"type":"integer"},{"type":"null"}]},"upgradable_projects":{"type":"array","items":{"type":"object","required":["id","name"],"properties":{"id":{"type":"string"},"name":{"type":"string"}}}},"new_account_window_ends_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"resolved":{"type":"boolean"}}},"ProjectInvite":{"type":"object","required":["emails"],"properties":{"emails":{"type":"array","items":{"type":"string","format":"email"},"minItems":1,"maxItems":10},"role":{"anyOf":[{"type":"string","enum":["owner"]},{"type":"string","enum":["member"]},{"type":"string","enum":["viewer"]}]}},"additionalProperties":false},"ProjectInviteResult":{"type":"object","required":["project","results"],"properties":{"project":{"$ref":"#/components/schemas/Project"},"results":{"type":"array","items":{"type":"object","required":["email","status"],"properties":{"email":{"type":"string"},"status":{"anyOf":[{"type":"string","enum":["invited"]},{"type":"string","enum":["invited_created_account"]},{"type":"string","enum":["already_member"]},{"type":"string","enum":["failed"]},{"type":"string","enum":["pending"]}]},"user_id":{"type":"string"},"error_code":{"type":"string"}}}}},"additionalProperties":false},"ProjectMember":{"type":"object","required":["user_id","role","name","email","picture"],"properties":{"user_id":{"type":"string"},"role":{"anyOf":[{"type":"string","enum":["owner"]},{"type":"string","enum":["collaborator"]}]},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"email":{"anyOf":[{"type":"string"},{"type":"null"}]},"picture":{"anyOf":[{"type":"string"},{"type":"null"}]}}},"ProjectMemberList":{"type":"object","required":["results"],"properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/ProjectMember"}}}},"ProjectInvitation":{"type":"object","required":["id","email","role","inviter_user_id","expires_at","created_at"],"properties":{"id":{"type":"string"},"email":{"type":"string"},"role":{"type":"string"},"inviter_user_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"expires_at":{"type":"string"},"created_at":{"type":"string"}}},"ProjectInvitationList":{"type":"object","required":["results"],"properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/ProjectInvitation"}}}},"ProjectActivityList":{"type":"object","required":["results","next"],"properties":{"results":{"type":"array","items":{"type":"object","required":["id","kind","at","actor","subject","email","role","previous_role"],"properties":{"id":{"type":"string"},"kind":{"type":"string"},"at":{"type":"string"},"actor":{"anyOf":[{"type":"object","required":["user_id","name","email"],"properties":{"user_id":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"email":{"anyOf":[{"type":"string"},{"type":"null"}]}}},{"type":"null"}]},"subject":{"anyOf":[{"type":"object","required":["user_id","name","email"],"properties":{"user_id":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"email":{"anyOf":[{"type":"string"},{"type":"null"}]}}},{"type":"null"}]},"email":{"anyOf":[{"type":"string"},{"type":"null"}]},"role":{"anyOf":[{"type":"string"},{"type":"null"}]},"previous_role":{"anyOf":[{"type":"string"},{"type":"null"}]}}}},"next":{"anyOf":[{"type":"string"},{"type":"null"}]}}},"ProjectInvitationAccept":{"type":"object","required":["token"],"properties":{"token":{"type":"string","minLength":1}},"additionalProperties":false},"ProjectInvitationAccepted":{"type":"object","required":["status","project_id"],"properties":{"status":{"anyOf":[{"type":"string","enum":["accepted"]},{"type":"string","enum":["already_member"]}]},"project_id":{"type":"string"}}},"MyProjectInvitation":{"type":"object","required":["id","project_id","project_name","role","inviter","expires_at","created_at"],"properties":{"id":{"type":"string"},"project_id":{"type":"string"},"project_name":{"type":"string"},"role":{"type":"string"},"inviter":{"anyOf":[{"type":"object","required":["name","email"],"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"email":{"anyOf":[{"type":"string"},{"type":"null"}]}}},{"type":"null"}]},"expires_at":{"type":"string"},"created_at":{"type":"string"}}},"MyProjectInvitationList":{"type":"object","required":["email_verified","results"],"properties":{"email_verified":{"type":"boolean"},"results":{"type":"array","items":{"$ref":"#/components/schemas/MyProjectInvitation"}}}},"Project":{"type":"object","required":["id","name","description","slug","team","collaborators","createdAt"],"properties":{"id":{"type":"string","description":"Project ID"},"name":{"type":"string"},"description":{"type":"string"},"slug":{"type":"string"},"team":{"type":"string"},"user_id":{"type":"string"},"collaborators":{"type":"array","items":{"type":"string"}},"members":{"type":"array","items":{"type":"object","required":["user_id","role"],"properties":{"user_id":{"type":"string"},"role":{"type":"string"}}}},"product_intent":{"anyOf":[{"type":"string","enum":["deploy"]},{"type":"string","enum":["auth"]},{"type":"string","enum":["undecided"]}]},"metadata":{"type":"object","properties":{"problem":{"type":"string"}},"additionalProperties":true},"owner_suspended":{"type":"boolean"},"owner_email":{"type":"string"},"organization":{"type":"string","nullable":true,"default":null},"createdAt":{"type":"string","description":"Project creation date"},"updatedAt":{"type":"string","description":"Project updated date"}},"description":"Project","additionalProperties":false},"EventProjectCreate":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["project.create"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/Project"}},"description":"Event Project Create","additionalProperties":false},"EventProjectUpdate":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["project.update"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/Project"}},"description":"Event Project Update","additionalProperties":false},"EventProjectDelete":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["project.delete"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/Project"}},"description":"Event Project Delete","additionalProperties":false},"ProjectCreate":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":3,"maxLength":50,"pattern":"^[A-Za-z0-9][A-Za-z0-9 ._'()&-]*[A-Za-z0-9)]$"},"description":{"type":"string"},"collaborators":{"type":"array","items":{"type":"string"}},"product_intent":{"anyOf":[{"type":"string","enum":["deploy"]},{"type":"string","enum":["auth"]},{"type":"string","enum":["undecided"]}]},"metadata":{"type":"object","properties":{"problem":{"type":"string","maxLength":1024}},"additionalProperties":false}},"additionalProperties":false},"ProjectUpdate":{"type":"object","properties":{"name":{"type":"string","minLength":3,"maxLength":50,"pattern":"^[A-Za-z0-9][A-Za-z0-9 ._'()&-]*[A-Za-z0-9)]$"},"description":{"type":"string"},"metadata":{"type":"object","properties":{"problem":{"type":"string","maxLength":1024}},"additionalProperties":false}},"additionalProperties":false},"AppStatus":{"type":"object","required":["phase","deployment","deployment_previous","last_commited_purge","last_commited_purge_key","last_commited_purge_service","versions","secrets_revision"],"properties":{"phase":{"enum":["UNKNOWN","QUEUED","BUILDING","ERROR","INITIALIZING","READY","CANCELED","TERMINATING"],"description":"High-level summary of where the Database is in its lifecycle","default":"UNKNOWN"},"deployment":{"anyOf":[{"type":"string","description":"Deployment id for this app"},{"type":"object","properties":{},"additionalProperties":true,"description":"Full Deployment object, present only when expanded via `?expand=status.deployment`"},{"type":"null"}],"default":null},"deployment_previous":{"anyOf":[{"type":"string","description":"Previous deployment id for this app"},{"type":"object","properties":{},"additionalProperties":true,"description":"Full Deployment object, present only when expanded via `?expand=status.deployment`"},{"type":"null"}],"default":null},"last_commited_purge":{"type":"string","description":"Last commited cache Purge","nullable":true,"default":null},"last_commited_purge_key":{"type":"string","description":"Last commited cache Purge key","nullable":true,"default":null},"last_commited_purge_service":{"type":"string","description":"Last commited cache Purge key","nullable":true,"default":null},"versions":{"anyOf":[{"type":"array","items":{"type":"object","required":["deployment","role","promoted_at","drain_until"],"properties":{"deployment":{"type":"string","description":"Deployment id of this version"},"role":{"anyOf":[{"type":"string","enum":["promoted"]},{"type":"string","enum":["coexisting"]}],"description":"promoted = current production; coexisting = draining"},"promoted_at":{"type":"string","description":"ISO date this version was promoted"},"drain_until":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"GC-eligibility deadline for a coexisting version (demotion + drain window). Null on the promoted entry."}},"additionalProperties":false}},{"type":"null"}],"default":null,"description":"Live versions of the app: the promoted one plus any draining (coexistence) versions"},"cold_state":{"anyOf":[{"type":"string","enum":["asleep"]},{"type":"string","enum":["waking"]},{"type":"string","enum":["archived"]},{"type":"null"}],"description":"Scale-to-zero sub-state of the promoted deployment (admin-only)"},"archived_at":{"anyOf":[{"type":"string","description":"ISO time"},{"type":"null"}],"description":"When the platform archived the app (admin-only); null once rehydrated"},"secrets_revision":{"type":"string","description":"Opaque revision token; changes whenever the app secrets change","nullable":true,"default":null},"resource_health":{"anyOf":[{"type":"object","required":["state","deployment","at"],"properties":{"state":{"anyOf":[{"type":"string","description":"The runtime exceeded its memory limit (OOMKilled)","enum":["memory_exceeded"]},{"type":"string","description":"The pod was evicted for exceeding its disk (ephemeral-storage) limit","enum":["disk_exceeded"]}],"description":"Which limit was hit"},"deployment":{"type":"string","description":"Deployment whose pod tripped the limit"},"at":{"type":"string","description":"ISO time the limit was last observed being hit"},"container":{"type":"string","description":"Container that was OOMKilled","nullable":true}},"description":"Resource-limit breach observed at runtime"},{"type":"null"}],"description":"Runtime resource-pressure flag (admin/owner-facing upgrade prompt)"},"runtime_health":{"anyOf":[{"type":"object","required":["state","deployment","at"],"properties":{"state":{"type":"string","description":"The container keeps exiting and being restarted (CrashLoopBackOff)","enum":["crash_loop"]},"deployment":{"type":"string","description":"Deployment whose pod is crash-looping"},"at":{"type":"string","description":"ISO time the crash loop was last observed"},"container":{"type":"string","description":"Container that is crash-looping","nullable":true},"restarts":{"type":"integer","description":"Restart count when observed","nullable":true},"exit_code":{"type":"integer","description":"Last exit code, when reported","nullable":true},"reason":{"type":"string","description":"K8s waiting/terminated reason","nullable":true}},"description":"Runtime crash loop observed on the live version"},{"type":"null"}],"description":"Runtime health flag for the promoted deployment (owner-facing outage signal)"}}},"AppRuntimeStrategy":{"type":"string"},"AppRuntime":{"enum":["node","php","python"]},"AppMode":{"enum":["standard","coexistence"],"default":"standard"},"AppBuildMode":{"enum":["local","remote"]},"AppDeployOutput":{"enum":["image","artifact"]},"AppCoexistenceRouting":{"enum":["cookie","path"],"default":"cookie"},"App":{"type":"object","required":["id","team","name","description","slug","url","active","runtime","runtime_strategy","mode","instance_type","region","status","metadata","createdAt"],"properties":{"id":{"type":"string","description":"App ID"},"team":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"slug":{"type":"string"},"url":{"type":"string"},"active":{"type":"boolean"},"disabled_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Reason the app was administratively disabled. Null when the app is active."},"runtime":{"anyOf":[{"enum":["node","php","python"]},{"type":"null"}]},"runtime_strategy":{"anyOf":[{"type":"string"},{"type":"null"}]},"detected":{"anyOf":[{"type":"object","required":["buildpack","runtime"],"properties":{"buildpack":{"type":"string"},"framework":{"type":"string"},"runtime":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"version":{"type":"string"},"source":{"type":"string"}}},"dockerfile_ignored":{"type":"boolean"},"process_type":{"type":"string","enum":["worker"]}}},{"type":"null"}]},"runtime_pin":{"anyOf":[{"type":"object","required":["name","version","pinned_at","source"],"properties":{"name":{"type":"string"},"version":{"type":"string"},"pinned_at":{"type":"string","format":"date-time"},"source":{"anyOf":[{"type":"string","enum":["first_build"]},{"type":"string","enum":["backfill"]},{"type":"string","enum":["user_upgrade"]},{"type":"string","enum":["retirement"]}]}}},{"type":"null"}]},"mode":{"$ref":"#/components/schemas/AppMode"},"build_mode":{"anyOf":[{"enum":["local","remote"]},{"type":"null"}]},"deploy_output":{"anyOf":[{"enum":["image","artifact"]},{"type":"null"}]},"root_dir":{"anyOf":[{"type":"string"},{"type":"null"}]},"gitlab_ci_project_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"git_provider":{"anyOf":[{"type":"string","enum":["github"]},{"type":"string","enum":["gitlab"]},{"type":"null"}]},"gitlab_project_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"gitlab_project_path":{"anyOf":[{"type":"string"},{"type":"null"}]},"gitlab_branch":{"anyOf":[{"type":"string"},{"type":"null"}]},"coexistence_routing":{"anyOf":[{"enum":["cookie","path"],"default":"cookie"},{"type":"null"}]},"coexistence_keep_versions":{"anyOf":[{"type":"number","description":"K: how many versions stay live under mode=coexistence (active + K-1 draining)"},{"type":"null"}]},"coexistence_drain_hours":{"anyOf":[{"type":"number","description":"Max hours a demoted version keeps serving pinned sessions before GC"},{"type":"null"}]},"kind":{"anyOf":[{"type":"string","enum":["code"]},{"type":"string","enum":["blueprint"]}],"description":"code: built from a repository or `faable deploy`. blueprint: run from a platform blueprint (WordPress…), no repository and no build. Fixed at creation."},"blueprint_id":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The blueprint a `kind: blueprint` app runs (blueprint id). Null for code apps."},"blueprint_version":{"anyOf":[{"type":"number"},{"type":"null"}]},"database_id":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A code app's managed database (database id), attached by Faable staff. Null for apps without one; a blueprint app's is on its installation."},"blueprint_runtime_key":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A blueprint app's base runtime chosen by Faable staff (`php:8.3`) instead of the blueprint's. Null = the blueprint's."},"instance":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Compute catalogue row the app runs on (computeinstance id). Replaces instance_type."},"instance_type":{"type":"string","description":"Faable instance types comprise varying combinations of CPU, memory, storage, and networking capacity. Name of `instance`; kept for existing clients."},"instance_resources":{"type":"object","required":["cpu","memory"],"properties":{"cpu":{"type":"string","description":"Kubernetes quantity, e.g. '600m'"},"memory":{"type":"string","description":"Kubernetes quantity, e.g. '512Mi'"}},"description":"The most CPU and memory the app can use on its instance (the limits). Read-only, derived from `instance`."},"instance_upgrades":{"type":"array","items":{"type":"object","required":["plan","instance","cpu","memory"],"properties":{"plan":{"type":"string","description":"free | hobby | pro"},"instance":{"type":"string"},"cpu":{"type":"string"},"memory":{"type":"string"}}},"description":"Each plan's default instance that gives more than this app's, smallest first. Read-only."},"region":{"enum":["gui1","poz1","ams1"],"description":"High-level summary of where the Database is in its lifecycle","default":"UNKNOWN"},"notify_deploy_success":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Success deploy emails (first deploy + every promote). Absent/null = enabled; false = muted. Failure emails are never gated by this."},"http3":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Whether HTTP/3 is advertised for this app. Absent/null = advertised; false = not advertised. Note it controls the advertisement, not the listener: clients that already discovered HTTP/3 may keep using it until the advertisement expires, and the server does not refuse it."},"status":{"$ref":"#/components/schemas/AppStatus"},"repository":{"type":"string"},"github_branch":{"type":"string"},"github_installation_id":{"type":"string"},"github_installation_state":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Platform GitHub access health, maintained by installation lifecycle webhooks. Absent/null = ok; 'deleted' | 'suspended' | 'repo_removed' = access broken until re-linked."},"github_repo_hidden":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The linked repository stopped being readable after the app was created, per the GitHub `repository` webhook. Absent/null = visible; 'deleted' | 'privatized' | 'archived' = the source was pulled after deploying (the analyzer re-scores the promoted deployment against our own build artifact)."},"github_disconnect_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Why the platform cleared the repository link automatically. Absent/null = never auto-disconnected (or re-linked since); 'push_flood' = the repo exceeded the hourly push limit and must be re-linked by the user."},"deploy_trigger":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Push-to-deploy trigger. Absent/null = 'workflow' (committed GitHub Action); 'webhook' = the push webhook creates deployments server-side (deploy v4)."},"deploy_gate":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Wait-for-CI gate for webhook triggers. Absent/null = deploy on push; 'ci' = deploy only after the commit's check suites complete green."},"autodeploy_paused":{"anyOf":[{"type":"object","required":["since","failures","failure_code","skipped_pushes","last_skipped_sha"],"properties":{"since":{"type":"string","format":"date-time"},"failures":{"type":"number"},"failure_code":{"type":"string"},"skipped_pushes":{"type":"number"},"last_skipped_sha":{"type":"string"}}},{"type":"null"}],"description":"Automatic deploys are paused: the app has never built and its last `failures` builds failed with the same `failure_code`. Pushes that do not touch build inputs are skipped until a manual deploy or a build that succeeds. Null/absent = not paused."},"metadata":{"type":"object","properties":{},"additionalProperties":true,"default":{}},"createdAt":{"type":"string","description":"App creation date"},"updatedAt":{"type":"string","description":"App updated date"}},"description":"App","additionalProperties":false},"EventAppCreate":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["app.create"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/App"}},"description":"Event App Create","additionalProperties":false},"EventAppUpdate":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["app.update"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/App"}},"description":"Event App Update","additionalProperties":false},"EventAppDelete":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["app.delete"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/App"}},"description":"Event App Delete","additionalProperties":false},"AppCreate":{"type":"object","required":["name"],"properties":{"name":{"type":"string","maxLength":255},"runtime":{"type":"string"},"description":{"type":"string"}},"additionalProperties":false},"AppUpdate":{"type":"object","properties":{"name":{"type":"string","maxLength":255},"description":{"type":"string"},"runtime":{"$ref":"#/components/schemas/AppRuntime"},"runtime_strategy":{"$ref":"#/components/schemas/AppRuntimeStrategy"},"instance":{"type":"string"},"instance_type":{"type":"string"},"notify_deploy_success":{"type":"boolean","nullable":true},"http3":{"type":"boolean","nullable":true}},"additionalProperties":false},"AppRepoLinkable":{"type":"object","required":["linkable"],"properties":{"linkable":{"type":"boolean"},"code":{"anyOf":[{"type":"string","enum":["repository_already_linked"]},{"type":"string","enum":["repository_taken_down"]}]},"message":{"type":"string"},"details":{"type":"object","required":["app_id"],"properties":{"app_id":{"type":"string"},"app_name":{"type":"string"},"project":{"type":"string"}}}},"description":"Repository link pre-check"},"DeployBranchResult":{"type":"object","required":["github_branch","workflow_synced"],"properties":{"github_branch":{"type":"string"},"workflow_synced":{"type":"boolean"}}},"RepoBranches":{"type":"object","required":["branches"],"properties":{"branches":{"type":"array","items":{"type":"string"}}}},"RepoWorkflows":{"type":"object","required":["workflows","deploy_workflow"],"properties":{"workflows":{"type":"array","items":{"type":"object","required":["name","path"],"properties":{"name":{"type":"string"},"path":{"type":"string"}}}},"deploy_workflow":{"anyOf":[{"type":"string"},{"type":"null"}]}}},"RepoConfig":{"type":"object","required":["root_dir","root_dir_source"],"properties":{"root_dir":{"anyOf":[{"type":"string"},{"type":"null"}]},"root_dir_source":{"anyOf":[{"type":"string","enum":["platform"]},{"type":"string","enum":["faable.json"]},{"type":"null"}]}}},"RepoDirectories":{"type":"object","required":["directories","truncated"],"properties":{"directories":{"type":"array","items":{"type":"object","required":["path","buildable"],"properties":{"path":{"type":"string"},"buildable":{"type":"boolean"}}}},"truncated":{"type":"boolean"}}},"DeployNowResult":{"type":"object","required":["status","commit","branch"],"properties":{"status":{"type":"string","enum":["created"]},"commit":{"type":"string"},"branch":{"type":"string"},"deployment_id":{"type":"string","description":"The deployment this call created."}}},"AppRuntimeVersionInfo":{"type":"object","required":["name","pinned","repo_pin","current","available","newest","retiring","last_upgrade"],"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"pinned":{"anyOf":[{"type":"object","required":["name","version","pinned_at","source"],"properties":{"name":{"type":"string"},"version":{"type":"string"},"pinned_at":{"type":"string","format":"date-time"},"source":{"type":"string"}}},{"type":"null"}]},"repo_pin":{"anyOf":[{"type":"string"},{"type":"null"}]},"current":{"anyOf":[{"type":"string"},{"type":"null"}]},"available":{"type":"array","items":{"type":"string"}},"newest":{"anyOf":[{"type":"string"},{"type":"null"}]},"retiring":{"anyOf":[{"type":"object","required":["retire_at","replacement","why"],"properties":{"retire_at":{"type":"string"},"replacement":{"type":"string"},"why":{"type":"string"}}},{"type":"null"}]},"last_upgrade":{"anyOf":[{"type":"object","required":["deployment_id","to","from","phase","created_at"],"properties":{"deployment_id":{"type":"string"},"to":{"type":"string"},"from":{"anyOf":[{"type":"string"},{"type":"null"}]},"phase":{"type":"string"},"created_at":{"type":"string","format":"date-time"}}},{"type":"null"}]}}},"AppRuntimeVersionResult":{"type":"object","required":["pinned","deployment_id"],"properties":{"pinned":{"type":"object","required":["name","version","pinned_at","source"],"properties":{"name":{"type":"string"},"version":{"type":"string"},"pinned_at":{"type":"string","format":"date-time"},"source":{"type":"string"}}},"deployment_id":{"anyOf":[{"type":"string"},{"type":"null"}]}}},"AppWafView":{"type":"object","required":["enabled","platform_profiles","rules"],"properties":{"enabled":{"type":"boolean"},"platform_profiles":{"type":"array","items":{"type":"object","required":["name","action","match","rule_count"],"properties":{"name":{"type":"string"},"action":{"type":"string"},"match":{"type":"string"},"rule_count":{"type":"integer"},"rules":{"type":"array","items":{"type":"object","required":["pattern"],"properties":{"pattern":{"type":"string"},"user_agent":{"type":"string"},"query":{"type":"string"},"description":{"type":"string"}}}}}}},"rules":{"type":"array","items":{"type":"object","required":["action","match"],"properties":{"pattern":{"type":"string"},"user_agent":{"type":"string"},"query":{"type":"string"},"action":{"type":"string"},"match":{"type":"string"},"description":{"type":"string"}}}}}},"DeploymentStatus":{"type":"object","required":["phase"],"properties":{"phase":{"enum":["UNKNOWN","QUEUED","BUILDING","ERROR","BUILD_ERROR","INITIALIZING","READY","CANCELED","TERMINATING","SUPERSEDED","QUOTA_HOLD"],"description":"High-level summary of where the Deployment is in its lifecycle","default":"QUEUED"},"controlled_at":{"type":"string","format":"date-time","description":"When a controller took control of this deployment"},"controlled_by":{"type":"string","description":"INSTANCE_NAME of the controller that claimed it"},"reason":{"type":"string","description":"Why the deployment failed (shown to the user on ERROR)"},"failure_code":{"type":"string","description":"Producer's own name for what failed (e.g. detect_no_buildpack). Not the fault taxonomy."},"failure_detail":{"type":"object","properties":{},"additionalProperties":true,"description":"Structured context the producer had before writing `reason` (found_files, subdir_hits, workdir, root_dir…). Never free build output: it must carry no repository paths beyond the entry names detection already lists."},"cold_state":{"anyOf":[{"type":"string","enum":["asleep"]},{"type":"string","enum":["waking"]},{"type":"null"}],"description":"Scale-to-zero sub-state (admin-only): asleep, waking or null"},"resource_limit":{"type":"object","required":["state","at","count"],"properties":{"state":{"anyOf":[{"type":"string","enum":["memory_exceeded"]},{"type":"string","enum":["disk_exceeded"]}],"description":"Which limit was hit"},"at":{"type":"string","description":"ISO time the limit was last hit"},"count":{"type":"integer","minimum":1,"description":"How many times this deployment hit it"}},"additionalProperties":false,"description":"Last hard resource limit (memory or disk) this deployment hit"},"runtime_image":{"type":"string","description":"Runtime image (immutable version tag) this artifact deployment runs"},"fetcher_image":{"type":"string","description":"Fetcher image (immutable version tag) of the artifact-prepare initContainer"},"overlay_image":{"type":"string","description":"Overlay image (immutable version tag) of a blueprint deployment"},"builder_image":{"type":"string","description":"Builder image (immutable version tag) that built this deployment"}},"additionalProperties":false},"DeploymentSource":{"type":"object","properties":{"manifest":{"type":"array","items":{"type":"object","required":["path","sha","size"],"properties":{"path":{"type":"string","minLength":1,"maxLength":1024},"sha":{"type":"string","pattern":"^[a-f0-9]{64}$"},"size":{"type":"integer","minimum":0,"maximum":104857600},"mode":{"type":"integer","minimum":0,"maximum":4095}}},"minItems":1,"maxItems":20000},"git":{"type":"object","required":["sha"],"properties":{"sha":{"type":"string","pattern":"^[a-f0-9]{40}$"},"ref":{"type":"string","minLength":1,"maxLength":250}}},"plan":{"type":"object","properties":{},"additionalProperties":true}}},"DeploymentArtifact":{"type":"object","required":["sha256","size","format","runtime","profile"],"properties":{"key":{"type":"string","maxLength":512},"sha256":{"type":"string","pattern":"^[a-f0-9]{64}$"},"size":{"type":"integer","minimum":1,"maximum":2147483648},"format":{"type":"string","enum":["tar.zst"]},"runtime":{"type":"object","required":["name"],"properties":{"name":{"enum":["node","python","php","static"]},"version":{"anyOf":[{"type":"string","pattern":"^[0-9]+(\\.[0-9]+){0,2}$","maxLength":16},{"type":"null"}]}}},"profile":{"enum":["node-full","next-standalone","static","python-venv","php"]},"start_command":{"anyOf":[{"type":"string","minLength":1,"maxLength":2048},{"type":"null"}]},"static":{"type":"object","required":["spa"],"properties":{"spa":{"type":"boolean"}}},"images":{"type":"object","properties":{"deviceSizes":{"type":"array","items":{"type":"integer","minimum":1,"maximum":16384},"maxItems":256},"imageSizes":{"type":"array","items":{"type":"integer","minimum":1,"maximum":16384},"maxItems":256},"formats":{"type":"array","items":{"type":"string","maxLength":64},"maxItems":8},"qualities":{"type":"array","items":{"type":"integer","minimum":1,"maximum":100},"maxItems":100},"minimumCacheTTL":{"type":"integer","minimum":0},"unoptimized":{"type":"boolean"},"loader":{"type":"string","maxLength":64},"path":{"type":"string","maxLength":512},"basePath":{"type":"string","maxLength":512},"assetPrefix":{"type":"boolean"},"dangerouslyAllowSVG":{"type":"boolean"},"remotePatterns":{"type":"integer","minimum":0}},"additionalProperties":false}}},"DeploymentDetected":{"type":"object","required":["buildpack","runtime"],"properties":{"buildpack":{"type":"string"},"framework":{"type":"string"},"runtime":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"version":{"type":"string"},"source":{"type":"string"}}},"dockerfile_ignored":{"type":"boolean"},"process_type":{"type":"string","enum":["worker"]}}},"Deployment":{"type":"object","required":["id","team","app_id","status","metadata","createdAt"],"properties":{"id":{"type":"string","description":"Deployment ID"},"team":{"type":"string"},"app_id":{"type":"string"},"image":{"type":"string"},"release":{"type":"string"},"github_commit":{"type":"string"},"github_ref":{"type":"string"},"github_actor":{"type":"string"},"github_commit_message":{"type":"string"},"artifact_id":{"type":"string"},"artifact_ready_at":{"type":"string","format":"date-time"},"blueprint":{"type":"object","required":["slug","version","image","overlay","start_command","volumes","database"],"properties":{"slug":{"type":"string"},"version":{"type":"integer"},"image":{"type":"object","required":["kind","key"],"properties":{"kind":{"anyOf":[{"type":"string","enum":["runtime"]},{"type":"string","enum":["blueprint"]}]},"key":{"type":"string"}}},"overlay":{"type":"string","nullable":true,"default":null},"start_command":{"type":"string"},"volumes":{"type":"array","items":{"type":"object","required":["name","path","mount_path"],"properties":{"name":{"type":"string"},"path":{"type":"string"},"mount_path":{"type":"string"}}}},"database":{"type":"boolean"}},"nullable":true,"default":null},"build_started_at":{"type":"string","format":"date-time"},"build_ms":{"type":"integer"},"redeploy_of":{"type":"string"},"rebuild_of":{"type":"string"},"runtime_fallback":{"type":"object","required":["name","from","to","of"],"properties":{"name":{"type":"string"},"from":{"type":"string"},"to":{"type":"string"},"of":{"type":"string"}}},"runtime_fallback_retry":{"type":"object","required":["to","at"],"properties":{"to":{"type":"string"},"at":{"type":"string","format":"date-time"},"deployment_id":{"type":"string"}}},"runtime_upgrade":{"type":"object","required":["name","to","previous"],"properties":{"name":{"type":"string"},"to":{"type":"string"},"previous":{"anyOf":[{"type":"object","required":["name","version","pinned_at","source"],"properties":{"name":{"type":"string"},"version":{"type":"string"},"pinned_at":{"type":"string","format":"date-time"},"source":{"type":"string"}}},{"type":"null"}]}}},"quota_released_at":{"type":"string","format":"date-time"},"trigger":{"anyOf":[{"type":"string"},{"type":"null"}]},"detected":{"$ref":"#/components/schemas/DeploymentDetected"},"build_warnings":{"type":"array","items":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","maxLength":64},"message":{"type":"string","maxLength":1000},"detail":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":false},"maxItems":20},"status":{"$ref":"#/components/schemas/DeploymentStatus"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"default":{}},"createdAt":{"type":"string","description":"Deployment creation date"},"updatedAt":{"type":"string","description":"Deployment updated date"}},"description":"Deployment","additionalProperties":false},"EventDeploymentCreate":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["deployment.create"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/Deployment"}},"description":"Event Deployment Create","additionalProperties":false},"EventDeploymentUpdate":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["deployment.update"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/Deployment"}},"description":"Event Deployment Update","additionalProperties":false},"EventDeploymentDelete":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["deployment.delete"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/Deployment"}},"description":"Event Deployment Delete","additionalProperties":false},"DeploymentCreate":{"type":"object","required":["app_id"],"properties":{"app_id":{"type":"string"},"image":{"type":"string"},"release":{"type":"string","maxLength":128},"github_commit":{"type":"string"},"github_ref":{"type":"string"},"github_actor":{"type":"string"},"github_commit_message":{"type":"string"},"type":{"type":"string"},"source":{"$ref":"#/components/schemas/DeploymentSource"}},"additionalProperties":false},"DeploymentUpdate":{"type":"object","properties":{"image":{"type":"string"},"artifact":{"$ref":"#/components/schemas/DeploymentArtifact"},"type":{"type":"string"},"detected":{"$ref":"#/components/schemas/DeploymentDetected"},"build_warnings":{"type":"array","items":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","maxLength":64},"message":{"type":"string","maxLength":1000},"detail":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":false},"maxItems":20},"plan":{"type":"object","properties":{},"additionalProperties":true}}},"DomainStatus":{"type":"object","required":["dns_state","dns_checked_at","dns_last_seen_at","dns_verified_at","dns_error_kind","dns_expected","dns_observed","dns_expected_ips","dns_message","dns_warning","acme_state","acme_expected","acme_observed","tls_state","tls_expires_at"],"properties":{"dns_state":{"enum":["pending","ok","misconfigured","error"],"description":"Outcome of the latest DNS verification check","default":"pending"},"dns_checked_at":{"type":"string","description":"ISO timestamp of the last DNS change","nullable":true,"default":null},"dns_last_seen_at":{"type":"string","description":"ISO timestamp of the last DNS check","nullable":true,"default":null},"dns_verified_at":{"type":"string","description":"ISO timestamp of the first successful DNS verification","nullable":true,"default":null},"dns_error_kind":{"type":"string","enum":["transient","proxy",null],"description":"Why an `error` verdict could not conclude","nullable":true,"default":null},"dns_expected":{"type":"array","items":{"type":"string"},"description":"Expected CNAME target(s) the fqdn should point to","default":[]},"dns_observed":{"type":"array","items":{"type":"string"},"description":"CNAME target(s) actually resolved for the fqdn","default":[]},"dns_expected_ips":{"type":"array","items":{"type":"string"},"description":"IPv4 address(es) the expected target currently resolves to","default":[]},"dns_message":{"type":"string","description":"Human-readable diagnostic of the DNS check","nullable":true,"default":null},"dns_warning":{"type":"string","description":"Advisory for a verified domain whose DNS is functional but not the record we publish","nullable":true,"default":null},"acme_state":{"enum":["pending","ok"],"description":"Whether `_acme-challenge.<fqdn>` delegates to Faable, so the certificate can be issued before the fqdn points here","default":"pending"},"acme_expected":{"type":"array","items":{"type":"string"},"description":"The record that pre-validates the certificate: `_acme-challenge.<fqdn>` CNAME to this target","default":[]},"acme_observed":{"type":"array","items":{"type":"string"},"description":"CNAME target(s) resolved for `_acme-challenge.<fqdn>`","default":[]},"tls_state":{"enum":["pending","ok"],"description":"Whether the edge serves a valid certificate for the fqdn (checked against the PoP with the fqdn as SNI)","default":"pending"},"tls_expires_at":{"type":"string","description":"Expiry of the certificate the edge serves for the fqdn, when `tls_state` is `ok`","nullable":true,"default":null}},"additionalProperties":false},"Domain":{"type":"object","required":["id","team","fqdn","tls","verified","active","status","metadata","createdAt"],"properties":{"id":{"type":"string","description":"Domain ID"},"team":{"type":"string"},"fqdn":{"type":"string","description":"Unique identifier of the domain"},"tls":{"type":"boolean","description":"TLS is enabled for this domain"},"app_id":{"type":"string","description":"Linked App","nullable":true,"default":null},"verified":{"type":"boolean","description":"True while the domain points at Faable: derived from `status.dns_state` (`ok`), the latest DNS check"},"active":{"type":"boolean"},"dns_alias":{"type":"string","description":"Internal DNS alias override host","nullable":true,"default":null},"dormant_at":{"type":"string","format":"date-time","description":"Set when the domain went dormant: it served no active app and had not pointed at Faable for two days. It is not checked nor served until `POST /domain/:id/recheck`.","nullable":true,"default":null},"recheck_at":{"type":"string","format":"date-time","description":"Last manual re-check (Retry) of this domain","nullable":true,"default":null},"status":{"$ref":"#/components/schemas/DomainStatus"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"default":{}},"createdAt":{"type":"string","description":"Domain creation date"},"updatedAt":{"type":"string","description":"Domain updated date"}},"description":"Domain","additionalProperties":false},"EventDomainCreate":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["domain.create"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/Domain"}},"description":"Event Domain Create","additionalProperties":false},"EventDomainUpdate":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["domain.update"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/Domain"}},"description":"Event Domain Update","additionalProperties":false},"EventDomainDelete":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["domain.delete"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/Domain"}},"description":"Event Domain Delete","additionalProperties":false},"DomainCreate":{"type":"object","required":["fqdn"],"properties":{"fqdn":{"type":"string"},"app_id":{"type":"string","description":"Linked App"},"tls":{"type":"boolean","default":true}}},"DomainUpdate":{"type":"object","properties":{"app_id":{"type":"string","description":"Link domain to this App","nullable":true},"active":{"type":"boolean"}}},"WafProfile":{"type":"object","required":["id","team","name","scope","source","action","match","apply","rules","enabled","lock","metadata","createdAt"],"properties":{"id":{"type":"string","description":"WafProfile ID"},"team":{"type":"string"},"name":{"type":"string","description":"Lookup name, e.g. scanner-php"},"scope":{"anyOf":[{"type":"string","enum":["global"]},{"type":"string","enum":["app"]}]},"app_id":{"type":"string","description":"Owning App (null for global)","nullable":true,"default":null},"source":{"anyOf":[{"type":"string","enum":["builtin"]},{"type":"string","enum":["manual"]},{"type":"string","enum":["auto"]}]},"action":{"anyOf":[{"type":"string","enum":["deny"]},{"type":"string","enum":["sink"]},{"type":"string","enum":["ratelimit"]},{"type":"string","enum":["probe"]},{"type":"string","enum":["allow"]}]},"match":{"anyOf":[{"type":"string","enum":["path"]},{"type":"string","enum":["user_agent"]},{"type":"string","enum":["path_user_agent"]},{"type":"string","enum":["query"]},{"type":"string","enum":["path_query"]}]},"apply":{"anyOf":[{"type":"string","enum":["all"]},{"type":"string","enum":["node"]},{"type":"string","enum":["php"]},{"type":"string","enum":["manual"]}]},"except":{"type":"array","items":{"anyOf":[{"type":"string","enum":["all"]},{"type":"string","enum":["node"]},{"type":"string","enum":["php"]},{"type":"string","enum":["manual"]}]},"description":"Runtimes excluded from `apply` (e.g. [\"php\"])"},"rules":{"type":"array","items":{"type":"object","required":["pattern"],"properties":{"pattern":{"type":"string","description":"RE2 fragment; see the profile `match`"},"user_agent":{"type":"string","description":"RE2 fragment matched against User-Agent. Only in `path_user_agent` profiles, where BOTH must match."},"query":{"type":"string","description":"LITERAL query parameter name (never a regex). Only in `path_query` profiles, where BOTH must match."},"description":{"type":"string","description":"What this rule blocks (human annotation)"}}}},"enabled":{"type":"boolean"},"lock":{"type":"boolean","description":"Profiler must not overwrite rules"},"sample_count":{"type":"number","nullable":true,"default":null},"updated_at":{"type":"string","nullable":true,"default":null},"metadata":{"type":"object","properties":{},"additionalProperties":true,"default":{}},"createdAt":{"type":"string","description":"WafProfile creation date"},"updatedAt":{"type":"string","description":"WafProfile updated date"}},"description":"WafProfile","additionalProperties":false},"EventWafprofileCreate":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["wafprofile.create"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/WafProfile"}},"description":"Event Wafprofile Create","additionalProperties":false},"EventWafprofileUpdate":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["wafprofile.update"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/WafProfile"}},"description":"Event Wafprofile Update","additionalProperties":false},"EventWafprofileDelete":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["wafprofile.delete"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/WafProfile"}},"description":"Event Wafprofile Delete","additionalProperties":false},"WafProfileCreate":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"scope":{"anyOf":[{"type":"string","enum":["global"]},{"type":"string","enum":["app"]}]},"app_id":{"type":"string","description":"Owning App"},"source":{"anyOf":[{"type":"string","enum":["builtin"]},{"type":"string","enum":["manual"]},{"type":"string","enum":["auto"]}]},"action":{"anyOf":[{"type":"string","enum":["deny"]},{"type":"string","enum":["sink"]},{"type":"string","enum":["ratelimit"]},{"type":"string","enum":["probe"]},{"type":"string","enum":["allow"]}]},"match":{"anyOf":[{"type":"string","enum":["path"]},{"type":"string","enum":["user_agent"]},{"type":"string","enum":["path_user_agent"]},{"type":"string","enum":["query"]},{"type":"string","enum":["path_query"]}]},"apply":{"anyOf":[{"type":"string","enum":["all"]},{"type":"string","enum":["node"]},{"type":"string","enum":["php"]},{"type":"string","enum":["manual"]}]},"rules":{"type":"array","items":{"type":"object","required":["pattern"],"properties":{"pattern":{"type":"string","description":"RE2 fragment; see the profile `match`"},"user_agent":{"type":"string","description":"RE2 fragment matched against User-Agent. Only in `path_user_agent` profiles, where BOTH must match."},"query":{"type":"string","description":"LITERAL query parameter name (never a regex). Only in `path_query` profiles, where BOTH must match."},"description":{"type":"string","description":"What this rule blocks (human annotation)"}}}},"enabled":{"type":"boolean"},"lock":{"type":"boolean"},"sample_count":{"type":"number"},"updated_at":{"type":"string"}}},"WafProfileUpdate":{"type":"object","properties":{"name":{"type":"string"},"action":{"anyOf":[{"type":"string","enum":["deny"]},{"type":"string","enum":["sink"]},{"type":"string","enum":["ratelimit"]},{"type":"string","enum":["probe"]},{"type":"string","enum":["allow"]}]},"match":{"anyOf":[{"type":"string","enum":["path"]},{"type":"string","enum":["user_agent"]},{"type":"string","enum":["path_user_agent"]},{"type":"string","enum":["query"]},{"type":"string","enum":["path_query"]}]},"apply":{"anyOf":[{"type":"string","enum":["all"]},{"type":"string","enum":["node"]},{"type":"string","enum":["php"]},{"type":"string","enum":["manual"]}]},"rules":{"type":"array","items":{"type":"object","required":["pattern"],"properties":{"pattern":{"type":"string","description":"RE2 fragment; see the profile `match`"},"user_agent":{"type":"string","description":"RE2 fragment matched against User-Agent. Only in `path_user_agent` profiles, where BOTH must match."},"query":{"type":"string","description":"LITERAL query parameter name (never a regex). Only in `path_query` profiles, where BOTH must match."},"description":{"type":"string","description":"What this rule blocks (human annotation)"}}}},"enabled":{"type":"boolean"},"lock":{"type":"boolean"},"sample_count":{"type":"number","nullable":true},"updated_at":{"type":"string","nullable":true}}},"AppWaf":{"type":"object","required":["id","team","app_id","enabled","profiles","lock","metadata","createdAt"],"properties":{"id":{"type":"string","description":"AppWaf ID"},"team":{"type":"string"},"app_id":{"type":"string","description":"Linked App"},"enabled":{"type":"boolean","description":"Master toggle for the app WAF"},"profiles":{"type":"array","items":{"type":"object","required":["profile","enabled"],"properties":{"profile":{"type":"string","description":"WafProfile id"},"enabled":{"type":"boolean"}}}},"lock":{"type":"boolean","description":"Profiler must not touch the list"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"default":{}},"createdAt":{"type":"string","description":"AppWaf creation date"},"updatedAt":{"type":"string","description":"AppWaf updated date"}},"description":"AppWaf","additionalProperties":false},"EventAppwafCreate":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["appwaf.create"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/AppWaf"}},"description":"Event Appwaf Create","additionalProperties":false},"EventAppwafUpdate":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["appwaf.update"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/AppWaf"}},"description":"Event Appwaf Update","additionalProperties":false},"EventAppwafDelete":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["appwaf.delete"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/AppWaf"}},"description":"Event Appwaf Delete","additionalProperties":false},"AppWafCreate":{"type":"object","required":["app_id"],"properties":{"app_id":{"type":"string","description":"Linked App"},"enabled":{"type":"boolean","default":true},"profiles":{"type":"array","items":{"type":"object","required":["profile","enabled"],"properties":{"profile":{"type":"string","description":"WafProfile id"},"enabled":{"type":"boolean"}}}},"lock":{"type":"boolean"}}},"AppWafUpdate":{"type":"object","properties":{"enabled":{"type":"boolean"},"profiles":{"type":"array","items":{"type":"object","required":["profile","enabled"],"properties":{"profile":{"type":"string","description":"WafProfile id"},"enabled":{"type":"boolean"}}}},"lock":{"type":"boolean"}}},"EventSecretCreate_batch":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["secret.create_batch"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"type":"object","required":["type","context","secrets"],"properties":{"type":{"type":"string","enum":["app"]},"context":{"$ref":"#/components/schemas/App"},"secrets":{"type":"array","items":{"$ref":"#/components/schemas/Secret"}}}}},"description":"Event Secret Create_batch","additionalProperties":false},"Secret":{"type":"object","required":["id","team","related","related_model","name","value","metadata","createdAt"],"properties":{"id":{"type":"string","description":"Secret ID"},"team":{"type":"string"},"app_id":{"type":"string","description":"Secret is linked to App"},"profile_id":{"type":"string","description":"Secret is linked to App"},"related":{"type":"string","description":"Secret is linked to App"},"related_model":{"type":"string","description":""},"name":{"type":"string","maxLength":255,"description":"Secret name"},"value":{"type":"string","maxLength":50000,"description":"Secret value"},"managed_by":{"anyOf":[{"type":"string","enum":["platform"]},{"type":"null"}],"description":"platform: set by the platform (a blueprint app database credentials); read-only for users"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"default":{}},"createdAt":{"type":"string","description":"Secret creation date"},"updatedAt":{"type":"string","description":"Secret updated date"}},"description":"Secret","additionalProperties":false},"SecretPage":{"type":"object","required":["next","results"],"properties":{"next":{"type":"string","description":"next cursor","nullable":true},"results":{"type":"array","items":{"$ref":"#/components/schemas/Secret"},"description":"List of results"}},"description":"PaginatedResponse","additionalProperties":false},"SecretCreateBatch":{"type":"object","required":["context_id","secrets"],"properties":{"context_id":{"type":"string"},"secrets":{"type":"array","items":{"type":"object","required":["name","value"],"properties":{"name":{"type":"string","maxLength":255,"description":"Secret name"},"value":{"type":"string","maxLength":50000,"description":"Secret value"}}}}},"description":"Create Secrets by Batch"},"SecretUpsert":{"type":"object","required":["app_id","name","value"],"properties":{"app_id":{"type":"string"},"name":{"type":"string","maxLength":255,"description":"Secret name"},"value":{"type":"string","maxLength":50000,"description":"Secret value"}},"description":"Upsert Secret"},"Contact":{"type":"object","required":["id","team","contact_type","metadata","createdAt"],"properties":{"id":{"type":"string","description":"Contact ID"},"team":{"type":"string"},"contact_type":{"enum":["primary","technical","security","emergency","billing"]},"email":{"type":"string"},"phone":{"type":"string"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"default":{}},"createdAt":{"type":"string","description":"Contact creation date"},"updatedAt":{"type":"string","description":"Contact updated date"}},"description":"Contact","additionalProperties":false},"ContactType":{"enum":["primary","technical","security","emergency","billing"]},"EventContactCreate":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["contact.create"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/Contact"}},"description":"Event Contact Create","additionalProperties":false},"EventContactUpdate":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["contact.update"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/Contact"}},"description":"Event Contact Update","additionalProperties":false},"EventContactDelete":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["contact.delete"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/Contact"}},"description":"Event Contact Delete","additionalProperties":false},"ContactCreate":{"type":"object","required":["contact_type"],"properties":{"contact_type":{"enum":["primary","technical","security","emergency","billing"]},"email":{"type":"string"},"phone":{"type":"string"}},"additionalProperties":false},"ContactUpdate":{"type":"object","properties":{"contact_type":{"enum":["primary","technical","security","emergency","billing"]},"email":{"type":"string"},"phone":{"type":"string"}},"additionalProperties":false},"ApiKey":{"type":"object","required":["id","team","apikey","user_id","metadata","createdAt"],"properties":{"id":{"type":"string","description":"ApiKey ID"},"team":{"type":"string"},"apikey":{"type":"string"},"user_id":{"type":"string"},"description":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"metadata":{"type":"object","properties":{},"additionalProperties":true,"default":{}},"createdAt":{"type":"string","description":"ApiKey creation date"},"updatedAt":{"type":"string","description":"ApiKey updated date"}},"description":"ApiKey","additionalProperties":false},"EventApikeyCreate":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["apikey.create"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/ApiKey"}},"description":"Event Apikey Create","additionalProperties":false},"EventApikeyUpdate":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["apikey.update"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/ApiKey"}},"description":"Event Apikey Update","additionalProperties":false},"EventApikeyDelete":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["apikey.delete"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/ApiKey"}},"description":"Event Apikey Delete","additionalProperties":false},"ApiKeyCreate":{"type":"object","properties":{"description":{"type":"string"}},"additionalProperties":false},"ApiKeyUpdate":{"type":"object","properties":{"description":{"type":"string"}},"additionalProperties":false},"DeployQuota":{"type":"object","required":["limited","over","used","limit","resets_at","held"],"properties":{"limited":{"type":"boolean"},"over":{"type":"boolean"},"used":{"type":"integer"},"limit":{"anyOf":[{"type":"integer"},{"type":"null"}]},"resets_at":{"type":"string","format":"date-time"},"held":{"type":"integer"}}},"DeployQuotaRelease":{"type":"object","required":["released","canceled"],"properties":{"released":{"type":"array","items":{"type":"string"}},"canceled":{"type":"array","items":{"type":"string"}}}},"EgressAllowance":{"type":"object","required":["project_id","limited","day","month","apps"],"properties":{"project_id":{"type":"string"},"limited":{"type":"boolean"},"day":{"type":"object","required":["used_bytes","limit_bytes","threshold","resets_at"],"properties":{"used_bytes":{"type":"integer"},"limit_bytes":{"type":"integer","nullable":true,"default":null},"threshold":{"type":"integer","enum":[80,90,100,null],"nullable":true,"default":null},"resets_at":{"type":"string","format":"date-time"}}},"month":{"type":"object","required":["used_bytes","limit_bytes","threshold","resets_at"],"properties":{"used_bytes":{"type":"integer"},"limit_bytes":{"type":"integer","nullable":true,"default":null},"threshold":{"type":"integer","enum":[80,90,100,null],"nullable":true,"default":null},"resets_at":{"type":"string","format":"date-time"}}},"apps":{"type":"array","items":{"type":"object","required":["app_id","day_bytes","month_bytes"],"properties":{"app_id":{"type":"string"},"day_bytes":{"type":"integer"},"month_bytes":{"type":"integer"}}}}}},"PlatformImage":{"type":"object","required":["id","team","kind","delivery","role","key","repository","current","min_version","auto_promote","rollout","versions","metadata","createdAt"],"properties":{"id":{"type":"string","description":"PlatformImage ID"},"team":{"type":"string"},"kind":{"anyOf":[{"type":"string","enum":["runtime"]},{"type":"string","enum":["builder"]},{"type":"string","enum":["fetcher"]},{"type":"string","enum":["buildkit"]},{"type":"string","enum":["detector"]},{"type":"string","enum":["overlay"]},{"type":"string","enum":["blueprint"]}]},"delivery":{"anyOf":[{"type":"string","enum":["workload"]},{"type":"string","enum":["cluster"]}]},"role":{"anyOf":[{"type":"string","enum":["build"]},{"type":"string","enum":["runtime"]}]},"key":{"type":"string","description":"Canonical key: node:22, python:3.11, php:8.4, fetcher, buildkit"},"repository":{"type":"string","description":"Repository ref without tag"},"current":{"type":"string","description":"Version served to NEW deployments/builds"},"min_version":{"type":"string","description":"Floor: deployments stamped below it are rolled to current (mandatory update)"},"auto_promote":{"type":"boolean","description":"A CI registration of a newer release moves current"},"rollout":{"type":"object","required":["mode","target","previous","state","failures","halted_reason","failed_version","gate"],"properties":{"mode":{"anyOf":[{"type":"string","enum":["manual"]},{"type":"string","enum":["auto"]}]},"target":{"type":"string","nullable":true,"default":null},"previous":{"type":"string","nullable":true,"default":null},"state":{"anyOf":[{"type":"string","enum":["idle"]},{"type":"string","enum":["running"]},{"type":"string","enum":["reverting"]},{"type":"string","enum":["halted"]},{"type":"string","enum":["done"]},{"type":"string","enum":["stalled"]}]},"started_at":{"type":"string","format":"date-time","nullable":true,"default":null},"updated_at":{"type":"string","format":"date-time","nullable":true,"default":null},"deadline_at":{"type":"string","format":"date-time","nullable":true,"default":null},"failures":{"type":"number"},"halted_reason":{"type":"string","nullable":true,"default":null},"failed_version":{"type":"string","nullable":true,"default":null},"gate":{"type":"string","nullable":true,"default":null}}},"versions":{"type":"array","items":{"type":"object","required":["version","tag","digest","registered_at","source"],"properties":{"version":{"type":"string","description":"Buildpacks release, X.Y.Z"},"tag":{"type":"string","description":"Immutable image tag, e.g. 22-v1.20.1"},"digest":{"type":"string","description":"sha256 the tag resolved to in ECR"},"size":{"type":"number","nullable":true,"default":null},"registered_at":{"type":"string","format":"date-time"},"source":{"anyOf":[{"type":"string","enum":["ci"]},{"type":"string","enum":["seed"]},{"type":"string","enum":["manual"]}]},"language_version":{"type":"string","description":"Exact language version inside the image (8.4.27). Null when unknown or the kind has no language","nullable":true,"default":null}}}},"updated_at":{"type":"string","format":"date-time","nullable":true,"default":null},"metadata":{"type":"object","properties":{},"additionalProperties":true,"default":{}},"createdAt":{"type":"string","description":"PlatformImage creation date"},"updatedAt":{"type":"string","description":"PlatformImage updated date"}},"description":"PlatformImage","additionalProperties":false},"EventPlatformimageCreate":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["platformimage.create"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/PlatformImage"}},"description":"Event Platformimage Create","additionalProperties":false},"EventPlatformimageUpdate":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["platformimage.update"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/PlatformImage"}},"description":"Event Platformimage Update","additionalProperties":false},"EventPlatformimageDelete":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["platformimage.delete"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/PlatformImage"}},"description":"Event Platformimage Delete","additionalProperties":false},"PlatformImageCreate":{"type":"object","required":["kind","key","repository","current","min_version"],"properties":{"kind":{"anyOf":[{"type":"string","enum":["runtime"]},{"type":"string","enum":["builder"]},{"type":"string","enum":["fetcher"]},{"type":"string","enum":["buildkit"]},{"type":"string","enum":["detector"]},{"type":"string","enum":["overlay"]},{"type":"string","enum":["blueprint"]}]},"key":{"type":"string"},"repository":{"type":"string"},"current":{"type":"string"},"min_version":{"type":"string"},"delivery":{"anyOf":[{"type":"string","enum":["workload"]},{"type":"string","enum":["cluster"]}]},"role":{"anyOf":[{"type":"string","enum":["build"]},{"type":"string","enum":["runtime"]}]},"auto_promote":{"type":"boolean"},"versions":{"type":"array","items":{"type":"object","required":["version","tag","digest","registered_at","source"],"properties":{"version":{"type":"string","description":"Buildpacks release, X.Y.Z"},"tag":{"type":"string","description":"Immutable image tag, e.g. 22-v1.20.1"},"digest":{"type":"string","description":"sha256 the tag resolved to in ECR"},"size":{"type":"number","nullable":true,"default":null},"registered_at":{"type":"string","format":"date-time"},"source":{"anyOf":[{"type":"string","enum":["ci"]},{"type":"string","enum":["seed"]},{"type":"string","enum":["manual"]}]},"language_version":{"type":"string","description":"Exact language version inside the image (8.4.27). Null when unknown or the kind has no language","nullable":true,"default":null}}}}}},"PlatformImageUpdate":{"type":"object","properties":{"auto_promote":{"type":"boolean"},"repository":{"type":"string"}}},"ComputeInstance":{"type":"object","required":["id","team","name","rank","resources","bandwidth","is_default","description","metadata","createdAt"],"properties":{"id":{"type":"string","description":"ComputeInstance ID"},"team":{"type":"string"},"name":{"type":"string","pattern":"^[a-z0-9]+(\\.[a-z0-9-]+)+$","maxLength":64,"description":"Instance type name, e.g. bi.small"},"rank":{"type":"number","description":"Smallest first"},"resources":{"type":"object","required":["requests","limits"],"properties":{"requests":{"type":"object","required":["cpu","memory"],"properties":{"cpu":{"type":"string","pattern":"^[1-9][0-9]*m?$","description":"Kubernetes CPU quantity: '600m', '1000m', '2'"},"memory":{"type":"string","pattern":"^[1-9][0-9]*(Mi|Gi)$","description":"Kubernetes memory quantity: '512Mi', '2Gi'"}}},"limits":{"type":"object","required":["cpu","memory"],"properties":{"cpu":{"type":"string","pattern":"^[1-9][0-9]*m?$","description":"Kubernetes CPU quantity: '600m', '1000m', '2'"},"memory":{"type":"string","pattern":"^[1-9][0-9]*(Mi|Gi)$","description":"Kubernetes memory quantity: '512Mi', '2Gi'"}}}}},"bandwidth":{"type":"object","required":["egress","ingress"],"properties":{"egress":{"type":"string","pattern":"^[1-9][0-9]*[MG]$","description":"CNI bandwidth: '10M', '50M'"},"ingress":{"type":"string","pattern":"^[1-9][0-9]*[MG]$","description":"CNI bandwidth: '10M', '50M'"}}},"is_default":{"type":"boolean","description":"The instance a new app gets, and the fallback for one the controller cannot resolve. Exactly one row."},"plans":{"type":"object","properties":{"free":{"type":"string","enum":["default","allowed",null],"nullable":true,"default":null},"hobby":{"type":"string","enum":["default","allowed",null],"nullable":true,"default":null},"pro":{"type":"string","enum":["default","allowed",null],"nullable":true,"default":null}}},"storage":{"type":"object","properties":{"ephemeral_request":{"type":"string","pattern":"^[1-9][0-9]*(Mi|Gi)$","nullable":true,"default":null,"description":"Kubernetes storage quantity: '256Mi', '3Gi'"},"ephemeral_limit":{"type":"string","pattern":"^[1-9][0-9]*(Mi|Gi)$","nullable":true,"default":null,"description":"Kubernetes storage quantity: '256Mi', '3Gi'"},"cache":{"type":"string","pattern":"^[1-9][0-9]*(Mi|Gi)$","nullable":true,"default":null,"description":"Kubernetes storage quantity: '256Mi', '3Gi'"},"shm":{"type":"string","pattern":"^[1-9][0-9]*(Mi|Gi)$","nullable":true,"default":null,"description":"Kubernetes storage quantity: '256Mi', '3Gi'"}},"nullable":true,"default":null},"ratelimit":{"type":"object","required":["average","burst","period"],"properties":{"average":{"type":"integer","minimum":1,"description":"Requests one client IP may sustain per `period`"},"burst":{"type":"integer","minimum":1,"description":"Requests one client IP may make at once before a 429"},"period":{"type":"string","pattern":"^[1-9][0-9]*(s|m|h)$","description":"Traefik duration: '1s', '1m'"}},"nullable":true,"default":null},"description":{"type":"string","nullable":true,"default":null},"metadata":{"type":"object","properties":{},"additionalProperties":true,"default":{}},"createdAt":{"type":"string","description":"ComputeInstance creation date"},"updatedAt":{"type":"string","description":"ComputeInstance updated date"}},"description":"ComputeInstance","additionalProperties":false},"EventComputeinstanceCreate":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["computeinstance.create"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/ComputeInstance"}},"description":"Event Computeinstance Create","additionalProperties":false},"EventComputeinstanceUpdate":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["computeinstance.update"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/ComputeInstance"}},"description":"Event Computeinstance Update","additionalProperties":false},"EventComputeinstanceDelete":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["computeinstance.delete"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/ComputeInstance"}},"description":"Event Computeinstance Delete","additionalProperties":false},"ComputeInstanceCreate":{"type":"object","required":["name","rank","resources","bandwidth"],"properties":{"name":{"type":"string","pattern":"^[a-z0-9]+(\\.[a-z0-9-]+)+$","maxLength":64,"description":"Instance type name, e.g. bi.small"},"rank":{"type":"number"},"resources":{"type":"object","required":["requests","limits"],"properties":{"requests":{"type":"object","required":["cpu","memory"],"properties":{"cpu":{"type":"string","pattern":"^[1-9][0-9]*m?$","description":"Kubernetes CPU quantity: '600m', '1000m', '2'"},"memory":{"type":"string","pattern":"^[1-9][0-9]*(Mi|Gi)$","description":"Kubernetes memory quantity: '512Mi', '2Gi'"}}},"limits":{"type":"object","required":["cpu","memory"],"properties":{"cpu":{"type":"string","pattern":"^[1-9][0-9]*m?$","description":"Kubernetes CPU quantity: '600m', '1000m', '2'"},"memory":{"type":"string","pattern":"^[1-9][0-9]*(Mi|Gi)$","description":"Kubernetes memory quantity: '512Mi', '2Gi'"}}}}},"bandwidth":{"type":"object","required":["egress","ingress"],"properties":{"egress":{"type":"string","pattern":"^[1-9][0-9]*[MG]$","description":"CNI bandwidth: '10M', '50M'"},"ingress":{"type":"string","pattern":"^[1-9][0-9]*[MG]$","description":"CNI bandwidth: '10M', '50M'"}}},"is_default":{"type":"boolean"},"plans":{"type":"object","properties":{"free":{"type":"string","enum":["default","allowed",null],"nullable":true,"default":null},"hobby":{"type":"string","enum":["default","allowed",null],"nullable":true,"default":null},"pro":{"type":"string","enum":["default","allowed",null],"nullable":true,"default":null}}},"storage":{"type":"object","properties":{"ephemeral_request":{"type":"string","pattern":"^[1-9][0-9]*(Mi|Gi)$","nullable":true,"default":null,"description":"Kubernetes storage quantity: '256Mi', '3Gi'"},"ephemeral_limit":{"type":"string","pattern":"^[1-9][0-9]*(Mi|Gi)$","nullable":true,"default":null,"description":"Kubernetes storage quantity: '256Mi', '3Gi'"},"cache":{"type":"string","pattern":"^[1-9][0-9]*(Mi|Gi)$","nullable":true,"default":null,"description":"Kubernetes storage quantity: '256Mi', '3Gi'"},"shm":{"type":"string","pattern":"^[1-9][0-9]*(Mi|Gi)$","nullable":true,"default":null,"description":"Kubernetes storage quantity: '256Mi', '3Gi'"}}},"ratelimit":{"type":"object","required":["average","burst","period"],"properties":{"average":{"type":"integer","minimum":1,"description":"Requests one client IP may sustain per `period`"},"burst":{"type":"integer","minimum":1,"description":"Requests one client IP may make at once before a 429"},"period":{"type":"string","pattern":"^[1-9][0-9]*(s|m|h)$","description":"Traefik duration: '1s', '1m'"}},"nullable":true},"description":{"type":"string","nullable":true}},"additionalProperties":false},"ComputeInstanceUpdate":{"type":"object","properties":{"rank":{"type":"number"},"resources":{"type":"object","required":["requests","limits"],"properties":{"requests":{"type":"object","required":["cpu","memory"],"properties":{"cpu":{"type":"string","pattern":"^[1-9][0-9]*m?$","description":"Kubernetes CPU quantity: '600m', '1000m', '2'"},"memory":{"type":"string","pattern":"^[1-9][0-9]*(Mi|Gi)$","description":"Kubernetes memory quantity: '512Mi', '2Gi'"}}},"limits":{"type":"object","required":["cpu","memory"],"properties":{"cpu":{"type":"string","pattern":"^[1-9][0-9]*m?$","description":"Kubernetes CPU quantity: '600m', '1000m', '2'"},"memory":{"type":"string","pattern":"^[1-9][0-9]*(Mi|Gi)$","description":"Kubernetes memory quantity: '512Mi', '2Gi'"}}}}},"bandwidth":{"type":"object","required":["egress","ingress"],"properties":{"egress":{"type":"string","pattern":"^[1-9][0-9]*[MG]$","description":"CNI bandwidth: '10M', '50M'"},"ingress":{"type":"string","pattern":"^[1-9][0-9]*[MG]$","description":"CNI bandwidth: '10M', '50M'"}}},"is_default":{"type":"boolean"},"plans":{"type":"object","properties":{"free":{"type":"string","enum":["default","allowed",null],"nullable":true,"default":null},"hobby":{"type":"string","enum":["default","allowed",null],"nullable":true,"default":null},"pro":{"type":"string","enum":["default","allowed",null],"nullable":true,"default":null}}},"storage":{"type":"object","properties":{"ephemeral_request":{"type":"string","pattern":"^[1-9][0-9]*(Mi|Gi)$","nullable":true,"default":null,"description":"Kubernetes storage quantity: '256Mi', '3Gi'"},"ephemeral_limit":{"type":"string","pattern":"^[1-9][0-9]*(Mi|Gi)$","nullable":true,"default":null,"description":"Kubernetes storage quantity: '256Mi', '3Gi'"},"cache":{"type":"string","pattern":"^[1-9][0-9]*(Mi|Gi)$","nullable":true,"default":null,"description":"Kubernetes storage quantity: '256Mi', '3Gi'"},"shm":{"type":"string","pattern":"^[1-9][0-9]*(Mi|Gi)$","nullable":true,"default":null,"description":"Kubernetes storage quantity: '256Mi', '3Gi'"}}},"ratelimit":{"type":"object","required":["average","burst","period"],"properties":{"average":{"type":"integer","minimum":1,"description":"Requests one client IP may sustain per `period`"},"burst":{"type":"integer","minimum":1,"description":"Requests one client IP may make at once before a 429"},"period":{"type":"string","pattern":"^[1-9][0-9]*(s|m|h)$","description":"Traefik duration: '1s', '1m'"}},"nullable":true},"description":{"type":"string","nullable":true}},"additionalProperties":false},"DatabaseTier":{"type":"object","required":["id","team","name","rank","connections","storage_gb","backup","price_eur_month","engines","description","metadata","createdAt"],"properties":{"id":{"type":"string","description":"DatabaseTier ID"},"team":{"type":"string"},"name":{"type":"string","pattern":"^db-[a-z0-9]+$","maxLength":32,"description":"Tier name, e.g. db-s"},"rank":{"type":"number","description":"Smallest first"},"connections":{"type":"integer","minimum":1,"maximum":500,"description":"The database user's max_user_connections; the overlay sizes the app's workers below it"},"storage_gb":{"type":"integer","minimum":1,"description":"Storage quota: over it, a warning and an upgrade gate, never a cut of writes"},"backup":{"type":"object","required":["frequency","retention_days"],"properties":{"frequency":{"type":"string","enum":["daily"]},"retention_days":{"type":"integer","minimum":1,"maximum":365}}},"price_eur_month":{"type":"number","minimum":0,"description":"List price per database instance and month (the charge is the Stripe price)"},"engines":{"type":"array","items":{"type":"string","enum":["mariadb"]},"minItems":1},"plans":{"type":"object","properties":{"hobby":{"type":"string","enum":["default","allowed",null],"nullable":true,"default":null},"pro":{"type":"string","enum":["default","allowed",null],"nullable":true,"default":null}},"additionalProperties":true},"description":{"type":"string","nullable":true,"default":null},"metadata":{"type":"object","properties":{},"additionalProperties":true,"default":{}},"createdAt":{"type":"string","description":"DatabaseTier creation date"},"updatedAt":{"type":"string","description":"DatabaseTier updated date"}},"description":"DatabaseTier","additionalProperties":false},"EventDatabasetierCreate":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["databasetier.create"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/DatabaseTier"}},"description":"Event Databasetier Create","additionalProperties":false},"EventDatabasetierUpdate":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["databasetier.update"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/DatabaseTier"}},"description":"Event Databasetier Update","additionalProperties":false},"EventDatabasetierDelete":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["databasetier.delete"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/DatabaseTier"}},"description":"Event Databasetier Delete","additionalProperties":false},"DatabaseTierCreate":{"type":"object","required":["name","rank","connections","storage_gb","backup","price_eur_month"],"properties":{"name":{"type":"string","pattern":"^db-[a-z0-9]+$","maxLength":32,"description":"Tier name, e.g. db-s"},"rank":{"type":"number","description":"Smallest first"},"connections":{"type":"integer","minimum":1,"maximum":500,"description":"The database user's max_user_connections; the overlay sizes the app's workers below it"},"storage_gb":{"type":"integer","minimum":1,"description":"Storage quota: over it, a warning and an upgrade gate, never a cut of writes"},"backup":{"type":"object","required":["frequency","retention_days"],"properties":{"frequency":{"type":"string","enum":["daily"]},"retention_days":{"type":"integer","minimum":1,"maximum":365}}},"price_eur_month":{"type":"number","minimum":0,"description":"List price per database instance and month (the charge is the Stripe price)"},"engines":{"type":"array","items":{"type":"string","enum":["mariadb"]},"minItems":1},"plans":{"type":"object","properties":{"hobby":{"type":"string","enum":["default","allowed",null],"nullable":true,"default":null},"pro":{"type":"string","enum":["default","allowed",null],"nullable":true,"default":null}},"additionalProperties":true},"description":{"type":"string","nullable":true}},"additionalProperties":false},"DatabaseTierUpdate":{"type":"object","properties":{"rank":{"type":"number","description":"Smallest first"},"connections":{"type":"integer","minimum":1,"maximum":500,"description":"The database user's max_user_connections; the overlay sizes the app's workers below it"},"storage_gb":{"type":"integer","minimum":1,"description":"Storage quota: over it, a warning and an upgrade gate, never a cut of writes"},"backup":{"type":"object","required":["frequency","retention_days"],"properties":{"frequency":{"type":"string","enum":["daily"]},"retention_days":{"type":"integer","minimum":1,"maximum":365}}},"price_eur_month":{"type":"number","minimum":0,"description":"List price per database instance and month (the charge is the Stripe price)"},"engines":{"type":"array","items":{"type":"string","enum":["mariadb"]},"minItems":1},"plans":{"type":"object","properties":{"hobby":{"type":"string","enum":["default","allowed",null],"nullable":true,"default":null},"pro":{"type":"string","enum":["default","allowed",null],"nullable":true,"default":null}},"additionalProperties":true},"description":{"type":"string","nullable":true}},"additionalProperties":false},"Blueprint":{"type":"object","required":["id","team","slug","name","version","state","image","overlay","start_command","volumes","database","reserved_env","health","waf_profile","plans","tasks_allowed","hooks","auth","compute_min","create_form","description","icon","metadata","createdAt"],"properties":{"id":{"type":"string","description":"Blueprint ID"},"team":{"type":"string"},"slug":{"type":"string","pattern":"^[a-z][a-z0-9-]{1,30}[a-z0-9]$","description":"Stable, unique: what `faable create --blueprint <slug>` takes"},"name":{"type":"string","minLength":1},"version":{"type":"integer","minimum":1},"state":{"anyOf":[{"type":"string","enum":["draft"]},{"type":"string","enum":["published"]},{"type":"string","enum":["deprecated"]}]},"image":{"type":"object","required":["kind","key"],"properties":{"kind":{"anyOf":[{"type":"string","enum":["runtime"]},{"type":"string","enum":["blueprint"]}]},"key":{"type":"string","description":"Catalogue key: 'php:8.4', or a slug for kind blueprint"}}},"overlay":{"type":"string","nullable":true,"default":null},"start_command":{"type":"string"},"volumes":{"type":"array","items":{"type":"object","required":["name","mount_path"],"properties":{"name":{"type":"string","pattern":"^[a-z][a-z0-9-]*$"},"mount_path":{"type":"string","pattern":"^/"},"backup":{"type":"boolean"},"quota_gb":{"type":"integer","minimum":1}}}},"database":{"type":"object","required":["engine"],"properties":{"engine":{"type":"string","enum":["mariadb"]},"tiers":{"type":"array","items":{"type":"string","pattern":"^db-[a-z0-9]+$"}}},"nullable":true,"default":null},"reserved_env":{"type":"array","items":{"type":"string","pattern":"^[A-Z][A-Z0-9_]*$"}},"health":{"type":"object","required":["path"],"properties":{"path":{"type":"string","pattern":"^/"}}},"waf_profile":{"type":"string","nullable":true,"default":null},"plans":{"type":"array","items":{"type":"string"}},"tasks_allowed":{"type":"array","items":{"type":"string"}},"hooks":{"type":"object","properties":{"install":{"type":"object","required":["command"],"properties":{"command":{"type":"array","items":{"type":"string"},"minItems":1},"confirm":{"type":"boolean"}}},"pre_update":{"type":"object","required":["command"],"properties":{"command":{"type":"array","items":{"type":"string"},"minItems":1},"confirm":{"type":"boolean"}}},"post_update":{"type":"object","required":["command"],"properties":{"command":{"type":"array","items":{"type":"string"},"minItems":1},"confirm":{"type":"boolean"}}},"on_domain_change":{"type":"object","required":["command"],"properties":{"command":{"type":"array","items":{"type":"string"},"minItems":1},"confirm":{"type":"boolean"}}},"nudge_update":{"type":"object","required":["command"],"properties":{"command":{"type":"array","items":{"type":"string"},"minItems":1},"confirm":{"type":"boolean"}}}},"additionalProperties":true},"auth":{"type":"object","required":["client","roles"],"properties":{"client":{"type":"boolean"},"roles":{"type":"object","additionalProperties":{"type":"string"}},"callback_path":{"type":"string","pattern":"^/[^\\s#]*$","maxLength":200,"description":"Appended to the site's origin: the OIDC redirect URI of the client the installation creates","nullable":true},"logout_path":{"type":"string","pattern":"^/[^\\s#]*$","maxLength":200,"description":"Appended to the site's origin: the client's logout URL","nullable":true},"management_scopes":{"type":"array","items":{"type":"string","pattern":"^[a-z]+:[a-z_]+$"},"description":"Management API scopes the client may get with client_credentials (per-client scopes in Faable Auth)"},"tenant_roles":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64},"description":"Roles the installation creates in the tenant when missing (existing roles are never modified)"},"admin_role":{"type":"string","minLength":1,"maxLength":64,"description":"The role given to the form's admin_email user","nullable":true}},"nullable":true,"default":null},"compute_min":{"type":"string"},"create_form":{"type":"array","items":{"type":"object","required":["name","label","type"],"properties":{"name":{"type":"string","pattern":"^[a-z][a-z0-9_]*$"},"label":{"type":"string"},"type":{"anyOf":[{"type":"string","enum":["string"]},{"type":"string","enum":["email"]},{"type":"string","enum":["locale"]}]},"required":{"type":"boolean"}}}},"description":{"type":"string","nullable":true,"default":null},"icon":{"type":"string","description":"Icon the dashboard shows: a key it knows (`wordpress`) or an https URL of an image","nullable":true,"default":null},"metadata":{"type":"object","properties":{},"additionalProperties":true,"default":{}},"createdAt":{"type":"string","description":"Blueprint creation date"},"updatedAt":{"type":"string","description":"Blueprint updated date"}},"description":"Blueprint","additionalProperties":false},"EventBlueprintCreate":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["blueprint.create"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/Blueprint"}},"description":"Event Blueprint Create","additionalProperties":false},"EventBlueprintUpdate":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["blueprint.update"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/Blueprint"}},"description":"Event Blueprint Update","additionalProperties":false},"EventBlueprintDelete":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["blueprint.delete"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"$ref":"#/components/schemas/Blueprint"}},"description":"Event Blueprint Delete","additionalProperties":false},"BlueprintCreate":{"type":"object","required":["slug","name","image","start_command","health","compute_min"],"properties":{"slug":{"type":"string","pattern":"^[a-z][a-z0-9-]{1,30}[a-z0-9]$","description":"Stable, unique: what `faable create --blueprint <slug>` takes"},"name":{"type":"string","minLength":1},"version":{"type":"integer","minimum":1},"state":{"anyOf":[{"type":"string","enum":["draft"]},{"type":"string","enum":["published"]},{"type":"string","enum":["deprecated"]}]},"image":{"type":"object","required":["kind","key"],"properties":{"kind":{"anyOf":[{"type":"string","enum":["runtime"]},{"type":"string","enum":["blueprint"]}]},"key":{"type":"string","description":"Catalogue key: 'php:8.4', or a slug for kind blueprint"}}},"overlay":{"type":"string","nullable":true},"start_command":{"type":"string"},"volumes":{"type":"array","items":{"type":"object","required":["name","mount_path"],"properties":{"name":{"type":"string","pattern":"^[a-z][a-z0-9-]*$"},"mount_path":{"type":"string","pattern":"^/"},"backup":{"type":"boolean"},"quota_gb":{"type":"integer","minimum":1}}}},"database":{"type":"object","required":["engine"],"properties":{"engine":{"type":"string","enum":["mariadb"]},"tiers":{"type":"array","items":{"type":"string","pattern":"^db-[a-z0-9]+$"}}},"nullable":true},"reserved_env":{"type":"array","items":{"type":"string","pattern":"^[A-Z][A-Z0-9_]*$"}},"health":{"type":"object","required":["path"],"properties":{"path":{"type":"string","pattern":"^/"}}},"waf_profile":{"type":"string","nullable":true},"plans":{"type":"array","items":{"type":"string"}},"tasks_allowed":{"type":"array","items":{"type":"string"}},"hooks":{"type":"object","properties":{"install":{"type":"object","required":["command"],"properties":{"command":{"type":"array","items":{"type":"string"},"minItems":1},"confirm":{"type":"boolean"}}},"pre_update":{"type":"object","required":["command"],"properties":{"command":{"type":"array","items":{"type":"string"},"minItems":1},"confirm":{"type":"boolean"}}},"post_update":{"type":"object","required":["command"],"properties":{"command":{"type":"array","items":{"type":"string"},"minItems":1},"confirm":{"type":"boolean"}}},"on_domain_change":{"type":"object","required":["command"],"properties":{"command":{"type":"array","items":{"type":"string"},"minItems":1},"confirm":{"type":"boolean"}}},"nudge_update":{"type":"object","required":["command"],"properties":{"command":{"type":"array","items":{"type":"string"},"minItems":1},"confirm":{"type":"boolean"}}}},"additionalProperties":true},"auth":{"type":"object","required":["client","roles"],"properties":{"client":{"type":"boolean"},"roles":{"type":"object","additionalProperties":{"type":"string"}},"callback_path":{"type":"string","pattern":"^/[^\\s#]*$","maxLength":200,"description":"Appended to the site's origin: the OIDC redirect URI of the client the installation creates","nullable":true},"logout_path":{"type":"string","pattern":"^/[^\\s#]*$","maxLength":200,"description":"Appended to the site's origin: the client's logout URL","nullable":true},"management_scopes":{"type":"array","items":{"type":"string","pattern":"^[a-z]+:[a-z_]+$"},"description":"Management API scopes the client may get with client_credentials (per-client scopes in Faable Auth)"},"tenant_roles":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64},"description":"Roles the installation creates in the tenant when missing (existing roles are never modified)"},"admin_role":{"type":"string","minLength":1,"maxLength":64,"description":"The role given to the form's admin_email user","nullable":true}},"nullable":true},"compute_min":{"type":"string"},"create_form":{"type":"array","items":{"type":"object","required":["name","label","type"],"properties":{"name":{"type":"string","pattern":"^[a-z][a-z0-9_]*$"},"label":{"type":"string"},"type":{"anyOf":[{"type":"string","enum":["string"]},{"type":"string","enum":["email"]},{"type":"string","enum":["locale"]}]},"required":{"type":"boolean"}}}},"description":{"type":"string","nullable":true},"icon":{"type":"string","nullable":true,"description":"Icon the dashboard shows: a key it knows (`wordpress`) or an https URL of an image"}},"additionalProperties":false},"BlueprintUpdate":{"type":"object","properties":{"slug":{"type":"string","pattern":"^[a-z][a-z0-9-]{1,30}[a-z0-9]$","description":"Stable, unique: what `faable create --blueprint <slug>` takes"},"name":{"type":"string","minLength":1},"version":{"type":"integer","minimum":1},"state":{"anyOf":[{"type":"string","enum":["draft"]},{"type":"string","enum":["published"]},{"type":"string","enum":["deprecated"]}]},"image":{"type":"object","required":["kind","key"],"properties":{"kind":{"anyOf":[{"type":"string","enum":["runtime"]},{"type":"string","enum":["blueprint"]}]},"key":{"type":"string","description":"Catalogue key: 'php:8.4', or a slug for kind blueprint"}}},"overlay":{"type":"string","nullable":true},"start_command":{"type":"string"},"volumes":{"type":"array","items":{"type":"object","required":["name","mount_path"],"properties":{"name":{"type":"string","pattern":"^[a-z][a-z0-9-]*$"},"mount_path":{"type":"string","pattern":"^/"},"backup":{"type":"boolean"},"quota_gb":{"type":"integer","minimum":1}}}},"database":{"type":"object","required":["engine"],"properties":{"engine":{"type":"string","enum":["mariadb"]},"tiers":{"type":"array","items":{"type":"string","pattern":"^db-[a-z0-9]+$"}}},"nullable":true},"reserved_env":{"type":"array","items":{"type":"string","pattern":"^[A-Z][A-Z0-9_]*$"}},"health":{"type":"object","required":["path"],"properties":{"path":{"type":"string","pattern":"^/"}}},"waf_profile":{"type":"string","nullable":true},"plans":{"type":"array","items":{"type":"string"}},"tasks_allowed":{"type":"array","items":{"type":"string"}},"hooks":{"type":"object","properties":{"install":{"type":"object","required":["command"],"properties":{"command":{"type":"array","items":{"type":"string"},"minItems":1},"confirm":{"type":"boolean"}}},"pre_update":{"type":"object","required":["command"],"properties":{"command":{"type":"array","items":{"type":"string"},"minItems":1},"confirm":{"type":"boolean"}}},"post_update":{"type":"object","required":["command"],"properties":{"command":{"type":"array","items":{"type":"string"},"minItems":1},"confirm":{"type":"boolean"}}},"on_domain_change":{"type":"object","required":["command"],"properties":{"command":{"type":"array","items":{"type":"string"},"minItems":1},"confirm":{"type":"boolean"}}},"nudge_update":{"type":"object","required":["command"],"properties":{"command":{"type":"array","items":{"type":"string"},"minItems":1},"confirm":{"type":"boolean"}}}},"additionalProperties":true},"auth":{"type":"object","required":["client","roles"],"properties":{"client":{"type":"boolean"},"roles":{"type":"object","additionalProperties":{"type":"string"}},"callback_path":{"type":"string","pattern":"^/[^\\s#]*$","maxLength":200,"description":"Appended to the site's origin: the OIDC redirect URI of the client the installation creates","nullable":true},"logout_path":{"type":"string","pattern":"^/[^\\s#]*$","maxLength":200,"description":"Appended to the site's origin: the client's logout URL","nullable":true},"management_scopes":{"type":"array","items":{"type":"string","pattern":"^[a-z]+:[a-z_]+$"},"description":"Management API scopes the client may get with client_credentials (per-client scopes in Faable Auth)"},"tenant_roles":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64},"description":"Roles the installation creates in the tenant when missing (existing roles are never modified)"},"admin_role":{"type":"string","minLength":1,"maxLength":64,"description":"The role given to the form's admin_email user","nullable":true}},"nullable":true},"compute_min":{"type":"string"},"create_form":{"type":"array","items":{"type":"object","required":["name","label","type"],"properties":{"name":{"type":"string","pattern":"^[a-z][a-z0-9_]*$"},"label":{"type":"string"},"type":{"anyOf":[{"type":"string","enum":["string"]},{"type":"string","enum":["email"]},{"type":"string","enum":["locale"]}]},"required":{"type":"boolean"}}}},"description":{"type":"string","nullable":true},"icon":{"type":"string","nullable":true,"description":"Icon the dashboard shows: a key it knows (`wordpress`) or an https URL of an image"}},"additionalProperties":false},"RuntimeRetirementListing":{"type":"object","required":["retirements"],"properties":{"retirements":{"type":"array","items":{"type":"object","required":["key","retire_at","replacement","announced","stage","why","apps"],"properties":{"key":{"type":"string"},"retire_at":{"type":"string"},"replacement":{"type":"string"},"announced":{"type":"boolean"},"stage":{"type":"string"},"why":{"type":"string"},"apps":{"type":"array","items":{"type":"object","required":["id","name","team"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"team":{"type":"string"}}}}}}}}},"AppChecknameResponse":{"type":"object","required":["name","slug","availiable"],"properties":{"name":{"type":"string"},"slug":{"type":"string"},"url":{"type":"string"},"availiable":{"type":"boolean","description":"check if slug is availiable"},"error":{"type":"string"}}},"EventAppStatus":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["app.status"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"type":"object","required":["id","status"],"properties":{"id":{"type":"string"},"status":{"$ref":"#/components/schemas/AppStatus"}}}},"description":"Event App Status","additionalProperties":false},"EventDeploymentStatus":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["deployment.status"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"type":"object","required":["id","status"],"properties":{"id":{"type":"string"},"status":{"$ref":"#/components/schemas/DeploymentStatus"}}}},"description":"Event Deployment Status","additionalProperties":false},"EventDomainStatus":{"type":"object","required":["type","produced_at","payload"],"properties":{"type":{"type":"string","enum":["domain.status"]},"produced_at":{"type":"number","description":"Timestamp"},"payload":{"type":"object","required":["id","status"],"properties":{"id":{"type":"string"},"status":{"$ref":"#/components/schemas/DomainStatus"}}}},"description":"Event Domain Status","additionalProperties":false},"ApiErrorCode":{"type":"string","description":"Machine-readable error code. One HTTP status per code.\n\n- `bad_request` (400): The request is malformed. `message` says what is wrong.\n- `validation_error` (400): The body, query or path failed schema validation. `details.issues` lists each failing field.\n- `unauthorized` (401): The request carries no valid credentials.\n- `payment_required` (402): The feature is not included in the current plan.\n- `forbidden` (403): The credentials are valid but do not allow this operation.\n- `not_found` (404): The resource does not exist, or not in the caller’s project.\n- `method_not_allowed` (405): The HTTP method is not supported on this path.\n- `conflict` (409): The request conflicts with the current state of the resource.\n- `payload_too_large` (413): The request body exceeds the size limit.\n- `unsupported_media_type` (415): The `Content-Type` is not accepted by this endpoint.\n- `unprocessable_entity` (422): The request is well-formed but cannot be processed.\n- `too_many_requests` (429): Rate limit exceeded. Honour the `Retry-After` header.\n- `internal_error` (500): Unexpected server error. Retry later; the request id is logged.\n- `gone` (410): The resource existed and is gone for good; retrying will not bring it back.\n- `bad_gateway` (502): An upstream the API depends on failed.\n- `watch_unavailable` (503): The watch cache is not ready (filling or resyncing). Retry with backoff.\n- `watch_expired` (410): The resourceVersion is older than the change log, or from another cache generation. List again and watch from the new resourceVersion.\n- `too_many_watchers` (429): Too many open watches on this server. Retry with backoff.\n- `service_unavailable` (503): The feature is not configured on this environment, or is temporarily out of service.\n- `database_unavailable` (503): Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.\n- `artifact_not_ready` (503): The build source of this deployment is still being stored. Transient: retry with backoff.\n- `analytics_unavailable` (503): Traffic and usage data are temporarily unavailable. Transient: retry after the `Retry-After` header.\n- `already_exists` (400): A resource with the same unique field already exists. `details.fields` names the field.\n- `invalid_id` (400): The id in the path is not a valid id for this resource.\n- `invalid_query` (400): The `?query=` FaableQL expression could not be parsed.\n- `invalid_expand` (400): An `?expand=` path is not allowed on this resource.\n- `search_not_supported` (400): This resource does not support `?q=`.\n- `access_denied` (403): The row exists but does not belong to the caller.\n- `invalid_token` (401): The bearer token is missing, malformed or expired.\n- `insufficient_scope` (403): The token is valid but was not allowed to do this: a connected app (an AI agent through the MCP server) can only do what the user allowed it on the consent screen, and never delete.\n- `invalid_apikey` (401): The API key does not exist or has been revoked.\n- `apikey_expired` (401): The API key predates projects and must be recreated.\n- `invalid_build_token` (401): The build token is not valid for this deployment.\n- `build_token_expired` (401): The build token is past its lifetime.\n- `build_token_unusable` (401): The deployment reached a terminal phase, so its build token no longer works.\n- `build_token_app_missing` (401): The app the build token was minted for no longer exists.\n- `user_token_required` (401): The endpoint acts on behalf of a person and the credential carries no user (an API key or a service account).\n- `team_required` (400): The operation needs a project: send it in the `x-faable-project` header (`project_<id>`).\n- `team_or_scope_required` (401): Listing needs either a project in the `x-faable-project` header or a global scope on the token.\n- `auth_account_not_found` (404): There is no Faable Auth account with that id.\n- `tenant_token_unavailable` (503): Faable Auth could not issue a management token for the account right now. Retry later.\n- `organization_not_found` (404): No organization with that id that you can see.\n- `organization_admin_required` (403): Only an admin of the organization can do this.\n- `project_owner_required` (403): Only an owner of the project can move it into or out of an organization.\n- `organization_pro_required` (403): An organization's domains, identity provider, auto-join and «Require SSO» need at least one of its projects on the Pro plan.\n- `organization_plan_unavailable` (503): The organization's plans could not be checked right now. Try again in a minute.\n- `sso_required` (403): This project's organization requires its people to sign in through the organization's identity provider. Sign in again with your company account. `details.organization` names it.\n- `role_required` (403): Your role in the project does not allow this action. `details.required` names the role it needs and `details.role` the one you have.\n- `project_access_denied` (403): The caller is not a member of the project named in `x-faable-project`, or the credential belongs to another project.\n- `project_header_conflict` (400): The request names two different projects (`x-faable-project` and the deprecated `x-faable-team`, or a query param). Send only `x-faable-project`.\n- `invalid_project_id` (400): The project in `x-faable-project` is not a valid project id (`project_` followed by 24 hex characters).\n- `user_suspended` (403): The account is suspended. Contact support@faable.com.\n- `superadmin_only` (403): The operation is restricted to platform staff.\n- `project_not_found` (404): No project with that id for this caller.\n- `owner_only` (403): Only the project owner can manage collaborators.\n- `owner_not_removable` (400): The project owner cannot be removed. Delete the project instead.\n- `last_owner` (409): A project needs at least one owner: make someone else an owner first, or delete the project.\n- `member_not_found` (404): That user is not a member of the project.\n- `billing_owner_only` (403): Only the member who pays for the project can hand billing over.\n- `billing_receiver_not_owner` (409): Billing can only be handed to an owner of the project.\n- `billing_receiver_free_limit` (402): The receiver already has as many free projects as the plan allows.\n- `billing_transfer_requires_support` (409): The project has an active subscription; handing it over is done by support.\n- `billing_unavailable` (503): Billing is not answering; nothing was changed.\n- `billing_owner_not_removable` (409): The member who pays for the project cannot be removed or leave until billing is handed over.\n- `collaborator_not_found` (404): That user is not a collaborator on the project.\n- `invalid_project_name` (400): The project name is not acceptable. `message` says why.\n- `invalid_project_description` (400): The project description is not acceptable (a link, or over the length limit). `message` says why.\n- `owner_suspension_failed` (502): The project owner could not be (un)suspended in Faable Auth. No app was touched; retry.\n- `moderation_hold` (403): The project contains an app disabled by moderation and cannot be deleted. Contact support@faable.com.\n- `app_not_found` (404): No app with that id or slug in this project.\n- `repository_not_linked` (400): The app has no linked repository.\n- `repository_empty` (400): The repository has no commits on the deploy branch yet.\n- `branch_missing` (400): The deploy branch does not exist on the repository. `message` lists the branches that do.\n- `deploy_mode_conflict` (400): The app deploys from its own workflow, so the platform will not create a deployment for it.\n- `deploy_refused` (409): The deploy was refused by quota, plan or an in-flight deployment. `message` says which.\n- `invalid_root_dir` (400): The Root Directory must be a path inside the repository — no leading `/` and no `..`.\n- `root_dir_not_found` (400): The Root Directory does not exist on that ref of the repository.\n- `root_dir_mismatch` (400): The workflow deploys from one directory and platform builds would run from another. Declare `rootDir` in faable.json.\n- `no_workflows` (400): The repository has no GitHub Actions workflows, so a CI gate would never open.\n- `no_deploy_workflow` (400): No workflow in the repository runs `faable deploy`.\n- `invalid_secret_name` (400): Secret names are 1-255 printable ASCII characters and cannot contain '='.\n- `invalid_secret_context` (400): Secrets are scoped to an app or to a profile, nothing else.\n- `app_secrets_only` (400): Only app secrets can be written in bulk.\n- `secrets_access_denied` (403): The caller may not read or write the secrets of that app or profile.\n- `status_forbidden_resource` (403): Only a platform service writes app or domain status.\n- `status_forbidden_field` (403): That field of a deployment status is platform bookkeeping. `message` names it.\n- `status_forbidden_phase` (403): That phase is set by the platform, not by the client.\n- `gitlab_not_configured` (503): GitLab linking is not enabled on this environment.\n- `gitlab_identity_missing` (412): The user has not connected GitLab. Send them through `connect_gitlab`.\n- `gitlab_token_invalid` (412): The GitLab connection has no usable token (revoked or expired). The user must connect GitLab again.\n- `gitlab_project_not_accessible` (403): The GitLab project does not exist or the user is not a Maintainer of it (creating the webhook needs Maintainer).\n- `gitlab_upstream_error` (502): GitLab answered with an error. `message` carries what it said.\n- `github_identity_missing` (412): The user has not connected GitHub. Send them through `connect_github`.\n- `github_installation_missing` (412): The Faable GitHub App is not installed on any repository of theirs.\n- `github_repository_not_found` (404): The repository does not exist or the installation cannot see it.\n- `github_token_invalid` (401): The stored GitHub token was rejected; the user must reconnect.\n- `github_permission_denied` (403): The installation lacks a permission this operation needs.\n- `github_installation_suspended` (403): The GitHub App installation is suspended.\n- `github_installation_not_found` (412): The installation id is unknown or the App was uninstalled.\n- `github_repository_blocked` (403): GitHub refuses to serve the source of that repository (403 or 451).\n- `github_rate_limited` (429): GitHub is rate-limiting Faable. Transient: retry after a short wait.\n- `github_token_refresh_failed` (401): GitHub refused to refresh the stored authorization. The user has to reconnect.\n- `github_error` (502): GitHub failed upstream. `message` carries what it said.\n- `repository_already_linked` (409): The repository is already linked to another app. `details.app_id` / `details.app_name` name it.\n- `repository_taken_down` (409): An app built from this repository was taken down for abuse, so the repository cannot be linked again. Not a plan limit — upgrading does not lift it. Restoring the taken-down app does.\n- `invalid_installation_id` (400): The `installation_id` is not a number.\n- `oidc_untrusted` (401): The CI OIDC token (GitHub Actions or GitLab CI) was rejected. `message` says why.\n- `app_ci_untrusted` (403): The app does not exist or does not trust the GitLab project the token comes from (`faable deploy apps set --gitlab-project <id>`).\n- `app_repository_mismatch` (403): The app does not exist or is not linked to the repository the token comes from.\n- `no_app_for_repository` (404): No app is linked to that repository.\n- `ambiguous_app_for_repository` (400): Several apps are linked to that repository; pass `--app-slug`.\n- `rebuild_not_promoted` (409): Only the deployment the app is serving can be rebuilt: promoting a rebuild of anything older would bring back stale code.\n- `rebuild_no_source` (409): The deployment has no stored source (CAS manifest or git commit) to rebuild from.\n- `rebuild_in_flight` (409): A rebuild of this deployment is already queued or building. `details.deployment_id` is that rebuild.\n- `runtime_pin_unavailable` (409): The app has no shared runtime to version: docker and static apps bring their own, and an app that never built has nothing to move.\n- `runtime_repo_pinned` (409): The repository forces the runtime version (runtime.txt, engines.node…), which outranks this setting. `message` names the file; remove that pin and push to manage the version from the platform.\n- `unsupported_runtime_version` (400): The requested version has no runtime image in the platform catalogue. `message` lists the supported versions.\n- `app_disabled` (409): The app is disabled, so it cannot be deployed or provisioned. `message` says why when the reason is public.\n- `app_name_taken` (409): The project already has an app with this name (case-insensitive). `message` names it.\n- `app_name_too_short` (400): The app name has fewer than 3 visible characters. Any other name is valid: accents, emoji and symbols are kept as typed, and the URL is derived from it.\n- `app_rate_limited` (429): A new account may only create a few apps in its first 24 hours.\n- `plan_required` (403): The build or feature needs a higher plan. `message` says which.\n- `remote_build_disabled` (409): Remote builds are off for this environment or this app. Deploy with a local build instead.\n- `artifact_deploy_disabled` (409): Artifact deploys are not enabled on this environment.\n- `artifact_too_large` (413): The artifact exceeds the size allowed for the plan.\n- `metrics_unavailable` (503): Traffic metrics are temporarily unavailable — this is not an app with no traffic.\n- `transfer_refused` (409): The app cannot be moved to that project. `message` lists the blockers.\n- `user_not_found` (400): No user with that id in Faable Auth.\n- `waf_rule_not_found` (404): No WAF rule with that pattern on this app.\n- `invite_limit_reached` (429): Too many invitations: a project has at most 20 pending, and a person can send at most 50 a day.\n- `invite_requires_verified_email` (403): Verify your email address before inviting people.\n- `invitation_not_found` (404): No pending invitation with that id in this project.\n- `invitation_invalid` (404): The invitation link is not valid (mistyped, or revoked and replaced).\n- `invitation_expired` (410): The invitation expired. Ask for a new one.\n- `invitation_used` (409): The invitation was already accepted or revoked.\n- `invitation_email_mismatch` (403): The invitation is for another email address than the signed-in account. `details.invited_email` shows it masked.\n- `invitation_email_unverified` (403): The signed-in account has the invited email but hasn't verified it yet.\n- `project_rate_limited` (429): A new account may only create a few projects in its first hours. It opens with time, or with a plan.\n- `project_plan_limited` (402): The Free plan project cap is reached. Upgrade to create more.\n- `domain_invalid` (400): Not a valid hostname. No protocol, port or path.\n- `domain_reserved` (403): Hostnames under that domain are assigned by Faable.\n- `domain_taken` (409): That hostname is already registered on Faable.\n- `invalid_json` (400): The request body is not valid JSON.\n- `missing_github_event` (400): The `x-github-event` header is missing.","enum":["bad_request","validation_error","unauthorized","payment_required","forbidden","not_found","method_not_allowed","conflict","payload_too_large","unsupported_media_type","unprocessable_entity","too_many_requests","internal_error","gone","bad_gateway","watch_unavailable","watch_expired","too_many_watchers","service_unavailable","database_unavailable","artifact_not_ready","analytics_unavailable","already_exists","invalid_id","invalid_query","invalid_expand","search_not_supported","access_denied","invalid_token","insufficient_scope","invalid_apikey","apikey_expired","invalid_build_token","build_token_expired","build_token_unusable","build_token_app_missing","user_token_required","team_required","team_or_scope_required","auth_account_not_found","tenant_token_unavailable","organization_not_found","organization_admin_required","project_owner_required","organization_pro_required","organization_plan_unavailable","sso_required","role_required","project_access_denied","project_header_conflict","invalid_project_id","user_suspended","superadmin_only","project_not_found","owner_only","owner_not_removable","last_owner","member_not_found","billing_owner_only","billing_receiver_not_owner","billing_receiver_free_limit","billing_transfer_requires_support","billing_unavailable","billing_owner_not_removable","collaborator_not_found","invalid_project_name","invalid_project_description","owner_suspension_failed","moderation_hold","app_not_found","repository_not_linked","repository_empty","branch_missing","deploy_mode_conflict","deploy_refused","invalid_root_dir","root_dir_not_found","root_dir_mismatch","no_workflows","no_deploy_workflow","invalid_secret_name","invalid_secret_context","app_secrets_only","secrets_access_denied","status_forbidden_resource","status_forbidden_field","status_forbidden_phase","gitlab_not_configured","gitlab_identity_missing","gitlab_token_invalid","gitlab_project_not_accessible","gitlab_upstream_error","github_identity_missing","github_installation_missing","github_repository_not_found","github_token_invalid","github_permission_denied","github_installation_suspended","github_installation_not_found","github_repository_blocked","github_rate_limited","github_token_refresh_failed","github_error","repository_already_linked","repository_taken_down","invalid_installation_id","oidc_untrusted","app_ci_untrusted","app_repository_mismatch","no_app_for_repository","ambiguous_app_for_repository","rebuild_not_promoted","rebuild_no_source","rebuild_in_flight","runtime_pin_unavailable","runtime_repo_pinned","unsupported_runtime_version","app_disabled","app_name_taken","app_name_too_short","app_rate_limited","plan_required","remote_build_disabled","artifact_deploy_disabled","artifact_too_large","metrics_unavailable","transfer_refused","user_not_found","waf_rule_not_found","invite_limit_reached","invite_requires_verified_email","invitation_not_found","invitation_invalid","invitation_expired","invitation_used","invitation_email_mismatch","invitation_email_unverified","project_rate_limited","project_plan_limited","domain_invalid","domain_reserved","domain_taken","invalid_json","missing_github_event"]},"ApiErrorResponse":{"type":"object","description":"Every error response has this shape. Branch on `error_code`, never on `message`: the message is written for a person and may change.","required":["status","message","error_code","docs"],"properties":{"status":{"type":"integer","description":"The HTTP status, repeated."},"message":{"type":"string","description":"Human-readable explanation. May change between releases."},"error_code":{"$ref":"#/components/schemas/ApiErrorCode","description":"Stable machine-readable code."},"docs":{"type":"string","format":"uri","description":"Link to the documentation of this error."},"details":{"type":"object","additionalProperties":true,"description":"Optional structured extras. `validation_error` carries `fields` and `issues`; `repository_already_linked` carries the app that holds the repository."},"action":{"type":"string","enum":["connect_github","install_github_app"],"description":"What the caller can do next, when there is a concrete step."},"statusCode":{"type":"integer","deprecated":true,"description":"Deprecated alias of `status`."},"error":{"type":"string","deprecated":true,"description":"Deprecated: the NAME of the HTTP status, never the reason."},"code":{"type":"string","deprecated":true,"description":"Deprecated alias of `error_code`."}}}}},"paths":{"/project/limits":{"get":{"operationId":"project/limits","summary":"Project creation limits for the caller","tags":["Projects"],"description":"Whether the caller can create another project, and why not","security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectLimits"}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`owner_only` — Only the project owner can manage collaborators.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["owner_only","forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/project/{id}/members":{"get":{"operationId":"project/listMembers","summary":"List the members of a project","tags":["Members & invitations"],"description":"Members of a project (owner first, then collaborators) with their profile","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectMemberList"}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`access_denied` — The row exists but does not belong to the caller.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["access_denied","forbidden","user_suspended"]}}}]}}}},"404":{"description":"`project_not_found` — No project with that id for this caller.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["project_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/project/{id}/invite":{"post":{"operationId":"project/invite","summary":"Invite collaborators to project","tags":["Members & invitations"],"description":"Invite collaborators to project","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectInvite"}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectInviteResult"}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"404":{"description":"`project_not_found` — No project with that id for this caller.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["project_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/project/{id}/collaborator/{user_id}":{"delete":{"operationId":"project/removeCollaborator","summary":"Remove a collaborator from a project","tags":["Members & invitations"],"description":"Remove a collaborator from a project","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true},{"schema":{"type":"string"},"in":"path","name":"user_id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Project","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}},"400":{"description":"`owner_not_removable` — The project owner cannot be removed. Delete the project instead.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["owner_not_removable","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`superadmin_only` — The operation is restricted to platform staff.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["superadmin_only","forbidden","user_suspended"]}}}]}}}},"404":{"description":"`collaborator_not_found` — That user is not a collaborator on the project.\n\n`project_not_found` — No project with that id for this caller.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["collaborator_not_found","project_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/project/{id}/invitations":{"get":{"operationId":"project/listInvitations","summary":"List the pending invitations of a project","tags":["Members & invitations"],"description":"Invitations sent and not yet accepted, revoked or expired. The link secret is never returned.","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectInvitationList"}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"404":{"description":"`project_not_found` — No project with that id for this caller.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["project_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/project/{id}/activity":{"get":{"operationId":"project/listActivity","summary":"Membership activity of a project","tags":["Members & invitations"],"description":"Who joined, left, was invited, removed or changed role, newest first. Changes show up a few seconds after they happen. `next` pages back.","parameters":[{"schema":{"type":"string"},"in":"query","name":"next","required":false},{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectActivityList"}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"404":{"description":"`project_not_found` — No project with that id for this caller.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["project_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/project/{id}/invitations/{invite_id}":{"delete":{"operationId":"project/revokeInvitation","summary":"Revoke a pending invitation","tags":["Members & invitations"],"description":"The link stops working at once.","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true},{"schema":{"type":"string"},"in":"path","name":"invite_id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`owner_only` — Only the project owner can manage collaborators.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["owner_only","forbidden","user_suspended"]}}}]}}}},"404":{"description":"`project_not_found` — No project with that id for this caller.\n\n`invitation_not_found` — No pending invitation with that id in this project.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["project_not_found","invitation_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/project/{id}/invitations/{invite_id}/resend":{"post":{"operationId":"project/resendInvitation","summary":"Send a pending invitation again","tags":["Members & invitations"],"description":"Emails a new link for the same address and role; the previous link stops working.","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true},{"schema":{"type":"string"},"in":"path","name":"invite_id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`owner_only` — Only the project owner can manage collaborators.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["owner_only","forbidden","user_suspended"]}}}]}}}},"404":{"description":"`project_not_found` — No project with that id for this caller.\n\n`invitation_not_found` — No pending invitation with that id in this project.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["project_not_found","invitation_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/project-invitations/accept":{"post":{"operationId":"project_invitations/accept","summary":"Accept a project invitation","tags":["Members & invitations"],"description":"Joins the project the invitation is for, as the signed-in user, if their verified email is the invited one. `token` is the secret from the invitation link's fragment.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectInvitationAccept"}}}},"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectInvitationAccepted"}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`invitation_email_mismatch` — The invitation is for another email address than the signed-in account. `details.invited_email` shows it masked.\n\n`invitation_email_unverified` — The signed-in account has the invited email but hasn't verified it yet.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invitation_email_mismatch","invitation_email_unverified","forbidden","user_suspended"]}}}]}}}},"404":{"description":"`invitation_invalid` — The invitation link is not valid (mistyped, or revoked and replaced).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invitation_invalid"]}}}]}}}},"409":{"description":"`invitation_used` — The invitation was already accepted or revoked.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invitation_used"]}}}]}}}},"410":{"description":"`invitation_expired` — The invitation expired. Ask for a new one.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invitation_expired"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/project-invitations":{"get":{"operationId":"project_invitations/listMine","summary":"List my pending project invitations","tags":["Members & invitations"],"description":"Invitations to projects addressed to the signed-in user's email, only if it is verified (`email_verified: false` and an empty list otherwise).","security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MyProjectInvitationList"}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/project-invitations/{invite_id}/accept":{"post":{"operationId":"project_invitations/acceptMine","summary":"Accept one of my project invitations","tags":["Members & invitations"],"description":"Joins the project, as the signed-in user, if their verified email is the invited one.","parameters":[{"schema":{"type":"string"},"in":"path","name":"invite_id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectInvitationAccepted"}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`invitation_email_mismatch` — The invitation is for another email address than the signed-in account. `details.invited_email` shows it masked.\n\n`invitation_email_unverified` — The signed-in account has the invited email but hasn't verified it yet.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invitation_email_mismatch","invitation_email_unverified","forbidden","user_suspended"]}}}]}}}},"404":{"description":"`invitation_invalid` — The invitation link is not valid (mistyped, or revoked and replaced).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invitation_invalid"]}}}]}}}},"409":{"description":"`invitation_used` — The invitation was already accepted or revoked.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invitation_used"]}}}]}}}},"410":{"description":"`invitation_expired` — The invitation expired. Ask for a new one.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invitation_expired"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/project-invitations/{invite_id}/decline":{"post":{"operationId":"project_invitations/declineMine","summary":"Decline one of my project invitations","tags":["Members & invitations"],"description":"The invitation is spent: its link stops working and it leaves the pending list of the project.","parameters":[{"schema":{"type":"string"},"in":"path","name":"invite_id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`invitation_email_mismatch` — The invitation is for another email address than the signed-in account. `details.invited_email` shows it masked.\n\n`invitation_email_unverified` — The signed-in account has the invited email but hasn't verified it yet.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invitation_email_mismatch","invitation_email_unverified","forbidden","user_suspended"]}}}]}}}},"404":{"description":"`invitation_invalid` — The invitation link is not valid (mistyped, or revoked and replaced).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invitation_invalid"]}}}]}}}},"409":{"description":"`invitation_used` — The invitation was already accepted or revoked.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invitation_used"]}}}]}}}},"410":{"description":"`invitation_expired` — The invitation expired. Ask for a new one.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invitation_expired"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/project/{id}/members/{user_id}":{"post":{"operationId":"project/updateMember","summary":"Change a member's role","tags":["Members & invitations"],"description":"Owner only. A project always keeps at least one owner (`last_owner`).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["role"],"properties":{"role":{"anyOf":[{"type":"string","enum":["owner"]},{"type":"string","enum":["member"]},{"type":"string","enum":["viewer"]}]}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true},{"schema":{"type":"string"},"in":"path","name":"user_id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Project","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`owner_only` — Only the project owner can manage collaborators.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["owner_only","forbidden","user_suspended"]}}}]}}}},"404":{"description":"`project_not_found` — No project with that id for this caller.\n\n`member_not_found` — That user is not a member of the project.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["project_not_found","member_not_found"]}}}]}}}},"409":{"description":"`last_owner` — A project needs at least one owner: make someone else an owner first, or delete the project.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["last_owner"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}},"delete":{"operationId":"project/removeMember","summary":"Remove a member, or leave the project","tags":["Members & invitations"],"description":"An owner removes anyone; any member removes themselves (leave). Never the last owner, nor the member who pays for the project.","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true},{"schema":{"type":"string"},"in":"path","name":"user_id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Project","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`owner_only` — Only the project owner can manage collaborators.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["owner_only","forbidden","user_suspended"]}}}]}}}},"404":{"description":"`project_not_found` — No project with that id for this caller.\n\n`member_not_found` — That user is not a member of the project.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["project_not_found","member_not_found"]}}}]}}}},"409":{"description":"`last_owner` — A project needs at least one owner: make someone else an owner first, or delete the project.\n\n`billing_owner_not_removable` — The member who pays for the project cannot be removed or leave until billing is handed over.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["last_owner","billing_owner_not_removable"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/project/{id}/billing-owner":{"post":{"operationId":"project/setBillingOwner","summary":"Hand over who pays for the project","tags":["Projects"],"description":"The member who pays hands billing to another owner. Only for projects without an active subscription; with one, support does it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["user_id"],"properties":{"user_id":{"type":"string"}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Project","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"402":{"description":"`billing_receiver_free_limit` — The receiver already has as many free projects as the plan allows.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["billing_receiver_free_limit"]}}}]}}}},"403":{"description":"`billing_owner_only` — Only the member who pays for the project can hand billing over.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["billing_owner_only","forbidden","user_suspended"]}}}]}}}},"404":{"description":"`project_not_found` — No project with that id for this caller.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["project_not_found"]}}}]}}}},"409":{"description":"`billing_receiver_not_owner` — Billing can only be handed to an owner of the project.\n\n`billing_transfer_requires_support` — The project has an active subscription; handing it over is done by support.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["billing_receiver_not_owner","billing_transfer_requires_support"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`billing_unavailable` — Billing is not answering; nothing was changed.\n\n`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["billing_unavailable","database_unavailable"]}}}]}}}}}}},"/project":{"get":{"operationId":"project/list","summary":"List Projects","tags":["Projects"],"description":"List Projects","parameters":[{"schema":{"type":"number","minimum":1,"maximum":200},"in":"query","name":"pageSize","required":false,"description":"Number of items per page (max 200)"},{"schema":{"type":"string"},"in":"query","name":"cursor","required":false,"description":"Cursor for next page"},{"schema":{"type":"string"},"in":"query","name":"next","required":false,"description":"Cursor returned by the previous page"},{"schema":{"type":"boolean"},"in":"query","name":"count","required":false,"description":"Also return `total`: the rows matching the whole filter, across every page."},{"schema":{"type":"string"},"in":"query","name":"query","required":false,"description":"Filter using a FaableQL query"},{"schema":{"type":"string","minLength":1,"maxLength":200},"in":"query","name":"q","required":false,"description":"Full-text search across: `name`, `description`, `slug`."}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["next","results"],"properties":{"next":{"anyOf":[{"type":"string"},{"type":"null"}]},"results":{"type":"array","items":{"$ref":"#/components/schemas/Project"}},"total":{"type":"integer","description":"Rows matching the whole filter, across every page. Only with `?count=true`."}},"additionalProperties":false}}}},"400":{"description":"`invalid_query` — The `?query=` FaableQL expression could not be parsed.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`search_not_supported` — This resource does not support `?q=`.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_query","invalid_expand","search_not_supported","validation_error"]}}}]}}}},"401":{"description":"`team_or_scope_required` — Listing needs either a project in the `x-faable-project` header or a global scope on the token.\n\n`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["team_or_scope_required","unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}},"post":{"operationId":"project/create","summary":"Create Project","tags":["Projects"],"description":"Create Project","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":3,"maxLength":50,"pattern":"^[A-Za-z0-9][A-Za-z0-9 ._'()&-]*[A-Za-z0-9)]$"},"description":{"type":"string"},"collaborators":{"type":"array","items":{"type":"string"}},"product_intent":{"anyOf":[{"type":"string","enum":["deploy"]},{"type":"string","enum":["auth"]},{"type":"string","enum":["undecided"]}]},"metadata":{"type":"object","properties":{"problem":{"type":"string","maxLength":1024}},"additionalProperties":false}},"additionalProperties":false}}}},"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"201":{"description":"Project","content":{"application/json":{"schema":{"type":"object","required":["id","name","description","slug","team","collaborators","createdAt"],"properties":{"id":{"type":"string","description":"Project ID"},"name":{"type":"string"},"description":{"type":"string"},"slug":{"type":"string"},"team":{"type":"string"},"user_id":{"type":"string"},"collaborators":{"type":"array","items":{"type":"string"}},"members":{"type":"array","items":{"type":"object","required":["user_id","role"],"properties":{"user_id":{"type":"string"},"role":{"type":"string"}}}},"product_intent":{"anyOf":[{"type":"string","enum":["deploy"]},{"type":"string","enum":["auth"]},{"type":"string","enum":["undecided"]}]},"metadata":{"type":"object","properties":{"problem":{"type":"string"}},"additionalProperties":true},"owner_suspended":{"type":"boolean"},"owner_email":{"type":"string"},"organization":{"type":"string","nullable":true,"default":null},"createdAt":{"type":"string","description":"Project creation date"},"updatedAt":{"type":"string","description":"Project updated date"}},"description":"Project","additionalProperties":false}}}},"400":{"description":"`already_exists` — A resource with the same unique field already exists. `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.\n\n`team_required` — The operation needs a project: send it in the `x-faable-project` header (`project_<id>`).\n\n`invalid_project_description` — The project description is not acceptable (a link, or over the length limit). `message` says why.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["already_exists","validation_error","team_required","invalid_project_description"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"402":{"description":"`project_plan_limited` — The Free plan project cap is reached. Upgrade to create more.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["project_plan_limited"]}}}]}}}},"403":{"description":"`user_suspended` — The account is suspended. Contact support@faable.com.\n\n`forbidden` — The credentials are valid but do not allow this operation.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["user_suspended","forbidden"]}}}]}}}},"429":{"description":"`project_rate_limited` — A new account may only create a few projects in its first hours. It opens with time, or with a plan.\n\n`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["project_rate_limited","too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/project/{id}":{"get":{"operationId":"project/get","summary":"Get Project","tags":["Projects"],"description":"Get Project","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Project","content":{"application/json":{"schema":{"type":"object","required":["id","name","description","slug","team","collaborators","createdAt"],"properties":{"id":{"type":"string","description":"Project ID"},"name":{"type":"string"},"description":{"type":"string"},"slug":{"type":"string"},"team":{"type":"string"},"user_id":{"type":"string"},"collaborators":{"type":"array","items":{"type":"string"}},"members":{"type":"array","items":{"type":"object","required":["user_id","role"],"properties":{"user_id":{"type":"string"},"role":{"type":"string"}}}},"product_intent":{"anyOf":[{"type":"string","enum":["deploy"]},{"type":"string","enum":["auth"]},{"type":"string","enum":["undecided"]}]},"metadata":{"type":"object","properties":{"problem":{"type":"string"}},"additionalProperties":true},"owner_suspended":{"type":"boolean"},"owner_email":{"type":"string"},"organization":{"type":"string","nullable":true,"default":null},"createdAt":{"type":"string","description":"Project creation date"},"updatedAt":{"type":"string","description":"Project updated date"}},"description":"Project","additionalProperties":false}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","invalid_expand","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`access_denied` — The row exists but does not belong to the caller.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["access_denied","forbidden","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist, or not in the caller’s project.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}},"post":{"operationId":"project/update","summary":"Update Project","tags":["Projects"],"description":"Update Project","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":3,"maxLength":50,"pattern":"^[A-Za-z0-9][A-Za-z0-9 ._'()&-]*[A-Za-z0-9)]$"},"description":{"type":"string"},"metadata":{"type":"object","properties":{"problem":{"type":"string","maxLength":1024}},"additionalProperties":false}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Project","content":{"application/json":{"schema":{"type":"object","required":["id","name","description","slug","team","collaborators","createdAt"],"properties":{"id":{"type":"string","description":"Project ID"},"name":{"type":"string"},"description":{"type":"string"},"slug":{"type":"string"},"team":{"type":"string"},"user_id":{"type":"string"},"collaborators":{"type":"array","items":{"type":"string"}},"members":{"type":"array","items":{"type":"object","required":["user_id","role"],"properties":{"user_id":{"type":"string"},"role":{"type":"string"}}}},"product_intent":{"anyOf":[{"type":"string","enum":["deploy"]},{"type":"string","enum":["auth"]},{"type":"string","enum":["undecided"]}]},"metadata":{"type":"object","properties":{"problem":{"type":"string"}},"additionalProperties":true},"owner_suspended":{"type":"boolean"},"owner_email":{"type":"string"},"organization":{"type":"string","nullable":true,"default":null},"createdAt":{"type":"string","description":"Project creation date"},"updatedAt":{"type":"string","description":"Project updated date"}},"description":"Project","additionalProperties":false}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`already_exists` — A resource with the same unique field already exists. `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.\n\n`invalid_project_description` — The project description is not acceptable (a link, or over the length limit). `message` says why.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","already_exists","validation_error","invalid_project_description"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`access_denied` — The row exists but does not belong to the caller.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["access_denied","forbidden","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist, or not in the caller’s project.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}},"delete":{"operationId":"project/delete","summary":"Delete Project","tags":["Projects"],"description":"Delete Project","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Project","content":{"application/json":{"schema":{"type":"object","required":["id","name","description","slug","team","collaborators","createdAt"],"properties":{"id":{"type":"string","description":"Project ID"},"name":{"type":"string"},"description":{"type":"string"},"slug":{"type":"string"},"team":{"type":"string"},"user_id":{"type":"string"},"collaborators":{"type":"array","items":{"type":"string"}},"members":{"type":"array","items":{"type":"object","required":["user_id","role"],"properties":{"user_id":{"type":"string"},"role":{"type":"string"}}}},"product_intent":{"anyOf":[{"type":"string","enum":["deploy"]},{"type":"string","enum":["auth"]},{"type":"string","enum":["undecided"]}]},"metadata":{"type":"object","properties":{"problem":{"type":"string"}},"additionalProperties":true},"owner_suspended":{"type":"boolean"},"owner_email":{"type":"string"},"organization":{"type":"string","nullable":true,"default":null},"createdAt":{"type":"string","description":"Project creation date"},"updatedAt":{"type":"string","description":"Project updated date"}},"description":"Project","additionalProperties":false}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`access_denied` — The row exists but does not belong to the caller.\n\n`moderation_hold` — The project contains an app disabled by moderation and cannot be deleted. Contact support@faable.com.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.\n\n`forbidden` — The credentials are valid but do not allow this operation.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["access_denied","moderation_hold","user_suspended","forbidden"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist, or not in the caller’s project.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/auth-accounts/{account_id}/token":{"post":{"operationId":"authAccount/issueToken","summary":"Get a management token for one of your Auth accounts","tags":["Faable Auth accounts"],"description":"Short-lived Management API token for the Faable Auth account, issued when the caller is a member of the project that owns it. With a deploy API key, only for a tenant of the key's project and with a narrower set of scopes (read users, logs, sessions, connections, clients; suspend users, end sessions, send password setup; no deletes). 204 when that account has no per-tenant token (the platform account): keep using your own token.","parameters":[{"schema":{"type":"string","pattern":"^account_[a-f0-9]{24}$"},"in":"path","name":"account_id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["access_token","expires_in"],"properties":{"access_token":{"type":"string"},"expires_in":{"type":"integer"}}}}}},"204":{"description":"No per-tenant token for this account (the platform one): use your own token."},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`project_access_denied` — The caller is not a member of the project named in `x-faable-project`, or the credential belongs to another project.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","project_access_denied","user_suspended"]}}}]}}}},"404":{"description":"`auth_account_not_found` — There is no Faable Auth account with that id.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["auth_account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`tenant_token_unavailable` — Faable Auth could not issue a management token for the account right now. Retry later.\n\n`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["tenant_token_unavailable","database_unavailable"]}}}]}}}}}}},"/project/{id}/auth-accounts":{"get":{"operationId":"project/listAuthAccounts","summary":"List a project's Auth accounts","tags":["Faable Auth accounts"],"parameters":[{"schema":{"type":"string"},"in":"query","name":"pageSize","required":false},{"schema":{"type":"string"},"in":"query","name":"next","required":false},{"schema":{"type":"string"},"in":"query","name":"expand","required":false},{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`access_denied` — The row exists but does not belong to the caller.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["access_denied","forbidden","user_suspended"]}}}]}}}},"404":{"description":"`project_not_found` — No project with that id for this caller.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["project_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}},"post":{"operationId":"project/createAuthAccount","summary":"Create an Auth account for a project","tags":["Faable Auth accounts"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`access_denied` — The row exists but does not belong to the caller.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["access_denied","forbidden","user_suspended"]}}}]}}}},"404":{"description":"`project_not_found` — No project with that id for this caller.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["project_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/organization":{"get":{"operationId":"organization/list","summary":"Your organizations","tags":["Organizations"],"description":"The organizations you administer or one of whose projects you are a member of.","security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`invalid_query` — The `?query=` FaableQL expression could not be parsed.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`search_not_supported` — This resource does not support `?q=`.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_query","invalid_expand","search_not_supported"]}}}]}}}},"401":{"description":"`team_or_scope_required` — Listing needs either a project in the `x-faable-project` header or a global scope on the token.\n\n`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["team_or_scope_required","unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}},"post":{"operationId":"organization/create","summary":"Create an organization from one of your projects","tags":["Organizations"],"description":"You become its admin and the project its first one. Belonging to it gives nobody access to its projects.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","project_id"],"properties":{"name":{"type":"string","minLength":1,"maxLength":100},"project_id":{"type":"string","pattern":"^project_[0-9a-f]{24}$","description":"A project you own; it becomes the first one."}},"additionalProperties":false}}}},"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`already_exists` — A resource with the same unique field already exists. `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.\n\n`team_required` — The operation needs a project: send it in the `x-faable-project` header (`project_<id>`).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["already_exists","validation_error","team_required"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`project_owner_required` — Only an owner of the project can move it into or out of an organization.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["project_owner_required","forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/organization/{id}":{"get":{"operationId":"organization/get","summary":"An organization","tags":["Organizations"],"parameters":[{"schema":{"type":"string","pattern":"^organization_[0-9a-f]{24}$"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","invalid_expand","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`access_denied` — The row exists but does not belong to the caller.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["access_denied","forbidden","user_suspended"]}}}]}}}},"404":{"description":"`organization_not_found` — No organization with that id that you can see.\n\n`not_found` — The resource does not exist, or not in the caller’s project.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["organization_not_found","not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}},"post":{"operationId":"organization/update","summary":"Update an organization's details","tags":["Organizations"],"description":"Name, description and logo. Organization admins only. The slug does not change.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":"string","maxLength":500},"logo_url":{"type":"string","format":"uri","maxLength":2048}},"additionalProperties":false,"minProperties":1}}}},"parameters":[{"schema":{"type":"string","pattern":"^organization_[0-9a-f]{24}$"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`bad_request` — The request is malformed. `message` says what is wrong.\n\n`invalid_id` — The id in the path is not a valid id for this resource.\n\n`already_exists` — A resource with the same unique field already exists. `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["bad_request","invalid_id","already_exists","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`organization_admin_required` — Only an admin of the organization can do this.\n\n`access_denied` — The row exists but does not belong to the caller.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["organization_admin_required","access_denied","forbidden","user_suspended"]}}}]}}}},"404":{"description":"`organization_not_found` — No organization with that id that you can see.\n\n`not_found` — The resource does not exist, or not in the caller’s project.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["organization_not_found","not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/organization/{id}/project":{"post":{"operationId":"organization/addProject","summary":"Put one of your projects in an organization","tags":["Organizations"],"description":"You must be an admin of the organization AND an owner of the project. A project is in one organization at most; this moves it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["project_id"],"properties":{"project_id":{"type":"string","pattern":"^project_[0-9a-f]{24}$"}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","pattern":"^organization_[0-9a-f]{24}$"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`organization_admin_required` — Only an admin of the organization can do this.\n\n`project_owner_required` — Only an owner of the project can move it into or out of an organization.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["organization_admin_required","project_owner_required","forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/organization/{id}/project/{project_id}":{"delete":{"operationId":"organization/removeProject","summary":"Take a project out of an organization","tags":["Organizations"],"description":"An admin of the organization or an owner of the project. The project becomes personal.","parameters":[{"schema":{"type":"string","pattern":"^organization_[0-9a-f]{24}$"},"in":"path","name":"id","required":true},{"schema":{"type":"string","pattern":"^project_[0-9a-f]{24}$"},"in":"path","name":"project_id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`organization_admin_required` — Only an admin of the organization can do this.\n\n`project_owner_required` — Only an owner of the project can move it into or out of an organization.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["organization_admin_required","project_owner_required","forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/organization/{id}/domain":{"post":{"operationId":"organization/addDomain","summary":"Claim an email domain","tags":["Organizations"],"description":"Adds it unverified, with the TXT record to publish. Free; nothing changes until it is verified.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["domain"],"properties":{"domain":{"type":"string","maxLength":253}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","pattern":"^organization_[0-9a-f]{24}$"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`organization_admin_required` — Only an admin of the organization can do this.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["organization_admin_required","forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/organization/{id}/domain/{domain}/verify":{"post":{"operationId":"organization/verifyDomain","summary":"Verify an email domain (Pro)","tags":["Organizations"],"parameters":[{"schema":{"type":"string","pattern":"^organization_[0-9a-f]{24}$"},"in":"path","name":"id","required":true},{"schema":{"type":"string","maxLength":253},"in":"path","name":"domain","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`organization_admin_required` — Only an admin of the organization can do this.\n\n`organization_pro_required` — An organization's domains, identity provider, auto-join and «Require SSO» need at least one of its projects on the Pro plan.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["organization_admin_required","organization_pro_required","forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`organization_plan_unavailable` — The organization's plans could not be checked right now. Try again in a minute.\n\n`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["organization_plan_unavailable","database_unavailable"]}}}]}}}}}}},"/organization/{id}/domain/{domain}":{"delete":{"operationId":"organization/removeDomain","summary":"Remove an email domain","tags":["Organizations"],"parameters":[{"schema":{"type":"string","pattern":"^organization_[0-9a-f]{24}$"},"in":"path","name":"id","required":true},{"schema":{"type":"string","maxLength":253},"in":"path","name":"domain","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`organization_admin_required` — Only an admin of the organization can do this.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["organization_admin_required","forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/organization/{id}/idp":{"put":{"operationId":"organization/setIdp","summary":"Set the organization's identity provider (Pro)","tags":["Organizations"],"description":"Any OpenID Connect provider — another Faable Auth tenant, Okta, Entra, Google Workspace. People of the verified domains are sent to it from the login screen. `client_secret` is write-only: omit it to keep the current one.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["issuer","authorize_url","token_url","userinfo_url","client_id"],"properties":{"issuer":{"type":"string","format":"uri"},"authorize_url":{"type":"string","format":"uri"},"token_url":{"type":"string","format":"uri"},"userinfo_url":{"type":"string","format":"uri"},"client_id":{"type":"string","minLength":1},"client_secret":{"type":"string","minLength":1},"scope":{"type":"array","items":{"type":"string"}}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","pattern":"^organization_[0-9a-f]{24}$"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`organization_admin_required` — Only an admin of the organization can do this.\n\n`organization_pro_required` — An organization's domains, identity provider, auto-join and «Require SSO» need at least one of its projects on the Pro plan.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["organization_admin_required","organization_pro_required","forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`organization_plan_unavailable` — The organization's plans could not be checked right now. Try again in a minute.\n\n`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["organization_plan_unavailable","database_unavailable"]}}}]}}}}}}},"/organization/{id}/idp/faable-auth":{"put":{"operationId":"organization/setIdpFaableAuth","summary":"Use one of your Faable Auth tenants as the organization’s identity provider (Pro)","tags":["Organizations"],"description":"You must be an admin of the organization and a member of the project that owns the tenant. Creates a client for Faable in that tenant and points the organization’s identity provider at it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["account_id"],"properties":{"account_id":{"type":"string","pattern":"^account_[0-9a-f]{24}$"}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","pattern":"^organization_[0-9a-f]{24}$"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`organization_admin_required` — Only an admin of the organization can do this.\n\n`organization_pro_required` — An organization's domains, identity provider, auto-join and «Require SSO» need at least one of its projects on the Pro plan.\n\n`project_access_denied` — The caller is not a member of the project named in `x-faable-project`, or the credential belongs to another project.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["organization_admin_required","organization_pro_required","project_access_denied","forbidden","user_suspended"]}}}]}}}},"404":{"description":"`auth_account_not_found` — There is no Faable Auth account with that id.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["auth_account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`organization_plan_unavailable` — The organization's plans could not be checked right now. Try again in a minute.\n\n`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["organization_plan_unavailable","database_unavailable"]}}}]}}}}}}},"/organization/{id}/auto-join":{"put":{"operationId":"organization/setAutoJoin","summary":"Who joins which project by email domain (Pro)","tags":["Organizations"],"description":"Whoever signs in with a verified email of a verified domain joins these projects of the organization with this role (viewer or member, never owner). An empty list turns it off.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["rules"],"properties":{"rules":{"type":"array","items":{"type":"object","required":["project_id","role"],"properties":{"project_id":{"type":"string","pattern":"^project_[0-9a-f]{24}$"},"role":{"anyOf":[{"type":"string","enum":["viewer"]},{"type":"string","enum":["member"]}]}},"additionalProperties":false},"maxItems":50}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","pattern":"^organization_[0-9a-f]{24}$"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`organization_admin_required` — Only an admin of the organization can do this.\n\n`organization_pro_required` — An organization's domains, identity provider, auto-join and «Require SSO» need at least one of its projects on the Pro plan.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["organization_admin_required","organization_pro_required","forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`organization_plan_unavailable` — The organization's plans could not be checked right now. Try again in a minute.\n\n`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["organization_plan_unavailable","database_unavailable"]}}}]}}}}}}},"/organization/{id}/require-sso":{"put":{"operationId":"organization/setRequireSso","summary":"Require SSO (Pro)","tags":["Organizations"],"description":"People of the verified domains reach the organization's projects only through its identity provider. External collaborators are not affected. Turning it off is always allowed.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["require_sso"],"properties":{"require_sso":{"type":"boolean"}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","pattern":"^organization_[0-9a-f]{24}$"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`organization_admin_required` — Only an admin of the organization can do this.\n\n`organization_pro_required` — An organization's domains, identity provider, auto-join and «Require SSO» need at least one of its projects on the Pro plan.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["organization_admin_required","organization_pro_required","forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`organization_plan_unavailable` — The organization's plans could not be checked right now. Try again in a minute.\n\n`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["organization_plan_unavailable","database_unavailable"]}}}]}}}}}}},"/organization/{id}/admin":{"post":{"operationId":"organization/addAdmin","summary":"Make someone an admin of the organization","tags":["Organizations"],"description":"Admins manage the organization; being one gives no access to its projects.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["user_id"],"properties":{"user_id":{"type":"string","pattern":"^user_[0-9a-f]{24}$"}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","pattern":"^organization_[0-9a-f]{24}$"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`organization_admin_required` — Only an admin of the organization can do this.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["organization_admin_required","forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/organization/{id}/admin/{user_id}":{"delete":{"operationId":"organization/removeAdmin","summary":"Remove an admin (or leave as one)","tags":["Organizations"],"parameters":[{"schema":{"type":"string","pattern":"^organization_[0-9a-f]{24}$"},"in":"path","name":"id","required":true},{"schema":{"type":"string","pattern":"^user_[0-9a-f]{24}$"},"in":"path","name":"user_id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`organization_admin_required` — Only an admin of the organization can do this.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["organization_admin_required","forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/app":{"get":{"operationId":"app/list","summary":"List Apps","tags":["Apps"],"description":"List Apps","parameters":[{"schema":{"type":"number","minimum":1,"maximum":200},"in":"query","name":"pageSize","required":false,"description":"Number of items per page (max 200)"},{"schema":{"type":"string"},"in":"query","name":"cursor","required":false,"description":"Cursor for next page"},{"schema":{"type":"string"},"in":"query","name":"next","required":false,"description":"Cursor returned by the previous page"},{"schema":{"type":"boolean"},"in":"query","name":"count","required":false,"description":"Also return `total`: the rows matching the whole filter, across every page."},{"schema":{"type":"string"},"in":"query","name":"query","required":false,"description":"Filter using a FaableQL query"},{"schema":{"type":"array","items":{"type":"string","enum":["status.deployment","status.deployment_previous"],"description":"Allowed expand paths on `app`: `status.deployment`, `status.deployment_previous`."},"maxItems":2},"in":"query","name":"expand","required":false,"description":"Expand id-only fields in the response. Pass one path per entry (e.g. `?expand=status.deployment`)."}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["next","results"],"properties":{"next":{"anyOf":[{"type":"string"},{"type":"null"}]},"results":{"type":"array","items":{"$ref":"#/components/schemas/App"}},"total":{"type":"integer","description":"Rows matching the whole filter, across every page. Only with `?count=true`."}},"additionalProperties":false}}}},"400":{"description":"`invalid_query` — The `?query=` FaableQL expression could not be parsed.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`search_not_supported` — This resource does not support `?q=`.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_query","invalid_expand","search_not_supported","validation_error"]}}}]}}}},"401":{"description":"`team_or_scope_required` — Listing needs either a project in the `x-faable-project` header or a global scope on the token.\n\n`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["team_or_scope_required","unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}},"post":{"operationId":"app/create","summary":"Create App","tags":["Apps"],"description":"Create App","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","maxLength":255},"runtime":{"type":"string"},"description":{"type":"string"}},"additionalProperties":false}}}},"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"201":{"description":"App","content":{"application/json":{"schema":{"type":"object","required":["id","team","name","description","slug","url","active","runtime","runtime_strategy","mode","instance_type","region","status","metadata","createdAt"],"properties":{"id":{"type":"string","description":"App ID"},"team":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"slug":{"type":"string"},"url":{"type":"string"},"active":{"type":"boolean"},"disabled_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Reason the app was administratively disabled. Null when the app is active."},"runtime":{"anyOf":[{"enum":["node","php","python"]},{"type":"null"}]},"runtime_strategy":{"anyOf":[{"type":"string"},{"type":"null"}]},"detected":{"anyOf":[{"type":"object","required":["buildpack","runtime"],"properties":{"buildpack":{"type":"string"},"framework":{"type":"string"},"runtime":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"version":{"type":"string"},"source":{"type":"string"}}},"dockerfile_ignored":{"type":"boolean"},"process_type":{"type":"string","enum":["worker"]}}},{"type":"null"}]},"runtime_pin":{"anyOf":[{"type":"object","required":["name","version","pinned_at","source"],"properties":{"name":{"type":"string"},"version":{"type":"string"},"pinned_at":{"type":"string","format":"date-time"},"source":{"anyOf":[{"type":"string","enum":["first_build"]},{"type":"string","enum":["backfill"]},{"type":"string","enum":["user_upgrade"]},{"type":"string","enum":["retirement"]}]}}},{"type":"null"}]},"mode":{"$ref":"#/components/schemas/AppMode"},"build_mode":{"anyOf":[{"enum":["local","remote"]},{"type":"null"}]},"deploy_output":{"anyOf":[{"enum":["image","artifact"]},{"type":"null"}]},"root_dir":{"anyOf":[{"type":"string"},{"type":"null"}]},"gitlab_ci_project_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"git_provider":{"anyOf":[{"type":"string","enum":["github"]},{"type":"string","enum":["gitlab"]},{"type":"null"}]},"gitlab_project_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"gitlab_project_path":{"anyOf":[{"type":"string"},{"type":"null"}]},"gitlab_branch":{"anyOf":[{"type":"string"},{"type":"null"}]},"coexistence_routing":{"anyOf":[{"enum":["cookie","path"],"default":"cookie"},{"type":"null"}]},"coexistence_keep_versions":{"anyOf":[{"type":"number","description":"K: how many versions stay live under mode=coexistence (active + K-1 draining)"},{"type":"null"}]},"coexistence_drain_hours":{"anyOf":[{"type":"number","description":"Max hours a demoted version keeps serving pinned sessions before GC"},{"type":"null"}]},"kind":{"anyOf":[{"type":"string","enum":["code"]},{"type":"string","enum":["blueprint"]}],"description":"code: built from a repository or `faable deploy`. blueprint: run from a platform blueprint (WordPress…), no repository and no build. Fixed at creation."},"blueprint_id":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The blueprint a `kind: blueprint` app runs (blueprint id). Null for code apps."},"blueprint_version":{"anyOf":[{"type":"number"},{"type":"null"}]},"database_id":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A code app's managed database (database id), attached by Faable staff. Null for apps without one; a blueprint app's is on its installation."},"blueprint_runtime_key":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A blueprint app's base runtime chosen by Faable staff (`php:8.3`) instead of the blueprint's. Null = the blueprint's."},"instance":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Compute catalogue row the app runs on (computeinstance id). Replaces instance_type."},"instance_type":{"type":"string","description":"Faable instance types comprise varying combinations of CPU, memory, storage, and networking capacity. Name of `instance`; kept for existing clients."},"instance_resources":{"type":"object","required":["cpu","memory"],"properties":{"cpu":{"type":"string","description":"Kubernetes quantity, e.g. '600m'"},"memory":{"type":"string","description":"Kubernetes quantity, e.g. '512Mi'"}},"description":"The most CPU and memory the app can use on its instance (the limits). Read-only, derived from `instance`."},"instance_upgrades":{"type":"array","items":{"type":"object","required":["plan","instance","cpu","memory"],"properties":{"plan":{"type":"string","description":"free | hobby | pro"},"instance":{"type":"string"},"cpu":{"type":"string"},"memory":{"type":"string"}}},"description":"Each plan's default instance that gives more than this app's, smallest first. Read-only."},"region":{"enum":["gui1","poz1","ams1"],"description":"High-level summary of where the Database is in its lifecycle","default":"UNKNOWN"},"notify_deploy_success":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Success deploy emails (first deploy + every promote). Absent/null = enabled; false = muted. Failure emails are never gated by this."},"http3":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Whether HTTP/3 is advertised for this app. Absent/null = advertised; false = not advertised. Note it controls the advertisement, not the listener: clients that already discovered HTTP/3 may keep using it until the advertisement expires, and the server does not refuse it."},"status":{"$ref":"#/components/schemas/AppStatus"},"repository":{"type":"string"},"github_branch":{"type":"string"},"github_installation_id":{"type":"string"},"github_installation_state":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Platform GitHub access health, maintained by installation lifecycle webhooks. Absent/null = ok; 'deleted' | 'suspended' | 'repo_removed' = access broken until re-linked."},"github_repo_hidden":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The linked repository stopped being readable after the app was created, per the GitHub `repository` webhook. Absent/null = visible; 'deleted' | 'privatized' | 'archived' = the source was pulled after deploying (the analyzer re-scores the promoted deployment against our own build artifact)."},"github_disconnect_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Why the platform cleared the repository link automatically. Absent/null = never auto-disconnected (or re-linked since); 'push_flood' = the repo exceeded the hourly push limit and must be re-linked by the user."},"deploy_trigger":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Push-to-deploy trigger. Absent/null = 'workflow' (committed GitHub Action); 'webhook' = the push webhook creates deployments server-side (deploy v4)."},"deploy_gate":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Wait-for-CI gate for webhook triggers. Absent/null = deploy on push; 'ci' = deploy only after the commit's check suites complete green."},"autodeploy_paused":{"anyOf":[{"type":"object","required":["since","failures","failure_code","skipped_pushes","last_skipped_sha"],"properties":{"since":{"type":"string","format":"date-time"},"failures":{"type":"number"},"failure_code":{"type":"string"},"skipped_pushes":{"type":"number"},"last_skipped_sha":{"type":"string"}}},{"type":"null"}],"description":"Automatic deploys are paused: the app has never built and its last `failures` builds failed with the same `failure_code`. Pushes that do not touch build inputs are skipped until a manual deploy or a build that succeeds. Null/absent = not paused."},"metadata":{"type":"object","properties":{},"additionalProperties":true,"default":{}},"createdAt":{"type":"string","description":"App creation date"},"updatedAt":{"type":"string","description":"App updated date"}},"description":"App","additionalProperties":false}}}},"400":{"description":"`already_exists` — A resource with the same unique field already exists. `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.\n\n`team_required` — The operation needs a project: send it in the `x-faable-project` header (`project_<id>`).\n\n`app_name_too_short` — The app name has fewer than 3 visible characters. Any other name is valid: accents, emoji and symbols are kept as typed, and the URL is derived from it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["already_exists","validation_error","team_required","app_name_too_short"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`user_suspended` — The account is suspended. Contact support@faable.com.\n\n`forbidden` — The credentials are valid but do not allow this operation.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["user_suspended","forbidden"]}}}]}}}},"409":{"description":"`app_name_taken` — The project already has an app with this name (case-insensitive). `message` names it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["app_name_taken"]}}}]}}}},"429":{"description":"`app_rate_limited` — A new account may only create a few apps in its first 24 hours.\n\n`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["app_rate_limited","too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/app/{id}":{"get":{"operationId":"app/get","summary":"Get App","tags":["Apps"],"description":"Get App","parameters":[{"schema":{"type":"array","items":{"type":"string","enum":["status.deployment","status.deployment_previous"],"description":"Allowed expand paths on `app`: `status.deployment`, `status.deployment_previous`."},"maxItems":2},"in":"query","name":"expand","required":false,"description":"Expand id-only fields in the response. Pass one path per entry (e.g. `?expand=status.deployment`)."},{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"App","content":{"application/json":{"schema":{"type":"object","required":["id","team","name","description","slug","url","active","runtime","runtime_strategy","mode","instance_type","region","status","metadata","createdAt"],"properties":{"id":{"type":"string","description":"App ID"},"team":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"slug":{"type":"string"},"url":{"type":"string"},"active":{"type":"boolean"},"disabled_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Reason the app was administratively disabled. Null when the app is active."},"runtime":{"anyOf":[{"enum":["node","php","python"]},{"type":"null"}]},"runtime_strategy":{"anyOf":[{"type":"string"},{"type":"null"}]},"detected":{"anyOf":[{"type":"object","required":["buildpack","runtime"],"properties":{"buildpack":{"type":"string"},"framework":{"type":"string"},"runtime":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"version":{"type":"string"},"source":{"type":"string"}}},"dockerfile_ignored":{"type":"boolean"},"process_type":{"type":"string","enum":["worker"]}}},{"type":"null"}]},"runtime_pin":{"anyOf":[{"type":"object","required":["name","version","pinned_at","source"],"properties":{"name":{"type":"string"},"version":{"type":"string"},"pinned_at":{"type":"string","format":"date-time"},"source":{"anyOf":[{"type":"string","enum":["first_build"]},{"type":"string","enum":["backfill"]},{"type":"string","enum":["user_upgrade"]},{"type":"string","enum":["retirement"]}]}}},{"type":"null"}]},"mode":{"$ref":"#/components/schemas/AppMode"},"build_mode":{"anyOf":[{"enum":["local","remote"]},{"type":"null"}]},"deploy_output":{"anyOf":[{"enum":["image","artifact"]},{"type":"null"}]},"root_dir":{"anyOf":[{"type":"string"},{"type":"null"}]},"gitlab_ci_project_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"git_provider":{"anyOf":[{"type":"string","enum":["github"]},{"type":"string","enum":["gitlab"]},{"type":"null"}]},"gitlab_project_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"gitlab_project_path":{"anyOf":[{"type":"string"},{"type":"null"}]},"gitlab_branch":{"anyOf":[{"type":"string"},{"type":"null"}]},"coexistence_routing":{"anyOf":[{"enum":["cookie","path"],"default":"cookie"},{"type":"null"}]},"coexistence_keep_versions":{"anyOf":[{"type":"number","description":"K: how many versions stay live under mode=coexistence (active + K-1 draining)"},{"type":"null"}]},"coexistence_drain_hours":{"anyOf":[{"type":"number","description":"Max hours a demoted version keeps serving pinned sessions before GC"},{"type":"null"}]},"kind":{"anyOf":[{"type":"string","enum":["code"]},{"type":"string","enum":["blueprint"]}],"description":"code: built from a repository or `faable deploy`. blueprint: run from a platform blueprint (WordPress…), no repository and no build. Fixed at creation."},"blueprint_id":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The blueprint a `kind: blueprint` app runs (blueprint id). Null for code apps."},"blueprint_version":{"anyOf":[{"type":"number"},{"type":"null"}]},"database_id":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A code app's managed database (database id), attached by Faable staff. Null for apps without one; a blueprint app's is on its installation."},"blueprint_runtime_key":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A blueprint app's base runtime chosen by Faable staff (`php:8.3`) instead of the blueprint's. Null = the blueprint's."},"instance":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Compute catalogue row the app runs on (computeinstance id). Replaces instance_type."},"instance_type":{"type":"string","description":"Faable instance types comprise varying combinations of CPU, memory, storage, and networking capacity. Name of `instance`; kept for existing clients."},"instance_resources":{"type":"object","required":["cpu","memory"],"properties":{"cpu":{"type":"string","description":"Kubernetes quantity, e.g. '600m'"},"memory":{"type":"string","description":"Kubernetes quantity, e.g. '512Mi'"}},"description":"The most CPU and memory the app can use on its instance (the limits). Read-only, derived from `instance`."},"instance_upgrades":{"type":"array","items":{"type":"object","required":["plan","instance","cpu","memory"],"properties":{"plan":{"type":"string","description":"free | hobby | pro"},"instance":{"type":"string"},"cpu":{"type":"string"},"memory":{"type":"string"}}},"description":"Each plan's default instance that gives more than this app's, smallest first. Read-only."},"region":{"enum":["gui1","poz1","ams1"],"description":"High-level summary of where the Database is in its lifecycle","default":"UNKNOWN"},"notify_deploy_success":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Success deploy emails (first deploy + every promote). Absent/null = enabled; false = muted. Failure emails are never gated by this."},"http3":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Whether HTTP/3 is advertised for this app. Absent/null = advertised; false = not advertised. Note it controls the advertisement, not the listener: clients that already discovered HTTP/3 may keep using it until the advertisement expires, and the server does not refuse it."},"status":{"$ref":"#/components/schemas/AppStatus"},"repository":{"type":"string"},"github_branch":{"type":"string"},"github_installation_id":{"type":"string"},"github_installation_state":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Platform GitHub access health, maintained by installation lifecycle webhooks. Absent/null = ok; 'deleted' | 'suspended' | 'repo_removed' = access broken until re-linked."},"github_repo_hidden":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The linked repository stopped being readable after the app was created, per the GitHub `repository` webhook. Absent/null = visible; 'deleted' | 'privatized' | 'archived' = the source was pulled after deploying (the analyzer re-scores the promoted deployment against our own build artifact)."},"github_disconnect_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Why the platform cleared the repository link automatically. Absent/null = never auto-disconnected (or re-linked since); 'push_flood' = the repo exceeded the hourly push limit and must be re-linked by the user."},"deploy_trigger":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Push-to-deploy trigger. Absent/null = 'workflow' (committed GitHub Action); 'webhook' = the push webhook creates deployments server-side (deploy v4)."},"deploy_gate":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Wait-for-CI gate for webhook triggers. Absent/null = deploy on push; 'ci' = deploy only after the commit's check suites complete green."},"autodeploy_paused":{"anyOf":[{"type":"object","required":["since","failures","failure_code","skipped_pushes","last_skipped_sha"],"properties":{"since":{"type":"string","format":"date-time"},"failures":{"type":"number"},"failure_code":{"type":"string"},"skipped_pushes":{"type":"number"},"last_skipped_sha":{"type":"string"}}},{"type":"null"}],"description":"Automatic deploys are paused: the app has never built and its last `failures` builds failed with the same `failure_code`. Pushes that do not touch build inputs are skipped until a manual deploy or a build that succeeds. Null/absent = not paused."},"metadata":{"type":"object","properties":{},"additionalProperties":true,"default":{}},"createdAt":{"type":"string","description":"App creation date"},"updatedAt":{"type":"string","description":"App updated date"}},"description":"App","additionalProperties":false}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","invalid_expand","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`access_denied` — The row exists but does not belong to the caller.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["access_denied","forbidden","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist, or not in the caller’s project.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}},"post":{"operationId":"app/update","summary":"Update App","tags":["Apps"],"description":"Update App","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":255},"description":{"type":"string"},"runtime":{"$ref":"#/components/schemas/AppRuntime"},"runtime_strategy":{"$ref":"#/components/schemas/AppRuntimeStrategy"},"instance":{"type":"string"},"instance_type":{"type":"string"},"notify_deploy_success":{"type":"boolean","nullable":true},"http3":{"type":"boolean","nullable":true}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"App","content":{"application/json":{"schema":{"type":"object","required":["id","team","name","description","slug","url","active","runtime","runtime_strategy","mode","instance_type","region","status","metadata","createdAt"],"properties":{"id":{"type":"string","description":"App ID"},"team":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"slug":{"type":"string"},"url":{"type":"string"},"active":{"type":"boolean"},"disabled_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Reason the app was administratively disabled. Null when the app is active."},"runtime":{"anyOf":[{"enum":["node","php","python"]},{"type":"null"}]},"runtime_strategy":{"anyOf":[{"type":"string"},{"type":"null"}]},"detected":{"anyOf":[{"type":"object","required":["buildpack","runtime"],"properties":{"buildpack":{"type":"string"},"framework":{"type":"string"},"runtime":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"version":{"type":"string"},"source":{"type":"string"}}},"dockerfile_ignored":{"type":"boolean"},"process_type":{"type":"string","enum":["worker"]}}},{"type":"null"}]},"runtime_pin":{"anyOf":[{"type":"object","required":["name","version","pinned_at","source"],"properties":{"name":{"type":"string"},"version":{"type":"string"},"pinned_at":{"type":"string","format":"date-time"},"source":{"anyOf":[{"type":"string","enum":["first_build"]},{"type":"string","enum":["backfill"]},{"type":"string","enum":["user_upgrade"]},{"type":"string","enum":["retirement"]}]}}},{"type":"null"}]},"mode":{"$ref":"#/components/schemas/AppMode"},"build_mode":{"anyOf":[{"enum":["local","remote"]},{"type":"null"}]},"deploy_output":{"anyOf":[{"enum":["image","artifact"]},{"type":"null"}]},"root_dir":{"anyOf":[{"type":"string"},{"type":"null"}]},"gitlab_ci_project_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"git_provider":{"anyOf":[{"type":"string","enum":["github"]},{"type":"string","enum":["gitlab"]},{"type":"null"}]},"gitlab_project_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"gitlab_project_path":{"anyOf":[{"type":"string"},{"type":"null"}]},"gitlab_branch":{"anyOf":[{"type":"string"},{"type":"null"}]},"coexistence_routing":{"anyOf":[{"enum":["cookie","path"],"default":"cookie"},{"type":"null"}]},"coexistence_keep_versions":{"anyOf":[{"type":"number","description":"K: how many versions stay live under mode=coexistence (active + K-1 draining)"},{"type":"null"}]},"coexistence_drain_hours":{"anyOf":[{"type":"number","description":"Max hours a demoted version keeps serving pinned sessions before GC"},{"type":"null"}]},"kind":{"anyOf":[{"type":"string","enum":["code"]},{"type":"string","enum":["blueprint"]}],"description":"code: built from a repository or `faable deploy`. blueprint: run from a platform blueprint (WordPress…), no repository and no build. Fixed at creation."},"blueprint_id":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The blueprint a `kind: blueprint` app runs (blueprint id). Null for code apps."},"blueprint_version":{"anyOf":[{"type":"number"},{"type":"null"}]},"database_id":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A code app's managed database (database id), attached by Faable staff. Null for apps without one; a blueprint app's is on its installation."},"blueprint_runtime_key":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A blueprint app's base runtime chosen by Faable staff (`php:8.3`) instead of the blueprint's. Null = the blueprint's."},"instance":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Compute catalogue row the app runs on (computeinstance id). Replaces instance_type."},"instance_type":{"type":"string","description":"Faable instance types comprise varying combinations of CPU, memory, storage, and networking capacity. Name of `instance`; kept for existing clients."},"instance_resources":{"type":"object","required":["cpu","memory"],"properties":{"cpu":{"type":"string","description":"Kubernetes quantity, e.g. '600m'"},"memory":{"type":"string","description":"Kubernetes quantity, e.g. '512Mi'"}},"description":"The most CPU and memory the app can use on its instance (the limits). Read-only, derived from `instance`."},"instance_upgrades":{"type":"array","items":{"type":"object","required":["plan","instance","cpu","memory"],"properties":{"plan":{"type":"string","description":"free | hobby | pro"},"instance":{"type":"string"},"cpu":{"type":"string"},"memory":{"type":"string"}}},"description":"Each plan's default instance that gives more than this app's, smallest first. Read-only."},"region":{"enum":["gui1","poz1","ams1"],"description":"High-level summary of where the Database is in its lifecycle","default":"UNKNOWN"},"notify_deploy_success":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Success deploy emails (first deploy + every promote). Absent/null = enabled; false = muted. Failure emails are never gated by this."},"http3":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Whether HTTP/3 is advertised for this app. Absent/null = advertised; false = not advertised. Note it controls the advertisement, not the listener: clients that already discovered HTTP/3 may keep using it until the advertisement expires, and the server does not refuse it."},"status":{"$ref":"#/components/schemas/AppStatus"},"repository":{"type":"string"},"github_branch":{"type":"string"},"github_installation_id":{"type":"string"},"github_installation_state":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Platform GitHub access health, maintained by installation lifecycle webhooks. Absent/null = ok; 'deleted' | 'suspended' | 'repo_removed' = access broken until re-linked."},"github_repo_hidden":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The linked repository stopped being readable after the app was created, per the GitHub `repository` webhook. Absent/null = visible; 'deleted' | 'privatized' | 'archived' = the source was pulled after deploying (the analyzer re-scores the promoted deployment against our own build artifact)."},"github_disconnect_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Why the platform cleared the repository link automatically. Absent/null = never auto-disconnected (or re-linked since); 'push_flood' = the repo exceeded the hourly push limit and must be re-linked by the user."},"deploy_trigger":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Push-to-deploy trigger. Absent/null = 'workflow' (committed GitHub Action); 'webhook' = the push webhook creates deployments server-side (deploy v4)."},"deploy_gate":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Wait-for-CI gate for webhook triggers. Absent/null = deploy on push; 'ci' = deploy only after the commit's check suites complete green."},"autodeploy_paused":{"anyOf":[{"type":"object","required":["since","failures","failure_code","skipped_pushes","last_skipped_sha"],"properties":{"since":{"type":"string","format":"date-time"},"failures":{"type":"number"},"failure_code":{"type":"string"},"skipped_pushes":{"type":"number"},"last_skipped_sha":{"type":"string"}}},{"type":"null"}],"description":"Automatic deploys are paused: the app has never built and its last `failures` builds failed with the same `failure_code`. Pushes that do not touch build inputs are skipped until a manual deploy or a build that succeeds. Null/absent = not paused."},"metadata":{"type":"object","properties":{},"additionalProperties":true,"default":{}},"createdAt":{"type":"string","description":"App creation date"},"updatedAt":{"type":"string","description":"App updated date"}},"description":"App","additionalProperties":false}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`already_exists` — A resource with the same unique field already exists. `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.\n\n`app_name_too_short` — The app name has fewer than 3 visible characters. Any other name is valid: accents, emoji and symbols are kept as typed, and the URL is derived from it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","already_exists","validation_error","app_name_too_short"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`access_denied` — The row exists but does not belong to the caller.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["access_denied","forbidden","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist, or not in the caller’s project.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found"]}}}]}}}},"409":{"description":"`app_name_taken` — The project already has an app with this name (case-insensitive). `message` names it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["app_name_taken"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}},"delete":{"operationId":"app/delete","summary":"Delete App","tags":["Apps"],"description":"Delete App","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"App","content":{"application/json":{"schema":{"type":"object","required":["id","team","name","description","slug","url","active","runtime","runtime_strategy","mode","instance_type","region","status","metadata","createdAt"],"properties":{"id":{"type":"string","description":"App ID"},"team":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"slug":{"type":"string"},"url":{"type":"string"},"active":{"type":"boolean"},"disabled_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Reason the app was administratively disabled. Null when the app is active."},"runtime":{"anyOf":[{"enum":["node","php","python"]},{"type":"null"}]},"runtime_strategy":{"anyOf":[{"type":"string"},{"type":"null"}]},"detected":{"anyOf":[{"type":"object","required":["buildpack","runtime"],"properties":{"buildpack":{"type":"string"},"framework":{"type":"string"},"runtime":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"version":{"type":"string"},"source":{"type":"string"}}},"dockerfile_ignored":{"type":"boolean"},"process_type":{"type":"string","enum":["worker"]}}},{"type":"null"}]},"runtime_pin":{"anyOf":[{"type":"object","required":["name","version","pinned_at","source"],"properties":{"name":{"type":"string"},"version":{"type":"string"},"pinned_at":{"type":"string","format":"date-time"},"source":{"anyOf":[{"type":"string","enum":["first_build"]},{"type":"string","enum":["backfill"]},{"type":"string","enum":["user_upgrade"]},{"type":"string","enum":["retirement"]}]}}},{"type":"null"}]},"mode":{"$ref":"#/components/schemas/AppMode"},"build_mode":{"anyOf":[{"enum":["local","remote"]},{"type":"null"}]},"deploy_output":{"anyOf":[{"enum":["image","artifact"]},{"type":"null"}]},"root_dir":{"anyOf":[{"type":"string"},{"type":"null"}]},"gitlab_ci_project_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"git_provider":{"anyOf":[{"type":"string","enum":["github"]},{"type":"string","enum":["gitlab"]},{"type":"null"}]},"gitlab_project_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"gitlab_project_path":{"anyOf":[{"type":"string"},{"type":"null"}]},"gitlab_branch":{"anyOf":[{"type":"string"},{"type":"null"}]},"coexistence_routing":{"anyOf":[{"enum":["cookie","path"],"default":"cookie"},{"type":"null"}]},"coexistence_keep_versions":{"anyOf":[{"type":"number","description":"K: how many versions stay live under mode=coexistence (active + K-1 draining)"},{"type":"null"}]},"coexistence_drain_hours":{"anyOf":[{"type":"number","description":"Max hours a demoted version keeps serving pinned sessions before GC"},{"type":"null"}]},"kind":{"anyOf":[{"type":"string","enum":["code"]},{"type":"string","enum":["blueprint"]}],"description":"code: built from a repository or `faable deploy`. blueprint: run from a platform blueprint (WordPress…), no repository and no build. Fixed at creation."},"blueprint_id":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The blueprint a `kind: blueprint` app runs (blueprint id). Null for code apps."},"blueprint_version":{"anyOf":[{"type":"number"},{"type":"null"}]},"database_id":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A code app's managed database (database id), attached by Faable staff. Null for apps without one; a blueprint app's is on its installation."},"blueprint_runtime_key":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A blueprint app's base runtime chosen by Faable staff (`php:8.3`) instead of the blueprint's. Null = the blueprint's."},"instance":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Compute catalogue row the app runs on (computeinstance id). Replaces instance_type."},"instance_type":{"type":"string","description":"Faable instance types comprise varying combinations of CPU, memory, storage, and networking capacity. Name of `instance`; kept for existing clients."},"instance_resources":{"type":"object","required":["cpu","memory"],"properties":{"cpu":{"type":"string","description":"Kubernetes quantity, e.g. '600m'"},"memory":{"type":"string","description":"Kubernetes quantity, e.g. '512Mi'"}},"description":"The most CPU and memory the app can use on its instance (the limits). Read-only, derived from `instance`."},"instance_upgrades":{"type":"array","items":{"type":"object","required":["plan","instance","cpu","memory"],"properties":{"plan":{"type":"string","description":"free | hobby | pro"},"instance":{"type":"string"},"cpu":{"type":"string"},"memory":{"type":"string"}}},"description":"Each plan's default instance that gives more than this app's, smallest first. Read-only."},"region":{"enum":["gui1","poz1","ams1"],"description":"High-level summary of where the Database is in its lifecycle","default":"UNKNOWN"},"notify_deploy_success":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Success deploy emails (first deploy + every promote). Absent/null = enabled; false = muted. Failure emails are never gated by this."},"http3":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Whether HTTP/3 is advertised for this app. Absent/null = advertised; false = not advertised. Note it controls the advertisement, not the listener: clients that already discovered HTTP/3 may keep using it until the advertisement expires, and the server does not refuse it."},"status":{"$ref":"#/components/schemas/AppStatus"},"repository":{"type":"string"},"github_branch":{"type":"string"},"github_installation_id":{"type":"string"},"github_installation_state":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Platform GitHub access health, maintained by installation lifecycle webhooks. Absent/null = ok; 'deleted' | 'suspended' | 'repo_removed' = access broken until re-linked."},"github_repo_hidden":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The linked repository stopped being readable after the app was created, per the GitHub `repository` webhook. Absent/null = visible; 'deleted' | 'privatized' | 'archived' = the source was pulled after deploying (the analyzer re-scores the promoted deployment against our own build artifact)."},"github_disconnect_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Why the platform cleared the repository link automatically. Absent/null = never auto-disconnected (or re-linked since); 'push_flood' = the repo exceeded the hourly push limit and must be re-linked by the user."},"deploy_trigger":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Push-to-deploy trigger. Absent/null = 'workflow' (committed GitHub Action); 'webhook' = the push webhook creates deployments server-side (deploy v4)."},"deploy_gate":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Wait-for-CI gate for webhook triggers. Absent/null = deploy on push; 'ci' = deploy only after the commit's check suites complete green."},"autodeploy_paused":{"anyOf":[{"type":"object","required":["since","failures","failure_code","skipped_pushes","last_skipped_sha"],"properties":{"since":{"type":"string","format":"date-time"},"failures":{"type":"number"},"failure_code":{"type":"string"},"skipped_pushes":{"type":"number"},"last_skipped_sha":{"type":"string"}}},{"type":"null"}],"description":"Automatic deploys are paused: the app has never built and its last `failures` builds failed with the same `failure_code`. Pushes that do not touch build inputs are skipped until a manual deploy or a build that succeeds. Null/absent = not paused."},"metadata":{"type":"object","properties":{},"additionalProperties":true,"default":{}},"createdAt":{"type":"string","description":"App creation date"},"updatedAt":{"type":"string","description":"App updated date"}},"description":"App","additionalProperties":false}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`access_denied` — The row exists but does not belong to the caller.\n\n`moderation_hold` — The project contains an app disabled by moderation and cannot be deleted. Contact support@faable.com.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.\n\n`forbidden` — The credentials are valid but do not allow this operation.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["access_denied","moderation_hold","user_suspended","forbidden"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist, or not in the caller’s project.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/app/slug/{slug}":{"get":{"operationId":"app/slug","summary":"Get App by unique slug","tags":["Apps"],"description":"Get App by unique slug","parameters":[{"schema":{"type":"string"},"in":"path","name":"slug","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"App","content":{"application/json":{"schema":{"$ref":"#/components/schemas/App"}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"404":{"description":"`app_not_found` — No app with that id or slug in this project.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["app_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/app/checkname":{"get":{"operationId":"app/checkname","summary":"Check if an app name is availiable","tags":["Apps"],"description":"Check if an app name is availiable","parameters":[{"schema":{"type":"string","maxLength":255},"in":"query","name":"name","required":true,"description":"Slug to test"}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppChecknameResponse"}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/app/repo-linkable":{"get":{"operationId":"app/repo_linkable","summary":"Check whether a repository can be linked to a new app","tags":["Source & build settings"],"description":"Runs the free-plan \"one repository, one app\" guard before the app exists. Always 200: `linkable:false` names the app that already has the repository (and its project when it is another one of yours), with the same message the link would refuse with.","parameters":[{"schema":{"type":"string","minLength":3,"maxLength":255},"in":"query","name":"repository","required":true,"description":"GitHub repository full name, e.g. acme/api"},{"schema":{"type":"string"},"in":"query","name":"github_installation_id","required":false,"description":"GitHub App installation the repo belongs to"}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Repository link pre-check","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppRepoLinkable"}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`user_token_required` — The endpoint acts on behalf of a person and the credential carries no user (an API key or a service account).\n\n`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["user_token_required","unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"412":{"description":"`github_identity_missing` — The user has not connected GitHub. Send them through `connect_github`.\n\n`github_installation_missing` — The Faable GitHub App is not installed on any repository of theirs.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["github_identity_missing","github_installation_missing"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"502":{"description":"`github_error` — GitHub failed upstream. `message` carries what it said.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["github_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/app/{app_id}/registry":{"get":{"operationId":"app/get_registry","summary":"Get app registry","tags":["Logs & traffic"],"description":"Get app registry","parameters":[{"schema":{"type":"string"},"in":"path","name":"app_id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["id","app_id","hostname","image","user","password"],"properties":{"id":{"type":"string"},"app_id":{"type":"string"},"hostname":{"type":"string"},"image":{"type":"string"},"user":{"type":"string"},"password":{"type":"string"}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"409":{"description":"`app_disabled` — The app is disabled, so it cannot be deployed or provisioned. `message` says why when the reason is public.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["app_disabled"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"502":{"description":"`bad_gateway` — An upstream the API depends on failed.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["bad_gateway"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/app/{app_id}/logs":{"get":{"operationId":"app/logs","summary":"Get app logs","tags":["Logs & traffic"],"description":"Get app logs","parameters":[{"schema":{"type":"boolean","default":false},"in":"query","name":"download","required":false},{"schema":{"type":"string"},"in":"query","name":"deployment_id","required":false},{"schema":{"type":"string"},"in":"path","name":"app_id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/app/{app_id}/traffic":{"get":{"operationId":"app/traffic","summary":"Get App Traffic","tags":["Logs & traffic"],"description":"Get App Traffic","parameters":[{"schema":{"type":"integer"},"in":"query","name":"from","required":false},{"schema":{"type":"integer"},"in":"query","name":"to","required":false},{"schema":{"type":"integer","minimum":60},"in":"query","name":"interval","required":false},{"schema":{"type":"string"},"in":"query","name":"deployment_id","required":false},{"schema":{"anyOf":[{"type":"string","enum":["requests"]},{"type":"string","enum":["req_bytes"]},{"type":"string","enum":["resp_bytes"]}]},"in":"query","name":"endpoints_sort","required":false},{"schema":{"type":"string"},"in":"query","name":"endpoints_method","required":false},{"schema":{"type":"integer"},"in":"query","name":"error_status","required":false},{"schema":{"type":"string"},"in":"path","name":"app_id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["range","top_endpoints","error_endpoints","status_codes","methods","transfer","origin_status_codes","latency","top_referrers","top_user_agents","degraded"],"properties":{"range":{"type":"object","required":["from","to","interval"],"properties":{"from":{"type":"integer"},"to":{"type":"integer"},"interval":{"type":"integer"},"deployment_id":{"type":"string"}}},"top_endpoints":{"type":"array","items":{"type":"object","required":["path","requests","req_bytes","resp_bytes"],"properties":{"path":{"type":"string"},"requests":{"type":"integer"},"req_bytes":{"type":"integer"},"resp_bytes":{"type":"integer"}}}},"error_endpoints":{"type":"array","items":{"type":"object","required":["path","status","errors"],"properties":{"path":{"type":"string"},"status":{"type":"integer"},"errors":{"type":"integer"}}}},"status_codes":{"type":"array","items":{"type":"object","required":["status","requests"],"properties":{"status":{"type":"integer"},"requests":{"type":"integer"}}}},"methods":{"type":"array","items":{"type":"object","required":["method","requests"],"properties":{"method":{"type":"string"},"requests":{"type":"integer"}}}},"transfer":{"type":"array","items":{"type":"object","required":["ts","inbound_bytes","outbound_bytes","requests","p50_ms","p95_ms"],"properties":{"ts":{"type":"string"},"inbound_bytes":{"type":"integer"},"outbound_bytes":{"type":"integer"},"requests":{"type":"integer"},"p50_ms":{"type":"number"},"p95_ms":{"type":"number"}}}},"origin_status_codes":{"type":"array","items":{"type":"object","required":["status","requests"],"properties":{"status":{"type":"integer"},"requests":{"type":"integer"}}}},"latency":{"type":"object","required":["p50","p95","p99","samples"],"properties":{"p50":{"type":"number"},"p95":{"type":"number"},"p99":{"type":"number"},"samples":{"type":"integer"}}},"top_referrers":{"type":"array","items":{"type":"object","required":["referer","requests"],"properties":{"referer":{"type":"string"},"requests":{"type":"integer"}}}},"top_user_agents":{"type":"array","items":{"type":"object","required":["user_agent","requests"],"properties":{"user_agent":{"type":"string"},"requests":{"type":"integer"}}}},"degraded":{"type":"array","items":{"type":"string"}}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`metrics_unavailable` — Traffic metrics are temporarily unavailable — this is not an app with no traffic.\n\n`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["metrics_unavailable","database_unavailable"]}}}]}}}}}}},"/app/{id}/link-repository":{"post":{"operationId":"app/link_repository","summary":"Link a GitHub repository to an app","tags":["Source & build settings"],"description":"Link a GitHub repository to an app after verifying the user has a connected GitHub identity with access to the repository.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["repository"],"properties":{"repository":{"type":"string","description":"GitHub repository in \"owner/repo\" form"},"github_branch":{"type":"string","description":"Branch to deploy from. Defaults to the repo default."},"github_installation_id":{"type":"string","description":"Installation the repo belongs to. When provided, verification is scoped to it (faster) instead of scanning every installation."}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"App","content":{"application/json":{"schema":{"$ref":"#/components/schemas/App"}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`github_token_invalid` — The stored GitHub token was rejected; the user must reconnect.\n\n`user_token_required` — The endpoint acts on behalf of a person and the credential carries no user (an API key or a service account).\n\n`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["github_token_invalid","user_token_required","unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`github_installation_suspended` — The GitHub App installation is suspended.\n\n`github_permission_denied` — The installation lacks a permission this operation needs.\n\n`github_repository_blocked` — GitHub refuses to serve the source of that repository (403 or 451).\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["github_installation_suspended","github_permission_denied","github_repository_blocked","forbidden","user_suspended"]}}}]}}}},"404":{"description":"`github_repository_not_found` — The repository does not exist or the installation cannot see it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["github_repository_not_found"]}}}]}}}},"409":{"description":"`repository_already_linked` — The repository is already linked to another app. `details.app_id` / `details.app_name` name it.\n\n`repository_taken_down` — An app built from this repository was taken down for abuse, so the repository cannot be linked again. Not a plan limit — upgrading does not lift it. Restoring the taken-down app does.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["repository_already_linked","repository_taken_down"]}}}]}}}},"412":{"description":"`github_identity_missing` — The user has not connected GitHub. Send them through `connect_github`.\n\n`github_installation_missing` — The Faable GitHub App is not installed on any repository of theirs.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["github_identity_missing","github_installation_missing"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"502":{"description":"`github_error` — GitHub failed upstream. `message` carries what it said.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["github_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/app/{id}/unlink-repository":{"post":{"operationId":"app/unlink_repository","summary":"Unlink the GitHub repository from an app","tags":["Source & build settings"],"description":"Unlink the GitHub repository from an app.","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"App","content":{"application/json":{"schema":{"$ref":"#/components/schemas/App"}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`user_token_required` — The endpoint acts on behalf of a person and the credential carries no user (an API key or a service account).\n\n`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["user_token_required","unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/app/{id}/deploy-branch":{"post":{"operationId":"app/set_deploy_branch","summary":"Change the deploy branch","tags":["Source & build settings"],"description":"Change the branch the app deploys from. Apps deploying from their own GitHub Actions workflow must update the branch trigger in the workflow file themselves.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["github_branch"],"properties":{"github_branch":{"type":"string","minLength":1,"maxLength":250}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["github_branch","workflow_synced"],"properties":{"github_branch":{"type":"string"},"workflow_synced":{"type":"boolean"}}}}}},"400":{"description":"`branch_missing` — The deploy branch does not exist on the repository. `message` lists the branches that do.\n\n`repository_not_linked` — The app has no linked repository.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["branch_missing","repository_not_linked","validation_error"]}}}]}}}},"401":{"description":"`user_token_required` — The endpoint acts on behalf of a person and the credential carries no user (an API key or a service account).\n\n`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["user_token_required","unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/app/{id}/repo/branches":{"get":{"operationId":"app/repo_branches","summary":"List repository branches","tags":["Source & build settings"],"description":"Branch names of the linked repository.","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["branches"],"properties":{"branches":{"type":"array","items":{"type":"string"}}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`github_token_invalid` — The stored GitHub token was rejected; the user must reconnect.\n\n`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["github_token_invalid","unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"404":{"description":"`github_repository_not_found` — The repository does not exist or the installation cannot see it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["github_repository_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"502":{"description":"`github_error` — GitHub failed upstream. `message` carries what it said.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["github_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/app/{id}/repo/workflows":{"get":{"operationId":"app/repo_workflows","summary":"List repository workflow files","tags":["Source & build settings"],"description":"GitHub Actions workflow files present on the linked repository (deploy branch).","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["workflows","deploy_workflow"],"properties":{"workflows":{"type":"array","items":{"type":"object","required":["name","path"],"properties":{"name":{"type":"string"},"path":{"type":"string"}}}},"deploy_workflow":{"anyOf":[{"type":"string"},{"type":"null"}]}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/app/{id}/repo/config":{"get":{"operationId":"app/repo_config","summary":"Get effective repository build config","tags":["Source & build settings"],"description":"Effective build configuration read from the repository: the monorepo Root Directory and its source (platform override or the repo's faable.json).","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["root_dir","root_dir_source"],"properties":{"root_dir":{"anyOf":[{"type":"string"},{"type":"null"}]},"root_dir_source":{"anyOf":[{"type":"string","enum":["platform"]},{"type":"string","enum":["faable.json"]},{"type":"null"}]}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`github_token_invalid` — The stored GitHub token was rejected; the user must reconnect.\n\n`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["github_token_invalid","unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"404":{"description":"`github_repository_not_found` — The repository does not exist or the installation cannot see it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["github_repository_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"502":{"description":"`github_error` — GitHub failed upstream. `message` carries what it said.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["github_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/app/{id}/repo/directories":{"get":{"operationId":"app/repo_directories","summary":"List repository directories","tags":["Source & build settings"],"description":"Directories of the linked repository on the deploy branch, flagged with whether each one holds a buildable manifest. Feeds the Root Directory picker so a monorepo is configured from what the repository actually contains. Limited to three levels deep; dotted directories and node_modules are excluded.","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["directories","truncated"],"properties":{"directories":{"type":"array","items":{"type":"object","required":["path","buildable"],"properties":{"path":{"type":"string"},"buildable":{"type":"boolean"}}}},"truncated":{"type":"boolean"}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/app/{id}/root-dir":{"post":{"operationId":"app/set_root_dir","summary":"Set the Root Directory","tags":["Source & build settings"],"description":"Set the platform Root Directory: the subdirectory of the repository the build runs from. Takes precedence over the repo's faable.json `rootDir`; `null` clears it and hands precedence back. The path must exist on the deploy branch.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["root_dir"],"properties":{"root_dir":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}]}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"App","content":{"application/json":{"schema":{"$ref":"#/components/schemas/App"}}}},"400":{"description":"`invalid_root_dir` — The Root Directory must be a path inside the repository — no leading `/` and no `..`.\n\n`root_dir_not_found` — The Root Directory does not exist on that ref of the repository.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_root_dir","root_dir_not_found","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/app/{id}/deploy-mode":{"post":{"operationId":"app/set_deploy_mode","summary":"Set the deploy mode","tags":["Source & build settings"],"description":"Choose how pushes deploy: 'push' = every push deploys directly; 'ci' = deploy once the commit's CI checks pass. Both are platform-managed (webhook) triggers.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["mode"],"properties":{"mode":{"anyOf":[{"type":"string","enum":["push"]},{"type":"string","enum":["ci"]},{"type":"string","enum":["workflow"]}]}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"App","content":{"application/json":{"schema":{"$ref":"#/components/schemas/App"}}}},"400":{"description":"`no_deploy_workflow` — No workflow in the repository runs `faable deploy`.\n\n`no_workflows` — The repository has no GitHub Actions workflows, so a CI gate would never open.\n\n`root_dir_mismatch` — The workflow deploys from one directory and platform builds would run from another. Declare `rootDir` in faable.json.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["no_deploy_workflow","no_workflows","root_dir_mismatch","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/app/{id}/ci-trust":{"post":{"operationId":"app/set_ci_trust","summary":"Set the CI allowed to deploy keyless","tags":["Source & build settings"],"description":"Set the GitLab project (numeric id) whose CI may deploy this app keyless through OIDC. `null` revokes it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["gitlab_project_id"],"properties":{"gitlab_project_id":{"type":"string","pattern":"^[0-9]{1,20}$","nullable":true}}}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"App","content":{"application/json":{"schema":{"$ref":"#/components/schemas/App"}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/app/{id}/deploy":{"post":{"operationId":"app/deploy_now","summary":"Deploy the latest commit now","tags":["Deployments"],"description":"Build and deploy the current head of the deploy branch, without waiting for a push. Same path as a push webhook, including the same-commit dedupe — unless `force=true`, which rebuilds a commit that is already deployed.","parameters":[{"schema":{"type":"boolean"},"in":"query","name":"force","required":false,"description":"Rebuild the head even if that commit is already deployed (a platform migration, a new runtime). A build of the same commit still in progress blocks it anyway."},{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["status","commit","branch"],"properties":{"status":{"type":"string","enum":["created"]},"commit":{"type":"string"},"branch":{"type":"string"},"deployment_id":{"type":"string","description":"The deployment this call created."}}}}}},"400":{"description":"`branch_missing` — The deploy branch does not exist on the repository. `message` lists the branches that do.\n\n`deploy_mode_conflict` — The app deploys from its own workflow, so the platform will not create a deployment for it.\n\n`repository_empty` — The repository has no commits on the deploy branch yet.\n\n`repository_not_linked` — The app has no linked repository.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["branch_missing","deploy_mode_conflict","repository_empty","repository_not_linked","validation_error"]}}}]}}}},"401":{"description":"`github_token_invalid` — The stored GitHub token was rejected; the user must reconnect.\n\n`user_token_required` — The endpoint acts on behalf of a person and the credential carries no user (an API key or a service account).\n\n`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["github_token_invalid","user_token_required","unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"409":{"description":"`deploy_refused` — The deploy was refused by quota, plan or an in-flight deployment. `message` says which.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["deploy_refused"]}}}]}}}},"412":{"description":"`github_identity_missing` — The user has not connected GitHub. Send them through `connect_github`.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["github_identity_missing"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"502":{"description":"`github_error` — GitHub failed upstream. `message` carries what it said.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["github_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/app/{id}/runtime-version":{"get":{"operationId":"app/runtime_version","summary":"Runtime version and upgrade options","tags":["Source & build settings"],"description":"The app's sticky runtime version (pin), and the versions it could move to — the data behind the upgrade button.","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["name","pinned","repo_pin","current","available","newest","retiring","last_upgrade"],"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"pinned":{"anyOf":[{"type":"object","required":["name","version","pinned_at","source"],"properties":{"name":{"type":"string"},"version":{"type":"string"},"pinned_at":{"type":"string","format":"date-time"},"source":{"type":"string"}}},{"type":"null"}]},"repo_pin":{"anyOf":[{"type":"string"},{"type":"null"}]},"current":{"anyOf":[{"type":"string"},{"type":"null"}]},"available":{"type":"array","items":{"type":"string"}},"newest":{"anyOf":[{"type":"string"},{"type":"null"}]},"retiring":{"anyOf":[{"type":"object","required":["retire_at","replacement","why"],"properties":{"retire_at":{"type":"string"},"replacement":{"type":"string"},"why":{"type":"string"}}},{"type":"null"}]},"last_upgrade":{"anyOf":[{"type":"object","required":["deployment_id","to","from","phase","created_at"],"properties":{"deployment_id":{"type":"string"},"to":{"type":"string"},"from":{"anyOf":[{"type":"string"},{"type":"null"}]},"phase":{"type":"string"},"created_at":{"type":"string","format":"date-time"}}},{"type":"null"}]}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}},"post":{"operationId":"app/set_runtime_version","summary":"Change the runtime version","tags":["Source & build settings"],"description":"Move the app to another supported runtime version. Writes the sticky pin and rebuilds the promoted deployment from its stored source — the version changes with a build, never in place. The previous deployment keeps serving until the rebuild is READY, and remains available to roll back to.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["version"],"properties":{"version":{"type":"string","description":"A supported minor (python/php, e.g. \"3.13\") or major (node, e.g. \"24\")"}}}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["pinned","deployment_id"],"properties":{"pinned":{"type":"object","required":["name","version","pinned_at","source"],"properties":{"name":{"type":"string"},"version":{"type":"string"},"pinned_at":{"type":"string","format":"date-time"},"source":{"type":"string"}}},"deployment_id":{"anyOf":[{"type":"string"},{"type":"null"}]}}}}}},"400":{"description":"`unsupported_runtime_version` — The requested version has no runtime image in the platform catalogue. `message` lists the supported versions.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unsupported_runtime_version","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"409":{"description":"`runtime_pin_unavailable` — The app has no shared runtime to version: docker and static apps bring their own, and an app that never built has nothing to move.\n\n`runtime_repo_pinned` — The repository forces the runtime version (runtime.txt, engines.node…), which outranks this setting. `message` names the file; remove that pin and push to manage the version from the platform.\n\n`rebuild_in_flight` — A rebuild of this deployment is already queued or building. `details.deployment_id` is that rebuild.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["runtime_pin_unavailable","runtime_repo_pinned","rebuild_in_flight"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/app/{app_id}/waf":{"get":{"operationId":"app/waf","summary":"Get App WAF","tags":["Security (WAF)"],"description":"Get the WAF rules in effect for an App","parameters":[{"schema":{"type":"string"},"in":"path","name":"app_id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["enabled","platform_profiles","rules"],"properties":{"enabled":{"type":"boolean"},"platform_profiles":{"type":"array","items":{"type":"object","required":["name","action","match","rule_count"],"properties":{"name":{"type":"string"},"action":{"type":"string"},"match":{"type":"string"},"rule_count":{"type":"integer"},"rules":{"type":"array","items":{"type":"object","required":["pattern"],"properties":{"pattern":{"type":"string"},"user_agent":{"type":"string"},"query":{"type":"string"},"description":{"type":"string"}}}}}}},"rules":{"type":"array","items":{"type":"object","required":["action","match"],"properties":{"pattern":{"type":"string"},"user_agent":{"type":"string"},"query":{"type":"string"},"action":{"type":"string"},"match":{"type":"string"},"description":{"type":"string"}}}}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/app/{app_id}/waf/rules":{"post":{"operationId":"app/waf/addRule","summary":"Add App WAF Rule","tags":["Security (WAF)"],"description":"Add a WAF rule to an App","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["action"],"properties":{"pattern":{"type":"string","description":"RE2 path-regex fragment, e.g. ^/robots\\.txt$"},"user_agent":{"type":"string","description":"RE2 fragment matched against User-Agent. With `pattern`, BOTH must match."},"query":{"type":"string","description":"LITERAL query parameter name, e.g. rest_route (never a regex). With `pattern`, BOTH must match."},"action":{"anyOf":[{"type":"string","enum":["deny"]},{"type":"string","enum":["sink"]},{"type":"string","enum":["allow"]}],"description":"deny → 403 at the edge; sink → synthetic 404 from the platform (the app is never woken); allow → a path the platform blocks for every app (e.g. *.zip) reaches this app (path rules only)"},"description":{"type":"string","maxLength":500},"force":{"type":"boolean","description":"Accept a user-agent pattern that also matches a known search/AI crawler or uptime monitor, or a query parameter that carries an auth flow"}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"app_id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["enabled","platform_profiles","rules"],"properties":{"enabled":{"type":"boolean"},"platform_profiles":{"type":"array","items":{"type":"object","required":["name","action","match","rule_count"],"properties":{"name":{"type":"string"},"action":{"type":"string"},"match":{"type":"string"},"rule_count":{"type":"integer"},"rules":{"type":"array","items":{"type":"object","required":["pattern"],"properties":{"pattern":{"type":"string"},"user_agent":{"type":"string"},"query":{"type":"string"},"description":{"type":"string"}}}}}}},"rules":{"type":"array","items":{"type":"object","required":["action","match"],"properties":{"pattern":{"type":"string"},"user_agent":{"type":"string"},"query":{"type":"string"},"action":{"type":"string"},"match":{"type":"string"},"description":{"type":"string"}}}}}}}}},"400":{"description":"`bad_request` — The request is malformed. `message` says what is wrong.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["bad_request","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"409":{"description":"`conflict` — The request conflicts with the current state of the resource.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["conflict"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}},"delete":{"operationId":"app/waf/removeRule","summary":"Remove App WAF Rule","tags":["Security (WAF)"],"description":"Remove a WAF rule from an App","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"pattern":{"type":"string"},"user_agent":{"type":"string"},"query":{"type":"string"},"action":{"anyOf":[{"type":"string","enum":["deny"]},{"type":"string","enum":["sink"]},{"type":"string","enum":["allow"]}]}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"app_id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["enabled","platform_profiles","rules"],"properties":{"enabled":{"type":"boolean"},"platform_profiles":{"type":"array","items":{"type":"object","required":["name","action","match","rule_count"],"properties":{"name":{"type":"string"},"action":{"type":"string"},"match":{"type":"string"},"rule_count":{"type":"integer"},"rules":{"type":"array","items":{"type":"object","required":["pattern"],"properties":{"pattern":{"type":"string"},"user_agent":{"type":"string"},"query":{"type":"string"},"description":{"type":"string"}}}}}}},"rules":{"type":"array","items":{"type":"object","required":["action","match"],"properties":{"pattern":{"type":"string"},"user_agent":{"type":"string"},"query":{"type":"string"},"action":{"type":"string"},"match":{"type":"string"},"description":{"type":"string"}}}}}}}}},"400":{"description":"`bad_request` — The request is malformed. `message` says what is wrong.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["bad_request","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"404":{"description":"`waf_rule_not_found` — No WAF rule with that pattern on this app.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["waf_rule_not_found"]}}}]}}}},"409":{"description":"`conflict` — The request conflicts with the current state of the resource.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["conflict"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/app/{app_id}/waf/activity":{"get":{"operationId":"app/waf/activity","summary":"Get App WAF activity","tags":["Security (WAF)"],"description":"Requests the app WAF stopped (deny and sink), over time and by path and user-agent","parameters":[{"schema":{"type":"integer"},"in":"query","name":"from","required":false},{"schema":{"type":"integer"},"in":"query","name":"to","required":false},{"schema":{"type":"integer","minimum":60},"in":"query","name":"interval","required":false},{"schema":{"type":"string"},"in":"path","name":"app_id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["range","totals","series","top_paths","top_user_agents","degraded"],"properties":{"range":{"type":"object","required":["from","to","interval"],"properties":{"from":{"type":"integer"},"to":{"type":"integer"},"interval":{"type":"integer"}}},"totals":{"type":"object","required":["deny","sink"],"properties":{"deny":{"type":"integer"},"sink":{"type":"integer"}}},"series":{"type":"array","items":{"type":"object","required":["ts","deny","sink"],"properties":{"ts":{"type":"string"},"deny":{"type":"integer"},"sink":{"type":"integer"}}}},"top_paths":{"type":"array","items":{"type":"object","required":["path","action","requests"],"properties":{"path":{"type":"string"},"action":{"anyOf":[{"type":"string","enum":["deny"]},{"type":"string","enum":["sink"]}]},"requests":{"type":"integer"}}}},"top_user_agents":{"type":"array","items":{"type":"object","required":["user_agent","requests"],"properties":{"user_agent":{"type":"string"},"requests":{"type":"integer"}}}},"degraded":{"type":"array","items":{"type":"string"}}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`metrics_unavailable` — Traffic metrics are temporarily unavailable — this is not an app with no traffic.\n\n`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["metrics_unavailable","database_unavailable"]}}}]}}}}}}},"/deployment":{"get":{"operationId":"deployment/list","summary":"List Deployments","tags":["Deployments"],"description":"List Deployments","parameters":[{"schema":{"type":"number","minimum":1,"maximum":200},"in":"query","name":"pageSize","required":false,"description":"Number of items per page (max 200)"},{"schema":{"type":"string"},"in":"query","name":"cursor","required":false,"description":"Cursor for next page"},{"schema":{"type":"string"},"in":"query","name":"next","required":false,"description":"Cursor returned by the previous page"},{"schema":{"type":"boolean"},"in":"query","name":"count","required":false,"description":"Also return `total`: the rows matching the whole filter, across every page."},{"schema":{"type":"string"},"in":"query","name":"query","required":false,"description":"Filter using a FaableQL query"}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["next","results"],"properties":{"next":{"anyOf":[{"type":"string"},{"type":"null"}]},"results":{"type":"array","items":{"$ref":"#/components/schemas/Deployment"}},"total":{"type":"integer","description":"Rows matching the whole filter, across every page. Only with `?count=true`."}},"additionalProperties":false}}}},"400":{"description":"`invalid_query` — The `?query=` FaableQL expression could not be parsed.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`search_not_supported` — This resource does not support `?q=`.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_query","invalid_expand","search_not_supported","validation_error"]}}}]}}}},"401":{"description":"`team_or_scope_required` — Listing needs either a project in the `x-faable-project` header or a global scope on the token.\n\n`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["team_or_scope_required","unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}},"post":{"operationId":"deployment/create","summary":"Create Deployment","tags":["Deployments"],"description":"Create Deployment","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["app_id"],"properties":{"app_id":{"type":"string"},"image":{"type":"string"},"release":{"type":"string","maxLength":128},"github_commit":{"type":"string"},"github_ref":{"type":"string"},"github_actor":{"type":"string"},"github_commit_message":{"type":"string"},"type":{"type":"string"},"source":{"$ref":"#/components/schemas/DeploymentSource"}},"additionalProperties":false}}}},"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"201":{"description":"Deployment","content":{"application/json":{"schema":{"type":"object","required":["id","team","app_id","status","metadata","createdAt"],"properties":{"id":{"type":"string","description":"Deployment ID"},"team":{"type":"string"},"app_id":{"type":"string"},"image":{"type":"string"},"release":{"type":"string"},"github_commit":{"type":"string"},"github_ref":{"type":"string"},"github_actor":{"type":"string"},"github_commit_message":{"type":"string"},"artifact_id":{"type":"string"},"artifact_ready_at":{"type":"string","format":"date-time"},"blueprint":{"type":"object","required":["slug","version","image","overlay","start_command","volumes","database"],"properties":{"slug":{"type":"string"},"version":{"type":"integer"},"image":{"type":"object","required":["kind","key"],"properties":{"kind":{"anyOf":[{"type":"string","enum":["runtime"]},{"type":"string","enum":["blueprint"]}]},"key":{"type":"string"}}},"overlay":{"type":"string","nullable":true,"default":null},"start_command":{"type":"string"},"volumes":{"type":"array","items":{"type":"object","required":["name","path","mount_path"],"properties":{"name":{"type":"string"},"path":{"type":"string"},"mount_path":{"type":"string"}}}},"database":{"type":"boolean"}},"nullable":true,"default":null},"build_started_at":{"type":"string","format":"date-time"},"build_ms":{"type":"integer"},"redeploy_of":{"type":"string"},"rebuild_of":{"type":"string"},"runtime_fallback":{"type":"object","required":["name","from","to","of"],"properties":{"name":{"type":"string"},"from":{"type":"string"},"to":{"type":"string"},"of":{"type":"string"}}},"runtime_fallback_retry":{"type":"object","required":["to","at"],"properties":{"to":{"type":"string"},"at":{"type":"string","format":"date-time"},"deployment_id":{"type":"string"}}},"runtime_upgrade":{"type":"object","required":["name","to","previous"],"properties":{"name":{"type":"string"},"to":{"type":"string"},"previous":{"anyOf":[{"type":"object","required":["name","version","pinned_at","source"],"properties":{"name":{"type":"string"},"version":{"type":"string"},"pinned_at":{"type":"string","format":"date-time"},"source":{"type":"string"}}},{"type":"null"}]}}},"quota_released_at":{"type":"string","format":"date-time"},"trigger":{"anyOf":[{"type":"string"},{"type":"null"}]},"detected":{"$ref":"#/components/schemas/DeploymentDetected"},"build_warnings":{"type":"array","items":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","maxLength":64},"message":{"type":"string","maxLength":1000},"detail":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":false},"maxItems":20},"status":{"$ref":"#/components/schemas/DeploymentStatus"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"default":{}},"createdAt":{"type":"string","description":"Deployment creation date"},"updatedAt":{"type":"string","description":"Deployment updated date"}},"description":"Deployment","additionalProperties":false}}}},"400":{"description":"`already_exists` — A resource with the same unique field already exists. `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.\n\n`team_required` — The operation needs a project: send it in the `x-faable-project` header (`project_<id>`).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["already_exists","validation_error","team_required"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`plan_required` — The build or feature needs a higher plan. `message` says which.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["plan_required","forbidden","user_suspended"]}}}]}}}},"409":{"description":"`app_disabled` — The app is disabled, so it cannot be deployed or provisioned. `message` says why when the reason is public.\n\n`remote_build_disabled` — Remote builds are off for this environment or this app. Deploy with a local build instead.\n\n`deploy_refused` — The deploy was refused by quota, plan or an in-flight deployment. `message` says which.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["app_disabled","remote_build_disabled","deploy_refused"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/deployment/{id}":{"get":{"operationId":"deployment/get","summary":"Get Deployment","tags":["Deployments"],"description":"Get Deployment","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Deployment","content":{"application/json":{"schema":{"type":"object","required":["id","team","app_id","status","metadata","createdAt"],"properties":{"id":{"type":"string","description":"Deployment ID"},"team":{"type":"string"},"app_id":{"type":"string"},"image":{"type":"string"},"release":{"type":"string"},"github_commit":{"type":"string"},"github_ref":{"type":"string"},"github_actor":{"type":"string"},"github_commit_message":{"type":"string"},"artifact_id":{"type":"string"},"artifact_ready_at":{"type":"string","format":"date-time"},"blueprint":{"type":"object","required":["slug","version","image","overlay","start_command","volumes","database"],"properties":{"slug":{"type":"string"},"version":{"type":"integer"},"image":{"type":"object","required":["kind","key"],"properties":{"kind":{"anyOf":[{"type":"string","enum":["runtime"]},{"type":"string","enum":["blueprint"]}]},"key":{"type":"string"}}},"overlay":{"type":"string","nullable":true,"default":null},"start_command":{"type":"string"},"volumes":{"type":"array","items":{"type":"object","required":["name","path","mount_path"],"properties":{"name":{"type":"string"},"path":{"type":"string"},"mount_path":{"type":"string"}}}},"database":{"type":"boolean"}},"nullable":true,"default":null},"build_started_at":{"type":"string","format":"date-time"},"build_ms":{"type":"integer"},"redeploy_of":{"type":"string"},"rebuild_of":{"type":"string"},"runtime_fallback":{"type":"object","required":["name","from","to","of"],"properties":{"name":{"type":"string"},"from":{"type":"string"},"to":{"type":"string"},"of":{"type":"string"}}},"runtime_fallback_retry":{"type":"object","required":["to","at"],"properties":{"to":{"type":"string"},"at":{"type":"string","format":"date-time"},"deployment_id":{"type":"string"}}},"runtime_upgrade":{"type":"object","required":["name","to","previous"],"properties":{"name":{"type":"string"},"to":{"type":"string"},"previous":{"anyOf":[{"type":"object","required":["name","version","pinned_at","source"],"properties":{"name":{"type":"string"},"version":{"type":"string"},"pinned_at":{"type":"string","format":"date-time"},"source":{"type":"string"}}},{"type":"null"}]}}},"quota_released_at":{"type":"string","format":"date-time"},"trigger":{"anyOf":[{"type":"string"},{"type":"null"}]},"detected":{"$ref":"#/components/schemas/DeploymentDetected"},"build_warnings":{"type":"array","items":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","maxLength":64},"message":{"type":"string","maxLength":1000},"detail":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":false},"maxItems":20},"status":{"$ref":"#/components/schemas/DeploymentStatus"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"default":{}},"createdAt":{"type":"string","description":"Deployment creation date"},"updatedAt":{"type":"string","description":"Deployment updated date"}},"description":"Deployment","additionalProperties":false}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","invalid_expand","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`access_denied` — The row exists but does not belong to the caller.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["access_denied","forbidden","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist, or not in the caller’s project.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}},"post":{"operationId":"deployment/update","summary":"Update Deployment","tags":["Deployments"],"description":"Update Deployment","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"image":{"type":"string"},"artifact":{"$ref":"#/components/schemas/DeploymentArtifact"},"type":{"type":"string"},"detected":{"$ref":"#/components/schemas/DeploymentDetected"},"build_warnings":{"type":"array","items":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","maxLength":64},"message":{"type":"string","maxLength":1000},"detail":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":false},"maxItems":20},"plan":{"type":"object","properties":{},"additionalProperties":true}}}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Deployment","content":{"application/json":{"schema":{"type":"object","required":["id","team","app_id","status","metadata","createdAt"],"properties":{"id":{"type":"string","description":"Deployment ID"},"team":{"type":"string"},"app_id":{"type":"string"},"image":{"type":"string"},"release":{"type":"string"},"github_commit":{"type":"string"},"github_ref":{"type":"string"},"github_actor":{"type":"string"},"github_commit_message":{"type":"string"},"artifact_id":{"type":"string"},"artifact_ready_at":{"type":"string","format":"date-time"},"blueprint":{"type":"object","required":["slug","version","image","overlay","start_command","volumes","database"],"properties":{"slug":{"type":"string"},"version":{"type":"integer"},"image":{"type":"object","required":["kind","key"],"properties":{"kind":{"anyOf":[{"type":"string","enum":["runtime"]},{"type":"string","enum":["blueprint"]}]},"key":{"type":"string"}}},"overlay":{"type":"string","nullable":true,"default":null},"start_command":{"type":"string"},"volumes":{"type":"array","items":{"type":"object","required":["name","path","mount_path"],"properties":{"name":{"type":"string"},"path":{"type":"string"},"mount_path":{"type":"string"}}}},"database":{"type":"boolean"}},"nullable":true,"default":null},"build_started_at":{"type":"string","format":"date-time"},"build_ms":{"type":"integer"},"redeploy_of":{"type":"string"},"rebuild_of":{"type":"string"},"runtime_fallback":{"type":"object","required":["name","from","to","of"],"properties":{"name":{"type":"string"},"from":{"type":"string"},"to":{"type":"string"},"of":{"type":"string"}}},"runtime_fallback_retry":{"type":"object","required":["to","at"],"properties":{"to":{"type":"string"},"at":{"type":"string","format":"date-time"},"deployment_id":{"type":"string"}}},"runtime_upgrade":{"type":"object","required":["name","to","previous"],"properties":{"name":{"type":"string"},"to":{"type":"string"},"previous":{"anyOf":[{"type":"object","required":["name","version","pinned_at","source"],"properties":{"name":{"type":"string"},"version":{"type":"string"},"pinned_at":{"type":"string","format":"date-time"},"source":{"type":"string"}}},{"type":"null"}]}}},"quota_released_at":{"type":"string","format":"date-time"},"trigger":{"anyOf":[{"type":"string"},{"type":"null"}]},"detected":{"$ref":"#/components/schemas/DeploymentDetected"},"build_warnings":{"type":"array","items":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","maxLength":64},"message":{"type":"string","maxLength":1000},"detail":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":false},"maxItems":20},"status":{"$ref":"#/components/schemas/DeploymentStatus"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"default":{}},"createdAt":{"type":"string","description":"Deployment creation date"},"updatedAt":{"type":"string","description":"Deployment updated date"}},"description":"Deployment","additionalProperties":false}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`already_exists` — A resource with the same unique field already exists. `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","already_exists","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`access_denied` — The row exists but does not belong to the caller.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["access_denied","forbidden","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist, or not in the caller’s project.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}},"delete":{"operationId":"deployment/delete","summary":"Delete Deployment","tags":["Deployments"],"description":"Delete Deployment","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Deployment","content":{"application/json":{"schema":{"type":"object","required":["id","team","app_id","status","metadata","createdAt"],"properties":{"id":{"type":"string","description":"Deployment ID"},"team":{"type":"string"},"app_id":{"type":"string"},"image":{"type":"string"},"release":{"type":"string"},"github_commit":{"type":"string"},"github_ref":{"type":"string"},"github_actor":{"type":"string"},"github_commit_message":{"type":"string"},"artifact_id":{"type":"string"},"artifact_ready_at":{"type":"string","format":"date-time"},"blueprint":{"type":"object","required":["slug","version","image","overlay","start_command","volumes","database"],"properties":{"slug":{"type":"string"},"version":{"type":"integer"},"image":{"type":"object","required":["kind","key"],"properties":{"kind":{"anyOf":[{"type":"string","enum":["runtime"]},{"type":"string","enum":["blueprint"]}]},"key":{"type":"string"}}},"overlay":{"type":"string","nullable":true,"default":null},"start_command":{"type":"string"},"volumes":{"type":"array","items":{"type":"object","required":["name","path","mount_path"],"properties":{"name":{"type":"string"},"path":{"type":"string"},"mount_path":{"type":"string"}}}},"database":{"type":"boolean"}},"nullable":true,"default":null},"build_started_at":{"type":"string","format":"date-time"},"build_ms":{"type":"integer"},"redeploy_of":{"type":"string"},"rebuild_of":{"type":"string"},"runtime_fallback":{"type":"object","required":["name","from","to","of"],"properties":{"name":{"type":"string"},"from":{"type":"string"},"to":{"type":"string"},"of":{"type":"string"}}},"runtime_fallback_retry":{"type":"object","required":["to","at"],"properties":{"to":{"type":"string"},"at":{"type":"string","format":"date-time"},"deployment_id":{"type":"string"}}},"runtime_upgrade":{"type":"object","required":["name","to","previous"],"properties":{"name":{"type":"string"},"to":{"type":"string"},"previous":{"anyOf":[{"type":"object","required":["name","version","pinned_at","source"],"properties":{"name":{"type":"string"},"version":{"type":"string"},"pinned_at":{"type":"string","format":"date-time"},"source":{"type":"string"}}},{"type":"null"}]}}},"quota_released_at":{"type":"string","format":"date-time"},"trigger":{"anyOf":[{"type":"string"},{"type":"null"}]},"detected":{"$ref":"#/components/schemas/DeploymentDetected"},"build_warnings":{"type":"array","items":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","maxLength":64},"message":{"type":"string","maxLength":1000},"detail":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":false},"maxItems":20},"status":{"$ref":"#/components/schemas/DeploymentStatus"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"default":{}},"createdAt":{"type":"string","description":"Deployment creation date"},"updatedAt":{"type":"string","description":"Deployment updated date"}},"description":"Deployment","additionalProperties":false}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`access_denied` — The row exists but does not belong to the caller.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["access_denied","forbidden","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist, or not in the caller’s project.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/deployment/{deployment_id}/logs":{"get":{"operationId":"deployment/get_logs","summary":"Get deployment build logs","tags":["Deployments"],"description":"Get the CLI build/deploy output attached to a deployment","parameters":[{"schema":{"type":"string"},"in":"path","name":"deployment_id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["id","deployment_id","app_id","team"],"properties":{"id":{"type":"string"},"deployment_id":{"type":"string"},"app_id":{"type":"string"},"team":{"type":"string"},"content":{"type":"string"},"truncated":{"type":"boolean"},"size":{"type":"number"},"runtime_content":{"type":"string"},"runtime_truncated":{"type":"boolean"},"runtime_size":{"type":"number"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist, or not in the caller’s project.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/deployment/{deployment_id}/cancel":{"post":{"operationId":"deployment/cancel","summary":"Cancel a queued or building deployment","tags":["Deployments"],"description":"Cancel a deployment that has not been handed over to the controller yet (QUEUED or BUILDING)","parameters":[{"schema":{"type":"string"},"in":"path","name":"deployment_id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Deployment","content":{"application/json":{"schema":{"type":"object","required":["id","team","app_id","status","metadata","createdAt"],"properties":{"id":{"type":"string","description":"Deployment ID"},"team":{"type":"string"},"app_id":{"type":"string"},"image":{"type":"string"},"release":{"type":"string"},"github_commit":{"type":"string"},"github_ref":{"type":"string"},"github_actor":{"type":"string"},"github_commit_message":{"type":"string"},"artifact_id":{"type":"string"},"artifact_ready_at":{"type":"string","format":"date-time"},"blueprint":{"type":"object","required":["slug","version","image","overlay","start_command","volumes","database"],"properties":{"slug":{"type":"string"},"version":{"type":"integer"},"image":{"type":"object","required":["kind","key"],"properties":{"kind":{"anyOf":[{"type":"string","enum":["runtime"]},{"type":"string","enum":["blueprint"]}]},"key":{"type":"string"}}},"overlay":{"type":"string","nullable":true,"default":null},"start_command":{"type":"string"},"volumes":{"type":"array","items":{"type":"object","required":["name","path","mount_path"],"properties":{"name":{"type":"string"},"path":{"type":"string"},"mount_path":{"type":"string"}}}},"database":{"type":"boolean"}},"nullable":true,"default":null},"build_started_at":{"type":"string","format":"date-time"},"build_ms":{"type":"integer"},"redeploy_of":{"type":"string"},"rebuild_of":{"type":"string"},"runtime_fallback":{"type":"object","required":["name","from","to","of"],"properties":{"name":{"type":"string"},"from":{"type":"string"},"to":{"type":"string"},"of":{"type":"string"}}},"runtime_fallback_retry":{"type":"object","required":["to","at"],"properties":{"to":{"type":"string"},"at":{"type":"string","format":"date-time"},"deployment_id":{"type":"string"}}},"runtime_upgrade":{"type":"object","required":["name","to","previous"],"properties":{"name":{"type":"string"},"to":{"type":"string"},"previous":{"anyOf":[{"type":"object","required":["name","version","pinned_at","source"],"properties":{"name":{"type":"string"},"version":{"type":"string"},"pinned_at":{"type":"string","format":"date-time"},"source":{"type":"string"}}},{"type":"null"}]}}},"quota_released_at":{"type":"string","format":"date-time"},"trigger":{"anyOf":[{"type":"string"},{"type":"null"}]},"detected":{"$ref":"#/components/schemas/DeploymentDetected"},"build_warnings":{"type":"array","items":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","maxLength":64},"message":{"type":"string","maxLength":1000},"detail":{"type":"object","properties":{},"additionalProperties":true}},"additionalProperties":false},"maxItems":20},"status":{"$ref":"#/components/schemas/DeploymentStatus"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"default":{}},"createdAt":{"type":"string","description":"Deployment creation date"},"updatedAt":{"type":"string","description":"Deployment updated date"}},"description":"Deployment","additionalProperties":false}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/deployment/{deployment_id}/timeline":{"get":{"operationId":"deployment/get_timeline","summary":"Get a deployment timeline","tags":["Deployments"],"description":"Where the deployment spent its time, phase by phase: queue, detection, each build step, hand-off and start-up.","parameters":[{"schema":{"type":"string"},"in":"path","name":"deployment_id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["deployment_id","outcome","started_at","total_ms","segments"],"properties":{"deployment_id":{"type":"string"},"outcome":{"type":"string"},"started_at":{"type":"string","format":"date-time"},"total_ms":{"type":"integer"},"segments":{"type":"array","items":{"type":"object","required":["key","label","started_at","ms","measured"],"properties":{"key":{"type":"string"},"label":{"type":"string"},"started_at":{"type":"string","format":"date-time"},"ms":{"type":"integer"},"measured":{"type":"boolean"}}}},"superseded_at":{"type":"string","format":"date-time"},"superseded_by":{"type":"object","required":["deployment_id","github_commit"],"properties":{"deployment_id":{"type":"string"},"github_commit":{"anyOf":[{"type":"string"},{"type":"null"}]}}}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist, or not in the caller’s project.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/deployment/{deployment_id}/fault":{"get":{"operationId":"deployment/get_fault","summary":"Who a deployment failure belongs to","tags":["Deployments"],"description":"Whether a failed deployment is the user's to fix or Faable's, with a stable error code.","parameters":[{"schema":{"type":"string"},"in":"path","name":"deployment_id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["deployment_id","phase","failed","fault_owner","error_code"],"properties":{"deployment_id":{"type":"string"},"phase":{"anyOf":[{"type":"string"},{"type":"null"}]},"failed":{"type":"boolean","description":"True when the deployment ended in BUILD_ERROR or ERROR."},"fault_owner":{"anyOf":[{"type":"string","enum":["user"]},{"type":"string","enum":["platform"]},{"type":"string","enum":["unknown"]},{"type":"null"}],"description":"Who has to act: `user` (the code, the config or the repository), `platform` (Faable — retrying or contacting support is the way out), `unknown` (not classified). Null when the deployment did not fail."},"error_code":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Stable code of the failure (e.g. `user_build_command_failed`, `buildpack_not_detected`). Null when unclassified or not failed."}},"additionalProperties":false}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/domain":{"get":{"operationId":"domain/list","summary":"List Domains","tags":["Domains"],"description":"List Domains","parameters":[{"schema":{"type":"number","minimum":1,"maximum":200},"in":"query","name":"pageSize","required":false,"description":"Number of items per page (max 200)"},{"schema":{"type":"string"},"in":"query","name":"cursor","required":false,"description":"Cursor for next page"},{"schema":{"type":"string"},"in":"query","name":"next","required":false,"description":"Cursor returned by the previous page"},{"schema":{"type":"boolean"},"in":"query","name":"count","required":false,"description":"Also return `total`: the rows matching the whole filter, across every page."},{"schema":{"type":"string"},"in":"query","name":"query","required":false,"description":"Filter using a FaableQL query"}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["next","results"],"properties":{"next":{"anyOf":[{"type":"string"},{"type":"null"}]},"results":{"type":"array","items":{"$ref":"#/components/schemas/Domain"}},"total":{"type":"integer","description":"Rows matching the whole filter, across every page. Only with `?count=true`."}},"additionalProperties":false}}}},"400":{"description":"`invalid_query` — The `?query=` FaableQL expression could not be parsed.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`search_not_supported` — This resource does not support `?q=`.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_query","invalid_expand","search_not_supported","validation_error"]}}}]}}}},"401":{"description":"`team_or_scope_required` — Listing needs either a project in the `x-faable-project` header or a global scope on the token.\n\n`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["team_or_scope_required","unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}},"post":{"operationId":"domain/create","summary":"Create Domain","tags":["Domains"],"description":"Create Domain","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["fqdn"],"properties":{"fqdn":{"type":"string"},"app_id":{"type":"string","description":"Linked App"},"tls":{"type":"boolean","default":true}}}}}},"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"201":{"description":"Domain","content":{"application/json":{"schema":{"type":"object","required":["id","team","fqdn","tls","verified","active","status","metadata","createdAt"],"properties":{"id":{"type":"string","description":"Domain ID"},"team":{"type":"string"},"fqdn":{"type":"string","description":"Unique identifier of the domain"},"tls":{"type":"boolean","description":"TLS is enabled for this domain"},"app_id":{"type":"string","description":"Linked App","nullable":true,"default":null},"verified":{"type":"boolean","description":"True while the domain points at Faable: derived from `status.dns_state` (`ok`), the latest DNS check"},"active":{"type":"boolean"},"dns_alias":{"type":"string","description":"Internal DNS alias override host","nullable":true,"default":null},"dormant_at":{"type":"string","format":"date-time","description":"Set when the domain went dormant: it served no active app and had not pointed at Faable for two days. It is not checked nor served until `POST /domain/:id/recheck`.","nullable":true,"default":null},"recheck_at":{"type":"string","format":"date-time","description":"Last manual re-check (Retry) of this domain","nullable":true,"default":null},"status":{"$ref":"#/components/schemas/DomainStatus"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"default":{}},"createdAt":{"type":"string","description":"Domain creation date"},"updatedAt":{"type":"string","description":"Domain updated date"}},"description":"Domain","additionalProperties":false}}}},"400":{"description":"`already_exists` — A resource with the same unique field already exists. `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.\n\n`team_required` — The operation needs a project: send it in the `x-faable-project` header (`project_<id>`).\n\n`domain_invalid` — Not a valid hostname. No protocol, port or path.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["already_exists","validation_error","team_required","domain_invalid"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`domain_reserved` — Hostnames under that domain are assigned by Faable.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["domain_reserved","forbidden","user_suspended"]}}}]}}}},"409":{"description":"`domain_taken` — That hostname is already registered on Faable.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["domain_taken"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/domain/{id}":{"get":{"operationId":"domain/get","summary":"Get Domain","tags":["Domains"],"description":"Get Domain","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Domain","content":{"application/json":{"schema":{"type":"object","required":["id","team","fqdn","tls","verified","active","status","metadata","createdAt"],"properties":{"id":{"type":"string","description":"Domain ID"},"team":{"type":"string"},"fqdn":{"type":"string","description":"Unique identifier of the domain"},"tls":{"type":"boolean","description":"TLS is enabled for this domain"},"app_id":{"type":"string","description":"Linked App","nullable":true,"default":null},"verified":{"type":"boolean","description":"True while the domain points at Faable: derived from `status.dns_state` (`ok`), the latest DNS check"},"active":{"type":"boolean"},"dns_alias":{"type":"string","description":"Internal DNS alias override host","nullable":true,"default":null},"dormant_at":{"type":"string","format":"date-time","description":"Set when the domain went dormant: it served no active app and had not pointed at Faable for two days. It is not checked nor served until `POST /domain/:id/recheck`.","nullable":true,"default":null},"recheck_at":{"type":"string","format":"date-time","description":"Last manual re-check (Retry) of this domain","nullable":true,"default":null},"status":{"$ref":"#/components/schemas/DomainStatus"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"default":{}},"createdAt":{"type":"string","description":"Domain creation date"},"updatedAt":{"type":"string","description":"Domain updated date"}},"description":"Domain","additionalProperties":false}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","invalid_expand","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`access_denied` — The row exists but does not belong to the caller.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["access_denied","forbidden","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist, or not in the caller’s project.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}},"post":{"operationId":"domain/update","summary":"Update Domain","tags":["Domains"],"description":"Update Domain","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"app_id":{"type":"string","description":"Link domain to this App","nullable":true},"active":{"type":"boolean"}}}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Domain","content":{"application/json":{"schema":{"type":"object","required":["id","team","fqdn","tls","verified","active","status","metadata","createdAt"],"properties":{"id":{"type":"string","description":"Domain ID"},"team":{"type":"string"},"fqdn":{"type":"string","description":"Unique identifier of the domain"},"tls":{"type":"boolean","description":"TLS is enabled for this domain"},"app_id":{"type":"string","description":"Linked App","nullable":true,"default":null},"verified":{"type":"boolean","description":"True while the domain points at Faable: derived from `status.dns_state` (`ok`), the latest DNS check"},"active":{"type":"boolean"},"dns_alias":{"type":"string","description":"Internal DNS alias override host","nullable":true,"default":null},"dormant_at":{"type":"string","format":"date-time","description":"Set when the domain went dormant: it served no active app and had not pointed at Faable for two days. It is not checked nor served until `POST /domain/:id/recheck`.","nullable":true,"default":null},"recheck_at":{"type":"string","format":"date-time","description":"Last manual re-check (Retry) of this domain","nullable":true,"default":null},"status":{"$ref":"#/components/schemas/DomainStatus"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"default":{}},"createdAt":{"type":"string","description":"Domain creation date"},"updatedAt":{"type":"string","description":"Domain updated date"}},"description":"Domain","additionalProperties":false}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`already_exists` — A resource with the same unique field already exists. `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","already_exists","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`access_denied` — The row exists but does not belong to the caller.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["access_denied","forbidden","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist, or not in the caller’s project.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}},"delete":{"operationId":"domain/delete","summary":"Delete Domain","tags":["Domains"],"description":"Delete Domain","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Domain","content":{"application/json":{"schema":{"type":"object","required":["id","team","fqdn","tls","verified","active","status","metadata","createdAt"],"properties":{"id":{"type":"string","description":"Domain ID"},"team":{"type":"string"},"fqdn":{"type":"string","description":"Unique identifier of the domain"},"tls":{"type":"boolean","description":"TLS is enabled for this domain"},"app_id":{"type":"string","description":"Linked App","nullable":true,"default":null},"verified":{"type":"boolean","description":"True while the domain points at Faable: derived from `status.dns_state` (`ok`), the latest DNS check"},"active":{"type":"boolean"},"dns_alias":{"type":"string","description":"Internal DNS alias override host","nullable":true,"default":null},"dormant_at":{"type":"string","format":"date-time","description":"Set when the domain went dormant: it served no active app and had not pointed at Faable for two days. It is not checked nor served until `POST /domain/:id/recheck`.","nullable":true,"default":null},"recheck_at":{"type":"string","format":"date-time","description":"Last manual re-check (Retry) of this domain","nullable":true,"default":null},"status":{"$ref":"#/components/schemas/DomainStatus"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"default":{}},"createdAt":{"type":"string","description":"Domain creation date"},"updatedAt":{"type":"string","description":"Domain updated date"}},"description":"Domain","additionalProperties":false}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`access_denied` — The row exists but does not belong to the caller.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["access_denied","forbidden","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist, or not in the caller’s project.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/domain/fqdn/{fqdn}":{"get":{"operationId":"domain/fqdn","summary":"Get domain by FQDN","tags":["Domains"],"description":"Get domain by FQDN","parameters":[{"schema":{"type":"string"},"in":"path","name":"fqdn","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Domain","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Domain"}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/domain/{domain_id}/recheck":{"post":{"operationId":"domain/recheck","summary":"Re-check a domain","tags":["Domains"],"description":"Check this domain’s DNS now. A dormant domain wakes up: it is checked and served again, and gets two more days to point at Faable.","parameters":[{"schema":{"type":"string"},"in":"path","name":"domain_id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Domain","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Domain"}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist, or not in the caller’s project.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/secret":{"post":{"operationId":"secrets/upsert","summary":"Upsert a secret","tags":["Secrets & environment"],"description":"Upsert a secret","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretUpsert"}}}},"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Secret","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Secret"}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/secret/{context_id}":{"get":{"operationId":"secrets/list_app","summary":"List all secrets assigned to an App or Profile contexts","tags":["Secrets & environment"],"description":"List all app secrets assigned to an App or Profile contexts","parameters":[{"schema":{"type":"string"},"in":"query","name":"cursor","required":false,"description":"Start from this cursor"},{"schema":{"type":"string"},"in":"query","name":"next","required":false,"description":"Start from this cursor"},{"schema":{"type":"number"},"in":"query","name":"pageSize","required":false,"description":"Size of the results array"},{"schema":{"type":"string"},"in":"path","name":"context_id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"PaginatedResponse","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretPage"}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/secret/{secret_id}":{"delete":{"operationId":"secrets/delete","summary":"Delete a secret","tags":["Secrets & environment"],"description":"Delete a secret","parameters":[{"schema":{"type":"string"},"in":"path","name":"secret_id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`access_denied` — The row exists but does not belong to the caller.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["access_denied","forbidden","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist, or not in the caller’s project.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/secret/createbatch":{"post":{"operationId":"secrets/create_batch","summary":"Create secrets in batch","tags":["Secrets & environment"],"description":"Replace the secrets of an app or profile. When the app has nothing running and its latest deployment failed at runtime, the save also redeploys it with the new variables.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretCreateBatch"}}}},"parameters":[{"schema":{"type":"boolean"},"in":"query","name":"with_redeploy","required":false,"description":"Answer `{ secrets, redeploy }` instead of the bare array, saying whether the save relaunched a failed deployment"}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Answer of a batch save with `?with_redeploy=true` (status 200)","content":{"application/json":{"schema":{"type":"object","required":["secrets","redeploy"],"properties":{"secrets":{"type":"array","items":{"$ref":"#/components/schemas/Secret"}},"redeploy":{"type":"object","required":["status","failed_deployment_id","deployment_id"],"properties":{"status":{"anyOf":[{"type":"string","description":"A redeploy of the failed deployment was launched with the new variables","enum":["started"]},{"type":"string","description":"The last deployment failed to BUILD; nothing was launched — retry the deployment to rebuild it","enum":["needs_rebuild"]},{"type":"string","description":"A redeploy was due but the platform refused it (quota, disabled app, no sources); nothing was launched","enum":["not_started"]}],"description":"What happened to the app after the variables changed"},"failed_deployment_id":{"type":"string","description":"The latest deployment, the failed one this is about"},"deployment_id":{"type":"string","description":"The deployment this save launched (status `started`)","nullable":true,"default":null}},"description":"Present when the app had nothing running and its latest deployment had failed. Null when the save changed nothing or the app is running (its pods restart with the new variables as usual).","nullable":true,"default":null}},"description":"Answer of a batch save with `?with_redeploy=true` (status 200)"}}}},"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Secret"}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/auth/github-oidc":{"post":{"operationId":"auth/github_oidc","summary":"Exchange github oidc token","tags":["CI & Git integrations"],"description":"Exchange github oidc token","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["token"],"properties":{"token":{"type":"string"},"app_id":{"type":"string","description":"Explicitly select wich app to deploy when multiple apps are linked to the same github repository"}}}}}},"responses":{"200":{"description":"Default Response"},"400":{"description":"`ambiguous_app_for_repository` — Several apps are linked to that repository; pass `--app-slug`.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["ambiguous_app_for_repository","validation_error"]}}}]}}}},"401":{"description":"`oidc_untrusted` — The CI OIDC token (GitHub Actions or GitLab CI) was rejected. `message` says why.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["oidc_untrusted"]}}}]}}}},"403":{"description":"`app_repository_mismatch` — The app does not exist or is not linked to the repository the token comes from.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["app_repository_mismatch"]}}}]}}}},"404":{"description":"`no_app_for_repository` — No app is linked to that repository.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["no_app_for_repository"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/auth/gitlab-oidc":{"post":{"operationId":"auth/gitlab_oidc","summary":"Exchange GitLab CI oidc token","tags":["CI & Git integrations"],"description":"Exchange a GitLab CI id_token for a Faable token scoped to one app","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["token","app_id"],"properties":{"token":{"type":"string"},"app_id":{"type":"string","description":"The app to deploy (`faable deploy --app`). Required: GitLab has no linked repository to infer it from."}}}}}},"responses":{"200":{"description":"Default Response"},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`oidc_untrusted` — The CI OIDC token (GitHub Actions or GitLab CI) was rejected. `message` says why.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["oidc_untrusted"]}}}]}}}},"403":{"description":"`app_ci_untrusted` — The app does not exist or does not trust the GitLab project the token comes from (`faable deploy apps set --gitlab-project <id>`).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["app_ci_untrusted"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/gitlab/projects":{"get":{"operationId":"gitlab/projects","summary":"List the GitLab projects the user can link","tags":["CI & Git integrations"],"description":"gitlab.com projects where the user is at least Maintainer (needed to create the deploy webhook).","parameters":[{"schema":{"type":"string","maxLength":100},"in":"query","name":"q","required":false}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["results"],"properties":{"results":{"type":"array","items":{"type":"object","required":["id","path_with_namespace","default_branch","visibility","web_url"],"properties":{"id":{"type":"string"},"path_with_namespace":{"type":"string"},"default_branch":{"anyOf":[{"type":"string"},{"type":"null"}]},"visibility":{"type":"string"},"web_url":{"type":"string"}}}}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"412":{"description":"`gitlab_identity_missing` — The user has not connected GitLab. Send them through `connect_gitlab`.\n\n`gitlab_token_invalid` — The GitLab connection has no usable token (revoked or expired). The user must connect GitLab again.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["gitlab_identity_missing","gitlab_token_invalid"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`gitlab_not_configured` — GitLab linking is not enabled on this environment.\n\n`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["gitlab_not_configured","database_unavailable"]}}}]}}}}}}},"/app/{id}/link-gitlab":{"post":{"operationId":"app/link_gitlab","summary":"Link a GitLab project (push to deploy)","tags":["Source & build settings"],"description":"Links a gitlab.com project to the app and creates a project webhook so every push to the branch deploys. Runs with the calling user's GitLab token; they must be Maintainer of the project.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["project_id"],"properties":{"project_id":{"type":"string","pattern":"^[0-9]{1,20}$"},"branch":{"type":"string","minLength":1,"maxLength":255,"nullable":true}}}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"App","content":{"application/json":{"schema":{"$ref":"#/components/schemas/App"}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`gitlab_project_not_accessible` — The GitLab project does not exist or the user is not a Maintainer of it (creating the webhook needs Maintainer).\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["gitlab_project_not_accessible","forbidden","user_suspended"]}}}]}}}},"409":{"description":"`repository_already_linked` — The repository is already linked to another app. `details.app_id` / `details.app_name` name it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["repository_already_linked"]}}}]}}}},"412":{"description":"`gitlab_identity_missing` — The user has not connected GitLab. Send them through `connect_gitlab`.\n\n`gitlab_token_invalid` — The GitLab connection has no usable token (revoked or expired). The user must connect GitLab again.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["gitlab_identity_missing","gitlab_token_invalid"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`gitlab_not_configured` — GitLab linking is not enabled on this environment.\n\n`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["gitlab_not_configured","database_unavailable"]}}}]}}}}}}},"/app/{id}/unlink-gitlab":{"post":{"operationId":"app/unlink_gitlab","summary":"Unlink the GitLab project","tags":["Source & build settings"],"description":"Stops push-to-deploy from GitLab and removes the webhook (best effort: a revoked token still unlinks).","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"App","content":{"application/json":{"schema":{"$ref":"#/components/schemas/App"}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/gitlab/webhook":{"post":{"operationId":"gitlab/webhook","summary":"GitLab push webhook","tags":["CI & Git integrations"],"description":"Receives push events from a linked gitlab.com project (X-Gitlab-Token = the per-app secret) and deploys pushes to the linked branch.","parameters":[{"schema":{"type":"string"},"in":"query","name":"app_id","required":true}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/usage/summary":{"get":{"operationId":"usage/summary","summary":"Get team usage summary for the current billing period","tags":["Quotas & usage"],"parameters":[{"schema":{"type":"boolean","default":true},"in":"query","name":"traffic","required":false}],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["resources","traffic","teams","billing_available"],"properties":{"resources":{"type":"object","required":["apps","domains","deployments"],"properties":{"apps":{"type":"integer"},"domains":{"type":"integer"},"deployments":{"type":"integer"}}},"traffic":{"anyOf":[{"type":"object","required":["used_gb","cost_estimate_cents"],"properties":{"used_gb":{"type":"number"},"cost_estimate_cents":{"type":"integer"}}},{"type":"null"}]},"teams":{"type":"array","items":{"type":"object","required":["id","name","slug","period","subscription","resources","traffic"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"period":{"type":"object","required":["start","end"],"properties":{"start":{"type":"string","format":"date-time"},"end":{"type":"string","format":"date-time"}}},"subscription":{"anyOf":[{"type":"object","required":["plan","status"],"properties":{"plan":{"anyOf":[{"type":"string"},{"type":"null"}]},"status":{"type":"string"}}},{"type":"null"}]},"resources":{"type":"object","required":["apps","domains","deployments"],"properties":{"apps":{"type":"integer"},"domains":{"type":"integer"},"deployments":{"type":"integer"}}},"traffic":{"anyOf":[{"type":"object","required":["used_gb","cost_estimate_cents"],"properties":{"used_gb":{"type":"number"},"cost_estimate_cents":{"type":"integer"}}},{"type":"null"}]}}}},"billing_available":{"type":"boolean"}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/usage/traffic":{"get":{"operationId":"usage/traffic","summary":"Get metered traffic usage per team for the current period","tags":["Quotas & usage"],"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["traffic","teams","billing_available"],"properties":{"traffic":{"anyOf":[{"type":"object","required":["used_gb","cost_estimate_cents"],"properties":{"used_gb":{"type":"number"},"cost_estimate_cents":{"type":"integer"}}},{"type":"null"}]},"teams":{"type":"array","items":{"type":"object","required":["id","traffic"],"properties":{"id":{"type":"string"},"traffic":{"anyOf":[{"type":"object","required":["used_gb","cost_estimate_cents"],"properties":{"used_gb":{"type":"number"},"cost_estimate_cents":{"type":"integer"}}},{"type":"null"}]}}}},"billing_available":{"type":"boolean"}}}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/deploy-quota":{"get":{"operationId":"deploy_quota/get","summary":"Get the team's daily deploy quota status","tags":["Quotas & usage"],"description":"Daily deploy allowance of the current team: how much was used today, when it resets, and how many builds are held waiting for it.","security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["limited","over","used","limit","resets_at","held"],"properties":{"limited":{"type":"boolean"},"over":{"type":"boolean"},"used":{"type":"integer"},"limit":{"anyOf":[{"type":"integer"},{"type":"null"}]},"resets_at":{"type":"string","format":"date-time"},"held":{"type":"integer"}}}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","invalid_expand"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`access_denied` — The row exists but does not belong to the caller.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["access_denied","forbidden","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist, or not in the caller’s project.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/deploy-quota/release":{"post":{"operationId":"deploy_quota/release","summary":"Roll out the builds held by the daily deploy quota","tags":["Quotas & usage"],"description":"Releases the team's held deployments if its current plan allows it (per app, only the newest held build rolls out; older ones are canceled). Safe to call at any time — it does nothing while the quota is still spent.","security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["released","canceled"],"properties":{"released":{"type":"array","items":{"type":"string"}},"canceled":{"type":"array","items":{"type":"string"}}}}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}},"/egress-allowance":{"get":{"operationId":"egress_allowance/get","summary":"Get the project's egress allowance status","tags":["Quotas & usage"],"description":"Egress used by the current project today and this month (UTC), against the allowance of its plan, and the highest warning threshold reached. Only the Free plan has a limited allowance.","security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["project_id","limited","day","month","apps"],"properties":{"project_id":{"type":"string"},"limited":{"type":"boolean"},"day":{"type":"object","required":["used_bytes","limit_bytes","threshold","resets_at"],"properties":{"used_bytes":{"type":"integer"},"limit_bytes":{"type":"integer","nullable":true,"default":null},"threshold":{"type":"integer","enum":[80,90,100,null],"nullable":true,"default":null},"resets_at":{"type":"string","format":"date-time"}}},"month":{"type":"object","required":["used_bytes","limit_bytes","threshold","resets_at"],"properties":{"used_bytes":{"type":"integer"},"limit_bytes":{"type":"integer","nullable":true,"default":null},"threshold":{"type":"integer","enum":[80,90,100,null],"nullable":true,"default":null},"resets_at":{"type":"string","format":"date-time"}}},"apps":{"type":"array","items":{"type":"object","required":["app_id","day_bytes","month_bytes"],"properties":{"app_id":{"type":"string"},"day_bytes":{"type":"integer"},"month_bytes":{"type":"integer"}}}}}}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","invalid_expand"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.\n\n`invalid_token` — The bearer token is missing, malformed or expired.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`access_denied` — The row exists but does not belong to the caller.\n\n`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The account is suspended. Contact support@faable.com.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["access_denied","forbidden","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist, or not in the caller’s project.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}},"503":{"description":"`database_unavailable` — Faable cannot reach its database right now. Transient: retry after the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["database_unavailable"]}}}]}}}}}}}},"servers":[{"url":"https://api.faable.com","description":"Faable API"}],"tags":[{"name":"Projects","description":"A project is the unit of ownership and billing: apps, domains, members and the plan live in it.","externalDocs":{"description":"Faable Deploy: how it works","url":"https://faable.com/docs/deploy"}},{"name":"Members & invitations","description":"Who is in a project and with which role: members, collaborators, invitations and the membership activity log."},{"name":"Organizations","description":"Several projects under one company: admins, verified email domains, auto-join and SSO (Pro)."},{"name":"Apps","description":"The app itself: create, read, update, delete, and check a name or slug.","externalDocs":{"description":"How a deploy works","url":"https://faable.com/docs/deploy/how-it-works"}},{"name":"Source & build settings","description":"Where the code comes from and how it is built: GitHub/GitLab linking, branch, root directory, deploy mode, keyless CI trust and the runtime version.","externalDocs":{"description":"Build requirements","url":"https://faable.com/docs/deploy/build-requirements"}},{"name":"Deployments","description":"Every build and rollout of an app: trigger, list, inspect logs, timeline and fault, cancel.","externalDocs":{"description":"How a deploy works","url":"https://faable.com/docs/deploy/how-it-works"}},{"name":"Domains","description":"Custom domains and their certificates: add, verify, re-check, remove.","externalDocs":{"description":"Custom domains","url":"https://faable.com/docs/deploy/domains/custom-domain"}},{"name":"Secrets & environment","description":"Environment variables and secrets of an app, set one by one or in batch.","externalDocs":{"description":"Environment variables","url":"https://faable.com/docs/deploy/environment"}},{"name":"Security (WAF)","description":"The per-app web application firewall: profile, rules and the activity it blocked."},{"name":"Logs & traffic","description":"What an app is doing: runtime logs, traffic and the container registry view."},{"name":"Quotas & usage","description":"Plan limits and metering for the current billing period: deploys per day, egress allowance, usage summary.","externalDocs":{"description":"Plans and pricing","url":"https://faable.com/docs/platform/pricing"}},{"name":"Faable Auth accounts","description":"The Faable Auth tenants owned by a project and the management tokens to drive them.","externalDocs":{"description":"Faable Auth","url":"https://faable.com/docs/auth"}},{"name":"CI & Git integrations","description":"Deploy from CI without stored secrets (GitHub Actions and GitLab CI OIDC token exchange), GitLab project listing and webhooks.","externalDocs":{"description":"Deploy from agents and CI","url":"https://faable.com/docs/deploy/ai-agents"}},{"name":"API keys","description":"Long-lived keys for scripts and integrations."},{"name":"Contacts","description":"Notification contacts of a project."},{"name":"Status","description":"Resource status and health."}],"externalDocs":{"description":"Faable documentation","url":"https://faable.com/docs"}}