{
  "openapi" : "3.0.3",
  "info" : {
    "title" : "Phase Two Cluster Management API",
    "description" : "Automate management of your Phase Two hosted Keycloak clusters: dedicated clusters, deployments (realms), custom domains, extensions, environment variables, IP rules, organizations, and billing.\n\nThis is the **control plane** API. It manages clusters themselves -- creating them, adding realms, attaching domains -- and is entirely separate from the Keycloak Admin REST API you use to configure what lives *inside* a realm.\n\n## Hosts\n\nTwo hostnames are involved, and both differ by environment: tokens are minted by the control-plane realm on the **console host**, while the operations in this reference are served from the **API host**.\n\n| Environment | Console host (tokens) | API host (operations) |\n|---|---|---|\n| Production *(default)* | `app.phasetwo.io` | `api.phasetwo.io` |\n| Staging | `app-staging.phasetwo.io` | `api-staging.phasetwo.io` |\n\nEverything below defaults to production. To work against staging, pick `api-staging` (or `app-staging`) in the server selector **and** select the `oidcClientCredentialsStaging` security scheme -- the token endpoint is a separate field that the server selector cannot retarget. The two must match: a token minted in one environment is not valid in the other.\n\n## Authentication\n\nEvery request needs an OAuth2 access token obtained via the **client credentials grant**. Create an API secret for your organization -- in the console under your team's **API Credentials** tab, or via `org.apiSecret.create` -- then exchange its client ID and secret for a token:\n\n```\ncurl -X POST https://app.phasetwo.io/auth/realms/self/protocol/openid-connect/token \\\n  -d grant_type=client_credentials \\\n  -d client_id=$PHASETWO_CLIENT_ID \\\n  -d client_secret=$PHASETWO_CLIENT_SECRET\n```\n\nSend the resulting token as `Authorization: Bearer <token>` on every call. The client secret is shown only once, when the secret is created.\n\nWhat a token can do is governed by the organization roles granted to its API secret, not by OAuth scopes.",
    "version" : "2.0.0"
  },
  "servers" : [ {
    "url" : "https://{apiHost}.phasetwo.io/v2",
    "description" : "Public API host. The canonical base URL for this API.",
    "variables" : {
      "apiHost" : {
        "default" : "api",
        "description" : "`api` for production, `api-staging` for staging. Must match the environment of the console host that issued your token.",
        "enum" : [ "api", "api-staging" ]
      }
    }
  }, {
    "url" : "https://{consoleHost}.phasetwo.io/auth/realms/self/v2",
    "description" : "Console host. The path the API host rewrites to; useful for debugging, or for an environment where the API host is not yet served.",
    "variables" : {
      "consoleHost" : {
        "default" : "app",
        "description" : "`app` for production, `app-staging` for staging. This is the same host you use to log into the Phase Two console, and the host that mints your access token.",
        "enum" : [ "app", "app-staging" ]
      }
    }
  } ],
  "security" : [ {
    "oidcClientCredentials" : [ ]
  }, {
    "oidcClientCredentialsStaging" : [ ]
  } ],
  "tags" : [ {
    "name" : "orgs",
    "description" : "Organizations and their API secrets."
  }, {
    "name" : "billing",
    "description" : "Subscriptions, payment methods, billing contacts, and Stripe billing portal sessions."
  }, {
    "name" : "clusters",
    "description" : "Dedicated Keycloak cluster lifecycle and configuration."
  }, {
    "name" : "deployments",
    "description" : "Deployments (realms) running on a dedicated cluster."
  }, {
    "name" : "customer-domains",
    "description" : "Custom hostnames for a dedicated cluster."
  }, {
    "name" : "extensions",
    "description" : "Custom Keycloak extensions (providers, themes) for a dedicated cluster."
  }, {
    "name" : "environment-variables",
    "description" : "Custom environment variables for a dedicated cluster."
  }, {
    "name" : "ip-rules",
    "description" : "IP allow/deny rules for a dedicated cluster."
  }, {
    "name" : "logs",
    "description" : "Log access for a dedicated cluster."
  } ],
  "paths" : {
    "/clusters" : {
      "get" : {
        "tags" : [ "clusters" ],
        "summary" : "List your dedicated clusters.",
        "description" : "Returns the dedicated clusters belonging to organizations the authenticated API client has access to. Archived clusters are excluded.",
        "operationId" : "cluster.list",
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "type" : "array",
                  "items" : {
                    "$ref" : "#/components/schemas/Cluster"
                  }
                }
              }
            }
          }
        }
      },
      "post" : {
        "tags" : [ "clusters" ],
        "summary" : "Create a dedicated cluster.",
        "description" : "Starts creation of a dedicated cluster for an organization. Provisioning begins once payment is settled, and the response tells you what is needed to get there — exactly one of three shapes:\n\n- `link` set: redirect the browser to this Stripe Checkout URL to complete payment setup. This is what you get whenever `payment_method_id` is omitted.\n- `cluster` set: an existing `payment_method_id` was charged successfully and the cluster is created; no further action is needed.\n- `requires_action` true, with `client_secret` and `cluster_id`: the payment method needs additional authentication (3DS/SCA). Complete the returned PaymentIntent client secret inline with Stripe.js; the cluster already exists and provisions once payment confirms.",
        "operationId" : "cluster.create",
        "requestBody" : {
          "description" : "The cluster to create.",
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/DedicatedClusterRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ClusterCreationResult"
                }
              }
            }
          }
        }
      }
    },
    "/clusters/name-available" : {
      "get" : {
        "tags" : [ "clusters" ],
        "summary" : "Check whether a cluster name is available.",
        "description" : "Validates a candidate name for a new dedicated cluster and reports whether it is available (well-formed, not reserved, and not already in use).",
        "operationId" : "cluster.nameAvailability.check",
        "parameters" : [ {
          "name" : "name",
          "in" : "query",
          "description" : "Candidate cluster name to check.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/NameAvailability"
                }
              }
            }
          }
        }
      }
    },
    "/clusters/regions" : {
      "get" : {
        "tags" : [ "clusters" ],
        "summary" : "List regions available for dedicated clusters.",
        "description" : "Returns the AWS regions a new dedicated cluster can be provisioned into.",
        "operationId" : "cluster.region.list",
        "responses" : {
          "200" : {
            "description" : "Regions available for dedicated clusters.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "type" : "array",
                  "items" : {
                    "$ref" : "#/components/schemas/Region"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/clusters/{id}" : {
      "get" : {
        "tags" : [ "clusters" ],
        "summary" : "Get a dedicated cluster by ID.",
        "description" : "Returns details about a single dedicated cluster.",
        "operationId" : "cluster.detail",
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Cluster"
                }
              }
            }
          }
        }
      },
      "delete" : {
        "tags" : [ "clusters" ],
        "summary" : "Remove a dedicated cluster.",
        "description" : "Schedules a dedicated cluster for deletion at the end of the current billing cycle (clusters that never completed billing setup are removed immediately). This cannot be undone.",
        "operationId" : "cluster.delete",
        "responses" : {
          "204" : {
            "description" : "Cluster removed or scheduled for removal."
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/billing" : {
      "get" : {
        "tags" : [ "clusters" ],
        "summary" : "Get subscription and billing information for a dedicated cluster.",
        "description" : "Returns the Stripe subscription backing this cluster's billing.",
        "operationId" : "cluster.subscription.detail",
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Subscription"
                }
              }
            }
          }
        }
      },
      "post" : {
        "tags" : [ "clusters" ],
        "summary" : "Create a Stripe billing portal session link for a cluster.",
        "description" : "Deprecated. Use `org.billingPortalSession.create` instead.",
        "operationId" : "cluster.billingSession.create",
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/RedirectLink"
                }
              }
            }
          }
        },
        "deprecated" : true
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/checkout" : {
      "post" : {
        "tags" : [ "clusters" ],
        "summary" : "Restore billing for a cluster that is not currently paid for. Returns a browser link: the existing subscription's open invoice when paying it repairs the subscription, otherwise a fresh Stripe checkout for a new subscription.",
        "operationId" : "cluster.checkout.resume",
        "requestBody" : {
          "description" : "Optional price/billing-period override for the resumed checkout.",
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/ResumeCheckoutRequest"
              }
            }
          }
        },
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/RedirectLink"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/deployments" : {
      "get" : {
        "tags" : [ "clusters" ],
        "summary" : "List deployments for a dedicated cluster.",
        "description" : "Returns the deployments (realms) running on this cluster.",
        "operationId" : "cluster.deployment.list",
        "parameters" : [ {
          "name" : "search",
          "in" : "query",
          "description" : "Filter deployments by name substring.",
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "type" : "array",
                  "items" : {
                    "$ref" : "#/components/schemas/Deployment"
                  }
                }
              }
            }
          }
        }
      },
      "post" : {
        "tags" : [ "clusters" ],
        "summary" : "Create a deployment for a dedicated cluster.",
        "description" : "Creates a new, empty deployment (Keycloak realm) on this cluster. The cluster must be ACTIVE, and the number of deployments is limited by the cluster's tier. To create a deployment from an existing realm export, use the import operation instead.",
        "operationId" : "cluster.deployment.create",
        "requestBody" : {
          "description" : "The deployment to create.",
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/ClusterDeploymentRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "201" : {
            "description" : "Deployment created. The response has no body; `Location` holds the new deployment's URL, whose last path segment is its ID.",
            "headers" : {
              "Location" : {
                "description" : "URL of the created deployment.",
                "style" : "simple",
                "schema" : {
                  "type" : "string"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/deployments/import" : {
      "post" : {
        "tags" : [ "clusters" ],
        "summary" : "Create a deployment from a realm export.",
        "description" : "Creates a new deployment on this cluster from an exported Keycloak realm JSON file. The realm is imported asynchronously; poll the returned deployment until its state leaves PENDING. Importing users is not supported — the file must not contain a `users` array. The cluster must be ACTIVE, and the number of deployments is limited by the cluster's tier.",
        "operationId" : "cluster.deployment.import",
        "requestBody" : {
          "content" : {
            "multipart/form-data" : {
              "schema" : {
                "required" : [ "file", "deployment-name" ],
                "type" : "object",
                "properties" : {
                  "file" : {
                    "format" : "binary",
                    "description" : "The exported realm JSON file to import."
                  },
                  "deployment-name" : {
                    "description" : "Name for the new deployment."
                  }
                }
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "201" : {
            "description" : "Deployment created; import started. The response has no body; `Location` holds the new deployment's URL, whose last path segment is its ID.",
            "headers" : {
              "Location" : {
                "description" : "URL of the created deployment.",
                "style" : "simple",
                "schema" : {
                  "type" : "string"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/deployments/name-available" : {
      "get" : {
        "tags" : [ "clusters" ],
        "summary" : "Check whether a deployment name is available on a cluster.",
        "description" : "Validates a candidate name for a new deployment on this cluster and reports whether it is available (well-formed, not reserved, and not already in use on this cluster).",
        "operationId" : "cluster.deployment.nameAvailability.check",
        "parameters" : [ {
          "name" : "name",
          "in" : "query",
          "description" : "Candidate deployment name to check.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/NameAvailability"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/domains" : {
      "get" : {
        "tags" : [ "customer-domains" ],
        "summary" : "List custom domains for a cluster.",
        "description" : "Returns the custom hostnames configured for this dedicated cluster.",
        "operationId" : "cluster.domain.list",
        "responses" : {
          "200" : {
            "description" : "Custom domains for the cluster.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "type" : "array",
                  "items" : {
                    "$ref" : "#/components/schemas/CustomerDomain"
                  }
                }
              }
            }
          }
        }
      },
      "post" : {
        "tags" : [ "customer-domains" ],
        "summary" : "Add a custom domain to a cluster.",
        "description" : "Registers a custom hostname for this dedicated cluster and starts DNS/TLS provisioning for it. Check the domain's status via the returned resource until its certificate is issued before using it as the cluster's primary hostname.",
        "operationId" : "cluster.domain.create",
        "requestBody" : {
          "description" : "The custom domain to add.",
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/ClusterDomain"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "Custom domain created; provisioning started. `domain.domainRecords` holds the DNS records to create for validation.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/CustomerDomainValidation"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/domains/{domainId}" : {
      "get" : {
        "tags" : [ "customer-domains" ],
        "summary" : "Get a custom domain by ID.",
        "description" : "Returns details about a single custom domain configured for a cluster.",
        "operationId" : "cluster.domain.detail",
        "responses" : {
          "200" : {
            "description" : "The custom domain.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/CustomerDomain"
                }
              }
            }
          }
        }
      },
      "delete" : {
        "tags" : [ "customer-domains" ],
        "summary" : "Remove a custom domain by ID.",
        "description" : "Deletes a custom domain from the cluster. If it is currently the cluster's primary hostname, switch the primary hostname away from it first.",
        "operationId" : "cluster.domain.delete",
        "responses" : {
          "204" : {
            "description" : "Custom domain removed."
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      }, {
        "name" : "domainId",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/domains/{domainId}/status" : {
      "get" : {
        "tags" : [ "customer-domains" ],
        "summary" : "Get a custom domain's provisioning status.",
        "description" : "Returns the DNS validation and TLS certificate status of a custom domain, along with the DNS records to create if validation is still pending.",
        "operationId" : "cluster.domain.status.detail",
        "responses" : {
          "200" : {
            "description" : "The custom domain's status.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/CustomerDomainValidation"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      }, {
        "name" : "domainId",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/env-vars" : {
      "get" : {
        "tags" : [ "environment-variables" ],
        "summary" : "List environment variables for a cluster.",
        "description" : "Returns the custom environment variables configured for this cluster's Keycloak deployment.",
        "operationId" : "cluster.envVar.list",
        "responses" : {
          "200" : {
            "description" : "Environment variables for the cluster.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "type" : "array",
                  "items" : {
                    "$ref" : "#/components/schemas/EnvironmentVariableRepresentation"
                  }
                }
              }
            }
          }
        }
      },
      "post" : {
        "tags" : [ "environment-variables" ],
        "summary" : "Add an environment variable to a cluster.",
        "description" : "Adds a custom environment variable to the cluster and restarts the Keycloak deployment to apply it. Only custom (non-reserved) SPI configuration variables are allowed. Fails with a 409 if a restart is already in progress.",
        "operationId" : "cluster.envVar.create",
        "requestBody" : {
          "description" : "The environment variable to add.",
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/EnvironmentVariableRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "Environment variable created.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/EnvironmentVariableRepresentation"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/env-vars/{envVariableId}" : {
      "get" : {
        "tags" : [ "environment-variables" ],
        "summary" : "Get an environment variable by ID.",
        "description" : "Returns details about a single custom environment variable.",
        "operationId" : "cluster.envVar.detail",
        "responses" : {
          "200" : {
            "description" : "The environment variable.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/EnvironmentVariableRepresentation"
                }
              }
            }
          }
        }
      },
      "put" : {
        "tags" : [ "environment-variables" ],
        "summary" : "Update an environment variable.",
        "description" : "Updates the name and/or value of a custom environment variable and restarts the Keycloak deployment to apply it. Fails with a 409 if a restart is already in progress.",
        "operationId" : "cluster.envVar.update",
        "requestBody" : {
          "description" : "The new name/value for the environment variable.",
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/EnvironmentVariableRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "Environment variable updated.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/EnvironmentVariableRepresentation"
                }
              }
            }
          }
        }
      },
      "delete" : {
        "tags" : [ "environment-variables" ],
        "summary" : "Remove an environment variable.",
        "description" : "Deletes a custom environment variable and restarts the Keycloak deployment to apply the change. Fails with a 409 if a restart is already in progress.",
        "operationId" : "cluster.envVar.delete",
        "parameters" : [ {
          "name" : "envVariableId",
          "in" : "path",
          "description" : "ID of the environment variable.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "204" : {
            "description" : "Environment variable removed."
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      }, {
        "name" : "envVariableId",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/extensions" : {
      "get" : {
        "tags" : [ "extensions" ],
        "summary" : "List extensions for a cluster.",
        "description" : "Returns the custom extensions (providers, themes) configured for this cluster, optionally filtered by type.",
        "operationId" : "cluster.extension.list",
        "parameters" : [ {
          "name" : "type",
          "in" : "query",
          "description" : "Only return extensions of this type.",
          "schema" : {
            "$ref" : "#/components/schemas/ResourceType"
          }
        } ],
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "type" : "array",
                  "items" : {
                    "$ref" : "#/components/schemas/Extension"
                  }
                }
              }
            }
          }
        }
      },
      "post" : {
        "tags" : [ "extensions" ],
        "summary" : "Create an extension for a cluster.",
        "description" : "Registers a new extension slot (provider or theme) for this cluster. This only creates the extension record; upload its jar via the extension's version sub-resource before it takes effect.",
        "operationId" : "cluster.extension.create",
        "requestBody" : {
          "description" : "The extension to create.",
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/ExtensionRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "201" : {
            "description" : "Extension created. The response has no body; `Location` holds the new extension's URL, whose last path segment is its ID.",
            "headers" : {
              "Location" : {
                "description" : "URL of the created extension.",
                "style" : "simple",
                "schema" : {
                  "type" : "string"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/extensions/cluster-update" : {
      "post" : {
        "tags" : [ "extensions" ],
        "summary" : "Restart a cluster to apply extension changes.",
        "description" : "Reconciles this cluster's configured extensions onto its running Keycloak deployment and restarts it to pick up the changes. Fails with a 409 if a restart is already in progress for this cluster.",
        "operationId" : "cluster.extension.reconcile",
        "responses" : {
          "202" : {
            "description" : "Restart accepted."
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/extensions/keycloak-versions" : {
      "get" : {
        "tags" : [ "extensions" ],
        "summary" : "List Keycloak major versions supported for extensions.",
        "description" : "Returns the Keycloak major versions that a version-dependent extension may target.",
        "operationId" : "cluster.extension.keycloakVersion.list",
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "type" : "array",
                  "items" : {
                    "format" : "int32",
                    "type" : "integer"
                  }
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/extensions/{extensionId}" : {
      "get" : {
        "tags" : [ "extensions" ],
        "summary" : "Get an extension by ID.",
        "operationId" : "cluster.extension.detail",
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Extension"
                }
              }
            }
          }
        }
      },
      "put" : {
        "tags" : [ "extensions" ],
        "summary" : "Enable or disable an extension.",
        "description" : "Toggles whether an extension is active. Disabling does not delete its versions.",
        "operationId" : "cluster.extension.update",
        "requestBody" : {
          "description" : "The desired enabled state.",
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/ToggleExtensionRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Extension"
                }
              }
            }
          }
        }
      },
      "delete" : {
        "tags" : [ "extensions" ],
        "summary" : "Remove an extension by ID.",
        "description" : "Deletes an extension and all of its versions.",
        "operationId" : "cluster.extension.delete",
        "responses" : {
          "204" : {
            "description" : "Extension removed."
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      }, {
        "name" : "extensionId",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/extensions/{extensionId}/standalone" : {
      "post" : {
        "tags" : [ "extensions" ],
        "summary" : "Create an upload URL for a standalone extension.",
        "description" : "Creates the extension's single (version-independent) version and returns a presigned S3 URL to upload its jar to. After uploading, call the extension's `confirm` operation to finalize it.",
        "operationId" : "cluster.extension.standalone.uploadUrl.create",
        "requestBody" : {
          "description" : "Label for the new version.",
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/ExtensionVersionUploadUrlRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The presigned upload target. PUT the jar to `url`.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/PresignedUrl"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      }, {
        "name" : "extensionId",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/extensions/{extensionId}/standalone/confirm" : {
      "put" : {
        "tags" : [ "extensions" ],
        "summary" : "Confirm a standalone extension upload.",
        "description" : "Verifies the jar was uploaded to the presigned URL and records its location on the extension version, making it available for deployment.",
        "operationId" : "cluster.extension.standalone.confirm",
        "requestBody" : {
          "description" : "The S3 resource key the jar was uploaded to.",
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/ExtensionVersionLocationValidationRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The confirmed extension version.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ExtensionVersion"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      }, {
        "name" : "extensionId",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/extensions/{extensionId}/versions" : {
      "post" : {
        "tags" : [ "extensions" ],
        "summary" : "Create a new extension version.",
        "description" : "Registers a new version of a Keycloak-version-dependent extension for a target major version. This only creates the version record; use the returned version's upload URL to upload its jar.",
        "operationId" : "cluster.extension.version.create",
        "requestBody" : {
          "description" : "The version to create.",
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/ExtensionVersionRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The created extension version.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ExtensionVersion"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      }, {
        "name" : "extensionId",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/extensions/{extensionId}/versions/{versionId}" : {
      "delete" : {
        "tags" : [ "extensions" ],
        "summary" : "Remove an extension version by ID.",
        "operationId" : "cluster.extension.version.delete",
        "parameters" : [ {
          "name" : "versionId",
          "in" : "path",
          "description" : "ID of the extension version.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "204" : {
            "description" : "Extension version removed."
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      }, {
        "name" : "extensionId",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/extensions/{extensionId}/versions/{versionId}/admin-block" : {
      "put" : {
        "tags" : [ "extensions" ],
        "summary" : "Block or unblock an extension version.",
        "description" : "Stops an extension version being deployed to any cluster, or lifts an existing block. A blocked version is never copied during a cluster reconcile, whatever its `valid` flag or scan state says. Requires the `manage-clusters` role on the `cluster-management` client.",
        "operationId" : "cluster.extension.version.adminBlock",
        "parameters" : [ {
          "name" : "versionId",
          "in" : "path",
          "description" : "ID of the extension version.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "requestBody" : {
          "description" : "Whether to block the version, and why if so.",
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/AdminBlockExtensionVersionRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The extension version, with its new block state.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ExtensionVersion"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      }, {
        "name" : "extensionId",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/extensions/{extensionId}/versions/{versionId}/confirm" : {
      "put" : {
        "tags" : [ "extensions" ],
        "summary" : "Confirm an extension version upload.",
        "description" : "Verifies the jar was uploaded to the presigned URL and records its location on the version. If extension scanning is enabled for the cluster, this also queues a security scan of the jar.",
        "operationId" : "cluster.extension.version.confirm",
        "parameters" : [ {
          "name" : "versionId",
          "in" : "path",
          "description" : "ID of the extension version.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "requestBody" : {
          "description" : "The S3 resource key the jar was uploaded to.",
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/ExtensionVersionLocationValidationRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The confirmed extension version.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ExtensionVersion"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      }, {
        "name" : "extensionId",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/extensions/{extensionId}/versions/{versionId}/report" : {
      "get" : {
        "tags" : [ "extensions" ],
        "summary" : "Read the security scan report for an extension version.",
        "description" : "Returns the extension checker's full report as written by the scanner: the verdict, the risk score and every individual finding. Requires the `manage-clusters` role on the `cluster-management` client.",
        "operationId" : "cluster.extension.version.report",
        "parameters" : [ {
          "name" : "versionId",
          "in" : "path",
          "description" : "ID of the extension version.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "description" : "The scan report, verbatim as the checker wrote it.",
            "content" : {
              "application/json" : { }
            }
          },
          "404" : {
            "description" : "No version with that id, or no report has been written for it."
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      }, {
        "name" : "extensionId",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/extensions/{extensionId}/versions/{versionId}/upload-url" : {
      "post" : {
        "tags" : [ "extensions" ],
        "summary" : "Create an upload URL for an extension version.",
        "description" : "Returns a presigned S3 URL to upload the version's jar to. After uploading, call the version's `confirm` operation to finalize it.",
        "operationId" : "cluster.extension.version.uploadUrl.create",
        "parameters" : [ {
          "name" : "versionId",
          "in" : "path",
          "description" : "ID of the extension version.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "requestBody" : {
          "description" : "Label for the upload.",
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/ExtensionVersionUploadUrlRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The presigned upload target. PUT the jar to `url`.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/PresignedUrl"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      }, {
        "name" : "extensionId",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/extensions/{extensionId}/versions/{versionId}/validate" : {
      "put" : {
        "tags" : [ "extensions" ],
        "summary" : "Approve or reject an extension version.",
        "description" : "Sets whether an extension version is considered valid for deployment. If the version's security scan result requires justification, approving it requires a `reason`.",
        "operationId" : "cluster.extension.version.validate",
        "parameters" : [ {
          "name" : "versionId",
          "in" : "path",
          "description" : "ID of the extension version.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "requestBody" : {
          "description" : "The desired valid/invalid state and, if approving over a scan finding, a reason.",
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/ToggleExtensionVersionValidRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The extension version, with its new valid state.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ExtensionVersion"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      }, {
        "name" : "extensionId",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/extensions/{extensionId}/well-known" : {
      "get" : {
        "tags" : [ "extensions" ],
        "summary" : "Get app association file state for a custom domain.",
        "description" : "Returns each well-known file, whether it has been uploaded, and the public URL the operating system will fetch.",
        "operationId" : "cluster.wellKnown.get",
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/WellKnown"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      }, {
        "name" : "extensionId",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/extensions/{extensionId}/well-known/change-password" : {
      "put" : {
        "tags" : [ "extensions" ],
        "summary" : "Set the change-password redirect for a custom domain.",
        "description" : "Password managers fetch /.well-known/change-password to offer a \"change password\" action. Send a null or empty url to clear it, which returns the path to 404 — spec-safe, and simply means the site does not advertise the convention.",
        "operationId" : "cluster.wellKnown.changePassword.set",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/WellKnown"
              }
            }
          }
        },
        "responses" : {
          "200" : {
            "description" : "Redirect target updated. Returns the domain's well-known state.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/WellKnown"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      }, {
        "name" : "extensionId",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/extensions/{extensionId}/well-known/{file}" : {
      "delete" : {
        "tags" : [ "extensions" ],
        "summary" : "Remove an app association file from a custom domain.",
        "operationId" : "cluster.wellKnown.delete",
        "parameters" : [ {
          "name" : "file",
          "in" : "path",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "description" : "File removed. Returns the domain's well-known state.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/WellKnown"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      }, {
        "name" : "extensionId",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/extensions/{extensionId}/well-known/{file}/confirm" : {
      "put" : {
        "tags" : [ "extensions" ],
        "summary" : "Validate and publish an uploaded app association file.",
        "description" : "Checks the uploaded object parses as JSON and has the shape the operating system expects, forces its content type, and records it against the domain.",
        "operationId" : "cluster.wellKnown.confirm",
        "parameters" : [ {
          "name" : "file",
          "in" : "path",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "description" : "File published. Returns the domain's well-known state.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/WellKnown"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      }, {
        "name" : "extensionId",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/extensions/{extensionId}/well-known/{file}/upload-url" : {
      "post" : {
        "tags" : [ "extensions" ],
        "summary" : "Create an upload URL for an app association file.",
        "description" : "Returns a short-lived presigned PUT URL. Upload the file to it, then call confirm to validate and publish it.",
        "operationId" : "cluster.wellKnown.uploadUrl.create",
        "parameters" : [ {
          "name" : "file",
          "in" : "path",
          "description" : "apple-app-site-association or assetlinks.json",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "description" : "The presigned upload target. PUT the file to `url`.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/PresignedUrl"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      }, {
        "name" : "extensionId",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/host" : {
      "put" : {
        "tags" : [ "clusters" ],
        "summary" : "Change the cluster's primary hostname.",
        "description" : "Switches the cluster's primary hostname to an already-provisioned domain: either the cluster's default `<name>.global.auth.ac` address or one of its custom domains that already has an issued TLS certificate. This does not provision a new domain — use the custom domains API to add one first. The candidate host must be reachable before the switch is applied.",
        "operationId" : "cluster.host.update",
        "requestBody" : {
          "description" : "The domain to switch to.",
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/ClusterDomain"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "Primary hostname updated. Returns the cluster with its new `host`.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Cluster"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/ip-rules" : {
      "get" : {
        "tags" : [ "ip-rules" ],
        "summary" : "List IP rules for a cluster.",
        "description" : "Returns the IP allow/deny rules configured for this cluster's admin console and realm endpoints.",
        "operationId" : "cluster.ipRule.list",
        "responses" : {
          "200" : {
            "description" : "IP rules for the cluster.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/IpRulesRepresentation"
                }
              }
            }
          }
        }
      },
      "post" : {
        "tags" : [ "ip-rules" ],
        "summary" : "Replace IP rules for a cluster.",
        "description" : "Sets the admin-allow, realm-allow, and realm-block IP rule lists for this cluster. Each category you include entirely replaces the existing rules in that category (rules not in the new list are removed; new addresses are added). Omit a category to leave it unchanged. The total number of rules is limited by the cluster's tier.",
        "operationId" : "cluster.ipRule.update",
        "requestBody" : {
          "description" : "IP rule lists to apply, by category.",
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/IpRestrictionsRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "Updated IP rules for the cluster.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/IpRulesRepresentation"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/ip-rules/{ipRuleId}" : {
      "get" : {
        "tags" : [ "ip-rules" ],
        "summary" : "Get an IP rule by ID.",
        "description" : "Returns details about a single IP rule.",
        "operationId" : "cluster.ipRule.detail",
        "responses" : {
          "200" : {
            "description" : "The IP rule.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/IpRuleRepresentation"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      }, {
        "name" : "ipRuleId",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/logs" : {
      "get" : {
        "tags" : [ "logs" ],
        "summary" : "List log files for a cluster.",
        "description" : "Returns log files for this cluster within an optional date range, paginated by `start`/`max`.",
        "operationId" : "cluster.log.list",
        "parameters" : [ {
          "name" : "from",
          "in" : "query",
          "description" : "ISO-8601 instant; only include logs at or after this time.",
          "schema" : {
            "type" : "string"
          }
        }, {
          "name" : "max",
          "in" : "query",
          "description" : "Maximum number of log files to return.",
          "schema" : {
            "format" : "int32",
            "default" : 20,
            "type" : "integer"
          }
        }, {
          "name" : "start",
          "in" : "query",
          "description" : "Offset into the result set, for pagination.",
          "schema" : {
            "format" : "int32",
            "default" : 0,
            "type" : "integer"
          }
        }, {
          "name" : "to",
          "in" : "query",
          "description" : "ISO-8601 instant; only include logs at or before this time.",
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "description" : "Log files matching the query.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "type" : "array",
                  "items" : {
                    "$ref" : "#/components/schemas/LogFile"
                  }
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/logs/{path}" : {
      "get" : {
        "tags" : [ "logs" ],
        "summary" : "Get a cluster log file.",
        "description" : "Returns a log file's metadata along with a temporary, presigned URL to download its contents.",
        "operationId" : "cluster.log.detail",
        "parameters" : [ {
          "name" : "path",
          "in" : "path",
          "description" : "Path of the log file, as returned by the list operation.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "name" : "minutes",
          "in" : "query",
          "description" : "How many minutes the presigned download URL remains valid.",
          "schema" : {
            "format" : "int32",
            "default" : 5,
            "type" : "integer"
          }
        } ],
        "responses" : {
          "200" : {
            "description" : "The log file, with a temporary access URL.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/LogFile"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/metrics" : {
      "get" : {
        "tags" : [ "clusters" ],
        "summary" : "Get health metrics for a dedicated cluster.",
        "operationId" : "cluster.metrics.detail",
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Metrics"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/restart-status" : {
      "get" : {
        "tags" : [ "clusters" ],
        "summary" : "Check whether a cluster restart is in progress.",
        "description" : "Configuration changes (environment variables, extensions) restart the cluster's Keycloak deployment to take effect; use this to poll until a restart completes before making another change.",
        "operationId" : "cluster.restartStatus.detail",
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/RestartStatus"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/systeminfo" : {
      "get" : {
        "tags" : [ "clusters" ],
        "summary" : "Get system info for a dedicated cluster.",
        "description" : "Returns the cluster's Keycloak server info (version, build time, etc) plus its Phase Two release build tag (issue #538). Only available while the cluster is ACTIVE.",
        "operationId" : "cluster.systemInfo.detail",
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ClusterSystemInfoRepresentation"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/telemetry-export" : {
      "get" : {
        "tags" : [ "clusters" ],
        "summary" : "Get telemetry export configuration.",
        "description" : "Returns the cluster's telemetry export configuration. The bearer token is never returned; tokenSet indicates whether one is stored.",
        "operationId" : "cluster.telemetryExport.detail",
        "responses" : {
          "200" : {
            "description" : "Telemetry export configuration"
          },
          "401" : {
            "description" : "Insufficient permissions"
          }
        }
      },
      "put" : {
        "tags" : [ "clusters" ],
        "summary" : "Update telemetry export configuration.",
        "description" : "Stores the configuration and publishes or withdraws the cluster's gateway config. Omit token to leave the stored one unchanged. Rejects settings the pipeline cannot honour rather than silently ignoring them.",
        "operationId" : "cluster.telemetryExport.update",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/TelemetryExport"
              }
            }
          }
        },
        "responses" : {
          "200" : {
            "description" : "Updated telemetry export configuration"
          },
          "400" : {
            "description" : "Invalid endpoint, token or unsupported setting"
          },
          "401" : {
            "description" : "Insufficient permissions"
          },
          "503" : {
            "description" : "Saved but could not be published"
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/clusters/{id}/telemetry-export/validate-endpoint" : {
      "get" : {
        "tags" : [ "clusters" ],
        "summary" : "Validate a telemetry export endpoint.",
        "description" : "Checks an endpoint without saving, so the caller can offer feedback before committing. Changes nothing. Resolution happens again at publish time, since validating once is defeated by DNS rebinding.",
        "operationId" : "cluster.telemetryExport.endpoint.validate",
        "parameters" : [ {
          "name" : "endpoint",
          "in" : "query",
          "description" : "Candidate OTLP endpoint to check.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "description" : "Validation outcome"
          },
          "401" : {
            "description" : "Insufficient permissions"
          }
        }
      },
      "parameters" : [ {
        "name" : "id",
        "in" : "path",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/deployments/{id}" : {
      "get" : {
        "tags" : [ "deployments" ],
        "summary" : "Get a deployment by ID.",
        "description" : "Returns details about a single deployment (realm).",
        "operationId" : "deployment.detail",
        "parameters" : [ {
          "name" : "id",
          "in" : "path",
          "description" : "ID of the deployment.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Deployment"
                }
              }
            }
          }
        }
      },
      "put" : {
        "tags" : [ "deployments" ],
        "summary" : "Update a deployment by ID.",
        "description" : "Updates mutable settings of a deployment. Only the owner may update it.",
        "operationId" : "deployment.update",
        "parameters" : [ {
          "name" : "id",
          "in" : "path",
          "description" : "ID of the deployment.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "requestBody" : {
          "description" : "Deployment fields to update.",
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/Deployment"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "204" : {
            "description" : "Deployment updated."
          }
        }
      },
      "delete" : {
        "tags" : [ "deployments" ],
        "summary" : "Remove a deployment by ID.",
        "description" : "Permanently deletes a deployment (realm). Only the owner may delete it.",
        "operationId" : "deployment.delete",
        "parameters" : [ {
          "name" : "id",
          "in" : "path",
          "description" : "ID of the deployment.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "204" : {
            "description" : "Deployment removed."
          }
        }
      }
    },
    "/deployments/{id}/app-link" : {
      "post" : {
        "tags" : [ "deployments" ],
        "summary" : "Create an app link for a deployment.",
        "description" : "Authenticates the deployment's admin user against the given client and redirect URI (e.g. to jump into an admin portal or IdP wizard already signed in) and returns a single-use link.",
        "operationId" : "deployment.appLink.create",
        "parameters" : [ {
          "name" : "id",
          "in" : "path",
          "description" : "ID of the deployment.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "requestBody" : {
          "description" : "Client and redirect URI to authenticate against.",
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/AppLinkRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ConsoleLink"
                }
              }
            }
          }
        }
      }
    },
    "/deployments/{id}/console-link" : {
      "post" : {
        "tags" : [ "deployments" ],
        "summary" : "Create an admin console link for a deployment.",
        "description" : "Creates a single-use link that signs the caller into this deployment's Keycloak admin console.",
        "operationId" : "deployment.consoleLink.create",
        "parameters" : [ {
          "name" : "id",
          "in" : "path",
          "description" : "ID of the deployment.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ConsoleLink"
                }
              }
            }
          }
        }
      }
    },
    "/deployments/{id}/credentials" : {
      "get" : {
        "tags" : [ "deployments" ],
        "summary" : "List admin credentials for a deployment.",
        "description" : "Lists the credentials created for this deployment, with the `realm-management` roles each one currently holds. Secrets are not included; read one back individually with `deployment.credential.secret.read`.",
        "operationId" : "deployment.credential.list",
        "parameters" : [ {
          "name" : "id",
          "in" : "path",
          "description" : "ID of the deployment.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "description" : "Credentials for the deployment.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "type" : "array",
                  "items" : {
                    "$ref" : "#/components/schemas/DeploymentCredential"
                  }
                }
              }
            }
          }
        }
      },
      "post" : {
        "tags" : [ "deployments" ],
        "summary" : "Create an admin credential for a deployment.",
        "description" : "Creates a service account client on the deployment's realm for administering that realm directly -- with the Keycloak Terraform provider, a provisioning script, an audit integration, or anything else that speaks Keycloak's admin API. Grants the `realm-management` roles given in `roles`, defaulting to `realm-admin`. The secret is included in this response, and can be read again later with `deployment.credential.secret.read`. See the read-back details on that method. Create a separate credential per holder so they can be revoked independently.",
        "operationId" : "deployment.credential.create",
        "parameters" : [ {
          "name" : "id",
          "in" : "path",
          "description" : "ID of the deployment.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "requestBody" : {
          "description" : "Name, note and roles for the new credential. All optional.",
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/DeploymentCredentialRequest"
              }
            }
          }
        },
        "responses" : {
          "200" : {
            "description" : "The new credential, including its secret.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/DeploymentCredential"
                }
              }
            }
          }
        }
      }
    },
    "/deployments/{id}/credentials/{clientId}" : {
      "delete" : {
        "tags" : [ "deployments" ],
        "summary" : "Revoke an admin credential for a deployment.",
        "description" : "Deletes the client from the deployment's realm, immediately invalidating the credential. Only credentials created through this API can be revoked here.",
        "operationId" : "deployment.credential.delete",
        "parameters" : [ {
          "name" : "clientId",
          "in" : "path",
          "description" : "Client ID of the credential to revoke.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "name" : "id",
          "in" : "path",
          "description" : "ID of the deployment.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "204" : {
            "description" : "Credential revoked."
          }
        }
      }
    },
    "/deployments/{id}/credentials/{clientId}/secret" : {
      "get" : {
        "tags" : [ "deployments" ],
        "summary" : "Read an admin credential's secret.",
        "description" : "Returns the credential including its client secret, read from the deployment's realm. Phase Two keeps no copy; this asks the realm, which is where the secret lives. Reading does not rotate it, so a tool can fetch it on each run instead of persisting it -- which for Terraform means keeping it out of `terraform.tfstate`.",
        "operationId" : "deployment.credential.secret.read",
        "parameters" : [ {
          "name" : "clientId",
          "in" : "path",
          "description" : "Client ID of the credential to read.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        }, {
          "name" : "id",
          "in" : "path",
          "description" : "ID of the deployment.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "description" : "The credential, including its secret.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/DeploymentCredential"
                }
              }
            }
          }
        }
      }
    },
    "/deployments/{id}/metrics" : {
      "get" : {
        "tags" : [ "deployments" ],
        "summary" : "Get metrics for a deployment by ID.",
        "operationId" : "deployment.metrics.detail",
        "parameters" : [ {
          "name" : "id",
          "in" : "path",
          "description" : "ID of the deployment.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Metrics"
                }
              }
            }
          }
        }
      }
    },
    "/deployments/{id}/token" : {
      "post" : {
        "tags" : [ "deployments" ],
        "summary" : "Exchange a token for a deployment access token.",
        "description" : "Exchanges the caller's access token for one scoped to this deployment's Keycloak instance, for use when calling that deployment's own admin API directly.",
        "operationId" : "deployment.token.create",
        "parameters" : [ {
          "name" : "id",
          "in" : "path",
          "description" : "ID of the deployment.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/TokenResponse"
                }
              }
            }
          }
        }
      }
    },
    "/orgs" : {
      "get" : {
        "tags" : [ "orgs" ],
        "summary" : "List your organizations.",
        "description" : "Returns the organizations the authenticated API client belongs to, along with the roles held in each.",
        "operationId" : "org.list",
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "type" : "array",
                  "items" : {
                    "$ref" : "#/components/schemas/Org"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/orgs/{orgId}/billing/contacts" : {
      "get" : {
        "tags" : [ "billing" ],
        "summary" : "List billing contacts for this organization.",
        "description" : "Returns the users designated to receive billing-related notifications (e.g. payment failures, upcoming renewals) for this organization.",
        "operationId" : "org.billingContact.list",
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "type" : "array",
                  "items" : {
                    "$ref" : "#/components/schemas/BillingContact"
                  }
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "orgId",
        "in" : "path",
        "description" : "ID of the organization.",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/orgs/{orgId}/billing/contacts/{userId}" : {
      "post" : {
        "tags" : [ "billing" ],
        "summary" : "Add a billing contact.",
        "description" : "Designates an existing user as a billing contact for this organization. The user must already hold the organization's billing management role. Requires manage-billing.",
        "operationId" : "org.billingContact.create",
        "parameters" : [ {
          "name" : "userId",
          "in" : "path",
          "description" : "ID of the user to add as a billing contact.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "204" : {
            "description" : "Billing contact added."
          }
        }
      },
      "delete" : {
        "tags" : [ "billing" ],
        "summary" : "Remove a billing contact.",
        "description" : "Removes a user's billing-contact designation for this organization. Requires manage-billing.",
        "operationId" : "org.billingContact.delete",
        "parameters" : [ {
          "name" : "userId",
          "in" : "path",
          "description" : "ID of the user to remove as a billing contact.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "204" : {
            "description" : "Billing contact removed."
          }
        }
      },
      "parameters" : [ {
        "name" : "orgId",
        "in" : "path",
        "description" : "ID of the organization.",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/orgs/{orgId}/billing/payment-methods" : {
      "get" : {
        "tags" : [ "billing" ],
        "summary" : "List payment methods for this organization.",
        "description" : "Returns the saved card payment methods on this organization's Stripe customer. Each entry indicates whether it is the customer's default and whether it is currently in use by an active subscription (a payment method in use cannot be removed). Empty if the organization has no Stripe customer yet.",
        "operationId" : "org.paymentMethod.list",
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "type" : "array",
                  "items" : {
                    "$ref" : "#/components/schemas/PaymentMethod"
                  }
                }
              }
            }
          }
        }
      },
      "post" : {
        "tags" : [ "billing" ],
        "summary" : "Create a Stripe Checkout link to add a payment method.",
        "description" : "Creates a Stripe Checkout session in setup mode, which collects and saves a card against this organization's Stripe customer without charging it. Redirect the browser to the returned link; Stripe returns the user to your `redirect_uri` with a `payment_method_setup=success` or `payment_method_setup=cancelled` query parameter appended. Requires manage-billing.",
        "operationId" : "org.paymentMethod.setupSession.create",
        "requestBody" : {
          "description" : "Where Stripe should return the user after the setup flow. `redirect_uri` is required.",
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/RedirectRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/RedirectLink"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "orgId",
        "in" : "path",
        "description" : "ID of the organization.",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/orgs/{orgId}/billing/payment-methods/{pmId}" : {
      "delete" : {
        "tags" : [ "billing" ],
        "summary" : "Remove a payment method.",
        "description" : "Detaches a saved card from this organization's Stripe customer. Fails with a 409 if the payment method is in use by an active subscription — switch that subscription to another card first. Requires manage-billing.",
        "operationId" : "org.paymentMethod.delete",
        "parameters" : [ {
          "name" : "pmId",
          "in" : "path",
          "description" : "ID of the payment method.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "204" : {
            "description" : "Payment method removed."
          }
        }
      },
      "parameters" : [ {
        "name" : "orgId",
        "in" : "path",
        "description" : "ID of the organization.",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/orgs/{orgId}/billing/payment-methods/{pmId}/default" : {
      "post" : {
        "tags" : [ "billing" ],
        "summary" : "Set this organization's default payment method.",
        "description" : "Makes the given payment method the default for this organization's future invoices. The payment method must already belong to this organization. Requires manage-billing.",
        "operationId" : "org.paymentMethod.setDefault",
        "parameters" : [ {
          "name" : "pmId",
          "in" : "path",
          "description" : "ID of the payment method.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "204" : {
            "description" : "Default payment method updated."
          }
        }
      },
      "parameters" : [ {
        "name" : "orgId",
        "in" : "path",
        "description" : "ID of the organization.",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/orgs/{orgId}/billing/portal" : {
      "post" : {
        "tags" : [ "billing" ],
        "summary" : "Create a Stripe billing portal session link.",
        "description" : "Creates a one-time link to the organization's Stripe billing portal, where the organization's billing contacts can update payment methods, view invoices, and manage subscriptions. The link is single-use and expires shortly after creation. Requires manage-billing.",
        "operationId" : "org.billingPortalSession.create",
        "requestBody" : {
          "description" : "Optional redirect URI Stripe returns the user to after they leave the billing portal.",
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/RedirectRequest"
              }
            }
          }
        },
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/RedirectLink"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "orgId",
        "in" : "path",
        "description" : "ID of the organization.",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/orgs/{orgId}/billing/subscriptions" : {
      "get" : {
        "tags" : [ "billing" ],
        "summary" : "List subscriptions for this organization.",
        "description" : "Returns the Stripe subscriptions billed to this organization. Empty if the organization has no Stripe customer yet.",
        "operationId" : "org.subscription.list",
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "type" : "array",
                  "items" : {
                    "$ref" : "#/components/schemas/Subscription"
                  }
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "orgId",
        "in" : "path",
        "description" : "ID of the organization.",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/orgs/{orgId}/secrets" : {
      "get" : {
        "tags" : [ "orgs" ],
        "summary" : "List API secrets for this organization.",
        "description" : "Returns the API secrets (OIDC client-credentials clients) created for this organization. Client secrets are never returned after creation; this listing always masks them.",
        "operationId" : "org.apiSecret.list",
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "type" : "array",
                  "items" : {
                    "$ref" : "#/components/schemas/Secret"
                  }
                }
              }
            }
          }
        }
      },
      "post" : {
        "tags" : [ "orgs" ],
        "summary" : "Create an API secret for this organization.",
        "description" : "Creates a new OIDC client-credentials client for this organization, with the alias and roles you specify. Use the returned client ID and client secret with the OAuth2 client credentials grant to obtain access tokens for this API — see the API's top-level authentication description. The client secret is returned only in this response and cannot be retrieved again; store it securely. Each organization may have at most 10 API secrets.",
        "operationId" : "org.apiSecret.create",
        "requestBody" : {
          "description" : "Alias and roles for the new API secret.",
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/SecretRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Secret"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "orgId",
        "in" : "path",
        "description" : "ID of the organization.",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/orgs/{orgId}/secrets/count" : {
      "get" : {
        "tags" : [ "orgs" ],
        "summary" : "Count API secrets for this organization.",
        "description" : "Returns how many API secrets this organization currently has, for comparing against the per-organization maximum without fetching the full listing.",
        "operationId" : "org.apiSecret.count",
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "format" : "int64",
                  "type" : "integer"
                }
              }
            }
          }
        }
      },
      "parameters" : [ {
        "name" : "orgId",
        "in" : "path",
        "description" : "ID of the organization.",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    },
    "/orgs/{orgId}/secrets/{secretId}" : {
      "get" : {
        "tags" : [ "orgs" ],
        "summary" : "Get an API secret.",
        "description" : "Returns a single API secret for this organization. As with the listing, the client secret is always masked.",
        "operationId" : "org.apiSecret.detail",
        "parameters" : [ {
          "name" : "secretId",
          "in" : "path",
          "description" : "ID of the API secret.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "200" : {
            "description" : "OK",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Secret"
                }
              }
            }
          }
        }
      },
      "delete" : {
        "tags" : [ "orgs" ],
        "summary" : "Remove an API secret.",
        "description" : "Deletes an API secret for this organization. Any access tokens already issued to it remain valid until they expire; new tokens can no longer be requested with it.",
        "operationId" : "org.apiSecret.delete",
        "parameters" : [ {
          "name" : "secretId",
          "in" : "path",
          "description" : "ID of the API secret.",
          "required" : true,
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "204" : {
            "description" : "API secret removed."
          }
        }
      },
      "parameters" : [ {
        "name" : "orgId",
        "in" : "path",
        "description" : "ID of the organization.",
        "required" : true,
        "schema" : {
          "type" : "string"
        }
      } ]
    }
  },
  "components" : {
    "schemas" : {
      "AdminBlockExtensionVersionRequest" : {
        "required" : [ "blocked" ],
        "type" : "object",
        "properties" : {
          "blocked" : {
            "description" : "True to block this version from being deployed to any cluster, false to lift an existing block.",
            "type" : "boolean"
          },
          "reason" : {
            "description" : "Why this version is being blocked. Required when `blocked` is true; ignored otherwise.",
            "type" : "string"
          }
        }
      },
      "AppLinkRequest" : {
        "required" : [ "client_id", "redirect_uri" ],
        "type" : "object",
        "properties" : {
          "client_id" : {
            "description" : "Client ID the generated link authenticates the deployment's admin user against.",
            "type" : "string"
          },
          "redirect_uri" : {
            "description" : "URI to redirect to after login. Must be a valid redirect URI for the given client.",
            "type" : "string"
          }
        }
      },
      "BillingContact" : {
        "required" : [ "id", "username" ],
        "type" : "object",
        "properties" : {
          "id" : {
            "description" : "Unique identifier of the user.",
            "type" : "string"
          },
          "username" : {
            "description" : "Username of the billing contact.",
            "type" : "string"
          },
          "email" : {
            "description" : "Email address billing notifications are sent to.",
            "type" : "string"
          },
          "firstName" : {
            "description" : "Given name of the billing contact, when set.",
            "type" : "string"
          },
          "lastName" : {
            "description" : "Family name of the billing contact, when set.",
            "type" : "string"
          }
        }
      },
      "BillingPeriod" : {
        "enum" : [ "monthly", "annual" ],
        "type" : "string"
      },
      "CertificateStatusMapping" : {
        "description" : "TLS certificate status of a custom domain.",
        "enum" : [ "PENDING_VALIDATION", "ISSUED", "INACTIVE", "EXPIRED", "VALIDATION_TIMED_OUT", "REVOKED", "FAILED", "NOT_FOUND" ],
        "type" : "string"
      },
      "Cluster" : {
        "required" : [ "id", "name", "host", "region", "status", "tier", "resource_limits", "created_at" ],
        "type" : "object",
        "properties" : {
          "id" : {
            "description" : "Unique identifier of the cluster.",
            "type" : "string"
          },
          "name" : {
            "description" : "Cluster name, unique across Phase Two.",
            "type" : "string"
          },
          "host" : {
            "description" : "Base URL of the cluster's Keycloak instance.",
            "type" : "string"
          },
          "region" : {
            "description" : "Region the cluster is provisioned in.",
            "required" : [ "name", "provider", "region" ],
            "type" : "object",
            "properties" : {
              "name" : {
                "description" : "Region identifier, usable as the `region` argument when creating a cluster.",
                "type" : "string"
              },
              "provider" : {
                "description" : "Cloud provider hosting this region.",
                "type" : "string"
              },
              "region" : {
                "description" : "Provider-specific region code.",
                "type" : "string"
              }
            }
          },
          "domain" : {
            "description" : "Custom domain recorded at provision time, when one was given. This is not the live hostname — use `cluster.host.update` to change which domain the cluster serves.",
            "type" : "string"
          },
          "status" : {
            "description" : "Lifecycle state. A cluster is only usable in `ACTIVE`; `PENDING_DELETION` and `ARCHIVED` are terminal.",
            "type" : "string",
            "allOf" : [ {
              "$ref" : "#/components/schemas/ClusterState"
            } ]
          },
          "owner" : {
            "description" : "ID of the organization that owns the cluster.",
            "type" : "string"
          },
          "variant" : {
            "description" : "`DEDICATED` when the cluster has an owning organization, `SHARED` otherwise.",
            "type" : "string"
          },
          "tier" : {
            "description" : "Subscription tier, which determines the cluster's resource limits.",
            "type" : "string",
            "allOf" : [ {
              "$ref" : "#/components/schemas/Tier"
            } ]
          },
          "resource_limits" : {
            "description" : "`STANDARD`, or `CUSTOM` when the cluster has been exempted from tier count limits.",
            "type" : "string",
            "allOf" : [ {
              "$ref" : "#/components/schemas/ResourceLimits"
            } ]
          },
          "created_at" : {
            "format" : "int64",
            "description" : "When the cluster was created, as epoch milliseconds.",
            "type" : "integer"
          }
        }
      },
      "ClusterCreationResult" : {
        "required" : [ "requires_action" ],
        "type" : "object",
        "properties" : {
          "link" : {
            "description" : "Stripe Checkout URL to redirect the browser to. Present when no `payment_method_id` was supplied.",
            "type" : "object",
            "allOf" : [ {
              "$ref" : "#/components/schemas/RedirectLink"
            } ]
          },
          "cluster" : {
            "description" : "The created cluster. Present when an existing payment method was charged successfully.",
            "type" : "object",
            "allOf" : [ {
              "$ref" : "#/components/schemas/Cluster"
            } ]
          },
          "requires_action" : {
            "description" : "True when the payment method needs additional authentication (3DS/SCA) before the cluster can provision. Complete `client_secret` inline with Stripe.js.",
            "type" : "boolean"
          },
          "client_secret" : {
            "description" : "Stripe PaymentIntent client secret to confirm inline. Present only when `requires_action` is true.",
            "type" : "string"
          },
          "cluster_id" : {
            "description" : "ID of the already-created cluster awaiting payment confirmation. Present only when `requires_action` is true.",
            "type" : "string"
          }
        }
      },
      "ClusterDeploymentRequest" : {
        "required" : [ "name" ],
        "type" : "object",
        "properties" : {
          "name" : {
            "description" : "Name for the deployment (becomes the Keycloak realm name). Max 255 characters and must not be a reserved name; lowercased automatically.",
            "type" : "string"
          }
        }
      },
      "ClusterDomain" : {
        "required" : [ "host" ],
        "type" : "object",
        "properties" : {
          "host" : {
            "description" : "The custom hostname, e.g. `auth.example.com`.",
            "type" : "string"
          }
        }
      },
      "ClusterState" : {
        "description" : "Lifecycle state of a dedicated cluster.",
        "enum" : [ "BILLING_SETUP", "PENDING_PAYMENT", "PROVISIONING", "SETUP_EXCEPTION", "ACTIVE", "DISABLED", "BILLING_REQUIRED", "PENDING_DELETION", "ARCHIVED" ],
        "type" : "string"
      },
      "ClusterSystemInfoRepresentation" : {
        "type" : "object",
        "properties" : {
          "version" : {
            "type" : "string"
          },
          "serverTime" : {
            "type" : "string"
          },
          "uptime" : {
            "type" : "string"
          },
          "uptimeMillis" : {
            "format" : "int64",
            "type" : "integer"
          },
          "javaVersion" : {
            "type" : "string"
          },
          "javaVendor" : {
            "type" : "string"
          },
          "javaVm" : {
            "type" : "string"
          },
          "javaVmVersion" : {
            "type" : "string"
          },
          "javaRuntime" : {
            "type" : "string"
          },
          "javaHome" : {
            "type" : "string"
          },
          "osName" : {
            "type" : "string"
          },
          "osArchitecture" : {
            "type" : "string"
          },
          "osVersion" : {
            "type" : "string"
          },
          "fileEncoding" : {
            "type" : "string"
          },
          "userName" : {
            "type" : "string"
          },
          "userDir" : {
            "type" : "string"
          },
          "userTimezone" : {
            "type" : "string"
          },
          "userLocale" : {
            "type" : "string"
          },
          "buildTag" : {
            "description" : "This cluster's Phase Two release build tag (e.g. `26.6.6.1787589931`), or `untagged` for a pre-phasetwo-containers#176 image. Null if the cluster's serverinfo couldn't be fetched.",
            "type" : "string"
          }
        }
      },
      "ConsoleLink" : {
        "required" : [ "user_id", "link", "expiration_seconds" ],
        "type" : "object",
        "properties" : {
          "user_id" : {
            "description" : "ID of the user the link authenticates as.",
            "type" : "string"
          },
          "link" : {
            "description" : "Single-use login URL.",
            "type" : "string"
          },
          "expiration_seconds" : {
            "format" : "int32",
            "description" : "How long the link remains valid, in seconds.",
            "type" : "integer"
          }
        }
      },
      "CustomerDomain" : {
        "description" : "A custom hostname configured for a dedicated cluster.",
        "required" : [ "id", "type", "domain" ],
        "type" : "object",
        "properties" : {
          "id" : {
            "description" : "Unique identifier of the custom domain.",
            "type" : "string"
          },
          "type" : {
            "description" : "What the hostname is used for.",
            "type" : "string",
            "allOf" : [ {
              "$ref" : "#/components/schemas/DomainType"
            } ]
          },
          "domain" : {
            "description" : "The custom hostname, e.g. `auth.example.com`.",
            "type" : "string"
          },
          "organizationId" : {
            "description" : "ID of the organization that owns the cluster.",
            "type" : "string"
          },
          "createdAt" : {
            "format" : "int64",
            "description" : "When the domain was added, as epoch milliseconds.",
            "type" : "integer"
          },
          "domainRecords" : {
            "description" : "DNS records to create in order to validate the domain and issue its certificate. Empty once validation has completed.",
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/DomainRecord"
            }
          }
        }
      },
      "CustomerDomainValidation" : {
        "description" : "DNS validation and TLS certificate status of a custom domain. The domain is only usable as a cluster's primary hostname once `certificateStatus` is `ISSUED`.",
        "required" : [ "status", "certificateStatus", "domain" ],
        "type" : "object",
        "properties" : {
          "status" : {
            "description" : "DNS validation status.",
            "type" : "string",
            "allOf" : [ {
              "$ref" : "#/components/schemas/DomainStatusMapping"
            } ]
          },
          "certificateStatus" : {
            "description" : "TLS certificate status.",
            "type" : "string",
            "allOf" : [ {
              "$ref" : "#/components/schemas/CertificateStatusMapping"
            } ]
          },
          "domain" : {
            "description" : "The domain, including the DNS records still to create if validation is pending.",
            "type" : "object",
            "allOf" : [ {
              "$ref" : "#/components/schemas/CustomerDomain"
            } ]
          }
        }
      },
      "DedicatedClusterRequest" : {
        "required" : [ "name", "region", "org_id" ],
        "type" : "object",
        "properties" : {
          "name" : {
            "description" : "Name for the cluster. Max 63 characters, lowercase letters only, not a reserved name, and unique across Phase Two. Forms the default hostname `<name>.global.auth.ac`.",
            "type" : "string"
          },
          "region" : {
            "description" : "Region to provision the cluster in. Must be one of the values returned by `cluster.region.list`.",
            "type" : "string"
          },
          "domain" : {
            "description" : "Custom domain to associate with the cluster at creation. Must be a valid domain name.",
            "type" : "string"
          },
          "price_id" : {
            "description" : "Deprecated. Explicit Stripe price ID. When set, `tier` and `billing_period` are not required.",
            "type" : "string",
            "deprecated" : true
          },
          "tier" : {
            "description" : "Subscription tier, which determines the cluster's resource limits. Required unless `price_id` is set.",
            "type" : "string",
            "allOf" : [ {
              "$ref" : "#/components/schemas/Tier"
            } ]
          },
          "billing_period" : {
            "description" : "Billing period. Required unless `price_id` is set. Starter is only available monthly.",
            "type" : "string",
            "allOf" : [ {
              "$ref" : "#/components/schemas/BillingPeriod"
            } ]
          },
          "org_id" : {
            "description" : "ID of the organization that will own and be billed for the cluster.",
            "type" : "string"
          },
          "payment_method_id" : {
            "description" : "ID of an existing payment method on the organization's Stripe customer to charge immediately. When omitted, the response instead returns a Stripe Checkout link.",
            "type" : "string"
          }
        }
      },
      "Deployment" : {
        "required" : [ "id", "name", "state" ],
        "type" : "object",
        "properties" : {
          "id" : {
            "description" : "Unique identifier of the deployment.",
            "type" : "string"
          },
          "name" : {
            "description" : "Deployment name, which is also its Keycloak realm name.",
            "type" : "string"
          },
          "display_name" : {
            "description" : "Human-readable deployment name, when one is set.",
            "type" : "string"
          },
          "org_id" : {
            "description" : "ID of the organization that owns the deployment.",
            "type" : "string"
          },
          "region" : {
            "description" : "Region of the cluster hosting this deployment.",
            "type" : "string"
          },
          "state" : {
            "description" : "Lifecycle state. A newly created or imported deployment starts in `PENDING`; poll until it reaches `ACTIVE`, or read `stateError` if it lands in `FAILED`.",
            "type" : "string",
            "allOf" : [ {
              "$ref" : "#/components/schemas/DeploymentState"
            } ]
          },
          "cluster" : {
            "description" : "The cluster this deployment runs on.",
            "type" : "object",
            "allOf" : [ {
              "$ref" : "#/components/schemas/Cluster"
            } ]
          },
          "created_by_user_id" : {
            "description" : "ID of the user who created the deployment.",
            "type" : "string"
          },
          "created_at" : {
            "format" : "int64",
            "description" : "When the deployment was created, as epoch milliseconds.",
            "type" : "integer"
          },
          "tags" : {
            "description" : "Tags applied to the deployment.",
            "type" : "array",
            "items" : {
              "type" : "string"
            }
          },
          "stateError" : {
            "description" : "Why the deployment last failed, when it is in a failed state.",
            "type" : "string"
          }
        }
      },
      "DeploymentCredential" : {
        "required" : [ "client_id", "roles", "server_url", "realm" ],
        "type" : "object",
        "properties" : {
          "client_id" : {
            "description" : "Client ID to authenticate with.",
            "type" : "string"
          },
          "client_secret" : {
            "description" : "Client secret to authenticate with. Returned on create and by `deployment.credential.secret.read`, which reads it from the realm rather than from us -- Phase Two keeps no copy. Reading does not rotate it, so prefer fetching it when needed over storing it.",
            "type" : "string"
          },
          "name" : {
            "description" : "Name this credential was created with, echoed back from the client ID.",
            "type" : "string"
          },
          "description" : {
            "description" : "Free-text note recorded on the client, to tell credentials apart.",
            "type" : "string"
          },
          "roles" : {
            "description" : "`realm-management` roles granted to this credential. On create this is what was actually granted, which is worth checking against what you asked for.",
            "type" : "array",
            "items" : {
              "type" : "string"
            }
          },
          "server_url" : {
            "description" : "Base URL of the deployment's Keycloak instance.",
            "type" : "string"
          },
          "realm" : {
            "description" : "Realm this credential administers.",
            "type" : "string"
          }
        }
      },
      "DeploymentCredentialRequest" : {
        "type" : "object",
        "properties" : {
          "name" : {
            "description" : "Short name for this credential, included in the generated client ID so it is recognisable in the realm's client list, e.g. `terraform` produces `api-terraform-9f3c1a2b`. Lower-case letters, digits and hyphens, up to 48 characters. Optional.",
            "type" : "string"
          },
          "description" : {
            "description" : "Free-text note recorded on the client so credentials can be told apart later, e.g. which pipeline or workstation holds it. Optional, but revoking the right one later is much easier with it.",
            "type" : "string"
          },
          "roles" : {
            "description" : "`realm-management` client roles to grant, e.g. `[\"view-users\", \"view-realm\"]`. Defaults to `[\"realm-admin\"]`, which is full administrative access to the realm and what the Keycloak Terraform provider generally needs. Narrow this where you can: a credential that only reads should only be able to read. Roles that do not exist are rejected rather than quietly skipped.",
            "type" : "array",
            "items" : {
              "type" : "string"
            }
          }
        }
      },
      "DeploymentState" : {
        "description" : "Lifecycle state of a deployment (realm).",
        "enum" : [ "PENDING", "ACTIVE", "DISABLED", "FAILED" ],
        "type" : "string"
      },
      "DomainRecord" : {
        "description" : "A single DNS record to create for domain validation.",
        "required" : [ "type", "name", "value" ],
        "type" : "object",
        "properties" : {
          "type" : {
            "description" : "Record type.",
            "type" : "string",
            "allOf" : [ {
              "$ref" : "#/components/schemas/DomainRecordType"
            } ]
          },
          "name" : {
            "description" : "Record name (the left-hand side).",
            "type" : "string"
          },
          "value" : {
            "description" : "Record value (the right-hand side).",
            "type" : "string"
          }
        }
      },
      "DomainRecordType" : {
        "description" : "DNS record type.",
        "enum" : [ "A", "AAAA", "CNAME", "MX", "NS", "SRV", "TXT" ],
        "type" : "string"
      },
      "DomainStatusMapping" : {
        "description" : "DNS validation status of a custom domain.",
        "enum" : [ "PENDING_VALIDATION", "SUCCESS", "FAILED", "NOT_FOUND" ],
        "type" : "string"
      },
      "DomainType" : {
        "description" : "What a custom hostname is used for.",
        "enum" : [ "WEB", "MAIL" ],
        "type" : "string"
      },
      "EndpointType" : {
        "description" : "Which endpoints an IP rule governs. `ADMIN` covers the cluster's admin console; `REALMS` covers its realm endpoints.",
        "enum" : [ "ADMIN", "REALMS" ],
        "type" : "string"
      },
      "EnvironmentVariableRepresentation" : {
        "description" : "A custom environment variable set on a cluster's Keycloak deployment.",
        "required" : [ "id", "name", "value", "type" ],
        "type" : "object",
        "properties" : {
          "id" : {
            "description" : "Unique identifier of the environment variable.",
            "type" : "string"
          },
          "name" : {
            "description" : "Variable name.",
            "type" : "string"
          },
          "value" : {
            "description" : "Variable value. Returned in full only when `type` is `STRING`; a `SECRET` value is always masked on read and can be set but never retrieved.",
            "type" : "string"
          },
          "type" : {
            "description" : "How the value is stored.",
            "type" : "string",
            "allOf" : [ {
              "$ref" : "#/components/schemas/EnvironmentVariableType"
            } ]
          }
        }
      },
      "EnvironmentVariableRequest" : {
        "required" : [ "name", "value" ],
        "type" : "object",
        "properties" : {
          "name" : {
            "description" : "Variable name. Must not start with `KC_` unless it starts with `KC_SPI_` — only custom SPI configuration may be set.",
            "type" : "string"
          },
          "value" : {
            "description" : "Variable value. Stored in AWS Parameter Store rather than on the cluster record.",
            "type" : "string"
          },
          "type" : {
            "description" : "How the value is stored — as plain text or as a secret.",
            "type" : "string",
            "allOf" : [ {
              "$ref" : "#/components/schemas/EnvironmentVariableType"
            } ]
          }
        }
      },
      "EnvironmentVariableType" : {
        "description" : "How the variable's value is stored. `STRING` is a plain value; `SECRET` is held in AWS Parameter Store as an encrypted parameter.",
        "enum" : [ "STRING", "SECRET" ],
        "type" : "string"
      },
      "Extension" : {
        "required" : [ "id", "name", "enabled", "resourceType" ],
        "type" : "object",
        "properties" : {
          "id" : {
            "description" : "Unique identifier of the extension.",
            "type" : "string"
          },
          "name" : {
            "description" : "Extension name, unique within the cluster.",
            "type" : "string"
          },
          "enabled" : {
            "description" : "Whether the extension is active on the cluster.",
            "type" : "boolean"
          },
          "created_at" : {
            "format" : "int64",
            "description" : "When the extension was created, as epoch milliseconds.",
            "type" : "integer"
          },
          "versions" : {
            "description" : "Uploaded versions of this extension. A version-independent extension has at most one.",
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/ExtensionVersion"
            }
          },
          "resourceType" : {
            "description" : "What kind of resource this is — determines whether versions are Keycloak-version-dependent.",
            "type" : "string",
            "allOf" : [ {
              "$ref" : "#/components/schemas/ResourceType"
            } ]
          }
        }
      },
      "ExtensionRequest" : {
        "required" : [ "name", "resourceType" ],
        "type" : "object",
        "properties" : {
          "name" : {
            "description" : "Name for the extension. Max 63 characters, lowercase letters only, no special characters.",
            "type" : "string"
          },
          "resourceType" : {
            "description" : "What kind of resource this is — determines whether versions are Keycloak-version-dependent.",
            "type" : "string",
            "allOf" : [ {
              "$ref" : "#/components/schemas/ResourceType"
            } ]
          }
        }
      },
      "ExtensionVersion" : {
        "required" : [ "id", "valid", "scan_state" ],
        "type" : "object",
        "properties" : {
          "id" : {
            "description" : "Unique identifier of the extension version.",
            "type" : "string"
          },
          "valid" : {
            "description" : "Whether this version is approved for deployment. This is the human approval decision, not the scanner's verdict — see `scan_state`.",
            "type" : "boolean"
          },
          "label" : {
            "description" : "Label recorded when the jar was uploaded.",
            "type" : "string"
          },
          "resourceKey" : {
            "description" : "S3 object key of the uploaded jar. Absent until an upload is confirmed.",
            "type" : "string"
          },
          "keycloakMajorVersion" : {
            "format" : "int32",
            "description" : "Keycloak major version this version targets. Absent for version-independent extensions.",
            "type" : "integer"
          },
          "scan_state" : {
            "description" : "Result of the security scan: `UNSCANNED`, `SCANNING`, `PASS`, `WARN`, `BLOCK` or `ERROR`. Never absent.",
            "type" : "string"
          },
          "risk_score" : {
            "format" : "int32",
            "description" : "Risk score from 0-100 reported by the scan. Absent until a scan has completed.",
            "type" : "integer"
          },
          "report_location" : {
            "description" : "Location of the full scan report, when one exists.",
            "type" : "string"
          },
          "scanned_at" : {
            "format" : "int64",
            "description" : "When the version was last scanned, as epoch milliseconds.",
            "type" : "integer"
          },
          "scanner_version" : {
            "description" : "Version of the scanner that produced the result.",
            "type" : "string"
          },
          "override_by" : {
            "description" : "ID of the user who approved this version despite the scan not passing. Cleared when the approval is withdrawn.",
            "type" : "string"
          },
          "override_reason" : {
            "description" : "Justification recorded with that approval.",
            "type" : "string"
          },
          "admin_blocked" : {
            "description" : "Whether Phase Two staff have blocked this version. A blocked version is never copied to the cluster, regardless of its `valid` flag or scan state.",
            "type" : "boolean"
          },
          "admin_blocked_by" : {
            "description" : "ID of the staff user who blocked it; absent when not blocked.",
            "type" : "string"
          },
          "admin_blocked_reason" : {
            "description" : "Why it was blocked; absent when not blocked.",
            "type" : "string"
          },
          "admin_blocked_at" : {
            "format" : "int64",
            "description" : "When it was blocked, as epoch milliseconds; absent when not blocked.",
            "type" : "integer"
          },
          "created_at" : {
            "format" : "int64",
            "description" : "When the version was created, as epoch milliseconds.",
            "type" : "integer"
          }
        }
      },
      "ExtensionVersionLocationValidationRequest" : {
        "required" : [ "resource_key" ],
        "type" : "object",
        "properties" : {
          "resource_key" : {
            "description" : "S3 object key the jar was uploaded to, as returned when the upload URL was created.",
            "type" : "string"
          }
        }
      },
      "ExtensionVersionRequest" : {
        "required" : [ "keycloak_major_version" ],
        "type" : "object",
        "properties" : {
          "keycloak_major_version" : {
            "format" : "int32",
            "description" : "Keycloak major version this version targets. Must be one of the values returned by `cluster.extension.keycloakVersion.list`.",
            "type" : "integer"
          }
        }
      },
      "ExtensionVersionUploadUrlRequest" : {
        "type" : "object",
        "properties" : {
          "label" : {
            "description" : "Human-readable label recorded against the uploaded version, e.g. a release tag.",
            "type" : "string"
          }
        }
      },
      "IpRestrictionsRequest" : {
        "type" : "object",
        "properties" : {
          "adminAllowedIpRules" : {
            "description" : "Addresses allowed to reach the cluster's admin endpoints. Replaces the existing admin-allow list entirely; omit to leave it unchanged.",
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/IpRule"
            }
          },
          "realmAllowedIpRules" : {
            "description" : "Addresses allowed to reach the cluster's realm endpoints. Replaces the existing realm-allow list entirely; omit to leave it unchanged.",
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/IpRule"
            }
          },
          "realmBlockedIpRules" : {
            "description" : "Addresses blocked from reaching the cluster's realm endpoints. Replaces the existing realm-block list entirely; omit to leave it unchanged.",
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/IpRule"
            }
          }
        }
      },
      "IpRule" : {
        "required" : [ "alias", "address" ],
        "type" : "object",
        "properties" : {
          "alias" : {
            "description" : "Human-readable label for the rule.",
            "type" : "string"
          },
          "address" : {
            "description" : "IPv4 address or CIDR block, e.g. `203.0.113.4/32`.",
            "type" : "string"
          }
        }
      },
      "IpRuleRepresentation" : {
        "description" : "A single IP allow/deny rule on a cluster.",
        "required" : [ "id", "alias", "address", "endpointType", "allowDeny" ],
        "type" : "object",
        "properties" : {
          "id" : {
            "description" : "Unique identifier of the rule.",
            "type" : "string"
          },
          "alias" : {
            "description" : "Human-readable label for the rule.",
            "type" : "string"
          },
          "address" : {
            "description" : "IP address or CIDR block the rule matches.",
            "type" : "string"
          },
          "endpointType" : {
            "description" : "Which endpoints the rule governs.",
            "type" : "string",
            "allOf" : [ {
              "$ref" : "#/components/schemas/EndpointType"
            } ]
          },
          "allowDeny" : {
            "description" : "`true` when the rule allows the address, `false` when it blocks it.",
            "type" : "boolean"
          },
          "evaluationOrder" : {
            "format" : "int32",
            "description" : "Order in which the rule is evaluated within its category.",
            "type" : "integer"
          },
          "createdAt" : {
            "format" : "int64",
            "description" : "When the rule was created, as epoch milliseconds.",
            "type" : "integer"
          }
        }
      },
      "IpRulesRepresentation" : {
        "description" : "A cluster's IP allow/deny rules, grouped by category.",
        "required" : [ "adminAllowedIpRules", "realmAllowedIpRules", "realmBlockedIpRules" ],
        "type" : "object",
        "properties" : {
          "adminAllowedIpRules" : {
            "description" : "Addresses allowed to reach the admin console.",
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/IpRuleRepresentation"
            }
          },
          "realmAllowedIpRules" : {
            "description" : "Addresses allowed to reach the realm endpoints.",
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/IpRuleRepresentation"
            }
          },
          "realmBlockedIpRules" : {
            "description" : "Addresses blocked from reaching the realm endpoints.",
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/IpRuleRepresentation"
            }
          }
        }
      },
      "LogFile" : {
        "required" : [ "file_name" ],
        "type" : "object",
        "properties" : {
          "file_name" : {
            "description" : "Path of the log file. Pass this to `cluster.log.detail` to obtain a download URL.",
            "type" : "string"
          },
          "last_modified" : {
            "format" : "int64",
            "description" : "When the log file was last written, as epoch milliseconds.",
            "type" : "integer"
          },
          "size" : {
            "format" : "int64",
            "description" : "Size of the log file in bytes.",
            "type" : "integer"
          },
          "temp_url" : {
            "description" : "Presigned download URL. Only populated by `cluster.log.detail`, not by the listing.",
            "type" : "string"
          }
        }
      },
      "Metrics" : {
        "required" : [ "name", "healthy" ],
        "type" : "object",
        "properties" : {
          "name" : {
            "description" : "Name of the cluster or deployment these metrics describe.",
            "type" : "string"
          },
          "counts" : {
            "description" : "Named counters, e.g. users and clients. May be empty.",
            "type" : "object",
            "additionalProperties" : {
              "format" : "int64",
              "type" : "integer"
            }
          },
          "healthy" : {
            "description" : "Whether the target is currently considered healthy.",
            "type" : "boolean"
          }
        }
      },
      "NameAvailability" : {
        "required" : [ "name", "available" ],
        "type" : "object",
        "properties" : {
          "name" : {
            "description" : "The name that was checked, lowercased.",
            "type" : "string"
          },
          "available" : {
            "description" : "Whether the name is well-formed, not reserved, and not already taken.",
            "type" : "boolean"
          },
          "recommendation" : {
            "description" : "An available alternative, when one is suggested.",
            "type" : "string"
          }
        }
      },
      "Org" : {
        "required" : [ "id", "name", "roles" ],
        "type" : "object",
        "properties" : {
          "id" : {
            "description" : "Unique identifier of the organization.",
            "type" : "string"
          },
          "name" : {
            "description" : "Organization name, unique within the realm.",
            "type" : "string"
          },
          "display_name" : {
            "description" : "Human-readable organization name, when one is set.",
            "type" : "string"
          },
          "roles" : {
            "description" : "Roles the authenticated caller holds in this organization.",
            "type" : "array",
            "items" : {
              "type" : "string"
            }
          }
        }
      },
      "PaymentMethod" : {
        "required" : [ "id", "type", "is_default", "in_use_by_active_subscription" ],
        "type" : "object",
        "properties" : {
          "id" : {
            "description" : "Stripe payment method ID. Pass this as `payment_method_id` when creating a cluster.",
            "type" : "string"
          },
          "type" : {
            "description" : "Payment method type. Only `card` is currently supported.",
            "type" : "string"
          },
          "brand" : {
            "description" : "Card brand, e.g. `visa`, `mastercard`.",
            "type" : "string"
          },
          "last4" : {
            "description" : "Last four digits of the card number.",
            "type" : "string"
          },
          "exp_month" : {
            "format" : "int64",
            "description" : "Card expiry month, 1-12.",
            "type" : "integer"
          },
          "exp_year" : {
            "format" : "int64",
            "description" : "Card expiry year, four digits.",
            "type" : "integer"
          },
          "is_default" : {
            "description" : "Whether this is the customer's default payment method for future invoices.",
            "type" : "boolean"
          },
          "in_use_by_active_subscription" : {
            "description" : "Whether an active subscription is currently billed to this payment method. Such a payment method cannot be removed until the subscription is moved to another card.",
            "type" : "boolean"
          },
          "in_use_by" : {
            "description" : "Names of the clusters whose active subscription is billed to this payment method. May be empty even when in_use_by_active_subscription is true, if the subscription isn't tied to a cluster.",
            "type" : "array",
            "items" : {
              "type" : "string"
            }
          }
        }
      },
      "PresignedUrl" : {
        "description" : "A short-lived, presigned S3 target to upload an extension jar to.",
        "required" : [ "url", "method", "resourceKey" ],
        "type" : "object",
        "properties" : {
          "url" : {
            "description" : "Presigned URL to upload the jar to.",
            "type" : "string"
          },
          "method" : {
            "description" : "HTTP method to use for the upload, e.g. `PUT`.",
            "type" : "string"
          },
          "resourceKey" : {
            "description" : "S3 resource key the jar will land at. Pass this back to the matching `confirm` operation.",
            "type" : "string"
          }
        }
      },
      "RedirectLink" : {
        "required" : [ "link" ],
        "type" : "object",
        "properties" : {
          "message" : {
            "description" : "Human-readable message to show while redirecting.",
            "type" : "string"
          },
          "link" : {
            "description" : "URL to redirect the browser to. Single-use and short-lived.",
            "type" : "string"
          }
        }
      },
      "RedirectRequest" : {
        "type" : "object",
        "properties" : {
          "redirect_uri" : {
            "description" : "URI Stripe returns the user to when they leave the hosted flow.",
            "type" : "string"
          }
        }
      },
      "Region" : {
        "description" : "A region a cluster can be provisioned in.",
        "required" : [ "name", "provider", "region" ],
        "type" : "object",
        "properties" : {
          "name" : {
            "description" : "Region identifier, usable as the `region` argument when creating a cluster.",
            "type" : "string"
          },
          "provider" : {
            "description" : "Cloud provider hosting this region.",
            "type" : "string"
          },
          "region" : {
            "description" : "Provider-specific region code.",
            "type" : "string"
          }
        }
      },
      "ResourceLimits" : {
        "enum" : [ "standard", "custom" ],
        "type" : "string"
      },
      "ResourceType" : {
        "description" : "Kind of custom resource an extension holds. `THEME` and `EXTENSION` are tied to a Keycloak major version and use the versioned upload flow; `PASSWORD_BLACKLIST` and `WELL_KNOWN` are version-independent and use the standalone upload flow.",
        "enum" : [ "THEME", "EXTENSION", "PASSWORD_BLACKLIST", "WELL_KNOWN" ],
        "type" : "string"
      },
      "RestartStatus" : {
        "required" : [ "restart_in_progress" ],
        "type" : "object",
        "properties" : {
          "restart_in_progress" : {
            "description" : "Whether a restart workflow is currently running for this cluster. Configuration changes are rejected with a 409 while true.",
            "type" : "boolean"
          }
        }
      },
      "ResumeCheckoutRequest" : {
        "type" : "object",
        "properties" : {
          "price_id" : {
            "description" : "Deprecated. Explicit Stripe price ID, overriding the price derived from the cluster's stored tier.",
            "type" : "string",
            "deprecated" : true
          },
          "billing_period" : {
            "description" : "Billing period for premium/enterprise clusters. Ignored for starter, which is monthly-only. Defaults to monthly.",
            "type" : "string",
            "allOf" : [ {
              "$ref" : "#/components/schemas/BillingPeriod"
            } ]
          }
        }
      },
      "Secret" : {
        "required" : [ "id", "name", "client_id", "client_secret", "roles" ],
        "type" : "object",
        "properties" : {
          "id" : {
            "description" : "Unique identifier of the API secret.",
            "type" : "string"
          },
          "name" : {
            "description" : "Name given to the API secret when it was created.",
            "type" : "string"
          },
          "client_id" : {
            "description" : "OAuth2 client ID to use with the client credentials grant.",
            "type" : "string"
          },
          "client_secret" : {
            "description" : "OAuth2 client secret. Returned in full only when the secret is first created; always masked as `********` on subsequent reads.",
            "type" : "string"
          },
          "roles" : {
            "description" : "Organization roles granted to this secret's service account.",
            "type" : "array",
            "items" : {
              "type" : "string"
            }
          },
          "created_at" : {
            "format" : "int64",
            "type" : "integer"
          },
          "created_by" : {
            "type" : "string"
          }
        }
      },
      "SecretRequest" : {
        "required" : [ "name" ],
        "type" : "object",
        "properties" : {
          "name" : {
            "description" : "Name for the API secret. Max 32 characters, lowercase letters only. Must be unique within the organization.",
            "type" : "string"
          },
          "roles" : {
            "description" : "Organization roles to grant the secret's service account. The calling user must already hold every role listed. Omit for a secret with no organization roles.",
            "type" : "array",
            "items" : {
              "type" : "string"
            }
          }
        }
      },
      "Subscription" : {
        "required" : [ "id", "status" ],
        "type" : "object",
        "properties" : {
          "id" : {
            "description" : "Stripe subscription ID.",
            "type" : "string"
          },
          "customer_id" : {
            "description" : "Stripe customer ID the subscription is billed to.",
            "type" : "string"
          },
          "start_date" : {
            "format" : "int64",
            "description" : "When the subscription began, as epoch milliseconds.",
            "type" : "integer"
          },
          "end_date" : {
            "format" : "int64",
            "description" : "When the subscription ended or is scheduled to end, as epoch milliseconds. Absent while it remains open-ended.",
            "type" : "integer"
          },
          "current_period_start" : {
            "format" : "int64",
            "description" : "Start of the period currently being billed, as epoch milliseconds.",
            "type" : "integer"
          },
          "current_period_end" : {
            "format" : "int64",
            "description" : "End of the period currently being billed — the next renewal or cancellation date, as epoch milliseconds.",
            "type" : "integer"
          },
          "status" : {
            "description" : "Stripe subscription status, e.g. `trialing`, `active`, `past_due`, `canceled`, `incomplete`.",
            "type" : "string"
          },
          "description" : {
            "description" : "Description of the subscribed plan.",
            "type" : "string"
          },
          "open_invoice" : {
            "description" : "True when the subscription is delinquent but repairable: it has an open invoice whose payment restores service, so billing can be fixed without starting a new subscription. Absent otherwise.",
            "type" : "boolean"
          },
          "open_invoice_amount_due" : {
            "format" : "int64",
            "description" : "Amount remaining on the open invoice, in the smallest currency unit (e.g. cents). Present only when `open_invoice` is true.",
            "type" : "integer"
          }
        }
      },
      "TelemetryExport" : {
        "type" : "object",
        "properties" : {
          "enabled" : {
            "type" : "boolean"
          },
          "endpoint" : {
            "type" : "string"
          },
          "protocol" : {
            "type" : "string"
          },
          "signals" : {
            "type" : "array",
            "items" : {
              "type" : "string"
            }
          },
          "loggerScope" : {
            "type" : "string"
          },
          "token" : {
            "type" : "string"
          },
          "tokenSet" : {
            "type" : "boolean"
          },
          "lastDeliveryAt" : {
            "format" : "date",
            "type" : "string",
            "example" : "2022-03-10"
          },
          "lastError" : {
            "type" : "string"
          }
        }
      },
      "Tier" : {
        "enum" : [ "starter", "premium", "enterprise" ],
        "type" : "string"
      },
      "ToggleExtensionRequest" : {
        "required" : [ "enabled" ],
        "type" : "object",
        "properties" : {
          "enabled" : {
            "description" : "Whether the extension should be active on the cluster.",
            "type" : "boolean"
          }
        }
      },
      "ToggleExtensionVersionValidRequest" : {
        "required" : [ "valid" ],
        "type" : "object",
        "properties" : {
          "valid" : {
            "description" : "Whether this version is approved for deployment.",
            "type" : "boolean"
          },
          "reason" : {
            "description" : "Justification for approving a version whose scan did not pass. Required when setting `valid` to true and the version's `scan_state` is not `PASS`; ignored otherwise.",
            "type" : "string"
          }
        }
      },
      "TokenResponse" : {
        "required" : [ "base_url", "access_token", "token_type" ],
        "type" : "object",
        "properties" : {
          "base_url" : {
            "description" : "Base URL of the deployment's Keycloak instance to call with this token.",
            "type" : "string"
          },
          "access_token" : {
            "description" : "Access token scoped to the deployment.",
            "type" : "string"
          },
          "token_type" : {
            "description" : "Token type, always `Bearer`.",
            "type" : "string"
          },
          "expires_in" : {
            "format" : "int64",
            "description" : "Token lifetime in seconds.",
            "type" : "integer"
          },
          "scope" : {
            "description" : "Scopes granted to the token.",
            "type" : "string"
          }
        }
      },
      "WellKnown" : {
        "type" : "object",
        "properties" : {
          "domain" : {
            "type" : "string"
          },
          "files" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/WellKnownFile"
            }
          },
          "change_password_url" : {
            "type" : "string"
          },
          "propagation_seconds" : {
            "format" : "int32",
            "type" : "integer"
          }
        }
      },
      "WellKnownFile" : {
        "type" : "object",
        "properties" : {
          "name" : {
            "type" : "string"
          },
          "url" : {
            "type" : "string"
          },
          "uploaded" : {
            "type" : "boolean"
          },
          "resource_key" : {
            "type" : "string"
          }
        }
      }
    },
    "securitySchemes" : {
      "oidcClientCredentials" : {
        "type" : "oauth2",
        "description" : "OIDC client credentials grant against **production**. Obtain a client ID/secret pair via the `org.apiSecret.create` operation, then exchange them for an access token at `https://app.phasetwo.io/auth/realms/self/protocol/openid-connect/token`. Use the scheme whose environment matches the server you selected.",
        "flows" : {
          "clientCredentials" : {
            "tokenUrl" : "https://app.phasetwo.io/auth/realms/self/protocol/openid-connect/token",
            "scopes" : { }
          }
        }
      },
      "oidcClientCredentialsStaging" : {
        "type" : "oauth2",
        "description" : "OIDC client credentials grant against **staging**. Obtain a client ID/secret pair via the `org.apiSecret.create` operation, then exchange them for an access token at `https://app-staging.phasetwo.io/auth/realms/self/protocol/openid-connect/token`. Use the scheme whose environment matches the server you selected.",
        "flows" : {
          "clientCredentials" : {
            "tokenUrl" : "https://app-staging.phasetwo.io/auth/realms/self/protocol/openid-connect/token",
            "scopes" : { }
          }
        }
      }
    }
  }
}