Configure Single Sign-On for your organization
5 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.
Enterprise SSO provides:
Before configuring SSO:
Organizations without the Insights entitlement receive the normal public API error shape with HTTP
403 and code: "FORBIDDEN"; provider credentials are not saved.
Create an OIDC application in your IdP with these settings:
| Setting | Value |
|---|---|
| Sign-in redirect URI | https://api.indexhog.com/api/auth/sso/callback |
| Sign-out redirect URI | https://indexhog.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.
Okta
Azure AD
https://login.microsoftonline.com/{tenant-id}/v2.0Once 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.indexhog.com/api/user/organizations/${ORG_ID}/settings/sso" \
-H "X-Api-Key: tkn_AbCdEf..." \
-H "Content-Type: application/json" \
-d '{
"provider": "okta",
"issuerUrl": "https://your-org.okta.com",
"clientId": "your-client-id",
"clientSecret": "your-secret"
}'const apiKey = '<your-api-key>'; // load this from a secure env var — never hardcode
const organizationId = '01HZ3K5R4X9Y2V6QF8TJ7W0CDN';
const res = await fetch(
`https://api.indexhog.com/api/user/organizations/${organizationId}/settings/sso`,
{
method: 'PUT',
headers: {
'X-Api-Key': apiKey,
'Content-Type': 'application/json',
},
body: JSON.stringify({
provider: 'okta',
issuerUrl: 'https://your-org.okta.com',
clientId: 'your-client-id',
clientSecret: 'your-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.
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.
POST /api/user/organizations/{organizationId}/domains — the response includes a DNS TXT record name and value.POST /api/user/organizations/{organizationId}/domains/{domainId}/verify.ORG_ID="01HZ3K5R4X9Y2V6QF8TJ7W0CDN"
# Add a domain
curl -X POST "https://api.indexhog.com/api/user/organizations/${ORG_ID}/domains" \
-H "X-Api-Key: tkn_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.indexhog.com/api/user/organizations/${ORG_ID}/domains/${DOMAIN_ID}/verify" \
-H "X-Api-Key: tkn_AbCdEf..."const apiKey = '<your-api-key>'; // load this from a secure env var — never hardcode
const organizationId = '01HZ3K5R4X9Y2V6QF8TJ7W0CDN';
// Add the domain
const added = await fetch(
`https://api.indexhog.com/api/user/organizations/${organizationId}/domains`,
{
method: 'POST',
headers: {
'X-Api-Key': apiKey,
'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.indexhog.com/api/user/organizations/${organizationId}/domains/${domainId}/verify`,
{
method: 'POST',
headers: { 'X-Api-Key': apiKey },
},
);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. |
requireMfaA 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.
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.
Users with verified-domain emails follow this flow:
/api/auth/sso/{slug}/login, which forwards to your IdP./api/auth/sso/callback./api/auth/sso/{slug}/login is the canonical entry point — {slug} is the organization's slug, available on GET /api/user/organizations.
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.
| Issue | Solution |
|---|---|
| Redirect URI mismatch | Confirm the callback URL is exactly https://api.indexhog.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 |
authRequired to any while you validate the IdP, then raise it once members can sign in.After setting up SSO:
How the API is structured, how authentication works, and how multi-tenant requests are scoped
Sign in, issue a Personal Access Token, and make your first authenticated call
Point any MCP-capable AI client at the hosted MCP server and drive Indexhog over OAuth — no token to paste.
Request headers, response envelope, pagination, and the query conventions shared by every endpoint