Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.kaireonai.com/llms.txt

Use this file to discover all available pages before exploring further.

KaireonAI implements the SCIM 2.0 protocol (RFC 7644) for automated user provisioning from enterprise identity providers like Okta, Azure AD, and OneLogin. SCIM endpoints require bearer-token authentication configured in your IdP — the bearer token is a Kaireon krn_ API key.

Authentication

Every SCIM request goes through requireSCIMAuth at src/lib/scim-auth.ts:23. The handler accepts two modes:
  1. Bearer token (production IdP integration). Send Authorization: Bearer krn_<your_api_key>. The token is verified against the ApiKey table and the bound tenantId is used for scoping. Failed verification returns a SCIM-formatted 401 error with schemas: ["urn:ietf:params:scim:api:messages:2.0:Error"].
  2. Session fallback (admin UI testing only). When no Authorization: Bearer header is present, the route falls back to the standard requireRole("admin") + requireTenant chain. This path is for debugging from a logged-in admin browser session and is not used by IdPs.

GET /api/v1/scim/v2/Users

List users in SCIM ListResponse format.

Query Parameters

ParameterTypeRequiredDescription
startIndexnumberNo1-based pagination index (default: 1)
countnumberNoPage size (default: 100, max: 200)
startIndex is clamped to >= 1 and count to [1, 200] (src/app/api/v1/scim/v2/Users/route.ts:48-49).

Response

{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
  "totalResults": 25,
  "startIndex": 1,
  "itemsPerPage": 25,
  "Resources": [
    {
      "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
      "id": "clx...",
      "userName": "john@example.com",
      "name": {
        "givenName": "John",
        "familyName": "Smith"
      },
      "displayName": "John Smith",
      "emails": [
        { "value": "john@example.com", "primary": true, "type": "work" }
      ],
      "active": true,
      "meta": {
        "resourceType": "User",
        "created": "2026-03-01T00:00:00.000Z",
        "lastModified": "2026-03-18T00:00:00.000Z",
        "location": "https://playground.kaireonai.com/api/v1/scim/v2/Users/clx..."
      }
    }
  ]
}
name.givenName is the first whitespace-delimited token of the user’s stored name; familyName is everything after the first space (src/app/api/v1/scim/v2/Users/route.ts:18-19). active is false only when the user has a lockedUntil timestamp in the future, otherwise true.

POST /api/v1/scim/v2/Users

Create a user from a SCIM resource. New users are provisioned with role member.

Request Body

{
  "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
  "userName": "jane@example.com",
  "name": {
    "givenName": "Jane",
    "familyName": "Doe"
  },
  "displayName": "Jane Doe",
  "emails": [
    { "value": "jane@example.com", "primary": true, "type": "work" }
  ]
}
The route resolves the email from userName first, then falls back to emails[0].value (src/app/api/v1/scim/v2/Users/route.ts:92). displayName is taken verbatim, otherwise composed from name.givenName + name.familyName, otherwise the email.

Response (201)

Returns the created SCIM User resource with the persisted id and meta.location.

Error — Missing userName/email (400)

{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"],
  "detail": "userName (email) is required",
  "status": 400
}

Error — User Exists (409)

The duplicate check is tenant-scoped, so the same email can exist in two different tenants without conflict.
{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"],
  "detail": "User already exists",
  "status": 409
}

GET /api/v1/scim/v2/Users/

Get a single user by ID. Tenant-scoped — returns 404 with a SCIM error envelope when the id does not belong to the bearer token’s tenant (src/app/api/v1/scim/v2/Users/[id]/route.ts:47-52).

PUT /api/v1/scim/v2/Users/

Replace a user resource. Supports updating displayName, userName, name, emails, and active status.
active valueEffect
falseSets lockedUntil = 2099-12-31 (effectively permanent until reactivated)
trueClears lockedUntil if it was set, otherwise no-op
omittedTreated as true (src/app/api/v1/scim/v2/Users/[id]/route.ts:94)
The audit row records the list of fields that were updated, not the values, to keep PII out of audit logs (src/app/api/v1/scim/v2/Users/[id]/route.ts:108-117).

DELETE /api/v1/scim/v2/Users/

Soft-deactivate a user by setting lockedUntil = 2099-12-31. The user record is not deleted — it is locked to preserve audit history and downstream foreign-key references (decision authorship, audit logs, etc.).

Response

204 No Content on success. 404 with SCIM error envelope when the id is not in the caller’s tenant.