Skip to documentation
Open Workflow Studio
On this page

Workflow expressions

Workflow reference | Expression step | Condition step

Expressions select and transform typed JSON values in steps and connectors. Use the Function selector in Properties to build an expression tree from the built-in functions, or author its equivalent JSON in an exported workflow.

Expression structure

Property Meaning
function Exact, case-sensitive function identifier from this reference. Defaults to input.
value Static input path, JSON literal, variable name, property path, or assertion error message. Defaults to "input"; functions that do not use it ignore it.
arguments Ordered array of nested expressions. Defaults to []. Each argument is evaluated according to the function's rules.

For example, this expression selects customer.name from the current input:

{
  "function": "get",
  "value": "customer.name",
  "arguments": [
    { "function": "input", "value": "input" }
  ]
}

literal.value contains JSON text. A string literal therefore requires escaped double quotes in the outer workflow JSON: {"function":"literal","value":"\"Hello\""}. Use "42", "true", "null", or "[1,2]" for a number, Boolean, null, or array.

Function-style notation in the catalog is a readable summary of expression trees. The runtime accepts the JSON structure above; it has no executable expression-string parser, interpolation-based function calls, arbitrary JavaScript, or general scripting function.

Where expressions run

Location Available values and result
node.expression Current input, named inputs, and established variables. Its typed result becomes the step output.
node.condition Same bindings. Must produce a Boolean; the step forwards the incoming data through matching branches.
edge.transform This source's output as input and under the connection name, plus established variables. Its result becomes that connection's value.
variable.valueExpression Current step bindings and established variables. Result is stored under the setter's variable name.
loop.items Loop input and named bindings. ForEach requires an array.
Loop body expressions input, item, zero-based index, previous, original, and inherited named inputs.
loop.until Evaluated after an Until or Poll body completes. input and result refer to the body result; iteration bindings are also available. Must produce a Boolean.
map and filter argument 2 item and zero-based index refer to each collection element. Original input bindings and variables remain available. Nested collection expressions temporarily shadow these two bindings.
multiTurn.until input is the original task, previous is the latest complete solution, and turn is one-based. Must produce a Boolean.
stitchVideo.videoUrls Must produce an ordered array of MP4 source strings.
imageWatermark.imageSource, videoWatermark.videoSource Must produce one supported media source string.

Templates such as {{input}} and {{previous}} read context values in supported template fields. Use an Expression step or connector transformation to apply functions before a value reaches a template.

Paths and missing values

Input paths begin with an available binding name. Supported forms include:

Form Example Meaning
Dotted path order.customer.name Select nested object properties.
Array index order.items[0].price Select an array element using a nonnegative index.
Optional $ prefix $.order.total Equivalent to order.total.
JSON Pointer /order/items/0/price Select the same value using pointer segments.
Escaped pointer segments /payload/a~1b/~0key Select property a/b, then property ~key.

Paths are case-sensitive, at most 512 characters and 32 segments, and cannot contain control characters. Dotted property segments support ASCII letters, digits, underscores, and hyphens. Use JSON Pointer for other property names. Wildcards, filters, slices, quoted bracket properties, negative indexes, and executable expressions are unsupported.

input first checks for an exact binding-name match. Otherwise, it resolves the first path segment as a binding. get first checks for an exact object-property match before treating value as a path. An empty path or $ in get returns its entire argument.

A missing root binding or unset variable is an error. A missing nested property, out-of-range array element, or path that cannot traverse its value returns null. coalesce can handle those null values; evaluating an unavailable root binding still fails.

Value functions

Function Arguments value Result Example
input 0 Binding name or path Copy of the selected typed input. input("order.total") selects the total.
literal 0 Valid JSON text Parsed JSON value. literal("[1,2]") returns [1,2].
variable 0 Static variable name Copy of an established run-scoped value. variable("threshold") reads the configured writer's value.
get 1: target Property name or path Selected value or null. get({"a":7}, "a") returns 7.
coalesce 1-32: candidate values Unused First non-null value, or null. coalesce(null, "", "fallback") returns "".
assert 2: Boolean condition, result value Nonblank error message Argument 2 when argument 1 is true; otherwise fails the run with value. assert(true, {"ready":true}) returns the object.

coalesce evaluates candidates left to right and stops at the first non-null value. Empty strings, false, zero, and empty arrays are valid non-null results. assert evaluates its result argument only after its condition succeeds. Its static error message must be at most 512 characters without control characters.

Variable references establish scheduling dependencies on their single writer, including references in branches that might short-circuit at runtime. Configure the writer in the workflow before using a variable expression.

Text functions

Function Arguments Result Example
concat 1-32: any JSON values Concatenated text. Strings are inserted directly; other values use compact JSON text. concat("Count: ", 3, null) returns "Count: 3null".
trim 1: string String with leading and trailing whitespace removed. trim(" Hello ") returns "Hello".
lower 1: string Invariant-culture lowercase string. lower("HeLLo") returns "hello".
upper 1: string Invariant-culture uppercase string. upper("HeLLo") returns "HELLO".
length 1: string, array, or object String length, array element count, or object property count. length({"a":1,"b":2}) returns 2.

length measures strings in .NET UTF-16 code units. trim, lower, and upper require strings and perform no automatic conversion from numbers or objects.

Collection functions

Function Arguments Result Example
map 2: array, item expression Array of transformed values. Argument 2 reads item and index. Mapping item.title selects each record's title.
filter 2: array, Boolean item expression Elements whose predicate is true, in their original order. Filtering item.enabled retains enabled records.
distinct 1: array First occurrence of each compact JSON representation, in input order. distinct([1,1,2]) returns [1,2].
join 2: string array, separator string Joined text, preserving empty elements. join(["","a",""], "\n") returns "\na\n".
contains 2: string or array, sought value Case-sensitive substring or structural array membership. contains([1,2], 2) returns true.

Collection transformation inputs are limited to 1,000 elements. Each complete evaluation has a shared 100,000-operation budget, including nested maps and filters. Original input JSON is unchanged. Each mapped or filtered output remains subject to the workflow text budget. A filter predicate must return a Boolean; automatic truthiness conversion is unsupported.

Date and time functions

Function Arguments Result Example
utcNow 0 Current UTC ISO 8601 timestamp. Use it for a fresh window on every scheduled run.
addMinutes 2: ISO 8601 timestamp, numeric offset UTC timestamp shifted by the supplied minutes. addMinutes("2026-10-15T19:00:00Z", 30) returns a timestamp at 19:30 UTC.
formatDateTime 2: ISO 8601 timestamp, .NET format string UTC text formatted with invariant culture. Format yyyy-MM-dd HH:mm 'UTC' gives 2026-10-15 19:00 UTC.
dateTicks 1: ISO 8601 timestamp UTC ticks for numeric comparisons. Equal instants with different offsets produce equal ticks.

Date offsets are bounded to ten years in either direction, and formatted dates use UTC. Invalid dates, out-of-range offsets, and invalid formats fail explicitly. utcNow reads the clock when evaluated; use a named upstream value when several steps need exactly the same instant.

Encoding and JSON functions

Function Arguments Result Example
base64Encode 1: value converted to text Base64 of UTF-8 text. base64Encode("Hello") returns "SGVsbG8=".
base64Decode 1: value converted to text Decoded UTF-8 string. base64Decode("SGVsbG8=") returns "Hello".
urlEncode 1: value converted to text Escaped URL-component string. urlEncode("a b&c/+") returns "a%20b%26c%2F%2B".
urlDecode 1: value converted to text Percent-decoded string. urlDecode("a%20b") returns "a b".
jsonParse 1: string containing valid JSON Parsed JSON value. jsonParse("[1,true]") returns [1,true].
jsonStringify 1: any JSON value String containing its compact JSON representation. jsonStringify({"a":1}) returns "{\"a\":1}".
webVttParse 1: WebVTT string Object with plain text and ordered cues. See WebVTT transformation.

Base64 decoding rejects malformed Base64 and invalid UTF-8. URL decoding rejects invalid percent escapes and keeps a literal + as +. JSON parsing rejects invalid JSON, duplicate properties, and nesting beyond 32 levels. jsonStringify includes JSON quotes around string values; converting a value to ordinary text has different string-quoting behavior.

WebVTT transformation

webVttParse returns:

{
  "text": "Welcome.",
  "cues": [
    { "startMilliseconds": 0, "endMilliseconds": 2000, "text": "Welcome." }
  ]
}

The source must start with a valid WEBVTT header and contain 1-128 spoken cues. Limits are 60,000 source characters, 1,500 spoken characters per cue, and five minutes total duration. End times must follow start times, and cues must be in start-time order. Overlaps are permitted. Caption markup is removed, HTML entities are decoded, and whitespace is normalized. NOTE, STYLE, and REGION blocks are skipped. Segment timestamp maps are rejected.

Pass text to ordinary text-to-speech input, or cues to a supported Model with useWebVttTiming enabled.

Boolean and comparison functions

Function Arguments Result Example
equals 2: JSON values Boolean structural equality; top-level numeric arguments compare as decimal values. equals(1, 1.0) is true; equals("1", 1) is false.
notEquals 2: JSON values Inverse of equals. notEquals([1,2], [2,1]) is true.
greaterThan 2: numbers Whether argument 1 is strictly greater. greaterThan(4, 3) is true.
lessThan 2: numbers Whether argument 1 is strictly smaller. lessThan(4, 3) is false.
and 2-32: Booleans True when every argument is true. and(true, false) is false.
or 2-32: Booleans True when at least one argument is true. or(false, true) is true.
not 1: Boolean Inverted Boolean. not(true) is false.

and and or evaluate left to right and short-circuit. Every expression tree is still validated, including unvisited arguments. Conditions require actual JSON booleans: strings such as "true", numbers, and nonempty objects have no implicit truthiness.

Arithmetic functions

Function Arguments Result Example
add 2-32: numbers Sum. add(1.25, 2.5) returns 3.75.
subtract 2: numbers Argument 1 minus argument 2. subtract(1.25, 2.5) returns -1.25.
multiply 2-32: numbers Product. multiply(1.25, 2.5) returns 3.125.
divide 2: numbers Argument 1 divided by argument 2. divide(7.5, 2.5) returns 3.

Arithmetic and numeric comparisons require finite JSON numbers representable as .NET decimals. Numeric strings are rejected. Decimal overflow and division by zero fail explicitly. Decimal division is bounded by decimal precision.

Evaluation function

Function Arguments Result
evaluationScore 1: evaluator result converted to text Normalized { "verdict", "score", "explanation" } object.

The evaluator result must contain a JSON object with a supported verdict, numeric score, and nonblank explanation. Verdicts are trimmed and normalized to Correct, Incomplete, or Wrong. Correct produces score 100; Wrong produces 0; Incomplete produces a score clamped to 1-99. Numeric score strings are accepted by this specialized parser, and fractional numeric scores round away from zero.

The parser can extract the JSON object from surrounding model text and repair invalid string backslashes. Explanations are normalized to at most three paragraphs and 4,999 characters. This function implements the same scoring normalization used by Benchmark; it does not independently judge an answer's correctness.

Retrieval functions

These four functions implement local processing around configured Cohere Embed, Rerank, and generation calls. The functions themselves make no provider requests.

Function Arguments, in order Result
ragPrepare 1: dataset input object Validated dataset with normalized documents and provider-ready texts.
cohereVectorSearch 3: prepared dataset, document Embed response, query Embed response Cosine-ranked candidates and aligned documents text shortlist.
cohereRerankResults 2: retrieval result, Rerank response vectorCandidates and mapped, reranked evidence.
ragReview 2: reranked evidence packet, generated draft object Citation-checked review packet with status, answer, citations, missingEvidence, and reviewNotice.

Dataset contract

ragPrepare requires the following fields:

Field Requirement
datasetName Nonblank text, up to 200 characters.
query Nonblank text, up to 4,000 characters.
documents 1-96 objects containing unique id, title, source, and text strings.
Document id Nonblank, up to 100 characters; unique with case-sensitive comparison.
Document title, source, text Nonblank; maximum lengths 200, 500, and 16,000 characters respectively.
dimensions Exactly 256, 512, 1024, or 1536.
candidateCount Integer from 1 through the number of documents.
topN Integer from 1 through candidateCount.

Output retains those fields and adds texts. Each text has the format <id> | <title> | <source>\n<text>. Document output is normalized to the four documented fields.

Vector retrieval and reranking

cohereVectorSearch expects embeddings.float arrays in both Embed responses. The document response needs one row per document; the query response needs one row. Every row must match dimensions, contain finite values between -1,000,000 and 1,000,000, and have at least one nonzero component.

The function sorts by descending cosine similarity, breaking ties by original document index. Each candidate includes its original document fields plus zero-based sourceIndex, one-based vectorRank, and cosineSimilarity. Output fields are datasetName, query, topN, candidates, and the aligned documents text array.

cohereRerankResults requires exactly topN entries in the provider's results array. Every entry needs a unique zero-based index into the shortlist and a finite relevance_score from 0 to 1. Entries must be in descending score order.

The result contains datasetName, query, vectorCandidates, and evidence. Each evidence item retains its candidate fields and adds rerankRank, rankChange (vector rank minus rerank rank), and relevanceScore. Provider indexes are interpreted against the shortlist.

Citation review

ragReview requires a draft with:

Field Requirement
answer Nonblank text, up to 24,000 characters.
answerable Boolean.
citations 1-12 entries when answerable; 0-12 otherwise. Each entry has sourceId and quote.
missingEvidence 0-12 strings when answerable; 1-12 otherwise. Each is nonblank and at most 2,000 characters.

Every citation must identify exactly one reranked evidence item and quote an exact, case-sensitive substring of its original document text. Output extends the evidence packet with status equal to draft-for-review or needs-evidence, together with the answer, citations, missing evidence, and a review notice.

These checks verify structure and citation correspondence. Human review is still required for factual correctness, source applicability, and missing evidence. Relevance scores are not probabilities.

Worked examples

The following JSON blocks are evaluation examples with inputs, expression, and expected. Copy the expression object into the step or connector configuration. The surrounding input and expected-result fields describe the example and are separate from workflow configuration.

Normalize a named input

{
  "inputs": { "customer": { "name": "  Ada  " } },
  "expression": {
    "function": "upper",
    "arguments": [
      {
        "function": "trim",
        "arguments": [
          { "function": "input", "value": "customer.name" }
        ]
      }
    ]
  },
  "expected": "ADA"
}

Select a fallback value

{
  "inputs": { "input": { "customer": {} } },
  "expression": {
    "function": "coalesce",
    "arguments": [
      { "function": "input", "value": "input.customer.displayName" },
      { "function": "literal", "value": "\"Guest\"" }
    ]
  },
  "expected": "Guest"
}

Configure a numeric branch

{
  "inputs": { "quote": { "total": 1500 } },
  "expression": {
    "function": "greaterThan",
    "arguments": [
      { "function": "input", "value": "quote.total" },
      { "function": "literal", "value": "1000" }
    ]
  },
  "expected": true
}

Use this expression in a Condition step. Create an approval connection with condition: true and a normal-processing connection with condition: false. Both destinations receive the original quote.

Parse structured user input

{
  "inputs": { "input": "{\"items\":[{\"price\":12.5}]}" },
  "expression": {
    "function": "get",
    "value": "items[0].price",
    "arguments": [
      {
        "function": "jsonParse",
        "arguments": [
          { "function": "input", "value": "input" }
        ]
      }
    ]
  },
  "expected": 12.5
}

Require a successful operation

{
  "inputs": { "input": { "status": "Succeeded", "result": { "count": 3 } } },
  "expression": {
    "function": "assert",
    "value": "The operation must succeed before its result can be published.",
    "arguments": [
      {
        "function": "equals",
        "arguments": [
          { "function": "input", "value": "input.status" },
          { "function": "literal", "value": "\"Succeeded\"" }
        ]
      },
      { "function": "input", "value": "input.result" }
    ]
  },
  "expected": { "count": 3 }
}

Normalize an evaluation result

{
  "inputs": {
    "input": "{\"verdict\":\"correct\",\"score\":78,\"explanation\":\"All requirements are satisfied.\"}"
  },
  "expression": {
    "function": "evaluationScore",
    "arguments": [
      { "function": "input", "value": "input" }
    ]
  },
  "expected": {
    "verdict": "Correct",
    "score": 100,
    "explanation": "All requirements are satisfied."
  }
}

Parse a subtitle cue

{
  "inputs": { "input": "WEBVTT\n\n00:00.000 --> 00:02.000\nWelcome.\n" },
  "expression": {
    "function": "webVttParse",
    "arguments": [
      { "function": "input", "value": "input" }
    ]
  },
  "expected": {
    "text": "Welcome.",
    "cues": [
      { "startMilliseconds": 0, "endMilliseconds": 2000, "text": "Welcome." }
    ]
  }
}

Prepare a retrieval dataset

{
  "inputs": {
    "input": {
      "datasetName": "Example policy",
      "query": "How long can items be returned?",
      "dimensions": 256,
      "candidateCount": 1,
      "topN": 1,
      "documents": [
        {
          "id": "policy-1",
          "title": "Returns",
          "source": "Illustrative policy",
          "text": "Return eligible items within 30 days."
        }
      ]
    }
  },
  "expression": {
    "function": "ragPrepare",
    "arguments": [
      { "function": "input", "value": "input" }
    ]
  },
  "expected": {
    "datasetName": "Example policy",
    "query": "How long can items be returned?",
    "dimensions": 256,
    "candidateCount": 1,
    "topN": 1,
    "documents": [
      {
        "id": "policy-1",
        "title": "Returns",
        "source": "Illustrative policy",
        "text": "Return eligible items within 30 days."
      }
    ],
    "texts": [
      "policy-1 | Returns | Illustrative policy\nReturn eligible items within 30 days."
    ]
  }
}

Send the prepared texts to the configured document Embed call and query to the query Embed call. Connect their typed results and the prepared dataset to an Expression using cohereVectorSearch, then pass its shortlist to the Rerank call.

Transform one connection

{
  "source": "fetch-order",
  "target": "process-order",
  "inputName": "order",
  "transform": {
    "function": "get",
    "value": "data",
    "arguments": [
      { "function": "input", "value": "input" }
    ]
  }
}

For source output {"data":{"id":"A-17","total":125}}, the destination receives {"id":"A-17","total":125} under order. Other connections retain their own values. A missing data property becomes null; wrap the extraction in assert when the downstream contract requires it.

Limits and troubleshooting

Expression trees must be acyclic, at most 16 levels deep, contain at most 256 operations, and have at most 32 arguments per function. JSON nesting is limited to 32 levels. Values and results also respect the host's text budget, which defaults to 16 MiB UTF-8.

The editor applies tighter authoring limits: nested editors become disabled at depth 12, and its Add argument control permits up to 16 arguments for concat, coalesce, and, and or. The runtime also accepts the larger arities documented above in imported JSON, including variadic add and multiply.

Symptom Correction
Input is unavailable Check the connection name and context. Use a named input at joins and previous for multi-turn results.
Variable has not been set Add exactly one writer and keep its inferred dependencies acyclic.
Boolean required Return true or false as JSON booleans; compare text or numbers explicitly.
Number required Supply decimal JSON numbers. Numeric strings need explicit JSON parsing or a corrected upstream contract.
String required Select a string field or convert a value deliberately with concat or jsonStringify.
Invalid JSON literal Double-quote string literals and escape those quotes in the enclosing workflow JSON.
Unsupported function or arity Use an exact catalog identifier and the documented number of arguments.
Output or depth limit exceeded Reduce the value size, nesting, or expression tree; split processing across bounded steps.

The runtime implementation is WorkflowExpressions. Specialized contracts are implemented in WorkflowCohereRag, WorkflowWebVtt, and WorkflowEvaluationScoring.

An unhandled error has occurred. Reload 🗙