Skip to content

SSO / SCIM

SAML-based single sign-on and SCIM 2.0 user/group provisioning.

Base paths: /api/sso (SSO), /api/scim (SCIM)

Plan requirement: Enterprise plan required for all configuration and management endpoints.

SSO Endpoints

Initiate SAML Login

GET /api/sso/saml/login

Authentication: None

Redirects the user to the organization's configured SAML Identity Provider for authentication.

Query Parameters

ParameterTypeRequiredDescription
orgIdstringYesOrganization ID to look up SSO configuration

Errors

StatusErrorDescription
400orgId is requiredMissing query parameter
404SSO not configured for this organizationNo enabled SSO config found

Example

bash
curl "https://api.controlinfra.com/api/sso/saml/login?orgId=ORG_ID"

SAML Assertion Callback

POST /api/sso/saml/callback

Authentication: None (SAML assertion verification)

Handles the SAML assertion from the Identity Provider. Creates or finds the user, adds them to the organization if not already a member, and redirects to the frontend with an exchange code.

On success, redirects to: {FRONTEND_URL}/auth/callback?code={code}

On failure, redirects to: {FRONTEND_URL}/auth/error?reason={reason}

Get SSO Configuration

GET /api/sso/config

Authentication: Bearer token | Role: Admin | Permission: integrations:write | Plan: Enterprise

Returns the current SSO configuration for the organization. The SAML certificate is masked in the response.

Response

json
{
  "configured": true,
  "config": {
    "provider": "okta",
    "saml": {
      "entryPoint": "https://idp.example.com/sso/saml",
      "issuer": "https://api.controlinfra.com",
      "cert": "****"
    },
    "enforcedDomains": ["example.com"],
    "defaultRole": "member",
    "enabled": true
  }
}

If not configured: { "configured": false }

Example

bash
curl -H "Authorization: Bearer TOKEN" \
  -H "x-org-id: ORG_ID" \
  https://api.controlinfra.com/api/sso/config

Update SSO Configuration

PUT /api/sso/config

Authentication: Bearer token | Role: Admin | Permission: integrations:write | Plan: Enterprise

Create or update the SSO configuration. Performs an upsert.

Request Body

FieldTypeRequiredDescription
providerstringNoIdP name (e.g. okta, azure_ad, custom). Default: custom
saml.entryPointstringYesIdP SSO URL
saml.issuerstringYesSP entity ID / issuer
saml.certstringYesIdP signing certificate (PEM, without headers)
saml.signatureAlgorithmstringNoSignature algorithm. Default: sha256
saml.wantAuthnResponseSignedbooleanNoRequire signed responses. Default: true
enforcedDomainsstring[]NoEmail domains that must use SSO
defaultRolestringNoRole assigned to new SSO users. Default: member
enabledbooleanNoEnable/disable SSO. Default: false

Example

bash
curl -X PUT https://api.controlinfra.com/api/sso/config \
  -H "Authorization: Bearer TOKEN" \
  -H "Content-Type: application/json" \
  -H "x-org-id: ORG_ID" \
  -d '{
    "provider": "okta",
    "saml": {
      "entryPoint": "https://idp.example.com/sso/saml",
      "issuer": "https://api.controlinfra.com",
      "cert": "MIIC..."
    },
    "enforcedDomains": ["example.com"],
    "enabled": true
  }'

Delete SSO Configuration

DELETE /api/sso/config

Authentication: Bearer token | Role: Admin | Permission: integrations:write | Plan: Enterprise

Remove the SSO configuration entirely. Users who were provisioned via SSO retain their accounts but must use another login method.

Response

json
{ "success": true }

SCIM 2.0 Protocol Endpoints

All SCIM protocol endpoints use bearer token authentication with a SCIM token (generated via the token management endpoints below). Responses use Content-Type: application/scim+json.

Base path: /api/scim/v2

Discovery

MethodEndpointDescription
GET/v2/ServiceProviderConfigSCIM service provider capabilities
GET/v2/ResourceTypesSupported resource types (User, Group)
GET/v2/SchemasSCIM schema definitions

Users

MethodEndpointDescription
GET/v2/UsersList users (supports filter, pagination)
POST/v2/UsersProvision a new user
GET/v2/Users/:idGet a single user
PUT/v2/Users/:idFull user replacement
PATCH/v2/Users/:idPartial update (activate/deactivate)
DELETE/v2/Users/:idRemove user from organization

List Users Query Parameters

ParameterTypeDescription
startIndexinteger1-based start index (default: 1)
countintegerPage size, max 200 (default: 100)
filterstringSCIM filter (e.g. userName eq "user@example.com")

Create User — Key Fields

FieldTypeRequiredDescription
userNamestringYesUser email address
displayNamestringNoDisplay name
name.givenNamestringNoFirst name
name.familyNamestringNoLast name
externalIdstringNoExternal identifier from IdP

Supports the urn:ietf:params:scim:schemas:extension:enterprise:2.0:User extension (employeeNumber, department, manager).

Groups

MethodEndpointDescription
GET/v2/GroupsList groups (supports filter, pagination)
POST/v2/GroupsCreate a group
GET/v2/Groups/:idGet a single group
PUT/v2/Groups/:idFull group replacement
PATCH/v2/Groups/:idPartial update (add/remove members)
DELETE/v2/Groups/:idDelete a group

Bulk Operations

POST /api/scim/v2/Bulk

Authentication: SCIM bearer token

Process up to 100 User and Group CRUD operations in a single request. Each operation specifies method, path (e.g. /Users, /Groups/:id), optional bulkId, and data.


SCIM Token Management

These endpoints are called from the frontend Settings UI (not by IdPs).

Authentication: Bearer JWT | Role: Admin | Permission: integrations:write | Plan: Enterprise

List Tokens

GET /api/scim/tokens

Response

json
{
  "tokens": [
    { "id": "...", "label": "Okta SCIM", "prefix": "a1b2c3d4", "createdAt": "...", "lastUsedAt": "..." }
  ]
}

Generate Token

POST /api/scim/tokens

FieldTypeRequiredDescription
labelstringNoToken label. Default: SCIM Token

Response (token shown only once)

json
{
  "id": "...",
  "label": "Okta SCIM",
  "token": "a1b2c3d4e5f6...",
  "createdAt": "2026-01-15T00:00:00.000Z"
}

Revoke Token

DELETE /api/scim/tokens/:id

Response

json
{ "message": "Token revoked" }

SCIM Group Mapping

Map SCIM groups to organization roles. When group membership changes, member roles are automatically synced (highest-privilege group wins).

Authentication: Bearer JWT | Role: Admin | Permission: integrations:write | Plan: Enterprise

List Group Mappings

GET /api/scim/groups

Response

json
{
  "groups": [
    { "id": "...", "displayName": "Engineers", "memberCount": 5, "mappedRole": "member", "mappedCustomRoleId": null }
  ]
}

Update Group Mapping

PATCH /api/scim/groups/:id/mapping

FieldTypeRequiredDescription
mappedRolestringNoRole to assign: admin, member, or viewer
mappedCustomRoleIdstringNoCustom RBAC role ID (or null to clear)

Response

json
{
  "id": "...",
  "displayName": "Engineers",
  "mappedRole": "admin",
  "mappedCustomRoleId": null
}