Skip to main content

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​

FieldTypeDescription
OperationsarrayArray of operations to execute
failOnErrorsintegerStop processing after this many errors (0 = continue all)

Operation Fields​

FieldDescription
methodHTTP method: POST, PUT, or DELETE
pathResource path (e.g., /Users, /Groups/123)
bulkIdClient-generated ID for cross-referencing
dataResource data for POST/PUT
BulkId References

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.

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"]
}'

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:

OperatorDescriptionExample
eqEqualuserName eq "john@acme.com"
neNot equalactive ne false
coContainsuserName co "acme"
swStarts withname.familyName sw "Sm"
ewEnds withuserName ew "@acme.com"
gtGreater thanmeta.created gt "2024-01-01"
geGreater or equalmeta.created ge "2024-01-01"
ltLess thanmeta.lastModified lt "2024-06-01"
leLess or equalmeta.lastModified le "2024-06-01"
prPresent (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​

ResourceAttributes
Usersid, 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 PUT body or PATCH operation that touches password returns 400 with scimType: mutability. Users change their password in their account settings or through password reset.
  • No userName changes. Changing userName returns 400 mutability.
# 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 nameStandard
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.