Docs
Open the console →
API reference

API reference: SSO

Cushy per-organization OIDC SSO configuration API: read, create/update (with server-side discovery and an encrypted secret), and enable or disable single sign-on.

Per-organization OIDC configuration. The entire surface — reads included — is org-admin only (manage_users_sso) and session-only. The client secret is encrypted at rest and never returned.

GET/api/org/ssoOrg Admin (session only)

Return the org's SSO config (or null). The secret is never included — only a set flag.

Example response
json
{ "sso": { "type": "oidc", "issuer": "https://idp.example.com", "clientId": "cc-app",
  "clientSecretSet": true, "defaultRole": "Viewer", "emailDomains": ["acme.io"],
  "enabled": true, "updatedAt": "2026-07-01T10:00:00.000Z" } }
Errors
StatusWhen
401Not signed in.
403Caller lacks manage_users_sso.
POST/api/org/ssoOrg Admin (session only)

Create or update the OIDC config. Runs discovery against the issuer and stores resolved endpoints; enables SSO.

Parameters
NameInRequiredDescription
issuerbodyyesIdP issuer URL (https).
clientIdbodyyesOIDC client id.
clientSecretbodynoClient secret (required on first save; blank keeps existing on update).
defaultRolebodynoRole for JIT users; defaults to Viewer.
emailDomainsbodynoUp to 20 domains routed to this IdP.
Example request
json
{ "issuer": "https://idp.example.com", "clientId": "cc-app", "clientSecret": "…", "defaultRole": "Viewer", "emailDomains": ["acme.io"] }
Example response
json
{ "ok": true, "sso": { "…": "as GET" } }
Errors
StatusWhen
400Non-https issuer, missing client id, bad role, invalid domains, missing first-time secret, or discovery failure.
403Caller lacks manage_users_sso.
503Secret storage is temporarily unavailable — retry.
PATCH/api/org/ssoOrg Admin (session only)

Enable or disable SSO without changing the config.

Parameters
NameInRequiredDescription
enabledbodyyestrue or false.
Example request
json
{ "enabled": false }
Example response
json
{ "ok": true, "sso": { "enabled": false, "…": "…" } }
Errors
StatusWhen
400enabled is not a boolean.
403Caller lacks manage_users_sso.
404SSO isn't configured yet.

Staff console SSO (platform realm)

A SEPARATE surface for the platform's OWN identity provider — what /admin signs in against. Staff realm only (cc_staff; a customer session, even a platform admin's, gets 401) plus the SCP actions platform.security:read_sso / :manage_sso. See Staff console SSO.

GET/api/admin/security/ssoStaff realm · platform.security:read_sso

The staff IdP config (or null), the redirect URI to register at the IdP, and the staff-role catalog. The client secret is never included.

Example response
json
{ "sso": { "issuer": "https://idp.snoweasl.com/realms/snoweasl", "clientId": "cushy-staff-console",
  "clientSecretSet": true, "emailDomains": ["snoweasl.com"], "groupsClaim": "groups",
  "roleMap": { "/cushy-platform-admins": "Platform Admin" }, "enabled": true },
  "redirectUri": "https://…/api/admin/auth/oidc/callback", "staffRoles": ["Platform Owner", "…"] }
Errors
StatusWhen
401No staff session.
403Staff role lacks platform.security:read_sso.
POST/api/admin/security/ssoStaff realm · platform.security:manage_sso

Configure the staff IdP. Runs OIDC discovery server-side, stores the resolved endpoints, encrypts the client secret, and enables SSO.

Parameters
NameInRequiredDescription
issuerbodyyeshttps:// issuer URL; discovery must resolve.
clientIdbodyyesThe confidential client's id.
clientSecretbodynoRequired on first save; omit to keep the stored secret.
emailDomainsbodynoAllowed email domains (array or comma string).
groupsClaimbodynoID-token claim carrying groups; required if roleMap is set.
roleMapbodyno{ group: staffRole }. Non-empty ⇒ sign-in requires a mapped group.
Example request
json
{
  "issuer": "https://idp.example.com/realms/staff",
  "clientId": "cushy-admin",
  "clientSecret": "…",
  "emailDomains": ["snoweasl.com"],
  "groupsClaim": "groups",
  "roleMap": { "cushy-platform-admins": "Platform Admin" }
}
Example response
json
{ "ok": true, "sso": { "issuer": "https://idp.example.com/realms/staff", "clientId": "cushy-admin",
  "authorizeUrl": "https://idp.example.com/realms/staff/protocol/openid-connect/auth",
  "tokenUrl": "…", "jwksUrl": "…", "redirectUri": "https://cushy.example.com/api/admin/auth/oidc/callback",
  "emailDomains": ["snoweasl.com"], "groupsClaim": "groups", "enabled": false, "clientSecretSet": true } }
Errors
StatusWhen
400Non-https issuer, discovery failure, missing client id/secret, unknown staff role, or a role map without a groups claim.
401No staff session.
403Staff role lacks platform.security:manage_sso.
503CC_DATA_KEY is not configured, so the secret can't be encrypted.
PATCH/api/admin/security/ssoStaff realm · platform.security:manage_sso

Enable or disable staff SSO without changing the configuration.

Example request
json
{ "enabled": false }
Example response
json
{ "ok": true, "sso": { "enabled": true, "clientSecretSet": true, "…": "…" } }
Errors
StatusWhen
400enabled is not a boolean.
404Staff SSO isn't configured yet.
DELETE/api/admin/security/ssoStaff realm · platform.security:manage_sso

Remove the staff IdP configuration and its stored secret. Password sign-in is unaffected.

Example response
json
{ "ok": true, "sso": null }
Errors
StatusWhen
404Staff SSO isn't configured yet.
GET/api/admin/auth/oidc/startPublic

Availability probe for the staff login page. Returns only whether staff SSO is on — no issuer, no client id, no account information.

Example response
json
{ "enabled": true }
Errors
StatusWhen
200Always — returns only { enabled }, never the issuer or client id.
POST/api/admin/auth/oidc/startPublic

Begin a staff SSO sign-in. Mints a single-use state/nonce/PKCE row stamped realm=staff and returns the IdP authorize URL.

Example request
json
{}  // no body — state, nonce and PKCE are generated server-side
Example response
json
{ "ok": true, "authorizeUrl": "https://idp.example/…/auth?…" }
Errors
StatusWhen
404Staff SSO isn't configured or is disabled.
GET/api/admin/auth/oidc/callbackPublic (IdP redirect)

Verify the ID token against the STAFF issuer + client id, match an EXISTING platform admin by verified email, and set cc_staff. Never creates or promotes an account.

Example response
json
// 302 redirect — never JSON.
// success → Location: /admin        (Set-Cookie: cc_staff=…)
// failure → Location: /admin/login?sso=failed
Errors
StatusWhen
400Unknown/expired/replayed state, a state from the customer realm, an issuer/audience/signature/nonce mismatch, an unverified or disallowed email, no mapped IdP group, or no platform-admin account — all one generic message.
429The shared staff lockout bucket is tripped.