Skip to main content
Every Workflow run produces a state object that grows as the Workflow executes. Blocks can read from this state at any point, giving them access to both top-level context (such as who initiated the run) and the output of any preceding block. This is what makes blocks composable: each one can build on what came before. The Workflow state object includes top-level context, actions, and variables keys:
  • context is immutable, set at the beginning, and available to all blocks.
    • If an entity changes during Workflow execution, those changes will not be represented in context.entity.
    • If secrets change during a Workflow, those changes will not be represented in context.secrets.
    • You can examine the context object via the Run context tab when viewing a Workflow run.
    • Learn more about the context key.
  • actions is built up as actions execute via blocks.
    • Each block will have an inputs and outputs key, but their format differs based on the block type.
  • variables contains the current values of any variables declared on the workflow. If a variable does not have a default value and hasn’t been set yet, its value will be null.
    • Note: since variables are mutable, the variable values can change over time. Depending on the ordering of blocks in your workflow, different blocks may have access to different variable values.
    • If the Workflow was started by a Run Workflow block in another Workflow, variables starts with the values that block passed in. Any variable without a passed-in value uses its default.
The state object looks similar to the following:

The Workflow state context key

The context object always has the following keys:
  • {{context.workflowRunId}} - The Workflow run ID
  • {{context.initiatedAtIso8601Timestamp}} - The timestamp at which this block was initiated
  • {{context.initiatedBy}} - The user object of the initiator
    • email - The user’s email
    • name - The initiator’s name
    • identityMappings - A mapping of the identity providers listed below to the initiating user’s corresponding external ID. The external ID is pulled from identity provider’s default configuration when there is more than one configuration.
      • azure_devops
      • bitbucket
      • clickup
      • github
      • gitlab
      • jira
      • microsoft_teams
      • opsgenie
      • pagerduty
      • service_now
      • slack
    • identityMappingsByAlias - Similar to identityMappings, but each provider maps to an object of configuration alias to external ID. This field is useful when users have different external IDs across an integration’s configurations.
  • {{context.secrets}} - An object of Cortex secret tags to their values
  • {{context.retry}} - Read-only information about retries of this run. It’s present on every run, so the same template works on a first run and on a retry. See Using a stable key across retries.
    • rootRunId - The ID of the first run in the retry chain. On a run that isn’t a retry, this is the run’s own ID.
    • count - 0 on a first run, 1 on the first retry, 2 on the second, and so on.
    • ofRunId - The ID of the run that this run retries, or null on a first run.
    • fromSlug - The slug of the block the retry started from, or null on a first run.

Accessing configured secrets

You can access your configured secrets by tag: {{context.secrets.SECRET_TAG}}. Note that Mustache syntax performs HTML escaping by default, so if your configured secret contains a reserved HTML character, use triple braces to use raw content: {{{context.secrets.SECRET_TAG}}}. Secret values are redacted in inputs and outputs of the workflow state.
Accessing a secret in the context object is not supported for Slack blocks.

Accessing entity data

When the block runs in the context of a catalog entity, the template context includes data about that entity under the {{context.entity}} key:
  • id
  • tag
  • name
  • descriptor (this is the cortex.yaml, as an object)
These can be accessed under the {{context.entity}} key, for example {{context.entity.tag}} or {{context.entity.descriptor.info.x-cortex-git.github.repository}} .

Using a stable key across retries

When you retry a failed run, Cortex starts a new run with a new context.workflowRunId. If a block sends the run ID to another system as a duplicate key (also called an idempotency key), each retry sends a different value, and the other system can’t tell that the request is a duplicate. Use context.retry.rootRunId instead. It has the same value in the first run and in every retry of it. On a run that isn’t a retry, it’s the run’s own ID, so you don’t need a separate template for retries. For example, in an HTTP request block that creates a ticket, add a header like this:
Add a suffix for each block, such as the block’s slug, so that different blocks in the same run send different keys. This only prevents duplicates if the other system supports a duplicate key. context.retry is available anywhere context is: in templates, in Data transformation input, in Branch conditions, and in the context argument of a JavaScript block. It never contains secrets, outputs, or identity information.

Referencing a block or workflow state

Every block reads the same document, with the top-level keys context, actions, and variables. The syntax for reaching a value depends on the field:
  • Text fields (URLs, headers, payloads, messages, descriptions) - Use Mustache templates: {{context.initiatedBy.email}}.
  • Data transformation (JQ) expressions - The input is the whole document, so start with a dot: .context.initiatedBy.email.
  • Branch path expressions (CEL) - Start from a top-level key, without a dot or braces: context.initiatedBy.email.
  • JavaScript and Set variable(s) scripts - The script runs as the body of async function run(actions, context, variables). Each parameter is already one of the top-level keys, so write context.initiatedBy.email, not context.context.initiatedBy.email. You can use await in the script.
  • Path fields (Scaffolder and User input overrides, Set variable(s) path, the Manual approval team override) - Enter a dot path on its own, with no braces: context.entity.tag.
If a slug contains a hyphen, use bracket notation in CEL, JavaScript, and JQ: actions["pick-name"].outputs.name (or .actions["pick-name"].outputs.name in JQ). Mustache templates and path fields accept hyphens as is: {{actions.pick-name.outputs.name}}.

Mustache templating rules

  • Spacing - Spaces inside the braces are ignored, so {{ context.entity.tag }} and {{context.entity.tag}} are the same.
  • Escaping - Two curly braces HTML-escape the value, so characters such as &, <, and " become HTML entities. Use three curly braces to insert the raw value: {{{context.secrets.api-token}}}. The one exception is {{callbackUrl}} in an Asynchronous HTTP request block, which must always use two curly braces.
  • Missing values - A path that doesn’t exist, such as a block that hasn’t run yet, renders as an empty string. It doesn’t cause an error.
  • Objects and arrays - A template renders text, not JSON. To insert an object or array into a JSON payload, convert it in a Data transformation block first (for example, .context.initiatedBy | tojson), then reference that block’s result with three curly braces:

Block and workflow state reference example

Assume a Workflow was created where you gather names from an “HTTP request” block, a “data transformation” block parses the array of names, the names are surfaced to users in a “user input” block where they can select a name, then a “branch” block runs a different path based on which name was selected during the previous “user input” block. In one of the branch paths, there is a “Slack” block that sends a templated message based on the output of previous blocks. The workflow contains the following blocks:
  • HTTP request block called users
    • In this example, the block sends a GET request to gather user names from a URL containing sample data.
  • Data transformation block called names
    • It contains a JQ query referencing the output of the users block: [.actions.users.outputs.body[] | .name]
  • User input block called pick-name
    • It contains an override that references the output of the names block: actions.names.outputs.result
  • Branch block called create-services
    • There are multiple paths in this branch, depending on which name you chose in the previous block. One of the name options is Ervin, and there is a conditional path called Ervin within this block that contains the following path expression. It references Ervin as the output of the pick-names block: actions['pick-name'].outputs.name == 'Ervin' && context.entity.descriptor.info['x-cortex-type'] == 'team'.
    • The Ervin path contains a Slack block to send a templated message via Slack, referencing the output of the pick-name block and referencing the workflow state by including the email address of the user who initiated the workflow: We created a service for {{actions.pick-name.outputs.name}}. [message from workflow triggered by {{context.initiatedBy.email}}]
When you run the workflow, the following chain of events occurs:
  1. The HTTP request block gathers user names.
    • In the “Inputs” tab of this block, you can see where the data was pulled from.
    • In the “Outputs” tab of this block, you can see the list of names under the body.
  2. The data transformation block parses an array of names from the HTTP request block.
    • In the “Outputs” tab of this block, you can see the list of names.
  3. The Workflow is paused while awaiting user input. The user input block surfaces a name field where the user can select a name from the list. The names are from the array parsed in the previous block.
  4. The user selects the name Ervin, and the Workflow continues to the next block.
  5. The branch block runs a path depending on which name you chose in the previous block. In this example, the Ervin path runs because the CEL expression for that conditional path was configured to run when the pick-name block’s output is Ervin.
    • In the “Outputs” tab of this block, you can see the conditions that were checked to verify which path to run.
  6. The Ervin path contains a Slack block that sends a templated message referencing the output the previous block and the user who initiated the Workflow. The message is sent to the user with the templated references translated: We created a service for Ervin. [message from workflow triggered by jdoe@example.com]
Last modified on November 5, 2026