Organization Management
Comprehensive API for managing multi-tenant organizations, including CRUD operations, member management, BYOO OAuth credentials, SMTP configuration, custom domains, branding, and end-user management.
Overview
Organizations are the core tenant entities in AuthOS. Each organization has:
- Members: Users with roles (owner, admin, member)
- Services: Applications that use AuthOS
- BYOO Credentials: Custom OAuth provider credentials
- SMTP Configuration: Custom email delivery settings
- Custom Domain: White-label domain configuration
- Branding: Logo and color customization
- End-Users: Customers who authenticate with the organization’s services
Organizations must be approved by a Platform Owner before becoming active. Only active organizations can authenticate users.
Data Models
Organization Model
{
"id": "uuid",
"slug": "unique-identifier",
"name": "Organization Name",
"owner_user_id": "uuid",
"status": "pending|active|suspended|rejected",
"tier_id": "uuid",
"max_services": 10,
"max_users": 100,
"custom_domain": "auth.example.com",
"domain_verified": true,
"smtp_host": "smtp.example.com",
"smtp_port": 587,
"smtp_username": "noreply@example.com",
"smtp_from_email": "noreply@example.com",
"smtp_from_name": "Example Team",
"brand_logo_url": "https://cdn.example.com/logo.png",
"brand_primary_color": "#FF5733",
"created_at": "datetime",
"updated_at": "datetime"
}
Membership Model
{
"id": "uuid",
"org_id": "uuid",
"user_id": "uuid",
"role": "owner|admin|member",
"created_at": "datetime"
}
Organization Member Response
{
"user": {
"id": "uuid",
"email": "user@example.com",
"is_platform_owner": false,
"created_at": "datetime"
},
"membership": {
"id": "uuid",
"org_id": "uuid",
"user_id": "uuid",
"role": "admin",
"created_at": "datetime"
}
}
Endpoints Summary
| Method | Path | Description | Permissions |
|---|---|---|---|
| POST | /api/organizations |
Create new organization | Public |
| GET | /api/organizations |
List user’s organizations | Member |
| GET | /api/organizations/:org_slug |
Get organization details | Member |
| PATCH | /api/organizations/:org_slug |
Update organization | Owner/Admin |
| DELETE | /api/organizations/:org_slug |
Delete organization | Owner |
| GET | /api/organizations/:org_slug/members |
List members | Member |
| PATCH | /api/organizations/:org_slug/members/:user_id |
Update member role | Owner |
| POST | /api/organizations/:org_slug/members/:user_id |
Remove member | Owner/Admin |
| POST | /api/organizations/:org_slug/transfer-ownership |
Transfer ownership | Owner |
| POST | /api/organizations/:org_slug/smtp |
Configure SMTP | Owner/Admin |
| GET | /api/organizations/:org_slug/smtp |
Get SMTP config | Owner/Admin |
| DELETE | /api/organizations/:org_slug/smtp |
Delete SMTP config | Owner/Admin |
| POST | /api/organizations/:org_slug/domain |
Set custom domain | Owner/Admin |
| POST | /api/organizations/:org_slug/domain/verify |
Verify custom domain | Owner/Admin |
| GET | /api/organizations/:org_slug/domain |
Get domain config | Member |
| DELETE | /api/organizations/:org_slug/domain |
Delete custom domain | Owner/Admin |
| PATCH | /api/organizations/:org_slug/branding |
Update branding | Owner/Admin |
| GET | /api/organizations/:org_slug/branding |
Get branding | Member |
| GET | /api/organizations/:org_slug/branding/public |
Get public branding | Public |
| POST | /api/organizations/:org_slug/oauth-credentials/:provider |
Set OAuth credentials | Owner/Admin |
| GET | /api/organizations/:org_slug/oauth-credentials/:provider |
Get OAuth credentials | Member |
| GET | /api/organizations/:org_slug/users |
List end-users | Member |
| GET | /api/organizations/:org_slug/users/:user_id |
Get end-user details | Member |
| DELETE | /api/organizations/:org_slug/users/:user_id/sessions |
Revoke user sessions | Owner/Admin |
Organization CRUD Operations
POST /api/organizations
Create a new organization (pending status). The authenticated user becomes the owner.
Permissions: Authenticated User
Headers:
| Header | Value |
|---|---|
Authorization |
Bearer {jwt} |
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
slug |
string | Yes | Unique URL-friendly identifier (3-50 chars, alphanumeric/hyphens/underscores) |
name |
string | Yes | Organization display name (2-100 chars) |
Example Request:
curl -X POST https://sso.example.com/api/organizations \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \
-d '{
"slug": "acme-corp",
"name": "Acme Corporation"
}'
Example Response (200 OK):
{
"organization": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"slug": "acme-corp",
"name": "Acme Corporation",
"owner_user_id": "user-uuid",
"status": "pending",
"tier_id": "free-tier-uuid",
"max_services": null,
"max_users": null,
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:30:00Z"
},
"owner": {
"id": "user-uuid",
"email": "admin@acme.com",
"is_platform_owner": false,
"created_at": "2025-01-15T10:30:00Z"
},
"membership": {
"id": "membership-uuid",
"org_id": "550e8400-e29b-41d4-a716-446655440000",
"user_id": "user-uuid",
"role": "owner",
"created_at": "2025-01-15T10:30:00Z"
},
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "550e8400-e29b-41d4-a716-446655440001"
}
Error Responses:
400 Bad Request: Invalid slug/name format, slug already taken, or reserved slug401 Unauthorized: Missing or invalid JWT token500 Internal Server Error: Database error
Notes:
- Requires JWT authentication; the authenticated user becomes the organization owner
- Response includes
access_tokenandrefresh_tokenwith organization context (org claim set) - No need to re-authenticate after creation; tokens are immediately usable
- Organization starts in
pendingstatus and requires Platform Owner approval - Owner is automatically added as a member with
ownerrole - Free tier is automatically assigned
- Reserved slugs:
api,auth,admin,platform,docs,www,mail
Slug Validation:
- Length: 3-50 characters
- Characters: Alphanumeric, hyphens, and underscores only
- Cannot use reserved slugs
Name Validation:
- Length: 2-100 characters
- Cannot be empty or whitespace-only
GET /api/organizations
List organizations the authenticated user is a member of.
Permissions: Authenticated User
Headers:
| Header | Value |
|---|---|
Authorization |
Bearer {jwt} |
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
page |
integer | No | Page number (default: 1, min: 1) |
limit |
integer | No | Items per page (default: 20, min: 1, max: 100) |
status |
string | No | Filter by status: pending, active, suspended, rejected |
Example Request:
curl -X GET "https://sso.example.com/api/organizations?page=1&limit=20&status=active" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
Example Response (200 OK):
[
{
"organization": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"slug": "acme-corp",
"name": "Acme Corporation",
"owner_user_id": "user-uuid",
"status": "active",
"tier_id": "tier-uuid",
"max_services": null,
"max_users": null,
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:30:00Z"
},
"membership_count": 5,
"service_count": 3,
"tier": {
"id": "tier-uuid",
"name": "Free",
"default_max_services": 5,
"default_max_users": 100
}
}
]
Error Responses:
401 Unauthorized: Invalid or missing JWT500 Internal Server Error: Database error
Notes:
- Returns only organizations where user is a member
- Includes organization statistics (member count, service count)
- Results are paginated
GET /api/organizations/:org_slug
Get detailed information about a specific organization.
Permissions: Member
Headers:
| Header | Value |
|---|---|
Authorization |
Bearer {jwt} |
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
Example Request:
curl -X GET https://sso.example.com/api/organizations/acme-corp \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
Example Response (200 OK):
{
"organization": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"slug": "acme-corp",
"name": "Acme Corporation",
"owner_user_id": "user-uuid",
"status": "active",
"tier_id": "tier-uuid",
"max_services": null,
"max_users": null,
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:30:00Z"
},
"membership_count": 5,
"service_count": 3,
"tier": {
"id": "tier-uuid",
"name": "Free",
"default_max_services": 5,
"default_max_users": 100
}
}
Error Responses:
401 Unauthorized: Invalid or missing JWT403 Forbidden: User is not a member of this organization404 Not Found: Organization not found500 Internal Server Error: Database error
PATCH /api/organizations/:org_slug
Update organization details.
Permissions: Owner or Admin
Headers:
| Header | Value |
|---|---|
Authorization |
Bearer {jwt} |
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | No | New organization name (2-100 chars) |
Example Request:
curl -X PATCH https://sso.example.com/api/organizations/acme-corp \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Corporation International"
}'
Example Response (200 OK):
{
"organization": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"slug": "acme-corp",
"name": "Acme Corporation International",
"owner_user_id": "user-uuid",
"status": "active",
"tier_id": "tier-uuid",
"max_services": null,
"max_users": null,
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T11:00:00Z"
},
"membership_count": 5,
"service_count": 3,
"tier": {
"id": "tier-uuid",
"name": "Free",
"default_max_services": 5,
"default_max_users": 100
}
}
Error Responses:
401 Unauthorized: Invalid or missing JWT403 Forbidden: User is not an owner or admin404 Not Found: Organization not found400 Bad Request: Invalid name format500 Internal Server Error: Database error
Notes:
- Only the organization name can be updated (slug is immutable)
- The
updated_attimestamp is automatically updated
DELETE /api/organizations/:org_slug
Delete an organization and all associated data.
Permissions: Owner only
Headers:
| Header | Value |
|---|---|
Authorization |
Bearer {jwt} |
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
Example Request:
curl -X DELETE https://sso.example.com/api/organizations/acme-corp \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
Example Response (200 OK):
{}
Error Responses:
401 Unauthorized: Invalid or missing JWT403 Forbidden: User is not the owner404 Not Found: Organization not found500 Internal Server Error: Database error
Notes:
- This is a destructive operation that cannot be undone
- Cascading deletes remove: members, services, plans, subscriptions, invitations, OAuth credentials, SMTP config, domain config, branding
- An audit log entry is created before deletion (best effort)
Member Management
GET /api/organizations/:org_slug/members
List all members of an organization.
Permissions: Member
Headers:
| Header | Value |
|---|---|
Authorization |
Bearer {jwt} |
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
page |
integer | No | Page number (default: 1, min: 1) |
limit |
integer | No | Items per page (default: 50, min: 1, max: 100) |
role |
string | No | Filter by role: owner, admin, member |
Example Request:
curl -X GET "https://sso.example.com/api/organizations/acme-corp/members?page=1&limit=50&role=admin" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
Example Response (200 OK):
{
"members": [
{
"user": {
"id": "user-uuid-1",
"email": "admin@acme.com",
"is_platform_owner": false,
"created_at": "2025-01-15T10:30:00Z"
},
"membership": {
"id": "membership-uuid-1",
"org_id": "org-uuid",
"user_id": "user-uuid-1",
"role": "owner",
"created_at": "2025-01-15T10:30:00Z"
}
},
{
"user": {
"id": "user-uuid-2",
"email": "user@acme.com",
"is_platform_owner": false,
"created_at": "2025-01-16T14:20:00Z"
},
"membership": {
"id": "membership-uuid-2",
"org_id": "org-uuid",
"user_id": "user-uuid-2",
"role": "admin",
"created_at": "2025-01-16T14:20:00Z"
}
}
],
"total": 5,
"limit": {
"current": 5,
"max": 100,
"source": "Free"
}
}
Error Responses:
401 Unauthorized: Invalid or missing JWT403 Forbidden: User is not a member404 Not Found: Organization not found500 Internal Server Error: Database error
Notes:
- Returns user and membership details for each member
limit.sourceindicates where the limit comes from (tier name or “custom”)- Results are paginated
PATCH /api/organizations/:org_slug/members/:user_id
Update a member’s role.
Permissions: Owner only
Headers:
| Header | Value |
|---|---|
Authorization |
Bearer {jwt} |
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
user_id |
string | User ID to update |
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
role |
string | Yes | New role: owner, admin, or member |
Example Request:
curl -X PATCH https://sso.example.com/api/organizations/acme-corp/members/user-uuid-2 \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "Content-Type: application/json" \
-d '{
"role": "admin"
}'
Example Response (200 OK):
{
"user": {
"id": "user-uuid-2",
"email": "user@acme.com",
"is_platform_owner": false,
"created_at": "2025-01-16T14:20:00Z"
},
"membership": {
"id": "membership-uuid-2",
"org_id": "org-uuid",
"user_id": "user-uuid-2",
"role": "admin",
"created_at": "2025-01-16T14:20:00Z"
}
}
Error Responses:
401 Unauthorized: Invalid or missing JWT403 Forbidden: User is not the owner404 Not Found: Organization or member not found400 Bad Request: Invalid role or attempting to change own role500 Internal Server Error: Database error
Notes:
- Cannot change your own role (prevents accidental owner lockout)
- If setting role to
owner, organization ownership is transferred - Previous owner is automatically demoted to
admin - An audit log entry is created for role changes
- Audit log includes additional entry for ownership transfers
POST /api/organizations/:org_slug/members/:user_id
Remove a member from an organization.
Permissions: Owner or Admin
Headers:
| Header | Value |
|---|---|
Authorization |
Bearer {jwt} |
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
user_id |
string | User ID to remove |
Example Request:
curl -X POST https://sso.example.com/api/organizations/acme-corp/members/user-uuid-3 \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
Example Response (200 OK):
{}
Error Responses:
401 Unauthorized: Invalid or missing JWT403 Forbidden: User is not an owner/admin, or admin attempting to remove owner/admin404 Not Found: Organization or member not found400 Bad Request: Attempting to remove yourself500 Internal Server Error: Database error
Notes:
- Cannot remove yourself from the organization
- Owner can remove any member
- Admin can only remove members (not other admins or owners)
- An audit log entry is created with removed user details
POST /api/organizations/:org_slug/transfer-ownership
Transfer organization ownership to another member.
Permissions: Owner only
Headers:
| Header | Value |
|---|---|
Authorization |
Bearer {jwt} |
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
new_owner_email |
string | Yes | Email of the new owner (must be an existing member) |
Example Request:
curl -X POST https://sso.example.com/api/organizations/acme-corp/transfer-ownership \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "Content-Type: application/json" \
-d '{
"new_owner_email": "newowner@acme.com"
}'
Example Response (200 OK):
{
"user": {
"id": "user-uuid-new",
"email": "newowner@acme.com",
"is_platform_owner": false,
"created_at": "2025-01-16T14:20:00Z"
},
"membership": {
"id": "membership-uuid-new",
"org_id": "org-uuid",
"user_id": "user-uuid-new",
"role": "owner",
"created_at": "2025-01-16T14:20:00Z"
}
}
Error Responses:
401 Unauthorized: Invalid or missing JWT403 Forbidden: User is not the owner404 Not Found: Organization, user, or membership not found500 Internal Server Error: Database error
Notes:
- New owner must already be a member of the organization
- Previous owner is automatically demoted to
adminrole - Organization’s
owner_user_idis updated - Transaction ensures atomic ownership transfer
- This operation is permanent and cannot be undone
SMTP Configuration
Custom SMTP settings allow organizations to send emails from their own email infrastructure.
POST /api/organizations/:org_slug/smtp
Configure SMTP settings for an organization.
Permissions: Owner or Admin
Headers:
| Header | Value |
|---|---|
Authorization |
Bearer {jwt} |
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
host |
string | Yes | SMTP server hostname |
port |
integer | Yes | SMTP server port (typically 25, 587, or 465) |
username |
string | Yes | SMTP authentication username |
password |
string | Yes | SMTP authentication password |
from_email |
string | Yes | From email address |
from_name |
string | No | From display name |
Example Request:
curl -X POST https://sso.example.com/api/organizations/acme-corp/smtp \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "Content-Type: application/json" \
-d '{
"host": "smtp.sendgrid.net",
"port": 587,
"username": "apikey",
"password": "SG.xxx",
"from_email": "noreply@acme.com",
"from_name": "Acme Team"
}'
Example Response (200 OK):
{
"message": "SMTP configuration saved successfully"
}
Error Responses:
401 Unauthorized: Invalid or missing JWT403 Forbidden: User is not an owner or admin404 Not Found: Organization not found500 Internal Server Error: Encryption or database error
Notes:
- Password is encrypted using AES-GCM before storage
- Requires
ENCRYPTION_KEYenvironment variable to be set - Emails will be sent from this SMTP server instead of platform default
- Test the configuration by triggering an email (e.g., invite a user)
GET /api/organizations/:org_slug/smtp
Get SMTP configuration (without password).
Permissions: Owner or Admin
Headers:
| Header | Value |
|---|---|
Authorization |
Bearer {jwt} |
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
Example Request:
curl -X GET https://sso.example.com/api/organizations/acme-corp/smtp \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
Example Response (200 OK):
{
"host": "smtp.sendgrid.net",
"port": 587,
"username": "apikey",
"from_email": "noreply@acme.com",
"from_name": "Acme Team",
"configured": true
}
Error Responses:
401 Unauthorized: Invalid or missing JWT403 Forbidden: User is not an owner or admin404 Not Found: Organization not found500 Internal Server Error: Database error
Notes:
- Password is never returned for security
configuredistrueif SMTP settings exist,falseotherwise- Returns empty values if SMTP is not configured
DELETE /api/organizations/:org_slug/smtp
Delete SMTP configuration (revert to platform default).
Permissions: Owner or Admin
Headers:
| Header | Value |
|---|---|
Authorization |
Bearer {jwt} |
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
Example Request:
curl -X DELETE https://sso.example.com/api/organizations/acme-corp/smtp \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
Example Response (200 OK):
{
"message": "SMTP configuration deleted successfully. Organization will now use platform-level SMTP."
}
Error Responses:
401 Unauthorized: Invalid or missing JWT403 Forbidden: User is not an owner or admin404 Not Found: Organization not found500 Internal Server Error: Database error
Notes:
- Clears all SMTP settings from the database
- Organization will fall back to platform-wide SMTP configuration
- Encrypted password is permanently deleted
Custom Domain & Branding
White-label your authentication experience with custom domains and visual branding.
POST /api/organizations/:org_slug/domain
Set a custom domain for the organization.
Permissions: Owner or Admin
Headers:
| Header | Value |
|---|---|
Authorization |
Bearer {jwt} |
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
domain |
string | Yes | Custom domain (e.g., auth.acme.com) |
Example Request:
curl -X POST https://sso.example.com/api/organizations/acme-corp/domain \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "Content-Type: application/json" \
-d '{
"domain": "auth.acme.com"
}'
Example Response (200 OK):
{
"verification_token": "550e8400-e29b-41d4-a716-446655440000",
"verification_methods": [
{
"method": "DNS TXT Record",
"instructions": "Add a TXT record to your domain's DNS settings",
"record_type": "TXT",
"record_name": "_sso-verification.auth.acme.com",
"record_value": "550e8400-e29b-41d4-a716-446655440000"
},
{
"method": "HTTP File",
"instructions": "Upload a file to http://auth.acme.com/.well-known/sso-verification.txt containing the verification token",
"record_type": null,
"record_name": null,
"record_value": "550e8400-e29b-41d4-a716-446655440000"
}
]
}
Error Responses:
401 Unauthorized: Invalid or missing JWT403 Forbidden: User is not an owner or admin404 Not Found: Organization not found400 Bad Request: Invalid domain format or domain already in use500 Internal Server Error: Database error
Notes:
- Domain must contain at least one dot and no spaces
- Domain must be unique across all organizations
- Domain is set to
domain_verified: falseuntil verified - Verification token is generated and stored
- An audit log entry is created
- Choose one verification method (DNS or HTTP)
DNS Verification:
- Add a TXT record with name
_sso-verification.{domain} - Set the value to the verification token
- Wait for DNS propagation (can take up to 48 hours)
- Call the verify endpoint
HTTP Verification:
- Create a file at
http://{domain}/.well-known/sso-verification.txt - File content should be the verification token (plain text)
- Ensure the file is publicly accessible
- Call the verify endpoint
POST /api/organizations/:org_slug/domain/verify
Verify custom domain ownership.
Permissions: Owner or Admin
Headers:
| Header | Value |
|---|---|
Authorization |
Bearer {jwt} |
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
Example Request:
curl -X POST https://sso.example.com/api/organizations/acme-corp/domain/verify \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
Example Response (200 OK) - Verified:
{
"verified": true,
"message": "Domain verified successfully via DNS TXT record"
}
Example Response (200 OK) - Not Verified:
{
"verified": false,
"message": "Domain verification failed. Please ensure the TXT record or HTTP file is correctly configured."
}
Error Responses:
401 Unauthorized: Invalid or missing JWT403 Forbidden: User is not an owner or admin404 Not Found: Organization not found400 Bad Request: No domain configured or already verified500 Internal Server Error: Database error
Notes:
- Checks both DNS TXT record and HTTP file
- If either verification method succeeds, domain is marked as verified
- Once verified,
domain_verifiedis set totrue - An audit log entry is created with verification method
- If already verified, returns success immediately
GET /api/organizations/:org_slug/domain
Get custom domain configuration.
Permissions: Member
Headers:
| Header | Value |
|---|---|
Authorization |
Bearer {jwt} |
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
Example Request:
curl -X GET https://sso.example.com/api/organizations/acme-corp/domain \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
Example Response (200 OK):
{
"custom_domain": "auth.acme.com",
"domain_verified": true
}
Error Responses:
401 Unauthorized: Invalid or missing JWT403 Forbidden: User is not a member404 Not Found: Organization not found500 Internal Server Error: Database error
Notes:
- Returns
nullforcustom_domainif not configured domain_verifiedisfalseuntil verification succeeds- All members can view domain configuration
DELETE /api/organizations/:org_slug/domain
Remove custom domain configuration.
Permissions: Owner or Admin
Headers:
| Header | Value |
|---|---|
Authorization |
Bearer {jwt} |
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
Example Request:
curl -X DELETE https://sso.example.com/api/organizations/acme-corp/domain \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
Example Response (204 No Content):
No response body.
Error Responses:
401 Unauthorized: Invalid or missing JWT403 Forbidden: User is not an owner or admin404 Not Found: Organization not found500 Internal Server Error: Database error
Notes:
- Clears
custom_domain,domain_verified, anddomain_verification_token - Organization reverts to using platform default domain
- An audit log entry is created (if domain was configured)
- DNS/HTTP verification files can be removed after deletion
PATCH /api/organizations/:org_slug/branding
Update organization branding (logo and colors).
Permissions: Owner or Admin
Headers:
| Header | Value |
|---|---|
Authorization |
Bearer {jwt} |
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
logo_url |
string | No | URL to organization logo (publicly accessible) |
primary_color |
string | No | Hex color code (e.g., #FF5733 or #F57) |
Example Request:
curl -X PATCH https://sso.example.com/api/organizations/acme-corp/branding \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "Content-Type: application/json" \
-d '{
"logo_url": "https://cdn.acme.com/logo.png",
"primary_color": "#FF5733"
}'
Example Response (200 OK):
{
"logo_url": "https://cdn.acme.com/logo.png",
"primary_color": "#FF5733"
}
Error Responses:
401 Unauthorized: Invalid or missing JWT403 Forbidden: User is not an owner or admin404 Not Found: Organization not found400 Bad Request: Invalid color format500 Internal Server Error: Database error
Notes:
- Both fields are optional - update only what you need
- Set to
nullto clear a branding field - Color must be hex format:
#RRGGBBor#RGB - Logo URL should be publicly accessible (CDN recommended)
- An audit log entry is created with updated fields
- Changes are reflected immediately on login pages
GET /api/organizations/:org_slug/branding
Get organization branding configuration.
Permissions: Member
Headers:
| Header | Value |
|---|---|
Authorization |
Bearer {jwt} |
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
Example Request:
curl -X GET https://sso.example.com/api/organizations/acme-corp/branding \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
Example Response (200 OK):
{
"logo_url": "https://cdn.acme.com/logo.png",
"primary_color": "#FF5733"
}
Error Responses:
401 Unauthorized: Invalid or missing JWT403 Forbidden: User is not a member404 Not Found: Organization not found500 Internal Server Error: Database error
Notes:
- Returns
nullfor fields that haven’t been configured - All members can view branding configuration
- Use this endpoint to preview branding in admin dashboards
GET /api/organizations/:org_slug/branding/public
Get organization branding (public, no authentication).
Permissions: Public (no authentication required)
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
Example Request:
curl -X GET https://sso.example.com/api/organizations/acme-corp/branding/public
Example Response (200 OK):
{
"logo_url": "https://cdn.acme.com/logo.png",
"primary_color": "#FF5733"
}
Error Responses:
404 Not Found: Organization not found500 Internal Server Error: Database error
Notes:
- No authentication required - used by login pages
- Returns
nullfor unconfigured fields - Use this endpoint to display branding on public authentication pages
- Ideal for white-labeling login/signup forms
BYOO OAuth Credentials
Bring Your Own OAuth (BYOO) allows organizations to use their own OAuth provider credentials for end-user authentication.
POST /api/organizations/:org_slug/oauth-credentials/:provider
Set or update OAuth credentials for a provider.
Permissions: Owner or Admin
Headers:
| Header | Value |
|---|---|
Authorization |
Bearer {jwt} |
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
provider |
string | OAuth provider: github, google, or microsoft |
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
client_id |
string | Yes | OAuth application client ID |
client_secret |
string | Yes | OAuth application client secret |
Example Request:
curl -X POST https://sso.example.com/api/organizations/acme-corp/oauth-credentials/github \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "Content-Type: application/json" \
-d '{
"client_id": "Iv1.abcdef123456",
"client_secret": "1234567890abcdefghij"
}'
Example Response (200 OK):
{
"provider": "github",
"client_id": "Iv1.abcdef123456",
"has_secret": true
}
Error Responses:
401 Unauthorized: Invalid or missing JWT403 Forbidden: User is not an owner or admin404 Not Found: Organization not found400 Bad Request: Invalid provider500 Internal Server Error: Encryption or database error
Notes:
- Valid providers:
github,google,microsoft - Client secret is encrypted using AES-GCM before storage
- Requires
ENCRYPTION_KEYenvironment variable - If credentials already exist for the provider, they are updated (upsert)
- End-users will authenticate using these credentials instead of platform defaults
- Test by initiating a login flow for your service
Setting up OAuth Applications:
GitHub:
- Go to Settings > Developer settings > OAuth Apps
- Create new OAuth App
- Set callback URL to:
https://your-sso-domain.com/auth/github/callback - Copy Client ID and generate Client Secret
Google:
- Go to Google Cloud Console > APIs & Services > Credentials
- Create OAuth 2.0 Client ID
- Set redirect URI to:
https://your-sso-domain.com/auth/google/callback - Copy Client ID and Client Secret
Microsoft:
- Go to Azure Portal > App registrations
- Create new registration
- Set redirect URI to:
https://your-sso-domain.com/auth/microsoft/callback - Copy Application (client) ID
- Create client secret in Certificates & secrets
GET /api/organizations/:org_slug/oauth-credentials/:provider
Get OAuth credentials for a provider (client ID only).
Permissions: Member
Headers:
| Header | Value |
|---|---|
Authorization |
Bearer {jwt} |
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
provider |
string | OAuth provider: github, google, or microsoft |
Example Request:
curl -X GET https://sso.example.com/api/organizations/acme-corp/oauth-credentials/github \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
Example Response (200 OK):
{
"provider": "github",
"client_id": "Iv1.abcdef123456",
"has_secret": true
}
Error Responses:
401 Unauthorized: Invalid or missing JWT403 Forbidden: User is not a member404 Not Found: Organization or credentials not found400 Bad Request: Invalid provider500 Internal Server Error: Database error
Notes:
- Client secret is never returned for security
has_secretis alwaystrueif credentials exist- All members can view OAuth credentials (but not the secret)
- Use this to verify which providers have BYOO configured
End-User Management
Manage customers who authenticate with your organization’s services.
GET /api/organizations/:org_slug/users
List all end-users (customers) of the organization.
Permissions: Member
Headers:
| Header | Value |
|---|---|
Authorization |
Bearer {jwt} |
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
page |
integer | No | Page number (default: 1, min: 1) |
limit |
integer | No | Items per page (default: 50, min: 1, max: 100) |
service_slug |
string | No | Filter by specific service |
Example Request:
curl -X GET "https://sso.example.com/api/organizations/acme-corp/users?page=1&limit=50&service_slug=main-app" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
Example Response (200 OK):
{
"users": [
{
"user": {
"id": "user-uuid-1",
"email": "customer@example.com",
"is_platform_owner": false,
"created_at": "2025-01-15T10:30:00Z"
},
"subscriptions": [
{
"service_id": "service-uuid",
"service_slug": "main-app",
"service_name": "Main Application",
"plan_id": "plan-uuid",
"plan_name": "Pro",
"status": "active",
"current_period_end": "2025-02-15T10:30:00Z",
"created_at": "2025-01-15T10:30:00Z"
}
],
"identities": [
{
"provider": "github",
"provider_user_id": "12345678",
"created_at": "2025-01-15T10:30:00Z"
}
]
}
],
"total": 150,
"page": 1,
"limit": 50
}
Error Responses:
401 Unauthorized: Invalid or missing JWT403 Forbidden: User is not a member404 Not Found: Organization or service not found500 Internal Server Error: Database error
Notes:
- End-users are those who have authenticated with the organization’s services
- Returns users with subscriptions and/or identities for the organization
- Each user includes their subscriptions and linked identities
- Filter by service to see users of a specific application
- Results are paginated
GET /api/organizations/:org_slug/users/:user_id
Get detailed information about a specific end-user.
Permissions: Member
Headers:
| Header | Value |
|---|---|
Authorization |
Bearer {jwt} |
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
user_id |
string | End-user ID |
Example Request:
curl -X GET https://sso.example.com/api/organizations/acme-corp/users/user-uuid-1 \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
Example Response (200 OK):
{
"user": {
"id": "user-uuid-1",
"email": "customer@example.com",
"is_platform_owner": false,
"created_at": "2025-01-15T10:30:00Z"
},
"subscriptions": [
{
"service_id": "service-uuid",
"service_slug": "main-app",
"service_name": "Main Application",
"plan_id": "plan-uuid",
"plan_name": "Pro",
"status": "active",
"current_period_end": "2025-02-15T10:30:00Z",
"created_at": "2025-01-15T10:30:00Z"
}
],
"identities": [
{
"provider": "github",
"provider_user_id": "12345678",
"created_at": "2025-01-15T10:30:00Z"
},
{
"provider": "google",
"provider_user_id": "987654321",
"created_at": "2025-01-16T14:20:00Z"
}
],
"session_count": 2
}
Error Responses:
401 Unauthorized: Invalid or missing JWT403 Forbidden: User is not a member404 Not Found: Organization or end-user not found500 Internal Server Error: Database error
Notes:
- Only shows users who have subscriptions to this organization’s services
- Includes all subscriptions across all services
- Includes all linked OAuth identities
session_countshows active sessions for this user- Use this endpoint to get full customer profile
DELETE /api/organizations/:org_slug/users/:user_id/sessions
Revoke all active sessions for an end-user.
Permissions: Owner or Admin
Headers:
| Header | Value |
|---|---|
Authorization |
Bearer {jwt} |
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
user_id |
string | End-user ID |
Example Request:
curl -X DELETE https://sso.example.com/api/organizations/acme-corp/users/user-uuid-1/sessions \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
Example Response (200 OK):
{
"message": "Sessions revoked successfully",
"revoked_count": 3
}
Error Responses:
401 Unauthorized: Invalid or missing JWT403 Forbidden: User is not an owner or admin404 Not Found: Organization or end-user not found500 Internal Server Error: Database error
Notes:
- Revokes all active JWT sessions for the user
- User must re-authenticate to access services
- Use this for security purposes (e.g., suspected account compromise)
revoked_countshows how many sessions were deleted- Only affects sessions for this user across all services in the organization
Risk Engine Settings
Organizations can customize the behavior of the platform’s Risk Engine to match their security requirements.
GET /api/organizations/:org_slug/risk-settings
Retrieve the current risk engine configuration for an organization.
Permissions: Organization admin, owner, or platform owner
Headers:
| Header | Value | Required |
|---|---|---|
Authorization |
Bearer {jwt} |
Yes |
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug identifier |
Example Request:
curl -X GET https://sso.example.com/api/organizations/acme-corp/risk-settings \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."
Success Response (200 OK):
{
"id": "risk-rules-id-123",
"org_id": "org-id-456",
"enforcement_mode": "enforce",
"low_threshold": 30,
"medium_threshold": 70,
"new_device_score": 20,
"impossible_travel_score": 50,
"velocity_threshold": 10,
"velocity_score": 30,
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-15T10:00:00Z"
}
Field Descriptions:
| Field | Type | Description |
|---|---|---|
enforcement_mode |
string | enforce or log_only (shadow mode) |
low_threshold |
integer | Risk score threshold for low-risk actions (0-100) |
medium_threshold |
integer | Risk score threshold for MFA challenge (0-100) |
new_device_score |
integer | Points added for new/unknown device |
impossible_travel_score |
integer | Points added for impossible travel detection |
velocity_threshold |
integer | Max login attempts within time window |
velocity_score |
integer | Points added for velocity rule violations |
Enforcement Modes:
- enforce: Risk engine actions are applied (block, MFA challenge, allow)
- log_only: All logins succeed, but risk scores are logged for analysis
PUT /api/organizations/:org_slug/risk-settings
Update risk engine configuration for an organization.
Permissions: Organization admin, owner, or platform owner
Headers:
| Header | Value | Required |
|---|---|---|
Authorization |
Bearer {jwt} |
Yes |
Content-Type |
application/json |
Yes |
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug identifier |
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
enforcement_mode |
string | No | enforce or log_only |
low_threshold |
integer | No | Low risk threshold (0-100) |
medium_threshold |
integer | No | Medium risk threshold (0-100) |
new_device_score |
integer | No | New device risk score |
impossible_travel_score |
integer | No | Impossible travel risk score |
velocity_threshold |
integer | No | Velocity detection threshold |
velocity_score |
integer | No | Velocity violation risk score |
Example Request:
curl -X PUT https://sso.example.com/api/organizations/acme-corp/risk-settings \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \
-H "Content-Type: application/json" \
-d '{
"enforcement_mode": "enforce",
"medium_threshold": 60,
"new_device_score": 25
}'
Success Response (200 OK):
{
"success": true,
"risk_settings": {
"id": "risk-rules-id-123",
"org_id": "org-id-456",
"enforcement_mode": "enforce",
"low_threshold": 30,
"medium_threshold": 60,
"new_device_score": 25,
"impossible_travel_score": 50,
"velocity_threshold": 10,
"velocity_score": 30,
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-15T14:30:00Z"
}
}
Validation Rules:
enforcement_modemust beenforceorlog_only- Thresholds must be between 0 and 100
medium_thresholdmust be greater thanlow_threshold- Score values must be non-negative integers
Use Cases:
Testing Mode (Shadow Mode):
{
"enforcement_mode": "log_only"
}
Enables monitoring without blocking users. Review risk assessments in login events before enabling enforcement.
Strict Security:
{
"enforcement_mode": "enforce",
"medium_threshold": 50,
"new_device_score": 30,
"impossible_travel_score": 60
}
Lower thresholds and higher scores for high-security environments.
Balanced Security:
{
"enforcement_mode": "enforce",
"medium_threshold": 70,
"new_device_score": 20,
"impossible_travel_score": 50
}
Default settings suitable for most organizations.
POST /api/organizations/:org_slug/risk-settings/reset
Reset risk engine settings to platform defaults.
Permissions: Organization admin, owner, or platform owner
Example Request:
curl -X POST https://sso.example.com/api/organizations/acme-corp/risk-settings/reset \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."
Success Response (200 OK):
Returns the risk settings object with default values.
Default Values:
enforcement_mode:log_onlylow_threshold:30medium_threshold:70new_device_score:20impossible_travel_score:50velocity_threshold:10velocity_score:30
SIEM Configuration
Configure Security Information and Event Management (SIEM) integrations to stream audit logs and login events to external security platforms.
POST /api/organizations/:org_slug/siem-configs
Create a new SIEM configuration for log streaming.
Permissions: Organization admin or owner
Headers:
| Header | Value | Required |
|---|---|---|
Authorization |
Bearer {jwt} |
Yes |
Content-Type |
application/json |
Yes |
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug identifier |
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Configuration name (e.g., “Production Datadog”) |
provider_type |
string | Yes | Datadog, Splunk, Elastic, or Custom |
endpoint_url |
string | Yes | HTTP endpoint for log delivery |
api_key |
string | No | API key for authentication |
auth_header |
string | No | Custom authorization header |
batch_size |
integer | No | Events per batch (default: 100) |
Example Request:
curl -X POST https://sso.example.com/api/organizations/acme-corp/siem-configs \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \
-H "Content-Type: application/json" \
-d '{
"name": "Production Datadog",
"provider_type": "Datadog",
"endpoint_url": "https://http-intake.logs.datadoghq.com/v1/input",
"api_key": "dd-api-key-abc123",
"batch_size": 50
}'
Success Response (201 Created):
{
"id": "siem-config-id-789",
"org_id": "org-id-456",
"name": "Production Datadog",
"provider_type": "Datadog",
"endpoint_url": "https://http-intake.logs.datadoghq.com/v1/input",
"batch_size": 50,
"enabled": true,
"last_successful_batch_at": null,
"failure_count": 0,
"created_at": "2025-01-15T10:00:00Z"
}
Supported Providers:
| Provider | Description | Authentication |
|---|---|---|
Datadog |
Datadog Log Management | api_key in DD-API-KEY header |
Splunk |
Splunk HEC (HTTP Event Collector) | api_key in Authorization header |
Elastic |
Elasticsearch | api_key or auth_header |
Custom |
Generic HTTP endpoint | Custom auth_header |
Event Types Streamed:
- Login events (successful and failed attempts)
- MFA audit logs (setup, verification, recovery code usage)
- Organization audit logs (member changes, settings updates)
- Platform audit logs (organization creation, deletion, suspension)
Security:
- API keys are encrypted at rest using AES-GCM
- Credentials never returned in API responses
- HTTPS required for all endpoint URLs
GET /api/organizations/:org_slug/siem-configs
List all SIEM configurations for an organization.
Permissions: Organization admin or owner
Example Request:
curl -X GET https://sso.example.com/api/organizations/acme-corp/siem-configs \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."
Success Response (200 OK):
{
"siem_configs": [
{
"id": "siem-config-id-789",
"org_id": "org-id-456",
"name": "Production Datadog",
"provider_type": "Datadog",
"endpoint_url": "https://http-intake.logs.datadoghq.com/v1/input",
"batch_size": 50,
"enabled": true,
"last_successful_batch_at": "2025-01-15T14:30:00Z",
"failure_count": 0,
"created_at": "2025-01-15T10:00:00Z"
}
],
"total": 1
}
Note: API keys and auth headers are never returned in responses for security.
PATCH /api/organizations/:org_slug/siem-configs/:config_id
Update an existing SIEM configuration.
Permissions: Organization admin or owner
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | No | Update configuration name |
endpoint_url |
string | No | Update endpoint URL |
api_key |
string | No | Update API key (null to remove) |
auth_header |
string | No | Update auth header (null to remove) |
batch_size |
integer | No | Update batch size |
enabled |
boolean | No | Enable/disable log streaming |
Example Request:
curl -X PATCH https://sso.example.com/api/organizations/acme-corp/siem-configs/siem-config-id-789 \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \
-H "Content-Type: application/json" \
-d '{
"enabled": false
}'
Success Response (200 OK):
Returns the updated SIEM configuration.
DELETE /api/organizations/:org_slug/siem-configs/:config_id
Delete a SIEM configuration. This stops log streaming immediately.
Permissions: Organization admin or owner
Example Request:
curl -X DELETE https://sso.example.com/api/organizations/acme-corp/siem-configs/siem-config-id-789 \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."
Success Response: 204 No Content
Warning: This action cannot be undone. Historical logs are not affected, but new events will no longer be streamed.
POST /api/organizations/:org_slug/siem-configs/:config_id/test
Test SIEM configuration by sending a test event.
Permissions: Organization admin or owner
Example Request:
curl -X POST https://sso.example.com/api/organizations/acme-corp/siem-configs/siem-config-id-789/test \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."
Success Response (200 OK):
{
"success": true,
"message": "Test event delivered successfully",
"response_status": 200,
"response_time_ms": 145
}
Failure Response (200 OK):
{
"success": false,
"message": "Connection failed: timeout after 30s",
"error": "Connection timeout"
}
Test Event Payload:
{
"event_type": "siem_config_test",
"timestamp": "2025-01-15T14:30:00Z",
"organization_id": "org-id-456",
"message": "Test event from AuthOS SIEM integration"
}
Use Cases:
- Verify endpoint connectivity before enabling
- Troubleshoot delivery failures
- Validate authentication credentials
- Check endpoint response time
SCIM Tokens
SCIM tokens enable automated user and group provisioning from identity providers.
POST /api/organizations/:org_slug/scim-tokens
Create a new SCIM token for automated provisioning.
Permissions: Organization admin or owner
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Token name (e.g., “Okta SCIM Integration”) |
expires_at |
string | No | ISO 8601 expiration timestamp |
Example Request:
curl -X POST https://sso.example.com/api/organizations/acme-corp/scim-tokens \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \
-H "Content-Type: application/json" \
-d '{
"name": "Okta SCIM Integration",
"expires_at": "2026-12-31T23:59:59Z"
}'
Success Response (201 Created):
{
"id": "scim-token-id-abc",
"name": "Okta SCIM Integration",
"token": "scim_live_abc123def456...",
"prefix": "scim_live_abc1",
"created_at": "2025-01-15T10:00:00Z",
"expires_at": "2026-12-31T23:59:59Z"
}
Important: The full token is only returned once during creation. Store it securely.
Token Format: scim_live_ prefix + 32-byte random value
See SCIM 2.0 API Reference for SCIM endpoint documentation.
GET /api/organizations/:org_slug/scim-tokens
List all SCIM tokens for an organization.
Example Response:
[
{
"id": "scim-token-id-abc",
"name": "Okta SCIM Integration",
"prefix": "scim_live_abc1",
"active": true,
"created_at": "2025-01-15T10:00:00Z",
"expires_at": "2026-12-31T23:59:59Z",
"last_used_at": "2025-01-15T14:30:00Z"
}
]
Note: Full token values are never returned after creation.
DELETE /api/organizations/:org_slug/scim-tokens/:token_id
Revoke and delete a SCIM token. This immediately invalidates the token.
Example Request:
curl -X DELETE https://sso.example.com/api/organizations/acme-corp/scim-tokens/scim-token-id-abc \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."
Success Response: 204 No Content
Security Considerations
Access Control:
- Organization status must be
activefor most operations - Pending organizations can only manage members and invitations
- Suspended organizations cannot authenticate users
- Platform Owners bypass some restrictions
SMTP Security:
- Passwords are encrypted at rest using AES-GCM
- Never returned in GET requests
- Requires
ENCRYPTION_KEYenvironment variable
OAuth Credentials (BYOO):
- Client secrets encrypted at rest using AES-GCM
- Never returned in API responses
- Used automatically for end-user authentication
- Falls back to platform defaults if not configured
Custom Domain Verification:
- Prevents domain hijacking
- Two verification methods: DNS TXT and HTTP file
- Domain must be unique across all organizations
- Verification token is random UUID
Role Hierarchy:
- Owner: Full control, can delete organization
- Admin: Manage members (except owner), configure settings
- Member: Read-only access to organization details
Audit Logging:
- All member management actions logged
- Domain and branding changes logged
- SMTP configuration changes logged
- Includes actor, target, and detailed change information
Rate Limiting:
- Organization creation: 10 per hour per IP
- Member operations: 60 per hour per user
- SMTP operations: 20 per hour per organization
- Domain verification: 10 per hour per organization
Data Isolation:
- Organizations are completely isolated
- Members cannot access other organizations’ data
- End-users are scoped to their organization’s services
- BYOO credentials are organization-specific