Skip to main content

Management API

The API version of the Phase Two console. It manages clusters — the dedicated Keycloak instances we run for you — and everything attached to them: the realms on a cluster, its custom domains, the extensions and themes you upload, its environment variables, its IP allow/deny rules, and its billing.

It does not manage anything inside a realm. Users, clients, groups, authentication flows and identity providers are the realm's business, handled by Keycloak's own Admin REST API and by our Extensions API. If you are looking for POST /users, you are in the wrong reference.

What it targets

Requests go to the Phase Two control plane, not to your cluster:

https://api.phasetwo.io/v2/...

That host is the same for every customer. Your clusters are identified by ID in the path, not by hostname — api.phasetwo.io is where you ask for a cluster, and the cluster's own host is where you use it.

Two hosts, two jobs
HostWhat it serves
app.phasetwo.ioThe console, and the token endpoint you authenticate against
api.phasetwo.ioThis API
<cluster>.global.auth.ac (or your custom domain)Your Keycloak — realms, users, logins

Tokens are minted by the control-plane realm on the console host; the API itself lives on the API host. Staging mirrors both: app-staging.phasetwo.io and api-staging.phasetwo.io.

The two must match — a token minted on one environment's console host is not valid against the other's API host. In the explorer below that means picking api-staging in the server selector and the oidcClientCredentialsStaging security scheme, because tokenUrl is a separate field that the server selector cannot retarget.

Authentication

An OAuth2 client credentials grant. In outline:

  1. In the console, open your team's API Credentials tab and create an API secret. You get a client ID and a client secret; the secret is shown once.
  2. Exchange them for an access token at the console host.
  3. Send that token as Authorization: Bearer <token> to api.phasetwo.io.
TOKEN=$(curl -s -X POST \
https://app.phasetwo.io/auth/realms/self/protocol/openid-connect/token \
-d grant_type=client_credentials \
-d client_id="$PHASETWO_CLIENT_ID" \
-d client_secret="$PHASETWO_CLIENT_SECRET" | jq -r .access_token)

curl -s https://api.phasetwo.io/v2/clusters -H "Authorization: Bearer $TOKEN"

What a token may do is governed by the organization roles granted to its API secret, not by OAuth scopes. A secret created with only view roles gets 403s on writes, which is the intended way to hand a read-only credential to a monitoring job.

The API keys guide covers creation, rotation, least privilege and the failure modes in full.

What is here

82 endpoints in nine groups.

GroupEndpointsWhat it covers
Clusters20Create, inspect and delete clusters; regions, name availability, metrics, restart and upgrade status, primary hostname, checkout, telemetry export
Cluster Extensions21Upload custom providers and themes, manage versions, read the security scan report, trigger the reconcile/restart
Billing9Subscriptions, payment methods, billing contacts, Stripe portal sessions
Deployments11Realms on a cluster, addressed directly by deployment ID; console and app links, token exchange, per-realm admin credentials
Organizations6The teams that own clusters, and their API secrets
Custom Domains5Attach a hostname, read its DNS records and certificate status
Environment Variables5Custom SPI configuration on a cluster
IP Rules3Admin and realm endpoint allow/deny lists
Logs2List log files and get a temporary download URL

Conventions worth knowing before you start

Operation IDs are {resource}.{operation}. cluster.list, cluster.create, cluster.domain.detail, org.apiSecret.create. The shape mirrors the URL hierarchy, so an ID tells you where the endpoint sits without looking it up. Generated clients derive method names from these.

Provisioning is asynchronous. cluster.create returns before the cluster exists. Poll cluster.detail and watch statusBILLING_SETUPPROVISIONINGACTIVE is the happy path. A cluster is not usable until ACTIVE.

Some changes restart Keycloak. Adding an environment variable or reconciling extensions restarts the cluster's Keycloak. The API rejects a second such change while one is in flight with a 409; cluster.restartStatus.detail tells you when it is safe to proceed.

Deleting a cluster is not immediate. Unless it never completed billing setup, delete moves it to PENDING_DELETION and teardown happens at the end of the billing cycle. It bills until then, and the name stays reserved — so a create-destroy-recreate loop under one name will fail.

Custom domains need DNS you create yourself. cluster.domain.create returns the records to add at your DNS provider. The certificate is only issued once they resolve, and only then can the domain become the cluster's primary host.

Tier limits are enforced server-side and surface as 409s — realms, themes, extensions, domains and IP rules all have per-tier caps.

Errors are Keycloak's errors. 401 means the token is absent, expired or malformed. 403 means the token is fine but the API secret lacks the organization role. If a call that should work returns 403, check the secret's roles before anything else.

Before you write a client

There is a Terraform provider generated from this spec that already handles the asynchronous provisioning, the restart serialization and the destroy semantics described above. For infrastructure-shaped work it is substantially less code than driving the API directly.

It is experimental at 0.1.0 — point it at a test environment rather than production until it settles.