krn_ API key scoped to "scim".
Base URL. The working SCIM base path is
/scim/v2/* (e.g. https://playground.kaireonai.com/scim/v2/Users). The /api/v1/scim/* alias exists in the codebase but Bearer-authenticated calls to it are blocked at middleware. Always use /scim/v2/ when configuring your IdP.Authentication
Every SCIM request runs through Kaireon’s SCIM auth gate, which accepts two modes:- Bearer token (production IdP integration). Send
Authorization: Bearer krn_<your_api_key>. The token is verified against your tenant’s API keys and the boundtenantIdis used for scoping. The API key must carry the"scim"scope — a key with an emptyscopesarray (legacy full-access key) is also accepted for backwards compatibility, but a non-emptyscopesarray that does not include"scim"returns a SCIM-formatted403 { detail: "API key lacks the 'scim' scope" }. Invalid or expired tokens return a SCIM-formatted401. - Session fallback (admin UI testing only). When no
Authorization: Bearerheader is present, the route falls back to the standardrequireRole("admin") + requireTenantchain. This path is for debugging from a logged-in admin browser session and is not used by IdPs.
Creating a SCIM-scoped API key
key value as Authorization: Bearer <key> when configuring your IdP.
GET /scim/v2/Users
List users in SCIM ListResponse format.Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
startIndex | number | No | 1-based pagination index (default: 1) |
count | number | No | Page size (default: 100, max: 200) |
startIndex is clamped to a minimum of 1 and count to the range [1, 200].
Response
userName is the user’s stored email (falling back to the user id when no email is set). name is emitted as { "formatted": "<stored full name>" }, and is omitted entirely when the user has no stored name. active is always true in this representation — the User resource does not carry a per-user disabled flag in this version. The default page size is count=50 (max 200) when no count query parameter is supplied.
POST /scim/v2/Users
Create a user from a SCIM resource.userName is required. New users are provisioned with role viewer.
Request Body
emails entry marked primary: true, then emails[0].value, then userName. The stored name is composed from name.givenName + name.familyName (space-joined, dropping any blank parts); when both are absent the name is stored as null.
Response (201)
Returns the created SCIM User resource (the same shape as the listResources[] entries — id, userName, name.formatted, emails, active: true, meta).
Error — Invalid body (400)
WhenuserName is missing or the body fails validation, the route returns the Zod validation message:
Error — User Exists (409)
The user-uniqueness check is by email. The error detail embeds the conflicting email.GET /scim/v2/Users/
Get a single user by ID. Tenant-scoped — returns404 { detail: "User not found" } with a SCIM error envelope when the id does not belong to the bearer token’s tenant.
PUT /scim/v2/Users/
Replace a user resource. The accepted body fields areuserName, name.formatted, and emails — the route updates the user’s email (from emails[0].value, falling back to userName, then the existing email) and name (from name.formatted, falling back to the existing name).
This version’s PUT does not accept or act on an
active field — the PutBody schema has no active property, so any active value in the request is ignored. There is no enable/disable-via-PUT behavior. The returned resource always reports active: true.404 { detail: "User not found" } when the id is not in the caller’s tenant, or 400 with the Zod validation message for a malformed body.
DELETE /scim/v2/Users/
Deletes the user record (prisma.user.delete). Tenant-scoped — the user must belong to the bearer token’s tenant.
Response
204 No Content on success. 404 { detail: "User not found" } with a SCIM error envelope when the id is not in the caller’s tenant.
Related
- Authentication — TOTP MFA, API keys, SSO
- OAuth 2.0 — machine-to-machine token grants