Formulas
Formulas let you compute values from existing columns for 0 credits.
Use them to extract fields, clean text, score journalists, and drive routing logic before expensive AI or enrichment runs. More filtering here means more credits spent on the right people.
AI Formula Generator
Don't write formulas by hand. We built a formula generator that writes them for you.
- Click + Add Column → Formula
- Click Use AI to generate formula
- Describe what you want in plain English (e.g., "check if email exists and match score is above 7")
- Review the generated formula and preview
- Click Apply when it looks right
The generator handles syntax, function names, and column references automatically. Use this for most formulas—it's faster and less error-prone than writing by hand.
When to write manually
Manual formulas are useful for quick tweaks or when you already know the syntax. For anything complex, let the generator handle it.
Syntax
Column references
Reference columns using double curly braces:
{{Email}}
{{Match Score}}
{{Journalist Profile}}Nested properties
Access nested fields in JSON columns:
{{Journalist Profile}}.email
{{Article}}.titleOperators
| Type | Operators | Example |
|---|---|---|
| Arithmetic | +, -, *, /, %, ^ | {{Base Score}} * 1.2 |
| Comparison | >, >=, <, <=, =, != | {{Match Score}} >= 7 |
| Logical | &&, ||, !, ?? | {{Email}} != null && {{Email}} != "" |
| Conditional | ? : | {{Match Score}} > 7 ? "Priority" : "Standard" |
Function Reference
String functions
| Function | Description | Example |
|---|---|---|
CONCAT(...values) | Join values | CONCAT("Hello", " ", "World") → "Hello World" |
UPPER(text) | Uppercase text | UPPER("hello") → "HELLO" |
LOWER(text) | Lowercase text | LOWER("HELLO") → "hello" |
TRIM(text) | Remove leading and trailing whitespace | TRIM(" hi ") → "hi" |
LENGTH(text) | String length | LENGTH("hello") → 5 |
LEFT(text, n) | First n characters | LEFT("hello", 2) → "he" |
RIGHT(text, n) | Last n characters | RIGHT("hello", 2) → "lo" |
MID(text, start, len) | Substring using 1-based index | MID("hello", 2, 3) → "ell" |
SUBSTRING(text, start, end) | Substring using 0-based indexes | SUBSTRING("hello", 1, 4) → "ell" |
REPLACE(text, find, repl) | Replace first match | REPLACE("hello", "l", "L") → "heLlo" |
SUBSTITUTE(text, find, repl) | Replace all matches | SUBSTITUTE("hello", "l", "L") → "heLLo" |
SPLIT(text, delim) | Split string into array | SPLIT("a,b,c", ",") → ["a","b","c"] |
CONTAINS(text, search) | Text contains substring | CONTAINS("hello", "ell") → true |
STARTSWITH(text, search) | Text starts with substring | STARTSWITH("hello", "he") → true |
ENDSWITH(text, search) | Text ends with substring | ENDSWITH("hello", "lo") → true |
REGEX_MATCH(text, pattern) | Regex match test | REGEX_MATCH("abc123", "[0-9]+") → true |
REGEX_REPLACE(text, pattern, repl) | Regex replace | REGEX_REPLACE("a1b2", "[0-9]", "X") → "aXbX" |
Array functions
| Function | Description | Example |
|---|---|---|
JOIN(array, sep) | Join array with separator | JOIN(["a","b"], "-") → "a-b" |
AT(array, index) | Get item by index | AT(["a","b","c"], 1) → "b" |
LENGTH(array) | Array length | LENGTH([1,2,3]) → 3 |
UNIQUE(array) | Remove duplicate values | UNIQUE([1,1,2]) → [1,2] |
FLATTEN(array) | Flatten nested arrays | FLATTEN([[1],[2,3]]) → [1,2,3] |
TAKE(array, n) | Keep first n items | TAKE([1,2,3,4], 2) → [1,2] |
DROP(array, n) | Remove first n items | DROP([1,2,3,4], 2) → [3,4] |
SLICE(array, start, end) | Slice by index range | SLICE([1,2,3,4], 1, 3) → [2,3] |
INCLUDES(array, value) | Array contains value | INCLUDES([1,2,3], 2) → true |
INDEX_OF(array, value) | Index of value | INDEX_OF(["a","b"], "b") → 1 |
JSON/object functions
| Function | Description | Example |
|---|---|---|
GET(obj, path, default) | Read nested property | GET({{Journalist Profile}}, "email", "") |
SET(obj, path, value) | Return object with updated path | SET({{Journalist Profile}}, "priority", "A") |
MERGE(base, updates) | Deep merge objects | MERGE({a:1}, {b:2}) → {a:1,b:2} |
PICK(obj, keys) | Keep selected keys | PICK({a:1,b:2}, ["a"]) → {a:1} |
OMIT(obj, keys) | Remove selected keys | OMIT({a:1,b:2}, ["a"]) → {b:2} |
KEYS(obj) | Get key list | KEYS({a:1,b:2}) → ["a","b"] |
VALUES(obj) | Get value list | VALUES({a:1,b:2}) → [1,2] |
HAS(obj, key) | Check key exists | HAS({a:1}, "a") → true |
STRINGIFY(value, space) | Convert to JSON string | STRINGIFY({a:1}) → "{\"a\":1}" |
PARSE(json) | Parse JSON string | PARSE("{\"a\":1}") → {a:1} |
Numeric functions
| Function | Description | Example |
|---|---|---|
ROUND(n, decimals) | Round to decimal places | ROUND(3.456, 2) → 3.46 |
ABS(n) | Absolute value | ABS(-5) → 5 |
TO_NUMBER(value) | Convert to number | TO_NUMBER("123") → 123 |
MIN(...values) | Lowest value | MIN(1, 5, 3) → 1 |
MAX(...values) | Highest value | MAX(1, 5, 3) → 5 |
SUM(...values) | Sum values | SUM(1, 2, 3) → 6 |
AVERAGE(...values) | Mean average | AVERAGE(1, 2, 3) → 2 |
FORMAT_CURRENCY(n, curr, locale) | Format currency string | FORMAT_CURRENCY(29.99, "USD") → "$29.99" |
FORMAT_PERCENT(n, dec, locale) | Format percent string | FORMAT_PERCENT(0.15, 0) → "15%" |
Logical functions
| Function | Description | Example |
|---|---|---|
IF(cond, ifTrue, ifFalse) | Conditional branch | IF({{Match Score}} > 7, "High", "Low") |
AND(...values) | True when all values are truthy | AND(true, true) → true |
OR(...values) | True when any value is truthy | OR(false, true) → true |
NOT(value) | Logical negation | NOT(true) → false |
COALESCE(...values) | First non-null value | COALESCE(null, "", "fallback") → "" |
ISNULL(value) | Check null or undefined | ISNULL(null) → true |
ISBLANK(value) | Check empty, null, or undefined | ISBLANK("") → true |
ISNUMBER(value) | Check numeric type | ISNUMBER(123) → true |
ISTEXT(value) | Check text/string type | ISTEXT("hello") → true |
Fewer credits, better targeting
Use formulas to compute triage columns like "Email Exists" or "Priority Tier", then reference those in runWhen conditions. This way, AI runs only on journalists who actually qualify.