OXL Expressions
What is OXL?
OXL (Owlie Expression Language) is a lightweight formula language. It lets you reference data from a context object and transform it using operators and functions — similar to a spreadsheet formula.
Syntax at a glance
Referencing context values
Every expression runs against a context object. Use dot notation or bracket notation to access values.
Given this context:
{
"identity": {
"firstName": "Jane",
"lastName": "Doe",
"email": "jane@acme.com"
}
}
| Expression | Result |
|---|---|
identity.email | "jane@acme.com" |
identity["firstName"] | "Jane" |
identity.firstName + " " + identity.lastName | "Jane Doe" |
Arithmetic
| Expression | Result |
|---|---|
10 + 5 | 15 |
10 - 5 | 5 |
3 * 4 | 12 |
10 / 3 | 3.333… |
10 // 3 | 3 (floor division) |
10 % 3 | 1 (remainder) |
2 ^ 8 | 256 (exponentiation) |
Comparison and logic
Given this context:
{
"age": 25,
"role": "admin",
"active": true
}
| Expression | Result |
|---|---|
age >= 18 | true |
role == "admin" | true |
role == "admin" && active | true |
!active | false |
The in operator
Checks membership in arrays and substrings in strings.
Given this context:
{
"roles": ["viewer", "admin"],
"email": "jane@acme.com"
}
| Expression | Result |
|---|---|
"admin" in roles | true |
"@acme" in email | true |
Conditional (ternary) expressions
condition ? valueIfTrue : valueIfFalse
Given this context:
{
"active": true,
"nickname": null,
"firstName": "Jane"
}
| Expression | Result |
|---|---|
active ? "enabled" : "disabled" | "enabled" |
nickname ?: firstName | "Jane" (shorthand — returns first truthy value) |
Arrays
Given this context:
{
"roles": ["viewer", "editor", "admin"],
"groups": [
{ "name": "eng", "active": true },
{ "name": "sales", "active": false }
]
}
| Expression | Result |
|---|---|
roles[0] | "viewer" |
first(roles) | "viewer" |
length(roles) | 3 |
groups[.active == true] | [{"name": "eng", "active": true}] |
Array filtering uses relative identifiers — the . prefix means "this element":
groups[.active == true]
This filters the groups array, keeping only elements where .active is true.
String functions
Given this context:
{
"identity": {
"firstName": "Jane",
"lastName": "Doe",
"email": "jane@acme.com"
}
}
| Expression | Result |
|---|---|
upper(identity.lastName) | "DOE" |
lower(identity.firstName) | "jane" |
split(identity.email, "@") | ["jane", "acme.com"] |
contains(identity.email, "@acme.com") | true |
replace(identity.email, "acme.com", "newco.com") | "jane@newco.com" |
Null-safe patterns
Given this context:
{
"nickname": null,
"firstName": "Jane",
"tags": []
}
| Expression | Result |
|---|---|
coalesce(nickname, firstName) | "Jane" |
isEmpty(tags) | true |
Object introspection
Given this context:
{
"attrs": {
"dept": "Engineering",
"level": "senior"
}
}
| Expression | Result |
|---|---|
keys(attrs) | ["dept", "level"] |
values(attrs) | ["Engineering", "senior"] |
length(attrs) | 2 |
Generating passwords
Use genPassword() in provisioning expressions to create secure random passwords.
| Expression | Result (example) |
|---|---|
genPassword() | "aK3!mR7@pL2#nQ5&" (16 chars) |
genPassword(24) | "xP4!kM8@rL2#nQ5&wT9*yB7=" (24 chars) |
Every generated password includes at least one lowercase letter, one uppercase letter, one digit, and one special character.
Putting it together
Build a display name from identity fields:
identity.firstName + " " + upper(substring(identity.lastName, 0, 1)) + "."
Result: "Jane D."
Assign a license tier based on role membership:
"admin" in roles ? "enterprise" : (length(roles) > 1 ? "pro" : "basic")
Result: "enterprise" (when roles include "admin")
Extract a domain from an email address:
last(split(config.email, "@"))
Result: "acme.com"
Check if a user belongs to any active engineering group:
length(groups[.active == true && startsWith(.name, "eng")]) > 0
Result: true
Troubleshooting
- Expression too long — Expressions are limited to 2,000 characters.
- Timeout errors — Expressions must evaluate within 500ms. Avoid filtering extremely large arrays.
- Variable not found — Double-check that the variable name matches the context. Names are case-sensitive.
Next steps
- OXL Reference — Complete list of operators, functions, types, and limits