Sunshine Workflows System Tasks

Important: The Sunshine Workflows API is in early access. This API is subject to change and should not be used in a production environment. Visit the Sunshine Workflows API EAP forum to provide feedback.

These types of tasks do not map to a specific Zendesk entity action, but rather allow for implementing behaviors that are customized to your application's business logic.

You can define the following system tasks for your Sunshine Workflows:

Choice

A choice task allows for the evaluation of an expression using JavaScript syntax in the condition field. The domain of possible output values for the condition expression must be accounted for in the choices field, where each option has a corresponding list of tasks that form a subworkflow. This subworkflow is executed in the event that its key is the output of the condition expression.

A choice task has three required fields:

  • type - The string "CHOICE"

  • condition - A string containing a valid JavaScript expression that conforms to the GraalJS engine's syntax. The expression must resolve to a value that can be mapped to one of the keys in the choices field.

    The expression is interpreted using the GraalVM JavaScript Engine. The engine supports full compatibility with the latest ECMASCRIPT specification. Therefore, the syntax must conform to this version

  • choices - An object containing key-value pairs that map all possible output values of the condition expression (keys) to lists of tasks (values). Note that the tasks in these lists must conform to the same schema as any standalone workflow task

For example, given this input:

{
  "type": "CHOICE",
  "key": "choice",
  "input": {
    "refund_amount": "${workflow.input.amount}"
  },
  "condition": "...",
  "choices": [
    "..."
  ]
}

The following expressions are valid:

Ternary expression:

  "condition": "$.refund_amount > 50 ? 'a' : 'b'"

Conditional if-else expression, where the last line in each branch is the return value:

  condition: "if ($.refund_amount > 50) { 'a' } else { 'b' }"

Conditional if-else expression with variables:

  condition: "if ($.refund_amount > 50) { let variable_a = 'a'; variable_a } else { let variable_b = 'b'; variable_b }"

Note that these are not function expressions and therefore no return statement is necessary. In fact, including a return statement is invalid syntax and will result in an error.

If there is a need to declare more complex behaviors as functions, remember to invoke the function. Otherwise the expression will not be valid. For example, the following are valid function declarations and invocations:

  condition: "function evaluate() { if ($.refund_amount > 50) { let variable_a = 'a'; return variable_a } else { let variable_b = 'b'; return variable_b } } evaluate();"
  condition: "(function() { if ($.refund_amount > 50) { let variable_a = 'a'; return variable_a } else { let variable_b = 'b'; return variable_b } })()"
Example

The following example illustrates a simple choice task where the input to the workflow contains a refund_amount. If this amount is greater than $50, the condition returns "ticket_pending"; otherwise it returns "ticket_solved". If "ticket_solved", the Support ticket with id = 1 will have its status updated to "solved", presumably because the amount is small enough that an automatic refund can be processed. If "ticket_pending", the ticket will have its status updated to "pending", since a larger amount may require an authorization.

{
  "type": "CHOICE",
  "key": "choice",
  "condition": "if ($.refund_amount > 50) { 'ticket_pending'; } else { 'ticket_solved'; }",
  "input": {
    "refund_amount": "${workflow.input.amount}"
  },
  "choices": {
    "ticket_solved": [
      {
        "type": "UPDATE_SUPPORT_TICKET",
        "key": "update_ticket_again",
        "input": {
          "ticket_id": "1",
          "ticket": {
            "status": "solved"
          }
        }
      }
    ],
    "ticket_pending": [
      {
        "type": "UPDATE_SUPPORT_TICKET",
        "key": "update_ticket_again",
        "input": {
          "ticket_id": "1",
          "ticket": {
            "status": "pending"
          }
        }
      }
    ]
  }
}

Lambda

A lambda task allows you to transform data in the workflow. This allows you to run evaluations in order to calculate new values that can be used in other steps of the workflow.

Place evaluations within the script field. Anything that returns a value within the body of a JavaScript function declaration will be valid.

How does script differ from the condition in a choice task? The choice task's condition takes a JavaScript expression. However, the lambda task's script field takes the body of a function declaration.

Therefore, the following script will work:

"script": "if (($.refund_amount * 2) > 50) { return { resultValue: 'Expensive request' } } else { return { resultValue: 'Cheap request' } }"

Since the script evaluates the body of a function declaration, you can declare variables on their own lines (separated by semicolons):

"script": "let max = 50; if (($.refund_amount * 2) > max) { return { resultValue: 'Expensive request' } } else { return { resultValue: 'Cheap request' } }"

Unlike the choice task's condition, a one-line expression like a ternary expression is not valid:

"script": "(($.refund_amount * 2) > 50) ? ({ resultValue:'Expensive request' }) : ({ resultValue:'Cheap request' })"
Example

The following example illustrates a lambda task where the input to the lambda task contains a refund_amount. The lambda task calculates the double of this refund_amount. If this result is greater than 50, the lambda's script returns { resultValue: 'Expensive request' }; otherwise it returns { resultValue: 'Cheap request' }. The resulting value can be accessed in further workflow tasks with ${lambda_task_key.output.result.resultValue}.

{
  "name": "12345_refund_flow",
  "description": "Workflow with a lambda task",
  "tasks": [
    {
      "type": "LAMBDA",
      "key": "lambda_task_key",
      "input": {
        "refund_amount": 30
      },
      "script": "if (($.refund_amount * 2) > 50) { return { resultValue: 'Expensive request' } } else { return { resultValue: 'Cheap request' } }"
    },
    {
      "type": "CREATE_SUPPORT_TICKET",
      "key": "create_ticket",
      "input": {
        "ticket": {
          "subject": "New refund requested - ${lambda_task_key.output.result.resultValue}",
          "description": "foo",
          "requester": {
            "name": "test user",
            "email": "[email protected]"
          }
        }
      }
    }
  ]
}

Pause

A pause task is a mechanism for introducing the outside world into the context of your running workflow. When a pause task is executed within a workflow, it logically pauses the running workflow and waits for something to resume it. Once resumed, the remainder of the workflow executes as it normally would.

A pause task is of type PAUSE and has an optional timeout field. The timeout field specifies the number of seconds the task is allowed to wait before the workflow is deemed to be in a failure state. If omitted, a default timeout value of 5 minutes is assigned. The API imposes a 72-hour ceiling on the timeout. If any larger number is specified, 72 hours will replace the larger value.

A pause task can be resumed with specific input for future tasks to make use of. For more details, see the Resume Running Workflow endpoint.

In the following example, a timeout of 300 seconds is provided. If no external action is taken within this time frame, the workflow will fail.

{
  "type": "PAUSE",
  "key": "Wait for approval",
  "description": "Wait for a manager's approval before proceeding",
  "timeout": 300
}