CEL Functions Reference
Complete reference for all functions available in Mycel transforms and conditions.
Context Variables
| Variable |
Description |
input |
Incoming data (request body, message payload, query result) |
output |
Already-computed output fields in the current transform |
ctx |
Request context: headers, path params, user info |
enriched |
Data fetched from enrich blocks |
step |
Results from named step blocks |
result |
Flow result (in aspect conditions) |
error |
Error message string (in aspect conditions) |
_flow |
Flow name (in aspects) |
_operation |
Operation name (in aspects) |
_target |
Target name (in aspects) |
_timestamp |
Unix timestamp (in aspects) |
Mycel Built-in Functions
Identity and Time
| Function |
Signature |
Returns |
Description |
uuid() |
() → string |
UUID v4 string |
Generate a random UUID |
now() |
() → string |
RFC3339 string |
Current UTC time |
now_unix() |
() → int |
Unix timestamp |
Current time as Unix seconds |
uuid() // "550e8400-e29b-41d4-a716-446655440000"
now() // "2025-12-29T15:04:05Z"
now_unix() // 1735488245
String Functions
| Function |
Signature |
Description |
lower(s) |
(string) → string |
Convert to lowercase |
upper(s) |
(string) → string |
Convert to uppercase |
trim(s) |
(string) → string |
Remove leading/trailing whitespace |
replace(s, old, new) |
(string, string, string) → string |
Replace all occurrences of old with new |
split(s, sep) |
(string, string) → list(string) |
Split string by separator |
join(list, sep) |
(list(string), string) → string |
Join list items with separator |
substring(s, start, end) |
(string, int, int) → string |
Extract substring (byte indices) |
len(s) |
(string) → int |
String length in bytes |
hash_sha256(s) |
(string) → string |
SHA-256 hash (hex encoded) |
format_date(date, fmt) |
(string, string) → string |
Reformat ISO date string |
lower("HELLO") // "hello"
upper("hello") // "HELLO"
trim(" hello ") // "hello"
replace("hello", "l", "L") // "heLLo"
split("a,b,c", ",") // ["a", "b", "c"]
join(["a", "b", "c"], "-") // "a-b-c"
substring("hello", 1, 4) // "ell"
len("hello") // 5
hash_sha256("password") // hex string
// format_date tokens: YYYY MM DD HH mm ss
format_date("2025-01-15T10:30:00Z", "YYYY-MM-DD") // "2025-01-15"
format_date(now(), "DD/MM/YYYY HH:mm") // "15/01/2025 10:30"
Default and Null Handling
| Function |
Signature |
Description |
default(value, fallback) |
(any, any) → any |
Return fallback if value is null or empty string |
coalesce(value, fallback) |
(any, any) → any |
Alias for default |
default(input.nickname, input.name) // Use name if nickname is null/empty
coalesce(input.phone, "N/A") // "N/A" if phone is null/empty
Map Functions
| Function |
Signature |
Description |
merge(m1, m2) |
(map, map) → map |
Merge two maps; m2 values override m1 |
merge(m1, m2, m3) |
(map, map, map) → map |
Merge three maps |
merge(m1, m2, m3, m4) |
(map, map, map, map) → map |
Merge four maps |
omit(m, k1) |
(map, string) → map |
Return map without key k1 |
omit(m, k1, k2) |
(map, string, string) → map |
Return map without keys k1 and k2 |
omit(m, k1, k2, k3) |
(map, string, string, string) → map |
Return map without up to 3 keys |
pick(m, k1) |
(map, string) → map |
Return map with only key k1 |
pick(m, k1, k2) |
(map, string, string) → map |
Return map with only keys k1 and k2 |
pick(m, k1, k2, k3) |
(map, string, string, string) → map |
Return map with only up to 3 keys |
merge(step.order, {"customer": step.customer})
omit(input, "password")
omit(input, "password", "secret_token")
pick(input, "id", "email")
Array Functions
| Function |
Signature |
Description |
first(list) |
(list) → any |
First element, or null if empty |
last(list) |
(list) → any |
Last element, or null if empty |
flatten(list) |
(list(list)) → list |
Flatten one level of nesting |
unique(list) |
(list) → list |
Remove duplicate values |
reverse(list) |
(list) → list |
Reverse list order |
pluck(list, key) |
(list(map), string) → list |
Extract a field from each map in a list |
sort_by(list, key) |
(list(map), string) → list |
Sort list of maps by field (ascending) |
sum(list) |
(list(number)) → number |
Sum numeric values |
avg(list) |
(list) → double |
Average of numeric values |
min_val(list) |
(list) → any |
Minimum value |
max_val(list) |
(list) → any |
Maximum value |
first(input.items) // first item or null
last(input.items) // last item or null
flatten([[1, 2], [3, 4]]) // [1, 2, 3, 4]
unique([1, 2, 2, 3]) // [1, 2, 3]
reverse([1, 2, 3]) // [3, 2, 1]
pluck(input.orders, "total") // [100, 200, 150]
sort_by(input.products, "price") // sorted by price asc
sum(pluck(input.items, "price")) // sum of prices
avg(pluck(input.scores, "value")) // average score
min_val(pluck(input.bids, "amount")) // minimum bid
max_val(pluck(input.bids, "amount")) // maximum bid
GraphQL Field Selection
| Function |
Signature |
Description |
has_field(input, path) |
(map, string) → bool |
True if field path was requested in the GraphQL query |
field_requested(input, path) |
(map, string) → bool |
Alias for has_field |
requested_fields(input) |
(map) → list(string) |
All requested field paths |
requested_top_fields(input) |
(map) → list(string) |
Top-level requested fields only |
has_field(input, "orders") // true if "orders" was queried
has_field(input, "orders.items") // true if nested field was requested
requested_fields(input) // ["id", "name", "orders", "orders.total"]
requested_top_fields(input) // ["id", "name", "orders"]
CEL Standard Extensions
ext.Strings — Extended String Operations
| Function |
Signature |
Description |
s.charAt(i) |
(string, int) → string |
Character at index |
s.indexOf(sub) |
(string, string) → int |
First index of substring (-1 if not found) |
s.lastIndexOf(sub) |
(string, string) → int |
Last index of substring |
s.upperAscii() |
(string) → string |
ASCII uppercase |
s.lowerAscii() |
(string) → string |
ASCII lowercase |
s.replace(old, new) |
(string, string, string) → string |
Replace first occurrence |
s.replace(old, new, n) |
(string, string, string, int) → string |
Replace up to n occurrences |
s.split(sep) |
(string, string) → list |
Split string |
s.substring(start, end) |
(string, int, int) → string |
Extract substring |
s.trim() |
(string) → string |
Trim whitespace |
s.reverse() |
(string) → string |
Reverse string |
list.join(sep) |
(list, string) → string |
Join list with separator |
"hello".charAt(1) // "e"
"hello world".indexOf("o") // 4
"hello world".lastIndexOf("o") // 7
"hello".upperAscii() // "HELLO"
"hello".replace("l", "L") // "heLLo"
"a,b,c".split(",") // ["a", "b", "c"]
"hello".substring(1, 4) // "ell"
" hello ".trim() // "hello"
"hello".reverse() // "olleh"
["a", "b", "c"].join("-") // "a-b-c"
ext.Encoders — Base64
| Function |
Signature |
Description |
base64.encode(bytes) |
(bytes) → string |
Base64 encode |
base64.decode(s) |
(string) → bytes |
Base64 decode |
base64.encode(b"hello") // "aGVsbG8="
base64.decode("aGVsbG8=") // b"hello"
ext.Math — Mathematical Functions
| Function |
Signature |
Description |
math.abs(n) |
(number) → number |
Absolute value |
math.ceil(n) |
(double) → double |
Ceiling |
math.floor(n) |
(double) → double |
Floor |
math.round(n) |
(double) → double |
Round to nearest |
math.sign(n) |
(number) → number |
Sign (-1, 0, 1) |
math.greatest(...) |
(number, ...) → number |
Maximum of arguments |
math.least(...) |
(number, ...) → number |
Minimum of arguments |
math.isNaN(n) |
(double) → bool |
Is NaN? |
math.isInf(n) |
(double) → bool |
Is infinite? |
math.abs(-5) // 5
math.ceil(4.2) // 5.0
math.floor(4.8) // 4.0
math.round(4.5) // 5.0
math.sign(-10) // -1
math.greatest(1, 5, 3) // 5
math.least(1, 5, 3) // 1
ext.Lists — Extended List Operations
| Function |
Signature |
Description |
list.slice(from, to) |
(list, int, int) → list |
Slice from index to index |
list.flatten() |
(list(list)) → list |
Flatten one level |
lists.range(n) |
(int) → list(int) |
Generate [0, 1, ..., n-1] |
[1, 2, 3, 4, 5].slice(1, 4) // [2, 3, 4]
[[1, 2], [3, 4]].flatten() // [1, 2, 3, 4]
lists.range(5) // [0, 1, 2, 3, 4]
ext.Sets — Set Operations
| Function |
Signature |
Description |
sets.contains(a, b) |
(list, list) → bool |
True if a contains all elements of b |
sets.equivalent(a, b) |
(list, list) → bool |
True if a and b have same elements |
sets.intersects(a, b) |
(list, list) → bool |
True if a and b share any element |
sets.contains([1, 2, 3], [2, 3]) // true
sets.equivalent([1, 2], [2, 1]) // true
sets.intersects([1, 2], [2, 3]) // true
CEL Built-ins
Type Conversions
int("42") // 42
double(42) // 42.0
string(42) // "42"
bool(1) // true
bytes("hello") // b"hello"
String Methods (CEL built-ins)
"hello".startsWith("he") // true
"hello".endsWith("lo") // true
"hello".contains("ell") // true
"ABC123".matches("[A-Z]{3}[0-9]+") // true (regex)
"hello".size() // 5
List Built-ins
[1, 2, 3].size() // 3
[1, 2, 3][0] // 1
"admin" in input.roles // membership check
// Comprehension macros
input.items.exists(x, x.price > 100) // any item over $100?
input.items.all(x, x.available) // all items available?
input.items.filter(x, x.price > 50) // items over $50
input.items.map(x, x.price * 1.1) // apply 10% markup
Operators
// Arithmetic
input.price * input.quantity
input.total + input.tax
input.balance - input.withdrawal
input.count / 2
input.value % 10
// Comparison
input.age >= 18
input.status == "active"
input.score != 0
input.amount > 100
input.priority < 5
// Logical
input.active && input.verified
input.admin || input.moderator
!input.deleted
// Conditional (ternary)
input.age >= 18 ? "adult" : "minor"
// Null coalescing
input.nickname ?? input.name
input.body.payload.jobId ?? ''
input.list ?? []
?? is preprocessed by Mycel before reaching CEL. When the left-hand side is a simple dotted path (e.g. input.body.payload.jobId), the rewrite uses CEL's has() macro to check every parent segment, so a missing intermediate field falls back cleanly instead of raising "no such key". For other left-hand sides (function calls, parenthesized expressions) the rewrite is coalesce(lhs, rhs), which catches present-but-null and present-but-empty-string values but evaluates lhs eagerly.
Chaining is right-associative: a ?? b ?? c behaves like a ?? (b ?? c).
When mixing ?? with the ternary ?: at the same depth, parenthesize for clarity: (a ?? b) ? c : d. The rewriter does not resolve precedence against ?: automatically.
Common Patterns
Email normalization
Slug generation
lower(replace(trim(input.name), " ", "-"))
Conditional field with fallback
default(input.display_name, input.first_name + " " + input.last_name)
Extract domain from email
split(input.email, "@")[1]
Compute order total
sum(pluck(input.items, "price"))
unique(flatten([input.tags, input.extra_tags]))
Conditional status based on multiple fields
input.paid && input.shipped ? "completed" : input.paid ? "processing" : "pending"