Create an SSO connection
/sso-connection
Requires `sso:manage` and the `sso` feature. Create it inactive, hand the returned `urls` to the IdP administrator, then activate it with PATCH — activation is refused while the protocol's required config is incomplete. Domains are attached separately, by pointing an org domain at this connection with `PATCH /org-domain/{id}`. A BROWSER SESSION must additionally carry a fresh step-up reauthentication (`X-Step-Up-Token`, obtained from `/auth/step-up`); without one the call answers `428 step_up_required` and `error.details` names the action and resource to prove. An organization API token is not challenged: step-up re-proves the person behind a session, and a token has no person behind it. It is gated because a verified domain and an SSO connection are DEFERRED membership grants: they admit a principal with no actor present at the moment they fire, which is `POST /invite` with a longer fuse.
Request Body
OIDC client secret. Write-only — stored encrypted, never returned.
Protocol-specific IdP settings. Never contains secrets — the OIDC client secret is write-only.
SAML: accept unsolicited IdP-initiated responses. Rejected by default.
OIDC: relying-party client id.
OIDC: discovery document URL.
SAML: IdP entity id.
SAML: paste IdP metadata to populate the fields above. Accepted on write; never returned.
SAML: IdP single sign-on URL.
SAML: IdP signing certificate (PEM).
OIDC: issuer URL.
SAML: sign SP-initiated AuthnRequests.
Role id (role_…).
See enforced on the response — this locks users to the IdP.
Immutable after creation.
oidcsaml
Response Body
Protocol-specific IdP settings. Never contains secrets — the OIDC client secret is write-only.
SAML: accept unsolicited IdP-initiated responses. Rejected by default.
OIDC: relying-party client id.
OIDC: discovery document URL.
SAML: IdP entity id.
SAML: paste IdP metadata to populate the fields above. Accepted on write; never returned.
SAML: IdP single sign-on URL.
SAML: IdP signing certificate (PEM).
OIDC: issuer URL.
SAML: sign SP-initiated AuthnRequests.
Role (role_…) explicitly configured for users provisioned through this connection, or null to track the default. Org Owner is refused here. Read effective_default_role_id to learn what a JIT login actually grants.
Server-computed: the role SSO just-in-time provisioning through this connection really grants. Equals default_role_id when that is set and the role still exists; otherwise the Member system role, which is what the join falls back to both when nothing is configured and when the configured role has since been deleted. null means even that fallback is missing, so auto-provisioning through this connection is broken and the first login seats nobody.
When enforced, users on the connection's verified domains can ONLY sign in through this IdP — social login and email codes are refused for them.
Whether an OIDC client secret is stored. The secret itself is never returned.
SSO connection id (sso_…).
A draft may be saved incomplete, but cannot be activated until the protocol's required fields are present.
The connection domain resolution picks when a domain has several.
Owning organization (org_…).
oidcsaml
The protocol URLs to hand to the IdP administrator (ACS / metadata / callback, by protocol).
curl -X POST 'https://api.elaichi.ai/sso-connection' \
-H 'Authorization: Bearer $ELAICHI_API_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"protocol":"oidc","name":"your_name","is_active":true,"enforced":true,"is_default":true,"config":{},"client_secret":"your_client_secret"}'const body = {
"protocol": "oidc",
"name": "your_name",
"is_active": true,
"enforced": true,
"is_default": true,
"config": {},
"client_secret": "your_client_secret"
};
const response = await fetch('https://api.elaichi.ai/sso-connection', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + process.env.ELAICHI_API_TOKEN,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
const data = await response.json();
console.log(data);import os
import requests
url = "https://api.elaichi.ai/sso-connection"
headers = {
"Authorization": f"Bearer {os.environ['ELAICHI_API_TOKEN']}",
"Content-Type": "application/json",
}
payload = {
"protocol": "oidc",
"name": "your_name",
"is_active": True,
"enforced": True,
"is_default": True,
"config": {},
"client_secret": "your_client_secret"
}
response = requests.post(url, headers=headers, json=payload)
print(response.json())