Enterprise SSO
Configure Single Sign-On for your organization
6 min readEnterprise Single Sign-On (SSO) lets organization members authenticate through your identity provider (IdP) over OIDC. This guide walks through configuring an OIDC application, registering it on the organization, verifying a domain so members are auto-routed to SSO, and choosing the right authentication requirement for the organization.
Overview
Enterprise SSO provides:
- OIDC sign-in through your identity provider (Okta, Azure AD, Google, or any compliant OIDC IdP)
- Email-domain auto-redirect: members signing in from a verified company domain are sent to your IdP automatically
- Organization-level authentication policy that can require MFA or restrict members to SSO only
Prerequisites
Before configuring SSO:
- Owner role in the organization — SSO and security settings are restricted to the organization owner.
- An OIDC-compliant identity provider with the ability to register a confidential client.
- A domain you can prove ownership of if you want email-based auto-redirect or stricter enforcement.
Step 1 — Configure your identity provider
Create an OIDC application in your IdP with these settings:
| Setting | Value |
|---|---|
| Sign-in redirect URI | https://api.example.com/api/auth/sso/callback |
| Sign-out redirect URI | https://app.example.com |
| Grant type | Authorization Code |
| Response type | Code |
| Scopes | openid, profile, email |
The callback URL is shared across all organizations. Every SSO callback is routed through a
single /api/auth/sso/callback endpoint; the state parameter identifies which organization
initiated the flow. Configuring an org-specific callback will produce a redirect-URI-mismatch
error from your IdP.
Identity provider examples
Okta
- Create a new Web Application
- Set the Sign-in redirect URI shown above
- Enable the Authorization Code grant
- Copy the Client ID and Client Secret
Azure AD
- Register a new application
- Add a Web platform redirect URI
- Create a client secret
- The Issuer URL is
https://login.microsoftonline.com/{tenant-id}/v2.0
Step 2 — Register SSO on the organization
Once your IdP application exists, send the credentials to the API. SSO settings live under the
organization, so the call is tenant-scoped — the organization id sits in the URL path and
authentication is the same as any other API request (session cookie or X-Api-Key).
ORG_ID="01HZ3K5R4X9Y2V6QF8TJ7W0CDN"
curl -X PUT "https://api.example.com/api/user/organizations/${ORG_ID}/settings/sso" \
-H "X-Api-Key: ba_AbCdEf..." \
-H "Content-Type: application/json" \
-d '{
"provider": "okta",
"issuerUrl": "https://your-org.okta.com",
"clientId": "your-client-id",
"clientSecret": "your-client-secret"
}'const organizationId = '01HZ3K5R4X9Y2V6QF8TJ7W0CDN';
const res = await fetch(
`https://api.example.com/api/user/organizations/${organizationId}/settings/sso`,
{
method: 'PUT',
headers: {
'X-Api-Key': process.env.API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
provider: 'okta',
issuerUrl: 'https://your-org.okta.com',
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
}),
},
);The request body has exactly four fields:
| Field | Description |
|---|---|
provider | The IdP family (e.g. okta, azure, google, oidc) |
issuerUrl | The OIDC issuer / discovery base URL (no /.well-known/... suffix) |
clientId | Client ID from the IdP application |
clientSecret | Client secret from the IdP application — stored encrypted at rest |
Email-domain auto-redirect is configured separately on the verified domain (Step 3), not on the SSO settings body. To remove SSO, send DELETE to the same URL.
Step 3 — Verify your domain
Domains are managed as a separate resource. Once a domain is verified, members signing in with a matching email address are auto-routed to your IdP, and the domain becomes eligible for auto-join and SSO enforcement.
- Add the domain via
POST /api/user/organizations/{organizationId}/domains— the response includes a DNS TXT record name and value. - Add that record to your DNS provider.
- Trigger verification with
POST /api/user/organizations/{organizationId}/domains/{domainId}/verify.
ORG_ID="01HZ3K5R4X9Y2V6QF8TJ7W0CDN"
# Add a domain
curl -X POST "https://api.example.com/api/user/organizations/${ORG_ID}/domains" \
-H "X-Api-Key: ba_AbCdEf..." \
-H "Content-Type: application/json" \
-d '{ "domain": "yourcompany.com" }'
# After publishing the DNS TXT record, run verification
DOMAIN_ID="01HZ3KA7V0J9Z8Q2G5B1T7XWDN"
curl -X POST "https://api.example.com/api/user/organizations/${ORG_ID}/domains/${DOMAIN_ID}/verify" \
-H "X-Api-Key: ba_AbCdEf..."const organizationId = '01HZ3K5R4X9Y2V6QF8TJ7W0CDN';
// Add the domain
const added = await fetch(
`https://api.example.com/api/user/organizations/${organizationId}/domains`,
{
method: 'POST',
headers: {
'X-Api-Key': process.env.API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({ domain: 'yourcompany.com' }),
},
);
// After publishing the DNS TXT record, run verification
const domainId = '01HZ3KA7V0J9Z8Q2G5B1T7XWDN';
const verified = await fetch(
`https://api.example.com/api/user/organizations/${organizationId}/domains/${domainId}/verify`,
{
method: 'POST',
headers: { 'X-Api-Key': process.env.API_KEY },
},
);Step 4 — Choose an authentication requirement
Each organization carries a security configuration with two fields. Update them via
PUT /api/user/organizations/{organizationId}/settings/security.
PUT /api/user/organizations/{organizationId}/settings/security
{
"authRequired": "any",
"requireMfa": false
}authRequired
| Value | Who can sign in | What it means in practice |
|---|---|---|
any | Any auth method | Default. Members may sign in with password, magic link, passkey, social, or SSO. |
mfa | Any method, but MFA is required | Sessions must complete MFA before tenant-scoped requests succeed. |
sso | SSO only | Password, magic link, and social sign-in are blocked for members of this organization. |
requireMfa
A boolean that enforces MFA on session sign-ins independently of authRequired. PATs are not subject to MFA — scope and pin them carefully to keep automated access safe.
Test the configuration
Trigger a server-side validation of the configured issuer URL with
POST /api/user/organizations/{organizationId}/settings/sso/test. It performs OIDC discovery
against the saved issuer and returns whether the configuration is reachable. The endpoint takes
no body.
{
"success": true,
"status": 200,
"code": "OK",
"message": "SSO configuration is valid",
"data": { "success": true, "message": "SSO configuration is valid" }
}Validate before raising the requirement to sso. If discovery fails after enforcement is in
place, members can be locked out until the requirement is relaxed or the configuration is fixed.
SSO login flow
Users with verified-domain emails follow this flow:
- User submits an email at sign-in.
- The sign-in flow detects a verified domain pointing to an SSO-enabled organization.
- The client is redirected to
/api/auth/sso/{slug}/login, which forwards to your IdP. - After IdP authentication, the IdP redirects back to
/api/auth/sso/callback. - A session is established and the user is returned to the app.
/api/auth/sso/{slug}/login is the canonical entry point — {slug} is the organization's slug, available on GET /api/user/organizations.
Auto-join configuration
Each verified domain carries an auto-join policy. Update it via the domain settings endpoint to enable automatic organization membership for users with matching email domains. Optionally require admin approval before joining — pending requests can then be reviewed via the organization members API.
Troubleshooting
| Issue | Solution |
|---|---|
| Redirect URI mismatch | Confirm the callback URL is exactly https://api.example.com/api/auth/sso/callback |
| Invalid issuer | The issuer URL is the discovery base; the API appends /.well-known/openid-configuration automatically |
| Domain auto-redirect not firing | The domain must be verified (not pending) and the organization must have SSO configured |
| Members locked out | Lower authRequired from sso to any (or mfa) to restore access while you fix the configuration |
Best Practices
- Configure and test before enforcing: Set
authRequiredtoanywhile you validate the IdP, then raise it once members can sign in. - Keep at least one non-SSO admin during rollout: A backup credential prevents lockouts if the IdP misbehaves.
- Use a verified domain for auto-redirect: Without it, SSO works but members must navigate to the SSO login URL manually.
- Treat the client secret as sensitive: It is stored encrypted at rest, but anyone who can read it can sign in as your application.
- Document SSO sign-in for your team: Include the email they should use and the expected IdP screen to reduce support traffic.
Next Steps
After setting up SSO:
- Configure API keys: Set up API Keys for integrations.
- Review security settings: Manage settings via the Organizations API.
- Manage domains: Add additional domains via the Organizations API.
Related Pages
Introduction
How the API is structured, how authentication works, and how multi-tenant requests are scoped
Quick Start
Sign in, issue a Personal Access Token, and make your first authenticated call
API Structure
Request headers, response envelope, pagination, and the query conventions shared by every endpoint
Error Handling
Error envelope, full code list, and the retry strategies that actually work