---
title: Workflow definition schema
source: https://docs.newrelic.com/docs/workflow-automation/workflow-automation-apis/definition-schema
---

## Overview

A workflow definition describes the automated process to execute. Workflow definitions use YAML with a `camelCase` naming convention. Each workflow consists of:

-   **Schema properties**: Basic information (name, description, and inputs)
-   **Steps**: The sequence of actions to perform
-   **Expressions**: Dynamic values using jq syntax
-   **Secrets**: Secure credential references

## Schema structure

### Schema properties

The following table describes the top-level properties of a workflow definition.

| Property         | Required or Optional | Type   | Format                                            | Constraints         | Description                                                                                                                                                                    |
| ---------------- | -------------------- | ------ | ------------------------------------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `name`           | Required             | String | Must conform to regex `^[A-Za-z_][A-Za-z0-9_-]*$` | **Max length**: 100 | The `name` values are case-insensitive. For example, `ExampleWorkflow`, `exampleworkflow`, and `EXAMPLEWORKFLOW` are all considered to represent the same workflow definition. |
| `description`    | Optional             | String | Must conform to regex `^[A-Za-z0-9 _-]*$`         | **Max length**: 200 | A description of the workflow's purpose.                                                                                                                                       |
| `workflowInputs` | Optional             |        | Map of maps                                       | **Max size**: 100   | A map of workflow inputs that the workflow accepts. See the workflowInputs properties in the following sections.                                                               |

### workflowInputs (Optional)

The following example shows a `workflowInputs` definition:

```yaml
workflowInputs:
  myInput1:
    type: String
  myInput2:
    type: Number
    defaultValue: 42
```

Each input entry supports the following fields:

-   `workflowInputs.<inputName>` (Required)

    -   **Type**: String (conforming to [expression-safe pattern](#expression-safe-pattern))
    -   **Min. length**: 1
    -   **Max length**: 50
    -   **Description**: The name of the workflow input.

-   `workflowInputs.<inputName>.type` (Required)
    -   **Type**: Enum (`Boolean, List, Map, String, Int, Float, Enum`)
    -   **Description**: The data type of the workflow input.

-   `workflowInputs.<inputName>.defaultValue` (Optional)
    -   **Type**: Any value conforming to `type`.
    -   **Description**: The default value for the workflow input.

-   `workflowInputs.<inputName>.required` (Optional)
    -   **Type**: Boolean (`True`, `False`).
    -   **Description**: The default value for this field is `true`.

-   `workflowInputs.<inputName>.enumValues` (Optional)
    -   **Type**: List (`String`).
    -   **Description**: This field defaults to an empty list `{}`. It is required when the `workflowInputs` type is `Enum`.

-   `workflowInputs.<inputName>.validations` (Optional)
    -   **Type**: Array of maps.
    -   **Description**: The validations to run on the workflow inputs the user provides. The properties listed here apply to all validation types. Specific validation types support additional properties. Refer to [Validation types](#validation-types) for details.

-   `validations[*].type` (Required)
    -   **Type**: String
    -   **Description**: The type of the validation indicating what validation occurs on this field. See [Validation types](#validation-types) for more information on each validation type.

-   `validations[*].errorMessage` (Required)
    -   **Type**: String
    -   **Description**: The error message to display when a particular validation fails.

#### Sample YAML for validations

```yaml
name: calendar_demo

workflowInputs:
  timezone:
    type: String
    defaultValue: 'America/Los_Angeles'
    validations:
      - type: regex
        errorMessage: "The provided timezone is not correct"
        pattern: "^[A-Za-z]+\/[A-Za-z_]+(?:\/[A-Za-z_]+)?$"

      - type: maxLength
        errorMessage: "Timezone length should be less than 100"
        length: 100
      
  accountId:
    type: Int
    validations:
      - type: minIntValue
        errorMessage: "Account id should be greater than 100000"
        minValue: 100000
      - type: maxIntValue
        errorMessage: "Account id should be less than 9999999"
        maxValue: 9999999

steps:
  - name: getCurrentTime
    type: action
    action: http.get
    version: 1
    inputs:
      url: 'https://worldtimeapi.org/api/timezone/${{ .workflowInputs.timezone }}'
      selectors:
        - name: timezone
          expression: '.responseBody | fromjson.abbreviation'
        - name: datetime
          expression: '.responseBody | fromjson.datetime'
```

### workflowConstants (Optional)

Workflow constants provide access to runtime metadata about the currently executing workflow. These constants are automatically available in all workflow definitions and you can reference them using the expression syntax `${{ .workflowConstants.<constantName> }}`.

#### Available constants

The following constants are available in all workflow definitions.

| Constant              | Type      | Description                                                         |
| --------------------- | --------- | ------------------------------------------------------------------- |
| `runId`               | String    | Unique identifier for the current workflow execution                |
| `accountId`           | Int       | Account ID where the workflow is running                            |
| `organizationId`      | String    | Organization ID associated with the workflow                        |
| `definitionName`      | String    | Name of the workflow definition                                     |
| `definitionVersion`   | String    | Version of the workflow definition                                  |
| `definitionScopeType` | String    | Scope type of the workflow definition                               |
| `startedAt`           | Timestamp | Epoch timestamp in milliseconds when the workflow execution started |

#### Usage examples

```yaml
 name: testWorkflowConstantsWithJQ
 description: Test workflow to demonstrate workflowConstants usage
  steps:
    - name: logWithConstants
      type: action
      action: newrelic.ingest.sendLogs
      version: '1'
      inputs:
        logs:
          - message: Workflow Execution Started
            attributes:
              accountId: ${{ .workflowConstants.accountId }}
              orgId: ${{ .workflowConstants.organizationId }}
              version: ${{ .workflowConstants.definitionVersion }}
              scopeType: ${{ .workflowConstants.definitionScopeType }}
              startedAt: ${{ .workflowConstants.startedAt }}
              runId: ${{ .workflowConstants.runId }}
              workflowDefinitionName: ${{ .workflowConstants.definitionName }}
```

Use workflow constants with jq expressions to transform values:

```yaml
steps:
    - name: transformConstants
      type: action
      action: newrelic.ingest.sendLogs
      version: '1'
      inputs:
        logs:
          - message: Testing JQ transformations on workflowConstants
            attributes:
              # Convert epoch milliseconds to ISO8601 format
              startedAtISO8601: ${{ (.workflowConstants.startedAt / 1000) | todateiso8601 }}
              # Convert to uppercase
              runIdUppercase: ${{ .workflowConstants.runId | ascii_upcase }}
              # Get string length
              definitionNameLength: ${{ .workflowConstants.definitionName | length }}
              # Convert to string
              accountIdString: ${{ .workflowConstants.accountId | tostring }}
              # Combine multiple constants
              combinedMetadata: ${{ .workflowConstants.definitionName + "-" + .workflowConstants.runId }}
```

The following example converts a timestamp using the `DateTime` action:

```yaml
 steps:
    - name: convertStartedAtFromEpoch
      type: action
      action: utils.datetime.fromEpoch
      version: 1
      inputs:
        timestamp: ${{ .workflowConstants.startedAt }}
        timezoneId: UTC
        pattern: "yyyy-MM-dd HH:mm:ss"
        timestampUnit: MILLISECONDS
        selectors:
          - name: datetime
            expression: ".datetime"
          - name: timezone
            expression: ".timezone"
```

#### Constraints and behavior

> #### ⚠️ IMPORTANT
>
> Note the following when using workflow constants:
>
> -   The runtime provides workflow constants automatically. They are read-only.
> -   The `startedAt` timestamp is in milliseconds (epoch format).
> -   Constants are available in all expression contexts throughout the workflow.
> -   Unlike `workflowInputs`, you don't need to declare constants in the workflow definition.

## Steps [#steps]

The following table describes the `steps` property of a workflow definition.

| Property | Required or Optional | Type          | Constraints | Description                                                                                                                                                                                                                                     |
| -------- | -------------------- | ------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `steps`  | Required             | array of maps |             | The steps to run when the workflow definition executes. There must be at least one step. The properties listed here apply to all step types. Specific step types support additional properties. Refer to [Step types](#step-types) for details. |

> #### ⚠️ IMPORTANT
>
> Steps run in the order you define them in the `steps` array. To change execution order, set the `steps[*].next` property to the name of the step to jump to.

### Common step properties

The following properties apply to all step types.

| Property                | Required or Optional | Type    | Format                                                                                   | Constraints         | Description                                                                                                                                                                                                                                                                            |
| ----------------------- | -------------------- | ------- | ---------------------------------------------------------------------------------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `steps[*].name`         | Required             | String  | Must conform to [expression-safe pattern](#expression-safe-pattern) and cannot be `end`. | **Max length:** 100 | The name of the step that `steps[*].next` references. It cannot be the special keywords `end`, `continue`, or `break`, as these indicate a terminating step, a loop continuation, or a loop break.                                                                                     |
| `steps[*].type`         | Required             | String  |                                                                                          |                     | The type of the step, indicating what the step does when it runs. Refer to [Step types](#step-types) for the available options.                                                                                                                                                        |
| `steps[*].next`         | Optional             | String  | Must conform to [expression-safe pattern](#expression-safe-pattern)                      |                     | The name of the next step to run when this step completes successfully. The special keyword `end` indicates this is the last step to run. If `next` is omitted, the next entry in the `steps` array is the implicit next step. If there is no following entry, the workflow completes. |
| `steps[*].ignoreErrors` | Optional             | Boolean |                                                                                          |                     | `ignoreErrors` is a step-level configuration option that defaults to `false`. When set to `true`, errors during this step's execution won't cause the entire workflow to fail — the workflow continues to subsequent steps.                                                            |

## Step types [#step-types]

### Action [#action]

A step that runs a specific action. Refer to the [Action catalog](https://docs.newrelic.com/docs/workflow-automation/setup-and-configure/actions-catalog/actions-catalog) for the available options.

-   `steps[*].action` (Required)

    -   **Type**: String
    -   **Description**: The fully qualified name of the action function to run. It should follow this convention: `<company domain>.<category of work>.<action name in camelCase>`

    The following are examples of valid action names:

    -   Action using New Relic services (for example, through NerdGraph): `newrelic.dashboards.getDashboard`
    -   Action using Slack: `slack.chat.postMessage`

-   `steps[*].version` (Required)
    -   **Type**: String
    -   **Description**: The version of the action function to run.

-   `steps[*].inputs` (Optional)

    -   **Type**: Map of values (includes expressions)
    -   **Description**:
        -   The inputs to pass to the action function. Each action defines the specific inputs it accepts.
        -   You can use expressions. See the expression strings section for details.

    > #### ⚠️ IMPORTANT
    >
    > Do not pass sensitive data (API keys, secrets, PII, PHI, or any personally identifiable data) as arguments.

-   `steps[*].inputs.selectors` (Optional)

    -   **Type**: list of maps in the form of `name` with `expression`.

    -   **Description**:
        -   The `selectors` input allows you to redefine the output to only return the specified elements.
        -   You can use expressions. See the [Expression strings](#expression-strings) section for details.

    -   The following example retrieves `timezone` and `datetime` from the http.get action output.

    ```yaml
      name: calendar_demo

      workflowInputs:
        timezone:
          type: String
          defaultValue: 'America/Los_Angeles'
        accountId:
          type: Int

      steps:
        - name: getCurrentTime
          type: action
          action: http.get
          version: 1
          inputs:
            url: 'https://worldtimeapi.org/api/timezone/${{ .workflowInputs.timezone }}'
            selectors:
              - name: timezone
                expression: '.responseBody | fromjson.abbreviation'
              - name: datetime
                expression: '.responseBody | fromjson.datetime'
    ```

### Loop [#loop]

A loop iterates over collections (lists, maps, arrays) and automatically creates `index` and `element` variables for each iteration. You can access these loop variables only within the loop using `${{ .steps.<loopStepName>.loop.element }}` or `${{ .steps.<loopStepName>.loop.index }}`.

The loop step supports the following properties:

-   `steps[*].for` (Required)
    -   **Type**: Constant
    -   **Description**: Signals the start of a loop.

-   `steps[*].in` (Required)
    -   **Type**: String (expression)
    -   **Description**: Expression that evaluates to a collection of elements.

-   `steps[*].steps` (Required)
    -   **Description**: Steps to execute on each iteration. Each step can be any step type, including nested loops.

> #### ⚠️ IMPORTANT
>
> Note the following when using loops:
>
> -   `for` (required) marks the beginning of the loop.
> -   `in` (required) must evaluate to a collection castable to a Java array.
> -   `steps` (required) defines the steps executed on each iteration.
> -   The runtime assigns `element` and `index` on each iteration. `index` is zero-based. `element` can be a complex type.
> -   You can access variables created inside the loop — including loop variables and step outputs — only within the loop. The loop clears them on exit.
> -   Loops can access variables defined outside the loop.

**Example: Basic loop**

```yaml
name: myRangeIterator
steps:
  - name: looper
    type: loop
    for:
      # iterate over [1..5]
      in: ${{ [range(1; 6)] }}
      steps:
        - name: logProgress
          type: action
          action: newrelic.ingest.sendLogs
          version: 1
          inputs:
            logs:
              - message: "Element: ${{ .steps.looper.loop.element | tostring }}" # not exist outside of this loop
                attributes:
                  # ranges over [0..4]
                  index: ${{ .steps.looper.loop.index }}
```

**Example: Use for loop with jq expression**

```yaml
name: process-orders
description: Process and validate orders

workflowInputs:
  validStatuses:
    type: List
    defaultValue: ["pending", "confirmed", "shipped"]

steps:
  - name: getOrders
    type: assign
    inputs:
      orders:
        - { id: "001", status: "pending", amount: 100 }
        - { id: "002", status: "invalid", amount: 50 }
        - { id: "003", status: "shipped", amount: 200 }

  - name: processOrders
    type: loop
    for:
      in: ${{ .steps.getOrders.outputs.orders }}
      steps:
        - name: validateStatus
          type: switch
          switch:
            - condition: ${{ .steps.processOrders.loop.element.status as $status | .workflowInputs.validStatuses | index($status) != null }}
              next: validOrder
          next: invalidOrder

        - name: invalidOrder
          type: action
          action: newrelic.ingest.sendLogs
          version: 1
          inputs:
            logs:
              - message: "Invalid order status"
                attributes:
                  orderId: ${{ .steps.processOrders.loop.element.id }}
                  status: ${{ .steps.processOrders.loop.element.status }}

        - name: validOrder
          type: action
          action: newrelic.ingest.sendLogs
          version: 1
          inputs:
            logs:
              - message: "Processing valid order"
                attributes:
                  orderId: ${{ .steps.processOrders.loop.element.id }}
                  status: ${{ .steps.processOrders.loop.element.status }}
                  amount: ${{ .steps.processOrders.loop.element.amount }}
```

**Example: Loop on map**

```yaml
name: myMapIterator
steps:
  - name: looper
    type: loop
    for:
      in: '${{ [ {"key1": "val1"}, {"key2": "val2"} ] }}'
      steps:
        - name: logProgress
          type: action
          action: newrelic.ingest.sendLogs
          version: 1
          inputs:
            logs:
              - message: "Element: ${{ .steps.looper.loop.element | tostring }}"
```

**Example: Jump within a loop**

You can jump between steps within the same loop. You cannot jump into or out of loops, between different loops, or to parent/child loops.

```yaml
name: myLoopJump
steps:
  - name: fistStep
    type: action
    action: newrelic.ingest.sendLogs
    version: 1
    inputs:
      logs:
        - message: "the first step"
  - name: loopStep
    type: loop
    for:
      in: ${{ [range(1; 6)] }}
      steps:
        - name: loopStep1
          type: action
          action: newrelic.ingest.sendLogs
          version: 1
          inputs:
            logs:
              - message: "the loop first step"
          next: loopStep3                         # Okay within the loop
        - name: loopStep2
          type: action
          action: newrelic.ingest.sendLogs
          version: 1
          inputs:
            logs:
              - message: "the loop second step, never executed"
        - name: loopStep3
          type: action
          action: newrelic.ingest.sendLogs
          version: 1
          inputs:
            logs:
              - message: "the loop third step"
          next: fistStep                        # Not okay, first step is not in the loop context
```

**Example: Break and continue**

Use `next: break` or `next: continue` to control loop flow. These are reserved keywords within loops. Outside loops, they jump to the workflow end. Inside a loop, `end` behaves like `break`.

```yaml
name: myLoopContinueBreak
steps:
  - name: fistStep
    type: action
    action: newrelic.ingest.sendLogs
    version: 1
    inputs:
      logs:
        - message: "the first step"
  - name: loopStep
    type: loop
    for:
      in: ${{ [range(1; 6)] }}
      steps:
        - name: loopStep1
          type: action
          action: newrelic.ingest.sendLogs
          version: 1
          inputs:
            logs:
              - message: "the loop first step"
          # continue with next iteration without executing loopStep2
          next: continue
        - name: loopStep2
          type: action
          action: newrelic.ingest.sendLogs
          version: 1
          inputs:
            logs:
              - message: "the loop second step, never executed"

  - name: loopAgain
    type: loop
    for:
      in: ${{ [range(1; 6)] }}
      steps:
        - name: loopAgainStep1
          type: action
          action: newrelic.ingest.sendLogs
          version: 1
          inputs:
            logs:
              - message: "the loop again first step"
          # stop iterating and continue with next step after the loop
          next: break
        - name: loopAgainStep2
          type: action
          action: newrelic.ingest.sendLogs
          version: 1
          inputs:
            logs:
              - message: "the loop again second step, never executed"

  - name: lastStep
    type: action
    action: newrelic.ingest.sendLogs
    version: 1
    inputs:
      logs:
        - message: "the last step"
```

### Switch [#switch]

A switch step checks various conditionals and takes the first branch that evaluates to true. It can contain any number of `condition` elements in a list, checking them in order and processing the first one that evaluates to true. If none evaluate to true, it runs its `next` step as defined in `steps[*].next`.

-   `steps[*].switch` (Required)
    -   **Type**: Array
    -   **Description**: An array of switch cases, specifying the ordered list of conditions to evaluate.

-   `steps[*].switch[*].condition` (Required)
    -   **Type**: String ([expression](#expression-strings))
    -   **Description**: The condition of the switch case. If the condition evaluates to true, the workflow executes the case's `next` step.
    -   See the [Expression strings](#expression-strings) section for details.

-   `steps[*].switch[*].next`
    (Required)

    -   **Type**: String (conforming to [expression-safe pattern](#expression-safe-pattern))
    -   **Description**: The name of the step to run if the case's condition evaluates to true. The special keyword `end` indicates this is the last step to run.

    ```yaml
      - name: hasCompleted
        type: switch
        switch:
          - condition: ${{ .steps.waitForCompletion.outputs.automationExecutionStatus == "Failed" }}
            next: displayError
          - condition: ${{ .steps.waitForCompletion.outputs.automationExecutionStatus == "Success" }}
            next: displaySuccess
        next: displayUnexpected
    ```

### Wait [#wait]

A step that pauses workflow execution for a specified number of seconds before continuing.

The wait step can also listen for one or more signals. Each signal must define a corresponding next step. If the wait step receives a signal, it processes the first one received and executes its defined next step. If no signal is received, the workflow continues to the next step after the wait period ends.

The wait step stores the received signal value in its output, making it available for use in subsequent steps.

The following example shows a wait step with a signal handler:

```yaml
name: waitSignalExample
steps:
  - name: waitStep
    type: wait
    seconds: 300
    signals: [{name: 'mySignal', next: 'mySignalHandler'}]
  - name: endStep
    type: action
    action: newrelic.ingest.sendLogs
    version: 1
    inputs:
      logs:
        - message: "didn't get signal"
    next: end
  - name: mySignalHandler
    type: action
    action: newrelic.ingest.sendLogs
    version: 1
    inputs:
      logs:
        # log entry "got signal with data bar" when using signalInputs [{"foo": "bar"}]
        - message: "got signal with data ${{ .steps.waitStep.outputs.signalInputs.foo }}"
```

The wait step accepts the following properties:

-   `steps[*].seconds` (Required)
    -   **Type**: Number
    -   **Description**: The number of seconds to wait before continuing the workflow run.

-   `steps[*].signals`
    -   **Type**: Array
    -   **Description**: The signals that divert program flow when the wait step receives them.

-   `steps[*].signals[*].name`
    -   **Type**: String
    -   **Description**: The name of the signal to listen for.

-   `steps[*].signals[*].next`
    -   **Type**: String
    -   **Description**: The step to execute if the specified signal is received.

### Assign [#assign]

A step that defines variables for use throughout the workflow. This step assigns values to variables that subsequent steps can reference. This step type lets you define all variables in one place for use throughout the workflow.

The following sample workflow shows how to use the assign step:

```yaml
name: sampleWorkflowWithAssign

workflowInputs:
  initialValue:
    type: String
    defaultValue: "abcd"
  anotherValue:
    type: Int
    defaultValue: 1234

steps:
  - name: variableInitialization
    type: assign
    inputs:
      stringVar: "${{ .workflowInputs.initialValue }}"
      intVar: "${{ .workflowInputs.anotherValue }}"
      concatenationVar: "${{ .workflowInputs.initialValue }} - concatenated"
      booleanVar: true
      mapVar:
        key1: "value1"
        key2: "${{ .workflowInputs.initialValue }}"
      listVar:
        - "listItem1"
        - "${{ .workflowInputs.initialValue }}"
        - "${{ .workflowInputs.anotherValue }}"

  - name: logVariables
    type: action
    action: newrelic.ingest.sendLogs
    version: 1
    inputs:
      logs:
        - message: "stringVar: ${{ .steps.variableInitialization.outputs.stringVar }}"
        - message: "intVar: ${{ .steps.variableInitialization.outputs.intVar }}"
        - message: "concatenationVar: ${{ .steps.variableInitialization.outputs.concatenationVar }}"
        - message: "booleanVar: ${{ .steps.variableInitialization.outputs.booleanVar }}"
        - message: "mapVar: ${{ .steps.variableInitialization.outputs.mapVar | tojson }}"
        - message: "listVar: ${{ .steps.variableInitialization.outputs.listVar | tojson }}"
```

The assign step accepts the following input:

-   `steps[*].inputs` (Required)
    -   **Type**: Map of values (includes [expressions](#expression-strings))
    -   **Description**:
        -   The inputs are a map of variable names and their assigned values. When you assign secret references to variables, they remain as references and the system doesn't convert them to their actual values. However, the workflow evaluates and converts other expressions (such as workflow inputs) to their actual values.
    -   Allowed input types: `Integer`, `Double`, `Boolean`, `String`, `Array`, `Map`

### State [#state]

A step that stores key/value pairs in the workflow's shared state. Values persist for the duration of the workflow run. Subsequent steps can access these values using the `.workflowState` accessor. If multiple state steps set the same key, each later value overwrites the earlier one.

The state step accepts the following input:

-   `steps[*].inputs` (Required)
    -   **Type**: Map of key/value pairs
    -   **Description**: The key/value pairs to store in the workflow state.
        -   Keys must be valid identifiers.
        -   Values can be any type — `String`, `Integer`, `Boolean`, `List`, or `Map` (including nested structures). Values include expressions. The runtime doesn't resolve secret references — it stores them as-is.

To access state values in expressions, use `.workflowState.<keyName>` in any step that follows the state step:

`${{ .workflowState.myKey }}`

The following example shows a state step storing and retrieving values:

```yaml
name: workflow-state-example
description: Store and retrieve state across steps
steps:
  - name: storeInitialData
    type: state
    inputs:
      count: 1
      foo: "bar"
  - name: updateCount
    type: state
    inputs:
      color: "green"
      count: 2
  - name: logState
    type: action
    action: newrelic.ingest.sendLogs
    version: 1
    inputs:
      logs:
        - message: "foo=${{ .workflowState.foo }}, color=${{ .workflowState.color }}, count=${{ .workflowState.count }}"
```

The state step has the following constraints:

-   Key name pattern: `^[a-zA-Z_][a-zA-Z0-9_]*$`
-   Key name max length: 255 characters
-   Max keys per state step: 10
-   Max total state keys per workflow run: 100
-   State value max length: 1,000 characters

### Error [#error]

A step that ends the workflow and reports a failure with a custom message. If a `condition` is present, the workflow evaluates it first and stops only when the condition is `true`. If you don't provide a `message`, the step uses the default message `failed as requested from step: failureStep`.

The following example shows an error step with a condition:

```yaml
name: sendLogWorkflow
description: 'This workflow send Logs to newrelic'
steps:
  - name: sendLog
    type: action
    action: newrelic.ingest.sendLogs
    version: 1
    inputs:
      logs:
        - message: Sending Logs to NewRelic
  - name: error_4
    type: error
    condition: ${{.steps.sendLog.outputs.success == false}}
    message: Terminating Workflow as sendLog failed.
    next: end
```

The following table describes the inputs accepted by the error step.

| Input field | Optionality | Type   | Example                                       |
| ----------- | ----------- | ------ | --------------------------------------------- |
| `message`   | Optional    | String | `"Oops Something went wrong while execution"` |
| `condition` | Optional    | String | `${{ .steps.main.outputs.success == false }}` |

## Validation types [#validation-types]

The following validation types are available for `workflowInputs`.

| Validation type | Property                  | Required or Optional | Type    | Description                                                                                                                 |
| --------------- | ------------------------- | -------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------- |
| `regex`         | `validations[*].pattern`  | Required             | String  | Validates the workflow input value against the provided regex pattern.                                                      |
| `maxIntValue`   | `validations[*].maxValue` | Required             | Integer | Validates that the workflow input value is less than the provided maxValue.                                                 |
| `minIntValue`   | `validations[*].minValue` | Required             | Integer | Validates that the workflow input value is greater than the provided minValue.                                              |
| `maxLength`     | `validations[*].length`   | Required             | Integer | Validates the maximum length of workflow input strings and collections (`Array`, `Set`, `Map`, and other collection types). |

## Expressions and secrets

### Expression strings [#expression-strings]

Several properties accept string values with embedded expressions that the workflow evaluates during execution, enabling dynamic values in workflow definitions.

Expression strings can contain one or more expressions, each enclosed within double curly braces. [jq](https://jqlang.org/manual/) evaluates the content within the braces, letting you access, filter, and transform values.

The following example gets the length of a workflow input string:

`${{ .workflowInputs.myString | length }}`

To validate and test your expressions, use the [JQ Playground](https://play.jqlang.org/).

### Expression properties [#expression-properties]

You can access these properties with expressions. They live in a `scope` object, so expressions must start with a period `(.)` to access those properties of the scope object.

The available properties are:

-   `workflowInputs` — Object containing the inputs passed to the workflow on start.

    **Example**: `${{ .workflowInputs.myInput }}`

-   `workflowState` — Object containing key/value pairs stored by `state` steps during the current workflow run.

    **Examples** (flat key, nested property, array index): `${{ .workflowState.myKey }}` · `${{ .workflowState.myMap.nestedProperty }}` · `${{ .workflowState.myList[0] }}`

-   `steps` — Object containing a property for each step in the workflow.

    -   `steps.<stepName>` — Object containing properties for a specific step.
    -   `steps.<stepName>.outputs` — Object containing result properties, specific to the step or action.

    **Example**: `${{ .steps.myStep.outputs.myResult }}`

### Expression evaluation results [#expression-evaluation-results]

A single jq expression can evaluate to any JSON type. However, the final result of an expression string depends on whether the string contains only the expression or additional content.

#### Single expression (preserves JSON type)

If an expression string consists of only one expression with no surrounding content, it evaluates to the jq expression's result while maintaining its original JSON type. For example, `${{ .workflowInputs.myArray }}` evaluates to an array. This preserves the type of complex data structures when passing them between steps.

#### Multiple expressions or mixed content (converts to string)

If an expression string contains content other than a single expression, it evaluates to a string result. This occurs when an expression has content before or after it, or when the string has multiple expressions within it. jq evaluates each expression and converts it to a string representation.

> #### ⚠️ IMPORTANT
>
> When a jq expression evaluates to null, a null node is returned. For example, the expression `${{ .workflowInputs.missingInput }}` returns null if `missingInput` is not given as workflow input.

The following examples use `myArray` with the value `[1, 2, 3]`.

| Expression string                                                                   | Result data                  | Result type      |
| ----------------------------------------------------------------------------------- | ---------------------------- | ---------------- |
| `${{ .workflowInputs.myArray }}`                                                    | `[1, 2, 3]`                  | Array of numbers |
| `${{ .workflowInputs.myArray | length }}`                                           | 3                            | Number           |
| `${{ .workflowInputs.myArray | length > 0 }}`                                       | True                         | Boolean          |
| `Input is not empty: ${{ .workflowInputs.myArray | length > 0 }}`                   | `"Input is not empty: true"` | String           |
| `${{ .workflowInputs.myArray }} has length ${{ .workflowInputs.myArray | length }}` | `"has length 3"`             | String           |

### Expression safe pattern [#expression-safe-pattern]

Properties you use in expressions must conform to: `^[A-Za-z_][A-Za-z0-9_]*$`

### Secret references [#secret-references]

Use secret values in actions by providing reference strings that specify the name of a secret to look up in the Secrets Service. To reference a secret in a workflow definition, use the syntax:

-   `${{ :secrets:<SECRET_NAME> }}` for a secret not in a `namespace`
-   `${{ :secrets:<NAMESPACE>:<SECRET_NAME> }}` for a secret in a `namespace`
-   `${{ :secrets:<SCOPE>:<NAMESPACE>:<SECRET_NAME> }}` for a secret in a scope and namespace

    `SCOPE` accepts `ACCOUNT` or `ORGANIZATION`.

An expression string can contain jq expressions, secret references, or both.

The following are examples of secret references used in actions:

```yaml
  steps:
  - name: mySecretStep
    type: action
    action: newrelic.instrumentation.log
    inputs:
      message: My message
      licenseKey: ${{ :secrets:<SECRET_NAME> }}
```

```yaml
  steps:
    - name: bearer_auth
      type: action
      action: utils.http.post
      inputs:
        headers:
          Authorization: Bearer ${{ :secrets:<SECRET_NAME> }}
```

## Complete example [#examples]

### Calendar demo

This complete workflow example demonstrates multiple workflow features, including workflow constants, selectors, wait steps, NRDB queries, and switch statements.

```yaml
name: calendar_demo

steps:
  - name: getUserCreated
    type: action
    action: newrelic.nerdgraph.execute
    version: 1
    inputs:
      graphql: |
        {
          actor {
            user {
              id
              createdAt
              timeZoneName
            }
          }
        }
      selectors:
        - name: id
          expression: ".data.actor.user.id"
        - name: createdAt
          expression: ".data.actor.user.createdAt"
        - name: timeZoneName
          expression: ".data.actor.user.timeZoneName"

  - name: getCreatedTime
    type: action
    action: utils.datetime.fromEpoch
    version: 1
    inputs:
      timestamp: ${{ .steps.getUserCreated.outputs.createdAt }}
      pattern: "yyyy-MM-dd HH:mm:ss"
      timezoneId: ${{ .steps.getUserCreated.outputs.timeZoneName }}
      selectors:
        - name: datetime
          expression: ".datetime"
        - name: abbreviation
          expression: ".timezone.abbreviation"

  - name: logTime
    type: action
    action: newrelic.ingest.sendLogs
    version: 1
    inputs:
      logs:
        - message: "Demo ${{ .workflowConstants.runId }} userId ${{ .steps.getUserCreated.outputs.id }} created at ${{ .steps.getCreatedTime.outputs.datetime }} ${{ .steps.getCreatedTime.outputs.abbreviation }}"

  - name: wait
    type: wait
    seconds: 1

  - name: queryForLog
    type: action
    action: newrelic.nrdb.query
    version: 1
    inputs:
      query: "FROM Log SELECT message, timestamp SINCE 5 minute ago WHERE message LIKE 'Demo ${{ .workflowConstants.runId }} userId ${{ .steps.getUserCreated.outputs.id }} created at%'"

  - name: checkQuery
    type: switch
    switch:
      - condition: ${{ .steps.queryForLog.outputs.results | length > 0 }}
        next: postResultsMessage

  - name: postWaitingMessage
    type: action
    action: newrelic.ingest.sendLogs
    version: 1
    inputs:
      logs:
        - message: "Waiting for log message..."
    next: wait

  - name: postResultsMessage
    type: action
    action: newrelic.ingest.sendLogs
    version: 1
    inputs:
      logs:
        - message: "Found log message! ${{ .steps.queryForLog.outputs.results[0].message }}"
```

## Related topics [#related-topics]

[Workflow examples](https://docs.newrelic.com/docs/workflow-automation/workflow-examples/workflow-examples)

See the schema in action with real-world workflow examples

[Create workflow definition](https://docs.newrelic.com/docs/workflow-automation/workflow-automation-apis/create-workflow-definition)

Create new workflow definitions using the NerdGraph API

[Validate workflow definition](https://docs.newrelic.com/docs/workflow-automation/workflow-automation-apis/validate-workflow-definition)

Validate workflow YAML syntax before deployment

[Actions catalog](https://docs.newrelic.com/docs/workflow-automation/setup-and-configure/actions-catalog/actions-catalog)

Browse all available actions and their input/output schemas
