The concepts explained in this section are all very interdependent on one another, so we’ve added a cheatsheet to help you get started.

Expressions Overview

Expressions are a powerful feature in Tracecat that gives you fine-grained control over your action logic. They are strings that can be evaluated into a value or data/object reference, prefixed with a context and wrapped in template ${{ ... }} syntax. We use JSONPath expressions (dot notation) to access data, objects, or functions inside JSON-like data structures.

In object-oriented programming terms, you can think of this as method invocation or attribute access on a specific context (analagous to a class or instance).

Some examples of expressions:

${{ SECRETS.my_secret.SUPER_SECRET }}
${{ ACTIONS.my_action.result }}
${{ FN.add(1, 2) }}
${{ INPUTS.my_input }}
${{ TRIGGER.my_trigger }}

Our convention is to use lower_snake_case if you need to replace whitespace in your paths.

We also support typecasting for expressions:

${{ <expression> -> <type> }}

Types we currently support are int, str, bool, and float.

For example, this expression will return the integer literal 3:

${{ FN.add(1, 2) -> int }}

Expression Contexts

An expression context is a top-level namespace that changes the behavior of an expression. Tracecat expression contexts are all uppercase qualifiers, e.g. SECRETS.

We use dot notation or JSONPath expressions to access data, objects, or functions in a particular context. We currently support the following contexts:

QualifierExpression syntaxDescription
SECRETSSECRETS.<name>.<key>Secrets manager
ACTIONSACTIONS.<jsonpath>.resultThe active workflow’s execution results
FNFN.<fn_name>(<arg1>, <arg2>, ...)Inline functions
INPUTSINPUTS.<jsonpath>The active workflow’s static inputs
TRIGGERTRIGGER.<jsonpath>The active workflow’s trigger inputs

Template syntax

Tracecat’s template syntax takes inspiration from Github Actions, so it may feel familiar to you!

A template is a string with a ${{ ... }} pattern that wraps an expression:

${{ <expression> }}

Templates can exist in two forms: full templates and inline templates.

Full templates

A full template is a sstring that only contains a single template with no surrounding whitespace:

"${{ ... }}"

It follow that when a full template wraps an expression, we call this a full expression:

"${{ <expression> }}"

The final datatype of the above expression depends on the return type of the expression and whether there is a typecast.

This expression will return integer literal 3 because FN.add(1, 2) returns an integer:

"${{ FN.add(1, 2) }}"

In contrast, this expression will return string literal "3" because FN.add(1, 2) was cast to a string:

"${{ FN.add(1, 2) -> str}}"

In YAML, by convention we omit the quotes around full expressions:

actions:
  - ref: my_action
    action: example.action
    args:
      value: ${{ FN.add(1, 2) }}

Inline templates

An inline template is a string where all templates are in the string body:

"An inline template ${{ ... }} looks ${{ ... }} like this."

As you’d expect, the following is called an inline expression:

"An inline template ${{ <expression-1> }} looks ${{ <expression-2> }} like this."

The final datatype of the above expression is always a string.

For example, this expression:

"An inline template ${{ FN.add(1, 2) }} looks ${{ FN.add(1, 2) -> str }} like this."

will return string literal "An inline template 3 looks 3 like this.":

In YAML, by convention we wrap quotes around inline expressions:

actions:
  - ref: my_action
    action: example.action
    args:
      value: "An inline template ${{ FN.add(1, 2) }} looks ${{ FN.add(1, 2) -> str }} like this."

Expression Types

Action Expressions

Action expressions are JSONPaths that target the ACTIONS context. It references data or objects that are returned from completed actions.

${{ ACTIONS.<jsonpath>.result }}

For example, if you have an action that was defined with the following UDF in your workflow definition:

@registry.register(namespace="example", description="Adds two numbers.")
def add(lhs: int, rhs: int) -> int:
    return lhs + rhs

and the following snippet of YAML in your workflow definition:

actions:
  - ref: add_two_numbers
    action: example.add
    args:
      lhs: 1
      rhs: 2

Then anywhere else in your workflow definition and given add_two_numbers has completed, you can use the ACTIONS.add_two_numbers.result expression to access the result of the action.

Revisiting the example above, if you added another action to the same workflow:

actions:
  - ref: add_two_numbers
    action: example.add
    args:
      lhs: 1
      rhs: 2
  - ref: add_three_numbers
    action: example.add
    depends_on:
      - add_two_numbers
    args:
      lhs: ${{ ACTIONS.add_two_numbers.result }} # This evaluates to 3!
      rhs: 3

you can use upstream computations in your downstream actions.

Tracecat’s workflow engine doesn’t enforce that actions require hierarchical dependencies on other actions to use their results. So long an action in the same workflow is completed, another action can use its result.

Action expressions always contain the following predefined keys:

KeyDescription
resultContains the action’s result.
result_typenameThe Python type annotation of the action’s result.

Input Expressions

Input expressions are JSONPath expressions that target the INPUTS context.

${{ INPUTS.<jsonpath> }}

Given the following static workflow inputs:

inputs:
  company: Tracecat
  url: https://tracecat.com
  contact:
    email: founders@tracecat.com
    location: San Francisco, CA

Some valid input expressions would look like:

${{ INPUTS.company }}
${{ INPUTS.url }}
${{ INPUTS.contact.email -> str }}
${{ INPUTS.contact.location }}

Trigger Expressions

Trigger expressions are JSONPath expressions that target the TRIGGER context.

${{ TRIGGER.<jsonpath> }}

Given the following webhook call:

curl -X POST $WEBHOOK_URL -H "Content-Type: application/json" -d '{
    "event_type": "update",
    "source": "http://data-source.com",
    "metadata": {
      "time":"Thursday, May 20th 11:40pm",
      "platform":"cool_platform"
    }
  }'

Some valid input expressions would look like:

${{ TRIGGER.event_type }}
${{ TRIGGER.metadata.time }}

Secret Expressions

Secret expressions are JSONPath expressions that target the SECRETS context. They allow you to pull created secrets from the Secrets Manager at runtime.

${{ SECRETS.<name>.<key> }}

Given the following secrets created like such:

curl -X PUT $TRACECAT__API_URL/secrets \
  -H "Content-Type: application/json" \
  -d '{
    "type":"custom",
    "name":"some_secret",
    "keys":[
      {
        "key":"SOME_API_KEY",
        "value": "SOME_SECRET_VALUE"
      }
    ]
  }'

To use SOME_SECRET_VALUE in your workflow, you would use the following expression:

${{ SECRETS.my_secret.SOME_API_KEY }}

For instance, to make an API call:

actions:
  - ref: my_action
    action: core.http_request
    args:
      url: https://api.some-example.com
      headers:
        Authorization: Bearer ${{ SECRETS.some_secret.SOME_API_KEY }}
        ...

Function Expressions

Function expressions are expressions that target the FN context.

${{ FN.<fn_name>(<arg1>, <arg2>, ...) }}

Our syntax allows for nested expressions, so you can use the result of one function as an argument to another function:

${{ FN.add(FN.add(1, 2), 3) }}

Supported Functions

NameDescription
less_thanless than comparison
less_than_or_equalless than or equal comparison
greater_thangreater than comparison
greater_than_or_equalgreater than or equal comparison
not_equalnot equal comparison
is_equalequal comparison
not_nullcheck if value is not null
is_nullcheck if value is null
regex_extractextract match using regex pattern
regex_matchcheck if text matches regex pattern
regex_not_matchcheck if text does not match regex pattern
containscheck if container contains item
does_not_containcheck if container does not contain item
lengthget the length of a collection
is_emptycheck if collection is empty
not_emptycheck if collection is not empty
addadd two numbers
subsubtract two numbers
mulmultiply two numbers
divdivide two numbers
modget the modulus of two numbers
powraise a number to a power
sumsum a collection of numbers
joinjoin items in a list with a separator
concatconcatenate multiple items into a string
formatformat a string
andlogical and operation
orlogical or operation
notlogical not operation
serialize_jsonconvert json to string
from_timestampconvert timestamp to datetime

This list is not exhaustive, and we plan to add more functions in the future.

Typecasts

We support the following typecasts:

TypePython Type
intint
floatfloat
strstr
boolCustom bool - true for any truthy value, 1, or upper/lower case true

You can perform a typecast on an expression like so:

Evaluation Procedure

Before an action runs, Tracecat performs the following steps to evaluate the expressions in the action:

  1. Find all secrets that are declared in secret expressions
  2. Pull these into the execution context
  3. Evaluate all types of expressions in one pass

After doing the above it then proceeds to run the action’s associated UDF.

Cheatsheet

Terminology

TermDescription
ContextA top-level namespace that changes the behavior of an expression, e.g. SECRETS or ACTIONS
ExpressionA string that can be evaluated into a value or data/object reference, often involving a context.
TemplateA string holding an expression ${{ <expr> }} that evaluates into a value or data/object reference.
Full templateA string that only contains one template: "${{ ... }}"
Full expressionA full template string with an expression: "${{ <expr> }}"
Inline templateAll templates are inline, e.g. "An inline template ${{ ... }} looks like this."
Inline expressionAll templates have expressions and are inline, e.g. "An inline expression ${{ <expr> }} looks like this."

Contexts

QualifierDescription
SECRETSSecrets manager context. The expression SECRETS.my_secret.SUPER_SECRET will return my_secret.SUPER_SECRET’s stored value.
ACTIONSThe active workflow’s execution context. Lets you access results from completed actions.
FNInline functions. The expression FN.add(1, 2) will return 3.
INPUTSThe active workflow’s static inputs. Lets you access static inputs from the workflow definition.
TRIGGERThe active workflow’s trigger inputs. Lets you access dynamic inputs from a webhook.