Summary and recommendation
The Khoros Community REST API is available at `https://{community-domain}/api/2.0` and supports core user lifecycle operations: create, read, update, ban, and soft-delete (anonymize). Authentication uses session tokens obtained via Basic Auth or LithiumSSO shared-secret token generation; there is no fine-grained OAuth scope system, so admin-level credentials are required for most user management calls.
LiQL (Lithium Query Language) is the primary mechanism for querying user collections. Standard REST list endpoints have limited filtering; structured queries against the `/api/2.0/search` endpoint with `SELECT ... FROM users WHERE ...` syntax are required for any meaningful filtering or bulk export.
Max `LIMIT` per LiQL query is 1000; use `OFFSET` pagination for larger sets.
For teams building against an identity graph, the `sso_id` field on the user object is the recommended join key between Khoros and the upstream IdP.
It must be populated at account creation or via SSO JIT - not all community configurations expose `sso_id` in LiQL by default, so verify field availability in your instance before relying on it for cross-system identity resolution.
API quick reference
| Has user API | Yes |
| Auth method | LithiumSSO token or HTTP Basic Auth (admin credentials); OAuth 2.0 referenced for SSO integrations but REST API primarily uses session/token-based auth |
| Base URL | Official docs |
| SCIM available | No |
| SCIM plan required | Enterprise |
Authentication
Auth method: LithiumSSO token or HTTP Basic Auth (admin credentials); OAuth 2.0 referenced for SSO integrations but REST API primarily uses session/token-based auth
Setup steps
- Obtain admin credentials or a service account for your Khoros Community instance.
- Authenticate via POST to /api/2.0/auth/signInWithClientCredentials or use Basic Auth header with base64-encoded admin:password.
- Retrieve the session token (value field) from the authentication response.
- Pass the token as a cookie (lithiumSSO={token}) or as a query parameter (restapi.session_key) on subsequent requests.
- For SSO-integrated environments, configure LithiumSSO shared secret in Community Admin > System > SSO settings and generate tokens server-side using the shared secret.
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| id | integer | Unique numeric identifier for the user | auto-assigned | immutable | Internal Khoros user ID |
| login | string | Username / login handle | required | supported | Must be unique within the community |
| string | User's email address | required | supported | Used for notifications and account recovery | |
| password | string | User password (write-only) | required (unless SSO) | supported | Never returned in GET responses |
| first_name | string | User's first name | optional | supported | Profile field |
| last_name | string | User's last name | optional | supported | Profile field |
| biography | string | User bio / about me text | optional | supported | Profile field |
| avatar | object | User avatar image reference | optional | supported | Contains profile_image URL |
| rank | object | User's community rank object | auto-assigned | admin-only | Contains rank name and id |
| roles | array | Roles assigned to the user | optional | supported (admin) | Controls permissions within the community |
| registration_time | datetime | Timestamp of user registration | auto-assigned | immutable | ISO 8601 format |
| last_visit_time | datetime | Timestamp of user's last visit | auto-assigned | system-managed | Read-only |
| status | string | Account status (e.g., enabled, banned) | optional | supported (admin) | Values: enabled, banned, anonymous |
| banned | boolean | Whether the user is banned | optional | supported (admin) | Setting true bans the user |
| deleted | boolean | Whether the user account is deleted/anonymized | n/a | supported (admin) | Soft-delete; anonymizes PII |
| sso_id | string | External SSO identifier for the user | optional | supported | Used to link Khoros account to external IdP identity |
| kudos_received | integer | Total kudos (likes) received by the user | auto | system-managed | Read-only engagement metric |
| messages_count | integer | Total posts/messages authored by the user | auto | system-managed | Read-only |
Core endpoints
Get User by ID
- Method: GET
- URL:
https://{community-domain}/api/2.0/users/{user_id} - Watch out for: Requires admin credentials or a session token with sufficient privileges. Regular user tokens can only retrieve their own profile.
Request example
GET /api/2.0/users/12345
Authorization: Basic {base64(admin:password)}
Response example
{
"status": "success",
"data": {
"type": "user",
"id": 12345,
"login": "jdoe",
"email": "jdoe@example.com"
}
}
List Users (LiQL query)
- Method: GET
- URL:
https://{community-domain}/api/2.0/search?q=SELECT+*+FROM+users+LIMIT+10 - Watch out for: LiQL (Lithium Query Language) is the primary way to query collections. Max LIMIT is 1000 per query. Use OFFSET for pagination.
Request example
GET /api/2.0/search?q=SELECT id,login,email FROM users LIMIT 10 OFFSET 0
Authorization: Basic {base64(admin:password)}
Response example
{
"status": "success",
"data": {
"type": "users",
"list": [
{"id": 1, "login": "alice"},
{"id": 2, "login": "bob"}
]
}
}
Create User
- Method: POST
- URL:
https://{community-domain}/api/2.0/users - Watch out for: Admin credentials required. Email verification may be triggered depending on community settings. SSO communities may use JIT provisioning instead.
Request example
POST /api/2.0/users
Content-Type: application/json
{"login":"newuser","email":"new@example.com","password":"Str0ng!"}
Response example
{
"status": "success",
"data": {
"type": "user",
"id": 99001,
"login": "newuser"
}
}
Update User
- Method: PUT
- URL:
https://{community-domain}/api/2.0/users/{user_id} - Watch out for: Full PUT semantics; omitting optional fields may reset them. Use only fields you intend to change and verify field behavior in your community version.
Request example
PUT /api/2.0/users/12345
Content-Type: application/json
{"first_name":"Jane","biography":"Updated bio"}
Response example
{
"status": "success",
"data": {
"type": "user",
"id": 12345,
"first_name": "Jane"
}
}
Delete (Anonymize) User
- Method: DELETE
- URL:
https://{community-domain}/api/2.0/users/{user_id} - Watch out for: Khoros performs a soft-delete / anonymization rather than hard deletion. User content is retained but PII is scrubbed. This action is irreversible.
Request example
DELETE /api/2.0/users/12345
Authorization: Basic {base64(admin:password)}
Response example
{
"status": "success",
"message": "User anonymized successfully"
}
Ban User
- Method: PUT
- URL:
https://{community-domain}/api/2.0/users/{user_id} - Watch out for: Banning is done via the update endpoint. Banned users cannot log in but their content remains visible unless separately moderated.
Request example
PUT /api/2.0/users/12345
Content-Type: application/json
{"banned":true}
Response example
{
"status": "success",
"data": {
"type": "user",
"id": 12345,
"banned": true
}
}
Assign Role to User
- Method: POST
- URL:
https://{community-domain}/api/2.0/users/{user_id}/roles - Watch out for: Role IDs are community-specific strings. Retrieve available roles via GET /api/2.0/roles before assigning. Board-scoped roles require specifying the board context.
Request example
POST /api/2.0/users/12345/roles
Content-Type: application/json
{"role":{"id":"moderator"}}
Response example
{
"status": "success",
"data": {
"type": "role",
"id": "moderator"
}
}
Get User by SSO ID (LiQL)
- Method: GET
- URL:
https://{community-domain}/api/2.0/search?q=SELECT+*+FROM+users+WHERE+sso_id+%3D+%27{sso_id}%27 - Watch out for: sso_id field must be populated at account creation or via SSO JIT. Not all community configurations expose sso_id in LiQL queries.
Request example
GET /api/2.0/search?q=SELECT id,login,email FROM users WHERE sso_id = 'ext-user-abc123'
Authorization: Basic {base64(admin:password)}
Response example
{
"status": "success",
"data": {
"type": "users",
"list": [{"id": 12345, "login": "jdoe"}]
}
}
Rate limits, pagination, and events
- Rate limits: Khoros Community REST API rate limits are not publicly documented with specific numeric thresholds. Limits are enforced per community instance and may vary by plan. Excessive requests result in HTTP 429 responses.
- Rate-limit headers: No
- Retry-After header: No
- Rate-limit notes: No official public documentation of specific rate limit values. Contact Khoros support for instance-level limits. HTTP 429 is returned when limits are exceeded.
- Pagination method: offset
- Default page size: 10
- Max page size: 1000
- Pagination pointer: page.offset / page.size (LiQL queries) or start / count (REST v2 list endpoints)
| Plan | Limit | Concurrent |
|---|---|---|
| Standard/Enterprise | Not publicly specified; enforced per instance | 0 |
- Webhooks available: No
- Webhook notes: Khoros Community does not offer a native outbound webhook system for user lifecycle events as documented in official developer docs. Event-driven integrations are typically handled via Khoros Flow (automation product) or polling the REST API.
- Alternative event strategy: Use Khoros Flow (if licensed) for event-driven automation, or poll the REST API using LiQL queries filtered by registration_time or last_visit_time to detect new/changed users.
SCIM API status
- SCIM available: No
- SCIM version: Not documented
- Plan required: Enterprise
- Endpoint: Not documented
Limitations:
- Khoros Community does not natively support SCIM 2.0 provisioning as of available documentation.
- User provisioning is handled via JIT (Just-in-Time) provisioning through SAML SSO.
- Enterprise plan required for SSO/JIT provisioning features.
- No SCIM endpoint is documented in the official Khoros developer portal.
- Deprovisioning must be handled manually via the REST API or by disabling SSO access at the IdP level.
Common scenarios
Provisioning a new user via the API requires a POST to /api/2.0/users with login, email, and either password or sso_id. In SSO-enabled communities, REST-created accounts and JIT-provisioned accounts can conflict if the same email or sso_id is used - confirm account coexistence behavior with your Khoros admin before building this flow.
Deprovisioning should follow a two-step sequence: first ban the user via PUT /api/2.0/users/{user_id} with {"banned": true} to immediately block login, then issue DELETE /api/2.0/users/{user_id} if PII removal is required. Critically, DELETE performs a soft anonymization - the user record and content are retained, only PII is scrubbed, and the action is irreversible. Disable the IdP account before or in parallel; if SSO remains active, JIT can re-provision a new account after anonymization.
Bulk export for audit or identity graph sync uses paginated LiQL: SELECT id, login, email, registration_time, status FROM users LIMIT 1000 OFFSET {n}. For communities with large member bases, filter by registration_time ranges to support incremental syncs and reduce per-page latency. Custom profile fields are community-specific and must be queried by their configured field names, which are not standardized across instances.
Provision a new community user from an external system
- Authenticate to the Khoros Community REST API using admin credentials via POST or Basic Auth header.
- POST to /api/2.0/users with required fields: login, email, password (or sso_id for SSO environments).
- Capture the returned user id for future reference.
- Optionally assign roles via POST /api/2.0/users/{user_id}/roles using the appropriate role id.
- Store the Khoros user id mapped to the external system's user identifier for future updates.
Watch out for: If the community uses SSO/JIT provisioning, creating users via the REST API may conflict with SSO-managed accounts. Confirm with Khoros admin whether REST-created accounts can coexist with SSO accounts.
Deprovision a user when they leave the organization
- Look up the user's Khoros id using a LiQL query: SELECT id FROM users WHERE sso_id = '{external_id}' or WHERE email = '{email}'.
- Ban the user immediately to prevent login: PUT /api/2.0/users/{user_id} with {"banned": true}.
- If full removal is required for GDPR/privacy compliance, issue DELETE /api/2.0/users/{user_id} to anonymize the account.
- Disable or remove the user's SSO access at the IdP level (e.g., Entra ID) to prevent JIT re-provisioning.
Watch out for: DELETE anonymizes rather than hard-deletes. If the user re-authenticates via SSO after anonymization, JIT provisioning may create a new account. Disable IdP access first.
Bulk-export users for audit or sync
- Use LiQL via GET /api/2.0/search?q=SELECT id,login,email,registration_time,status FROM users LIMIT 1000 OFFSET 0.
- Iterate through pages by incrementing OFFSET by 1000 until the returned list size is less than 1000.
- For each user, optionally fetch extended profile fields via GET /api/2.0/users/{user_id} if LiQL does not expose custom fields.
- Write results to your target system, mapping Khoros user id to external identifiers.
Watch out for: LiQL LIMIT is capped at 1000. For communities with large user bases, pagination via OFFSET can be slow. Filter by registration_time ranges to reduce result sets for incremental syncs.
Why building this yourself is a trap
The most significant integration trap is the ban/JIT re-provisioning gap. Banning a user via the API sets banned: true on the Khoros record but has no effect on the upstream IdP session.
If the IdP account remains active, the user can re-authenticate via SSO and JIT will create a new Khoros account, silently bypassing the ban. Any deprovision workflow that does not include an IdP-side disable step is incomplete.
Authentication tokens obtained via Basic Auth are session-scoped and subject to expiration. Long-running integrations must implement token refresh logic; there is no documented token TTL, so treat any 401 response as a signal to re-authenticate rather than a hard failure.
Rate limits are not publicly documented with numeric thresholds. HTTP 429 is returned when limits are exceeded, but no Retry-After header is provided. Production integrations must implement exponential backoff.
For teams using an MCP server with 60+ deep IT/identity integrations, Khoros should be treated as a polling-dependent source - there is no native webhook system for user lifecycle events. Event-driven patterns require either Khoros Flow (if licensed) or scheduled LiQL polling filtered by registration_time or last_visit_time.
Automate Khoros workflows without one-off scripts
Stitchflow builds and maintains end-to-end IT automation across your SaaS stack, including apps without APIs. Built for exactly how your company works, with human approvals where they matter.