Bulk Operations & Search
Perform multiple SCIM 2.0 (System for Cross-domain Identity Management, the standard for syncing users and groups between systems) operations in a single request or run POST-based search across resource types. Use bulk operations to cut down on round trips when provisioning many users or groups at once; use .search when a filter expression would be too long for a query string.
Bulk Operations
POST
/orgs/\{orgId\}/api/v1/scim2.0/Bulk
Execute multiple SCIM operations (POST, PUT, DELETE) in a single HTTP request. Operations are processed sequentially and can reference resources created earlier in the same request using bulkId.
Each operation is checked against the organization's provisioning policy individually. A switched-off operation gets "status": "403" and a SCIM error as its response; the other operations in the same request still run and the HTTP status of the Bulk response stays 200.
Passwords in bulk POST and PUT operations are checked against the organization's password policy and the breached-password list like any other SCIM write. A rejected password gives that operation "status": "400" with scimType: invalidValue; a replaced password signs the user out everywhere and revokes their tokens.
{
"method": "POST",
"bulkId": "user3",
"status": "403",
"response": {
"schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status": "403",
"scimType": "mutability",
"detail": "User creation is disabled by this organization's SCIM provisioning policy"
}
}
curl -X POST "https://app.lumoauth.dev/orgs/acme-corp/api/v1/scim2.0/Bulk" \
-H "Authorization: Bearer $SCIM_TOKEN" \
-H "Content-Type: application/scim+json" \
-d '{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:BulkRequest"],
"Operations": [
{
"method": "POST",
"path": "/Users",
"bulkId": "user1",
"data": {
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName": "alice@acme.com",
"name": {"givenName": "Alice", "familyName": "Wonder"}
}
},
{
"method": "POST",
"path": "/Users",
"bulkId": "user2",
"data": {
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName": "bob@acme.com",
"name": {"givenName": "Bob", "familyName": "Builder"}
}
},
{
"method": "POST",
"path": "/Groups",
"data": {
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"displayName": "New Team",
"members": [
{"value": "bulkId:user1"},
{"value": "bulkId:user2"}
]
}
}
]
}'
Response
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:BulkResponse"],
"Operations": [
{
"method": "POST",
"bulkId": "user1",
"location": "https://app.lumoauth.dev/orgs/acme-corp/api/v1/scim2.0/Users/100",
"status": "201",
"response": {
"id": "100",
"userName": "alice@acme.com"
}
},
{
"method": "POST",
"bulkId": "user2",
"location": "https://app.lumoauth.dev/orgs/acme-corp/api/v1/scim2.0/Users/101",
"status": "201",
"response": {
"id": "101",
"userName": "bob@acme.com"
}
},
{
"method": "POST",
"location": "https://app.lumoauth.dev/orgs/acme-corp/api/v1/scim2.0/Groups/50",
"status": "201",
"response": {
"id": "50",
"displayName": "New Team"
}
}
]
}
Request Parameters
| Field | Type | Description |
|---|---|---|
Operations | array | Array of operations to execute |
failOnErrors | integer | Stop processing after this many errors (0 = continue all) |
Operation Fields
| Field | Description |
|---|---|
method | HTTP method: POST, PUT, or DELETE |
path | Resource path (e.g., /Users, /Groups/123) |
bulkId | Client-generated ID for cross-referencing |
data | Resource data for POST/PUT |
bulkId references let you create related resources in one request — for example, create a user and add them to a group in the same bulk call.
Search
POST
/orgs/\{orgId\}/api/v1/scim2.0/.search
POST-based search allows complex queries that may be too long for URL query parameters. This endpoint can search across all resource types.
curl -X POST "https://app.lumoauth.dev/orgs/acme-corp/api/v1/scim2.0/.search" \
-H "Authorization: Bearer $SCIM_TOKEN" \
-H "Content-Type: application/scim+json" \
-d '{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:SearchRequest"],
"filter": "name.familyName sw \"Sm\" and active eq true",
"startIndex": 1,
"count": 50,
"sortBy": "userName",
"sortOrder": "ascending",
"attributes": ["userName", "name", "emails"]
}'
Resource-Specific Search
You can also POST to resource-specific search endpoints:
# Search only Users
POST /scim2.0/Users/.search
# Search only Groups
POST /scim2.0/Groups/.search
Filter Syntax
SCIM filters follow the SCIM 2.0 protocol's filter syntax. Supported operators:
| Operator | Description | Example |
|---|---|---|
eq | Equal | userName eq "john@acme.com" |
ne | Not equal | active ne false |
co | Contains | userName co "acme" |
sw | Starts with | name.familyName sw "Sm" |
ew | Ends with | userName ew "@acme.com" |
gt | Greater than | meta.created gt "2024-01-01" |
ge | Greater or equal | meta.created ge "2024-01-01" |
lt | Less than | meta.lastModified lt "2024-06-01" |
le | Less or equal | meta.lastModified le "2024-06-01" |
pr | Present (has value) | externalId pr |
Complex Filters
Combine expressions with and, or, and not, group them with parentheses, and filter inside multi-valued attributes with the attr[...] syntax:
# Active users in a specific domain
userName ew "@acme.com" and active eq true
# Either of two family-name prefixes
name.familyName sw "Sm" or name.familyName sw "Jo"
# Negation with grouping
not (active eq false) and meta.lastModified gt "2024-06-01T00:00:00Z"
# Match within a multi-valued attribute (a work email ending in @acme.com)
emails[type eq "work" and value ew "@acme.com"]
Filterable attributes
| Resource | Attributes |
|---|---|
| Users | id, externalId, userName, name.givenName, name.familyName, name.middleName, name.formatted, displayName, nickName, profileUrl, title, userType, preferredLanguage, locale, timezone, active, meta.created, meta.lastModified, isSoftDeleted, plus emails and phoneNumbers (below) |
Groups (.search) | id, externalId, displayName, meta.created, meta.lastModified, isSoftDeleted |
Value-path filters work on emails and phoneNumbers, either as emails[...] or as a dotted sub-attribute (emails.value co "@acme.com"). Inside the brackets you can use the sub-attributes value, type and primary, every comparison operator, and and / or / not. LumoAuth stores one e-mail and one phone number per user and returns each with type: "work" and primary: true, so type and primary filters compare against those values. Nested value paths (emails[value[...]]) are not supported.
GET /Groups accepts a narrower set. The whole filter must be exactly one of:
displayName eq "Engineering" # also co, sw
externalId eq "okta-00g1"
id eq "10"
members[value eq "123"] # groups the user with id 123 belongs to
Invalid filters
An unknown attribute, an unsupported sub-attribute or operator, or a malformed expression returns 400 with scimType: invalidFilter. LumoAuth never ignores the part of a filter it does not understand, so a filter can no longer silently match every user or group.
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status": "400",
"scimType": "invalidFilter",
"detail": "The filter expression is not valid."
}
Pass any supported filter as the filter value in a .search body or as the filter query parameter on GET /Users / GET /Groups.
Me Endpoint
GET
PUT
PATCH
DEL
/orgs/\{orgId\}/api/v1/scim2.0/Me
/Me is a self-service alias that resolves to the currently authenticated user, so a client never needs to know its own resource id. It returns the same representation as /Users/{id}. Call it with an OAuth access token that carries the scim scope ($ACCESS_TOKEN below).
- The provisioning token gets
501. It authenticates an integration, not a person, so it has no/Me. Use/Users/{id}. - No password changes. A
PUTbody orPATCHoperation that touchespasswordreturns400withscimType: mutability. Users change their password in their account settings or through password reset. - No
userNamechanges. ChanginguserNamereturns400mutability.
# Get my profile
curl -X GET "https://app.lumoauth.dev/orgs/acme-corp/api/v1/scim2.0/Me" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Accept: application/scim+json"
# Update my display name
curl -X PATCH "https://app.lumoauth.dev/orgs/acme-corp/api/v1/scim2.0/Me" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/scim+json" \
-d '{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{"op": "replace", "path": "displayName", "value": "Johnny S"}
]
}'
PUT /Me replaces the caller's profile attributes. DELETE /Me is not supported and returns 501.
Standards used on this page
| Plain name | Standard |
|---|---|
| SCIM 2.0 (user and group provisioning) | RFC 7642, RFC 7643 and RFC 7644 |
See the standards reference for what each one covers and how LumoAuth uses it.