{"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}},"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"]}]},"user_id":{"type":"string"}}}}},"additionalProperties":false},"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"}},"product_intent":{"anyOf":[{"type":"string","enum":["deploy"]},{"type":"string","enum":["auth"]},{"type":"string","enum":["undecided"]}]},"owner_suspended":{"type":"boolean"},"owner_email":{"type":"string"},"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"]}]}},"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"}},"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":"null"}],"description":"Scale-to-zero sub-state of the promoted deployment (admin-only)"},"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":{"type":"string","description":"The runtime exceeded its memory limit (OOMKilled)","enum":["memory_exceeded"]},"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"}},"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"},"restarts":{"type":"integer","description":"Restart count when observed"},"exit_code":{"type":"integer","description":"Last exit code, when reported"},"reason":{"type":"string","description":"K8s waiting/terminated reason"}},"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"}}}}},{"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"}]},"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"}]},"instance_type":{"type":"string","description":"Faable instance types comprise varying combinations of CPU, memory, storage, and networking capacity."},"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."},"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."},"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":{"type":"string"},"notify_deploy_success":{"anyOf":[{"type":"boolean"},{"type":"null"}]}},"additionalProperties":false},"AppRepoLinkable":{"type":"object","required":["linkable"],"properties":{"linkable":{"type":"boolean"},"code":{"type":"string","enum":["repository_already_linked"]},"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"}}},"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"},"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"},"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"}}}}},"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"}}}}},"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"},"build_started_at":{"type":"string","format":"date-time"},"build_ms":{"type":"integer"},"redeploy_of":{"type":"string"},"quota_released_at":{"type":"string","format":"date-time"},"trigger":{"anyOf":[{"type":"string"},{"type":"null"}]},"detected":{"$ref":"#/components/schemas/DeploymentDetected"},"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"}}},"DeploymentUpdate":{"type":"object","properties":{"image":{"type":"string"},"artifact":{"$ref":"#/components/schemas/DeploymentArtifact"},"type":{"type":"string"},"detected":{"$ref":"#/components/schemas/DeploymentDetected"}}},"DomainStatus":{"type":"object","required":["dns_state","dns_checked_at","dns_expected","dns_observed","dns_message","dns_warning"],"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 check","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_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}},"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":"If the domain has the ownership verified"},"active":{"type":"boolean"},"dns_alias":{"type":"string","description":"Internal DNS alias override host","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,"default":null},"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"]}]},"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"]}]},"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"]}]},"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,"default":null},"updated_at":{"type":"string","nullable":true,"default":null}}},"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"},"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"}}}},"PlatformImage":{"type":"object","required":["id","team","kind","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"]}]},"role":{"anyOf":[{"type":"string","enum":["workload"]},{"type":"string","enum":["cluster"]}]},"key":{"type":"string","description":"Canonical key: node:22, python:3.11, static, 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"]}]}}}},"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"]}]},"key":{"type":"string"},"repository":{"type":"string"},"current":{"type":"string"},"min_version":{"type":"string"},"role":{"anyOf":[{"type":"string","enum":["workload"]},{"type":"string","enum":["cluster"]}]},"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"]}]}}}}}},"PlatformImageUpdate":{"type":"object","properties":{"auto_promote":{"type":"boolean"},"repository":{"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- `service_unavailable` (503): The feature is not configured on this environment, or is temporarily out of service.\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` (401): The row exists but does not belong to the caller.\n- `invalid_token` (401): The bearer token is missing, malformed or expired.\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-team` header.\n- `team_or_scope_required` (401): Listing needs either a project in `x-faable-team` or a global scope on the token.\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- `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- `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` (401): 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- `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_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- `invalid_installation_id` (400): The `installation_id` is not a number.\n- `oidc_untrusted` (401): The GitHub Actions OIDC token was rejected. `message` says why.\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- `app_disabled` (409): The app is disabled, so it cannot be deployed or provisioned. `message` says why when the reason is public.\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- `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","service_unavailable","already_exists","invalid_id","invalid_query","invalid_expand","search_not_supported","access_denied","invalid_token","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","user_suspended","superadmin_only","project_not_found","owner_only","owner_not_removable","collaborator_not_found","invalid_project_name","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","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_error","repository_already_linked","invalid_installation_id","oidc_untrusted","app_repository_mismatch","no_app_for_repository","ambiguous_app_for_repository","app_disabled","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","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"]}}}]}}}}}}},"/project/{id}/invite":{"post":{"operationId":"project/invite","summary":"Invite collaborators to project","tags":["projects"],"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"]}}}]}}}}}}},"/project/{id}/collaborator/{user_id}":{"delete":{"operationId":"project/removeCollaborator","summary":"Remove a collaborator from a project","tags":["projects"],"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"]}}}]}}}}}}},"/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":"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"}}},"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 `x-faable-team` 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"]}}}]}}}}}},"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"]}]}},"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"}},"product_intent":{"anyOf":[{"type":"string","enum":["deploy"]},{"type":"string","enum":["auth"]},{"type":"string","enum":["undecided"]}]},"owner_suspended":{"type":"boolean"},"owner_email":{"type":"string"},"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-team` header.","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"]}}}]}}}},"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"]}}}]}}}}}}},"/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"}},"product_intent":{"anyOf":[{"type":"string","enum":["deploy"]},{"type":"string","enum":["auth"]},{"type":"string","enum":["undecided"]}]},"owner_suspended":{"type":"boolean"},"owner_email":{"type":"string"},"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":"`access_denied` — The row exists but does not belong to the caller.\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":["access_denied","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"]}}}]}}}}}},"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"}},"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"}},"product_intent":{"anyOf":[{"type":"string","enum":["deploy"]},{"type":"string","enum":["auth"]},{"type":"string","enum":["undecided"]}]},"owner_suspended":{"type":"boolean"},"owner_email":{"type":"string"},"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.","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":"`access_denied` — The row exists but does not belong to the caller.\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":["access_denied","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"]}}}]}}}}}},"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"}},"product_intent":{"anyOf":[{"type":"string","enum":["deploy"]},{"type":"string","enum":["auth"]},{"type":"string","enum":["undecided"]}]},"owner_suspended":{"type":"boolean"},"owner_email":{"type":"string"},"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":"`access_denied` — The row exists but does not belong to the caller.\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":["access_denied","unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`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":["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"]}}}]}}}}}}},"/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":"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"}}},"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 `x-faable-team` 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"]}}}]}}}}}},"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"}}}}},{"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"}]},"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"}]},"instance_type":{"type":"string","description":"Faable instance types comprise varying combinations of CPU, memory, storage, and networking capacity."},"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."},"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."},"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-team` header.","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":"`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":"`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"]}}}]}}}}}}},"/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"}}}}},{"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"}]},"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"}]},"instance_type":{"type":"string","description":"Faable instance types comprise varying combinations of CPU, memory, storage, and networking capacity."},"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."},"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."},"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":"`access_denied` — The row exists but does not belong to the caller.\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":["access_denied","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"]}}}]}}}}}},"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":{"type":"string"},"notify_deploy_success":{"anyOf":[{"type":"boolean"},{"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":{"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"}}}}},{"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"}]},"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"}]},"instance_type":{"type":"string","description":"Faable instance types comprise varying combinations of CPU, memory, storage, and networking capacity."},"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."},"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."},"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.","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":"`access_denied` — The row exists but does not belong to the caller.\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":["access_denied","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"]}}}]}}}}}},"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"}}}}},{"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"}]},"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"}]},"instance_type":{"type":"string","description":"Faable instance types comprise varying combinations of CPU, memory, storage, and networking capacity."},"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."},"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."},"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":"`access_denied` — The row exists but does not belong to the caller.\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":["access_denied","unauthorized","invalid_token"]}}}]}}}},"403":{"description":"`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":["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"]}}}]}}}}}}},"/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"]}}}]}}}}}}},"/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"]}}}]}}}}}}},"/app/repo-linkable":{"get":{"operationId":"app/repo_linkable","summary":"Check whether a repository can be linked to a new app","tags":["apps","github"],"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"]}}}]}}}}}}},"/app/{app_id}/registry":{"get":{"operationId":"app/get_registry","summary":"Get app registry","tags":["apps"],"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"]}}}]}}}}}}},"/app/{app_id}/logs":{"get":{"operationId":"app/logs","summary":"Get app logs","tags":["apps"],"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"]}}}]}}}}}}},"/app/{app_id}/traffic":{"get":{"operationId":"app/traffic","summary":"Get App Traffic","tags":["apps"],"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.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["metrics_unavailable"]}}}]}}}}}}},"/app/{id}/link-repository":{"post":{"operationId":"app/link_repository","summary":"Link a GitHub repository to an app","tags":["apps","github"],"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.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["repository_already_linked"]}}}]}}}},"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"]}}}]}}}}}}},"/app/{id}/unlink-repository":{"post":{"operationId":"app/unlink_repository","summary":"Unlink the GitHub repository from an app","tags":["apps","github"],"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"]}}}]}}}}}}},"/app/{id}/deploy-branch":{"post":{"operationId":"app/set_deploy_branch","summary":"Change the deploy branch","tags":["apps","github"],"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"]}}}]}}}}}}},"/app/{id}/repo/branches":{"get":{"operationId":"app/repo_branches","summary":"List repository branches","tags":["apps","github"],"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"]}}}]}}}}}}},"/app/{id}/repo/workflows":{"get":{"operationId":"app/repo_workflows","summary":"List repository workflow files","tags":["apps","github"],"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"]}}}]}}}}}}},"/app/{id}/repo/config":{"get":{"operationId":"app/repo_config","summary":"Get effective repository build config","tags":["apps","github"],"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"]}}}]}}}}}}},"/app/{id}/repo/directories":{"get":{"operationId":"app/repo_directories","summary":"List repository directories","tags":["apps","github"],"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"]}}}]}}}}}}},"/app/{id}/root-dir":{"post":{"operationId":"app/set_root_dir","summary":"Set the Root Directory","tags":["apps","github"],"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"]}}}]}}}}}}},"/app/{id}/deploy-mode":{"post":{"operationId":"app/set_deploy_mode","summary":"Set the deploy mode","tags":["apps","github"],"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"]}}}]}}}}}}},"/app/{id}/deploy":{"post":{"operationId":"app/deploy_now","summary":"Deploy the latest commit now","tags":["apps","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.","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":["status","commit","branch"],"properties":{"status":{"type":"string","enum":["created"]},"commit":{"type":"string"},"branch":{"type":"string"}}}}}},"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"]}}}]}}}}}}},"/app/{app_id}/waf":{"get":{"operationId":"app/waf","summary":"Get App WAF","tags":["apps"],"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"]}}}]}}}}}}},"/app/{app_id}/waf/rules":{"post":{"operationId":"app/waf/addRule","summary":"Add App WAF Rule","tags":["apps"],"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"]}],"description":"deny → 403 at the edge; sink → synthetic 404 from the platform (the app is never woken)"},"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"]}}}]}}}}}},"delete":{"operationId":"app/waf/removeRule","summary":"Remove App WAF Rule","tags":["apps"],"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"]}]}},"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"]}}}]}}}}}}},"/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":"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"}}},"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 `x-faable-team` 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"]}}}]}}}}}},"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"}}}}}},"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"},"build_started_at":{"type":"string","format":"date-time"},"build_ms":{"type":"integer"},"redeploy_of":{"type":"string"},"quota_released_at":{"type":"string","format":"date-time"},"trigger":{"anyOf":[{"type":"string"},{"type":"null"}]},"detected":{"$ref":"#/components/schemas/DeploymentDetected"},"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-team` header.","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"]}}}]}}}}}}},"/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"},"build_started_at":{"type":"string","format":"date-time"},"build_ms":{"type":"integer"},"redeploy_of":{"type":"string"},"quota_released_at":{"type":"string","format":"date-time"},"trigger":{"anyOf":[{"type":"string"},{"type":"null"}]},"detected":{"$ref":"#/components/schemas/DeploymentDetected"},"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":"`access_denied` — The row exists but does not belong to the caller.\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":["access_denied","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"]}}}]}}}}}},"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"}}}}}},"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"},"build_started_at":{"type":"string","format":"date-time"},"build_ms":{"type":"integer"},"redeploy_of":{"type":"string"},"quota_released_at":{"type":"string","format":"date-time"},"trigger":{"anyOf":[{"type":"string"},{"type":"null"}]},"detected":{"$ref":"#/components/schemas/DeploymentDetected"},"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":"`access_denied` — The row exists but does not belong to the caller.\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":["access_denied","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"]}}}]}}}}}},"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"},"build_started_at":{"type":"string","format":"date-time"},"build_ms":{"type":"integer"},"redeploy_of":{"type":"string"},"quota_released_at":{"type":"string","format":"date-time"},"trigger":{"anyOf":[{"type":"string"},{"type":"null"}]},"detected":{"$ref":"#/components/schemas/DeploymentDetected"},"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":"`access_denied` — The row exists but does not belong to the caller.\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":["access_denied","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"]}}}]}}}}}}},"/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"]}}}]}}}}}}},"/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"},"build_started_at":{"type":"string","format":"date-time"},"build_ms":{"type":"integer"},"redeploy_of":{"type":"string"},"quota_released_at":{"type":"string","format":"date-time"},"trigger":{"anyOf":[{"type":"string"},{"type":"null"}]},"detected":{"$ref":"#/components/schemas/DeploymentDetected"},"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"]}}}]}}}}}}},"/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":"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"}}},"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 `x-faable-team` 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"]}}}]}}}}}},"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":"If the domain has the ownership verified"},"active":{"type":"boolean"},"dns_alias":{"type":"string","description":"Internal DNS alias override host","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-team` header.\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"]}}}]}}}}}}},"/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":"If the domain has the ownership verified"},"active":{"type":"boolean"},"dns_alias":{"type":"string","description":"Internal DNS alias override host","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":"`access_denied` — The row exists but does not belong to the caller.\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":["access_denied","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"]}}}]}}}}}},"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,"default":null},"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":"If the domain has the ownership verified"},"active":{"type":"boolean"},"dns_alias":{"type":"string","description":"Internal DNS alias override host","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":"`access_denied` — The row exists but does not belong to the caller.\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":["access_denied","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"]}}}]}}}}}},"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":"If the domain has the ownership verified"},"active":{"type":"boolean"},"dns_alias":{"type":"string","description":"Internal DNS alias override host","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":"`access_denied` — The row exists but does not belong to the caller.\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":["access_denied","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"]}}}]}}}}}}},"/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"]}}}]}}}}}}},"/secret":{"post":{"operationId":"secrets/upsert","summary":"Upsert a secret","tags":["secrets"],"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"]}}}]}}}}}}},"/secret/{context_id}":{"get":{"operationId":"secrets/list_app","summary":"List all secrets assigned to an App or Profile contexts","tags":["secrets"],"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"]}}}]}}}}}}},"/secret/{secret_id}":{"delete":{"operationId":"secrets/delete","summary":"Delete a secret","tags":["secrets"],"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":"`access_denied` — The row exists but does not belong to the caller.\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":["access_denied","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"]}}}]}}}}}}},"/secret/createbatch":{"post":{"operationId":"secrets/create_batch","summary":"Create secrets in batch","tags":["secrets"],"description":"Create a secrets in batch","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretCreateBatch"}}}},"security":[{"faable_user":[]},{"faable_cli":[]},{"faable_m2m":[]},{"faable_machine":[]},{"apikey_basic":[]},{"apikey":[]}],"responses":{"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"]}}}]}}}}}}},"/auth/github-oidc":{"post":{"operationId":"auth/github_oidc","summary":"Exchange github oidc token","tags":["github"],"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 GitHub Actions OIDC token 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"]}}}]}}}}}}},"/usage/summary":{"get":{"operationId":"usage/summary","summary":"Get team usage summary for the current billing period","tags":["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"]}}}]}}}}}}},"/usage/traffic":{"get":{"operationId":"usage/traffic","summary":"Get metered traffic usage per team for the current period","tags":["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"]}}}]}}}}}}},"/deploy-quota":{"get":{"operationId":"deploy_quota/get","summary":"Get the team's daily deploy quota status","tags":["deployments"],"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":"`access_denied` — The row exists but does not belong to the caller.\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":["access_denied","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"]}}}]}}}}}}},"/deploy-quota/release":{"post":{"operationId":"deploy_quota/release","summary":"Roll out the builds held by the daily deploy quota","tags":["deployments"],"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"]}}}]}}}}}}}},"servers":[{"url":"https://api.faable.com","description":"Faable API"}],"tags":[{"name":"projects","description":"Manage Faable Projects (teams)","externalDocs":{"description":"Official Faable documentation","url":"https://faable.com/docs/projects"}},{"name":"apps","description":"Manage Faable Apps","externalDocs":{"description":"Official Faable documentation","url":"https://faable.com/docs/apps"}},{"name":"deployments","description":"Manage and trigger deployments","externalDocs":{"description":"Official Faable documentation","url":"https://faable.com/docs/deployments"}},{"name":"domains","description":"Manage custom domains","externalDocs":{"description":"Official Faable documentation","url":"https://faable.com/docs/domains"}},{"name":"secrets","description":"Manage app secrets and environment variables","externalDocs":{"description":"Official Faable documentation","url":"https://faable.com/docs/secrets"}},{"name":"apikeys","description":"Manage API keys","externalDocs":{"description":"Official Faable documentation","url":"https://faable.com/docs/api-keys"}},{"name":"contacts","description":"Manage contacts","externalDocs":{"description":"Official Faable documentation","url":"https://faable.com/docs/contacts"}},{"name":"github","description":"GitHub integration: OIDC token exchange, webhooks and setup","externalDocs":{"description":"Official Faable documentation","url":"https://faable.com/docs/github"}},{"name":"usage","description":"App usage and metering","externalDocs":{"description":"Official Faable documentation","url":"https://faable.com/docs/usage"}},{"name":"status","description":"Resource status and health","externalDocs":{"description":"Official Faable documentation","url":"https://faable.com/docs/status"}}]}