Both fields use the same operators and attributes. This is the smallest useful condition:
Engineering.
How a condition is built
A condition is either a rule or a combination of conditions. A rule names oneattribute, one operator, and the value to compare against, like the
example above. Put exactly one operator in each rule. A rule with two operators returns 400.
Combine conditions with and and or. Each takes a list of at least one condition, and
combinations can nest.
not. These two forms mean the same thing. The second is the one the condition
builder in Lumos writes.
attribute itself, can instead be an object with a label and a
value. Lumos compares value and keeps label for display.
What Lumos rejects
Lumos checks a condition when you save it, and returns400 for a condition it can’t evaluate:
- A key it doesn’t recognize, such as a misspelled operator.
- A rule with more than one operator.
- An attribute that isn’t in Attributes, including a misspelled path or a custom attribute your organization has deleted.
- An operator that isn’t offered for the attribute’s type, or one that isn’t offered negated.
- A date-time without a time zone, as Values describes.
code validation_error on an Access Policy and bad_request on a
workflow. Every other problem returns bad_request, and detail names each problem. On a
workflow, detail starts with the branch, such as branches[0].condition.
Apart from date-times, Lumos doesn’t check values, so a misspelled group ID or status saves, and
equals on it never matches.
A condition saved in Lumos before an attribute was deleted fails the same check, so an update that
sends it back returns 400.
Patch Access Policy without
access_condition keeps the stored condition.
Stored form
Lumos returns a condition in its stored form. In that form,attribute and every value are objects
with a label and a value, and Lumos adds the labels you didn’t send.
You send
You read back
value fields and ignore the labels.
Operators
Offered for lists the attribute types each operator is offered for. Pair each
attribute with an operator offered for its type. Any other pairing returns
400.
Every operator except equals, !=, >, >=, <, and <= is offered negated with not.
Each of those six has an opposite operator, so it isn’t offered negated, and negating one returns
400.
Write != rather than negating equals, and <= rather than negating >. exists works either
way, because "exists": false and "not": { "exists": true } match the same identities.
Text comparisons are case-sensitive, so "ends_with": "@acme.com" doesn’t match Jane@ACME.com.
contains, in, contains_any, and is_subset_of
These four names sound alike, but the operators behave differently.
contains matches part of the attribute’s text. contains_any compares whole values, so a rule
asking for the group app-dropbox-read doesn’t match an identity in the group app-dropbox-readwrite.
Give in a list. Given a single string, in matches when the attribute’s value appears inside
that string. For example, {"attribute": "identity.team", "in": "Engineering"} also matches a team
named Eng, and a team whose value is an empty string.
is_subset_of is the “every value” counterpart to contains_any. An attribute with no value never
matches it, even against an empty list. No attribute on this page offers it, so a rule that uses it
returns 400.
Attributes
Anattribute is a path into the identity being checked. These are the paths Lumos offers:
In a rule, use the group’s
integration_specific_id, not its id. An integration_specific_id is
unique only within the app that syncs the group. If two apps sync groups that have the same
integration_specific_id, a rule that names that ID matches the members of both groups.
In a workflow branch step, identity.source_app.id never has a value. Rules on it work only in
Access Policies.
Custom attributes
Every custom attribute your organization defines on users is available by its label. The path depends on the custom attribute’s type:
Custom attributes of other types aren’t offered.
Inside a JSON string, escape the double quotes around the label
(
"identity.custom_attributes[\"Department\"]") or use single quotes
("identity.custom_attributes['Department']"). Both work, and the examples on this page use single
quotes.
To find a label, call
List Custom Attribute Definitions
with config_type=user. The label field is what goes in the path.
The Custom Attribute Definition object covers
the other fields.
To see one identity’s values, call
GET /v1/users/{user_id}?expand=custom_attributes. Its
custom_attributes map is keyed by the same labels.
identity.team, identity.title, identity.start_date, and identity.end_date have a value only
for identities that hold the matching custom attribute. To write a rule on an identity’s manager,
use identity.custom_attributes['Manager'].id, which is offered as a string, rather than
identity.manager.uuid.
Values
- Date-times are ISO 8601 strings with a time zone, such as
2025-01-01T00:00:00Z. - Booleans are JSON
trueandfalse. The string"true"never equalstrue. - Numbers and text compare loosely in
equalsandin, so5030matches"5030".
2025-01-01 or 2025-01-01T00:00:00, returns 400, and
so does a value that isn’t ISO 8601. exists takes true or false, so this doesn’t apply to it.
Missing values
An attribute has no value when the identity has nothing for it, such as no team or no end date. A list with no items counts as no value too, so an identity in no groups has no value foridentity.groups[*].external_id. An empty string is a value.
exists tests for this. true matches identities that have a value, and false matches those
that don’t.
Every other operator treats a missing value as a mismatch, except != and negated operators, which
match it:
A rule for “everyone not in Sales” includes identities with no team. To exclude them, add an
exists rule with and:
Examples
Department is Engineering
Department custom attribute is exactly Engineering. An identity with
no department doesn’t match.
Member of any of these groups
integration_specific_id from GET /v1/groups.
Everyone
Don’t write this condition yourself. On an Access Policy, setis_everyone_condition to true, and Lumos
stores this condition:
access_condition, and Lumos reads
it as is_everyone_condition: true. A workflow branch step rejects it, because
identity.stable_identifier isn’t an attribute it offers.