Admin API & CLI
Everything you can do in the admin console — create realms, register clients, configure federation —
you can also do programmatically over the Admin API, authenticated with a bearer token. The API is
hosted at https://api.self.semauth.cc (self is the reserved system-organization slug).
How access works
Admin API tokens are minted by your own organization's admin realm and carry an
org_id claim. The API verifies the token against that realm's keys and scopes every query to your org —
so a token for org A can never touch org B's data. Two ways to get a token:
- Machine-to-machine — an Admin API client using
client_credentials(for backends, scripts, CI). - Human-from-terminal — the
semauth-clipublic client using authorization-code + PKCE (audit attributes to the person).
Every token must carry the scope management:full; the audience is the Admin API URL.
The APIs catalog
The admin console's APIs page (/apis) lists the resource servers your clients can
request tokens for — the platform Admin API (semauth-admin-api) plus any APIs your org owns. It's the
source of truth for valid audience values when registering a client.
Creating a machine-to-machine client
In the admin console open API Access (/api-access) and click
New Admin API client:
- Label — e.g. prod-backend or ci-pipeline.
- Auth method —
- Client secret (default): SemAuth generates a secret and shows it once. Simplest.
- private_key_jwt: you host a JWKS URL and sign assertions locally; SemAuth never holds your private key.
The confirmation screen shows the Client ID (sa_mgmt_…), the secret (once), the
token endpoint, the Admin API base URL, and a ready-to-run curl example. Grant
type, scope (management:full), and audience are fixed for you. From API Access you can
also Rotate secret (client_secret clients) or Revoke a client.
Getting a token and calling the API
# Mint a token from your admin realm's token endpoint (shown on the confirmation page):
TOKEN=$(curl -sS -X POST "$TOKEN_URL" \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-d grant_type=client_credentials \
-d audience="$ADMIN_API_URL" \
-d scope=management:full | jq -r .access_token)
# Use it against the Admin API:
curl -sS "$ADMIN_API_URL/v1/realms" -H "Authorization: Bearer $TOKEN"
The CLI client (human from a terminal)
Every admin realm also has a built-in public client semauth-cli for interactive use. It uses
authorization-code + PKCE with loopback redirects (RFC 8252): a local tool opens your browser, you sign in with your
normal realm methods (magic link / passkey / federation), and the tool receives a user-scoped token — no secret on
disk, and audit logs attribute actions to you rather than a service account. Scopes include
openid email profile offline_access management:full; the refresh token rotates and keeps minting tokens
for the Admin API audience.
Endpoints (v1)
All requests require Authorization: Bearer <token>. Every mutation is recorded as an event and
shows up in your Activity feed, exactly like the equivalent console action.
| Method | Path | Purpose |
|---|---|---|
| POST | /v1/realms | Create a realm (body { "name": "…" }). Returns issuer_id, discovery URL, and defaults. |
| GET | /v1/realms | List your org's realms. |
| GET | /v1/realms/{issuer_id} | Fetch one realm. |
| PATCH | /v1/realms/{issuer_id} | Update name, enabled, magic_link_enabled, passkey_enabled, min_aal (1–3), and custom_domain (with CNAME verification; null to clear). |
| POST | /v1/realms/{issuer_id}/clients | Create an OIDC client (application, label, auth_method, grant types, scopes, audiences, redirect URIs). Returns the secret once for client_secret clients. |
| POST | /v1/realms/{issuer_id}/external-idps | Configure a federation provider. You supply all endpoints (no server-side discovery). |
Example: create a realm
curl -sS -X POST "$ADMIN_API_URL/v1/realms" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Production"}'
# 201 Created
{
"issuer_id": "vctxj6g1egkv",
"name": "Production",
"enabled": true,
"discovery_url": "https://vctxj6g1egkv.semauth.app/.well-known/openid-configuration",
"magic_link_enabled": true,
"passkey_enabled": true,
"min_aal": 1
}
Error responses
The API uses RFC 6749-style JSON errors. Common cases: 401 with invalid_token (missing
/ invalid / wrong-issuer token), 403 with insufficient_scope (token lacks
management:full), and 400 for validation failures (e.g. an unreachable min_aal
or an unverifiable custom domain).