Skip to navigation

Function Reference

View as Markdown

Beyond arithmetic and comparisons, an expression can call functions to do more: count a list, check whether text contains a word, keep only the items that match a rule, or format a phone number. This page lists the functions commonly available. The builder also offers a function picker, which is the authoritative, up-to-date list while you are editing an expression.

Functions come in two flavors. Standard functions are the general-purpose tools that come with the expression language. Custom functions are extras built specifically for Gail workflows, like formatting a phone number.

How functions are written

A function name is followed by its inputs in parentheses. Some functions are written after the value they act on, joined by a dot, which reads naturally:

size(node.items.list)
"hello world".contains("world")

Function names use camelCase (for example startsWith, endsWith), while the values you reference use lowercase names with underscores (for example node.contact_phone). The two styles sit together cleanly, so normalizePhoneNumber(node.contact_phone) reads without confusion.

Standard functions

A selection of the general-purpose functions you will reach for most often:

FunctionWhat it doesExample
sizeCounts the items in a list, or the characters in textsize(node.contacts.list)
containsChecks whether text contains a smaller piece of textnode.subject.value.contains("urgent")
startsWithChecks whether text begins with a given piecenode.code.value.startsWith("POL-")
endsWithChecks whether text ends with a given piecenode.file.name.endsWith(".pdf")
matchesChecks whether text fits a patternnode.email.value.matches(".+@.+")
mapBuilds a new list by transforming each itemnode.quotes.list.map(q, q.premium)
filterKeeps only the items that pass a testnode.amounts.list.filter(n, n > 0)
intTurns a value into a whole numberint(node.count.value)
stringTurns a value into textstring(node.total.value)

With map and filter, the first name in the parentheses (like q or n) stands for the current item as the function walks the list.

[1, 2, 3, 4].filter(n, n % 2 == 0) // keeps 2 and 4

Custom functions

normalizePhoneNumber

Formats a phone number into the standard international format (like +442083661177). Give it the number and a two-letter country code so it knows how to read a local number.

normalizePhoneNumber("020 8366 1177", "GB") // "+442083661177"

This is handy for tidying up phone numbers before you store them or pass them to a call or messaging step, so they are all in a consistent shape.

The number has to belong to the country code you give. A number already written with a leading + for a different country fails rather than coming back unchanged, so normalizePhoneNumber("+447700900123", "US") is an error.

Falling back instead of failing

Add a third input to get a fallback value back for a number that cannot be read, instead of stopping the run:

normalizePhoneNumber("notaphone", "US") // error, the run stops
normalizePhoneNumber("notaphone", "US", "") // ""

This matters most when you tidy a whole list at once, for example every phone number in an uploaded contact file with a Map. Without a fallback, one bad number fails the entire list. With one, the bad rows come out as the fallback value and you can filter them out in a later step. An empty text "" is a good choice, because a real formatted number is never empty.

Only the number falls back. A country code that is not valid still fails, because that is a mistake in the workflow rather than in the data.

replace

Replaces every occurrence of one piece of text with another. Give it a count as a third input to replace only the first few, left to right.

"hello world".replace("o", "0") // "hell0 w0rld"
"o-o-o-o".replace("o", "X", 2) // "X-X-o-o"

Replacing with empty text removes the piece, which is the usual way to clean up a value before sending it somewhere:

"(954) 552-7023".replace(" ", "").replace("(", "").replace(")", "").replace("-", "")
// "9545527023"

The text to look for cannot be empty.

regexReplace

Replaces every match of a pattern, for clean-ups that a plain replace cannot express in one step. Use $1, $2, and so on in the replacement to put back part of what the pattern matched:

"o-o-o".regexReplace("o", "X") // "X-X-X"
"+15551234567".regexReplace("^\\+1(.*)$", "$1") // "5551234567"

To write a literal dollar sign in the replacement, write it twice: $$.

Most pattern features work: character classes, \s, \d, \b, alternation, anchors, and quantifiers. Look-ahead, look-behind, and back-references are not supported, and a pattern that uses them fails with an error that names the unsupported part.

first and last

Return the first or last item of a list, without writing the list’s name twice to work out its position:

[1, 2, 3].first() // 1
[1, 2, 3].last() // 3
node.http_request.response_body.last().id // the id of the final item

The item comes back as it is, so you can keep reading fields from it, as with .id above.

first and last fail on an empty list. If a list can be empty, check its size first: size(node.quotes.list) > 0 ? node.quotes.list.last().id : "". Do not compare the result to null instead - that check does not work here.

If a function is given something it cannot handle, such as a phone number it cannot make sense of, the expression fails and the run stops with an error. Feed functions values you expect to be valid, and use a Conditional or Check earlier if you need to guard against bad input.