# Authentication for the Github Package repository

NPM_REGISTRY_AUTH_LINES=//npm.pkg.github.com/:_authToken=
```

Once the code executor is configured, you can install packages through the **Modify package.json** option in the Workflows editor.

```txt title="package.json"
{
    "dependencies": { 
        "@mycompany/internal-library": "1.0.0", 
    }
}
```

## Configure block settings

Refer to the [Code block reference](../../../reference/objects/block/code.mdx) for information about the block's settings.

---

## Execute Python with the Code block

import Codesvg from "/img/icons/workflows/blocks/code.svg";

Use  **Code** blocks to write custom Python code that can transform data and perform complex logic. You can also use popular [Python libraries](#available-libraries) to further extend the functionality of workflows.

:::info

To use Python code blocks on self-hosted deployments, you must have the **code-executor** [container](../../../../self-hosted/concepts/architecture.mdx) configured.

:::

`{{ }}` curly braces around embedded expressions are not required in Python.

## Add a Python Code block

To use Python in a workflow, add a Code block to the canvas and select **Python**.

## Write and execute Python code

The Python code editor has much of the same features as the JavaScript editor, such as autocomplete and syntax highlighting. For example, you can transform an array of records in a similar manner to `map()` with JavaScript:

```python
data = query1.data
return [{
   "fullName": customer['name'],
   "emailAddress": customer['email']
} for customer in data]
```

## Python limitations

import Loopsvg from "/img/icons/workflows/blocks/loop.svg";
import Filtersvg from "/img/icons/workflows/blocks/filter.svg";

[JavaScript](../../../../queries/quickstart.mdx) is the primary method for manipulating and transforming data in Retool. To maintain interoperability with other blocks, Python does not support:

- Function block calls or triggering queries.
- Usage within  [Loop](../logic/loop.mdx) and  [Filter](../logic/filter.mdx) blocks.
- Data output in types other than serialized JSON.
- User-imported Python libraries.

## Use Python libraries

Retool supports many popular libraries, and you can import custom libraries from [PyPI](https://pypi.org/).

### Built-in libraries

Workflows includes built-in support for many popular libraries. This enables you to extend the functionality of workflows beyond data transformation. 

To use a built-in Python library:

1. Open the **Libraries** tab, and click **+**. 
2. Select **Add Python library**.
3. Search for the libraries you want to include, and toggle the checkbox to make it available.
4. Click **Add selected libraries**.

export function PythonLibrary() {
  return (
    
      
    
  )
}

You can browse through all built-in libraries below. Some libraries are only available on self-hosted deployments.

### Add custom libraries

:::caution

Retool automatically tries to use [NsJail](https://github.com/google/nsjail) to sandbox the creation of execution environments for custom libraries. NsJail requires [privileged](https://docs.docker.com/reference/cli/docker/container/run/#privileged) container access. While NsJail [is not required](/changelog/workflow-libaries-without-nsjail) to use custom libraries, it is strongly recommended. 

Privileged mode is not supported by ECS/Fargate deployments. To make use of custom libraries, you must either:

- Migrate to an ECS/EC2 deployment or another supported deployment framework.
- Host the [code-executor](../../../../self-hosted/concepts/architecture.mdx#code-executor) service in a separate "jailed" cluster that can run containers in privileged mode on ECS/EC2 or another supported deployment framework. You then configure the [`CODE_EXECUTOR_INGRESS_DOMAIN`](../../../../self-hosted/reference/environment-variables/workflows.mdx#variable-code_executor_ingress_domain) environment variable to communicate with the `code-executor` service over `http` or `https`.

:::

Both Cloud and self-hosted organizations can import public packages from PyPI. Self-hosted organizations can also import private packages from PyPI.

#### Add a public PyPI repository

As with typical Python development, you provide a list of libraries to use in a `requirements.txt` file. Retool makes this available through the Workflow editor. To populate it, navigate to the **Libraries** tab, click **+**, then select **Modify requirements.txt**.

```txt title="requirements.txt"
bson==0.5.10
numpy==1.24.1
pandas==1.5.2
```

export function Public() {
  return (
    
      
    
  )
}

#### Add a private PyPI respository

:::caution Requirements

Private PyPI repositories are only available on self-hosted deployments that have a configured [code-executor](../../../../self-hosted/concepts/architecture.mdx#code-executor) service.

:::

Configuring a private PyPI repository requires setting of two environment variables in the code executor deployment: `PYPI_REPOSITORIES` and `TRUSTED_HOSTS`.

- `PYPI_REPOSITORIES` is a comma-separated list of domains, and each entry is the same format as [index-url flag in pip](https://pip.pypa.io/en/stable/cli/pip_install/#cmdoption-0). The default value is [https://pypi.org/simple](https://pypi.org/simple). Some configurations are below:
- `TRUSTED_HOSTS` is a comma-separated list of domains, and each entry is a domain name, similar to the [trusted-host flag in pip](https://pip.pypa.io/en/stable/cli/pip/#cmdoption-trusted-host). This allows for authentication through pip and may not be required in most circumstances.

```
PYPI_REPOSITORIES=https://pypi.org/simple,https://repository.example.com/simple
TRUSTED_HOSTS=pypi.org,repository.example.com
```

```
PYPI_REPOSITORIES=https://repository.example.com/simple
TRUSTED_HOSTS=repository.example.com
```

Once configured, you can install packages through the **Modify requirements.txt** option in the Workflows editor using the same steps listed in the [Add a public PyPI repository](#add-a-public-pypi-repository) section.

```txt title="requirements.txt"
my_internal_library==0.1.0
```

## Configure block settings

Refer to the [Code block reference](../../../reference/objects/block/code.mdx) for information about the block's settings.

---

## Perform conditional logic with the Branch block

import Branchsvg from "/img/icons/workflows/blocks/branch.svg";

Instead of writing [if...else](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else) statements in JavaScript, you can use  **Branch blocks** to visually build conditional statements that control different connected blocks. This is useful for breaking out complex JavaScript logic into more manageable blocks.

A Branch block evaluates the data it receives from a connected block using the defined condition. If the condition evaluates as a truthy value, the workflow follows the control flow for the **If** statement. If not, it follows the control flow for the **Else** statement.

## Use a Branch block

For example, you could build a workflow that alerts your shipping department if the number of unshipped orders exceeds a certain threshold, such as `query1.data.length > 2`.

You can add multiple branches as **Else if** statements to visually build out complex conditional logic. As you test your workflow, the condition that evaluates as `true` is highlighted in green.

Each conditional statement has its own connector so you can connect different blocks and define separate control flows for each outcome.

## Configure block settings

Refer to the [Branch block reference](../../../reference/objects/block/branch.mdx) for information about the block's settings.

---

## Filter data with the Filter block

Filter blocks enable you to write logic to perform actions with a specific subset of data. If an item evaluates as `true`, the Filter block includes it in the data it returns.

:::warning Apply filters to queries first

Optimize your queries to filter data and return only the results you need first, such as `LIMIT` or `WHERE` clauses for SQL databases or pagination for API requests.

:::

Filter blocks function similar to [Loop blocks](loop.mdx) and iterate through the query you select from the **Iterable** dropdown. Set the **Filter Expression** to a condition that evaluates as `true`, using `value` and `index` to reference evaluated items and their indexes.

For example, you can use a Filter block to return a list of customers with an email that includes `github`.

## Configure block settings

Refer to the [Filter block reference](../../../reference/objects/block/filter.mdx) for information about the block's settings.

---

## Assemble logic blocks in Retool Workflows



---

## Iterate through data with the Loop block

:::info Try out the new Loop block

A [new version of the Loop block](./loop.mdx) is currently in beta.

:::

import Loopsvg from "/img/icons/workflows/blocks/loop.svg";

A  **Loop** block contains an embedded block which runs for each evaluated item in an array. You use `value` and `index` to reference evaluated items and their indexes.

For example, you can automate a welcome email to new customers and customize the message for each recipient, using values like `{{ value.email }}` and `{{ value.name }}`.

## Loop models

The default mode for Loop blocks is **GUI**. This mode automatically configures the loop to iterate through the query data you select from the **Loop input** dropdown. If you require more complex loop logic, use **Code**. This allows you to configure the JavaScript code used by the Loop block. You must specify the query data through which to loop using `value` or `index`.

The _lambda_ operation is run on each item in the loop input. Operations are generally [resource queries](../../../../queries/quickstart.mdx#resource-queries) but you can also write custom JavaScript.

## Configure block settings

:::note Retry settings per iteration or loop

Toggle **Retry per iteration** or **Retry entire loop** to configure retry settings for the query that runs for each item or the loop itself.

:::

import Blocksettings from "../../../reference/objects/block/_settings/_settings.mdx";
import Timeoutsettings from "../../../reference/objects/block/_settings/_timeout.mdx";
import Retrysettings from "../../../reference/objects/block/_settings/_retry.mdx";
import Intervalsettings from "../../../reference/objects/block/_settings/_interval.mdx";
import Exponentialsettings from "../../../reference/objects/block/_settings/_exponential.mdx";
import Finallysettings from "../../../reference/objects/block/_settings/_finally.mdx";
import Coefficientsettings from "../../../reference/objects/block/_settings/_coefficient.mdx";
import Maxintervalsettings from "../../../reference/objects/block/_settings/_max_interval.mdx";

---

## Loop block

A  **Loop block** contains an embedded block which runs for each evaluated item in an array. You use `value` and `index` to reference evaluated items and their indexes.

For example, you can automate a welcome email to new customers and customize the message for each recipient, using values like `{{ value.email }}` and `{{ value.name }}`.

## Configure execution mode

Loop blocks support different modes for processing loop iterations:

- **Parallel**: Executes all iterations simultaneously.
- **Sequential**: Executes each iteration one at a time.
- **Batch**: Combines parallel and sequential modes to execute a parallel iterations in batches.

Within a Loop block, set the **Execution mode** to the mode you want to use.

:::tip

The **Sequential** or **Batch** modes include an optional delay between iterations. This can help prevent API rate limits if the workflow needs loop through a large number of iterations.

:::

### Parallel

**Parallel** mode processes all iterations concurrently. For example, a Loop block that iterates through a list of users to update their information makes the same API request simultaneously.

Parallel loops complete more quickly than sequential or batched loops, resulting in shorter run times. Parallel loops are best for processing small amounts of data. Looping through large datasets can result in API limits or degraded service of your data source.

### Sequential

**Sequential** mode processes each iteration sequentially. Each iteration must complete before the next one starts. For example, a Loop block that iterates through a list of users to update their information makes the same API request one at a time.

If you want to add a delay between sequential iterations, set **Iteration delay (ms)** to the desired amount of time.

Sequential loops take longer to process as each iteration must complete before the next one begins. This is especially useful if you're using APIs with strict rate limits or need to make the minimum amount of changes per iteration.

### Batch

**Batch** mode runs a number of iterations in parallel, waiting for all to complete before moving to the next batch. Update **Batch size** to specify how many iterations should run per batch (default is `10`).

If you want to add a delay between batched iterations, set **Batch delay (ms)** to the desired amount of time.

For example, a Loop block that iterates through a list of users to get their information performs parallel loops in batches. The following diagram illustrates a loop where each batch contains two items.

Batched loops are a combination of parallel and sequential loops. You can find the right balance between concurrent iterations and the delay between each batch for speed and effectiveness.

## Configure block settings

Refer to the [Loop block reference](../../../reference/objects/block/loop.mdx) for information about the block's settings.

---

## Query resources with the Resource query block

:::tip

Learn more about Retool's built-in [API](../../../data-sources/guides/integrations/index.mdx) and [database](../../../data-sources/guides/integrations/index.mdx) integrations, and how to [connect your own data sources](../../../data-sources/index.mdx).

:::

You can add Resource query blocks to workflows that enable you to write [queries](../../../queries/quickstart.mdx#resource-queries) that interact with the resources in your Retool organization, such as SQL databases or APIs.

The available options in a Resource query block depend upon the selected resource. For example, if you select a PostgreSQL database then you can write SQL statements or use **GUI** mode to [construct queries that write or modify data](../../../queries/guides/sql/writes.mdx).

:::warning OAuth resources and workflows

If your resource uses OAuth, make sure **Share credentials between users** is enabled on the resource configuration page. This ensures workflows run automatically.

:::

## Configure block settings

Refer to the [Resource query block reference](../../reference/objects/block/resource-query.mdx) for information about the block's settings.

---

## Respond to webhook events with the Response block

import Responsesvg from "/img/icons/workflows/blocks/response.svg";

Use  **Response** blocks to configure custom responses to [webhook events](../triggers/webhooks.mdx) that trigger workflows. This block returns data to the origin along with a specified HTTP status code.

Webhook-triggered workflows without a Response block immediately respond to the origin. Workflows with a Response block do not respond until this block runs, returning the specified data and HTTP status code.

:::tip Workflows with a Response block are synchronous
The Response block is for use in workflows that provide a timely response to a webhook event. There is a 120-second timeout per block when triggering synchronous workflows.
:::

## Add a Response block

To respond to webhooks, add a  **Response** block to the canvas.

For example, you could use a workflow to look up customer details based on their email, which is returned in the webhook response. You can specify the HTTP **status code** and a JSON **response body** to return, which can include any data from the workflow. The demo below illustrates a brief example of this. 

## Respond differently based on conditions

You can add multiple Response blocks to return different responses based on certain conditions. For example, if there are no results when attempting to look up a customer by email, a different response block could return an error message and status code.

You can also configure a Response block to act as [global error handler](workflows/quickstart.mdx), which runs if an error occurs in a workflow.

## Configure block settings

Refer to the [Response block reference](../../reference/objects/block/response.mdx) for information about the block's settings.

---

## Run another workflow with the Workflow block

import Workflowsvg from "/img/icons/workflows/blocks/workflow.svg";

The  **Workflow** block passes data from the current workflow to another. This is useful for reducing complexity as it enables you to:

- Break down workflows into more manageable functions that perform specific actions (e.g., perform multiple API requests).
- Reuse the same actions across multiple workflows, reducing the amount of duplication across similar workflows (e.g., transform and store data in [Retool Vectors](../../../data-sources/guides/vectors/embeddings.mdx)).

The Workflow block triggers the specified workflow, passing the provided data for it to use. Once run, the specified workflow returns the data back to the current workflow for it to continue.

export function RunWorkflow() {
  return (
    
      
    
  );
}

## Add a Workflow block

:::tip Retool Workflow resource

The Workflow block is powered by the Retool Workflow resource. You can also use this resource in web and mobile apps to run workflows.

:::

To run another workflow, add a  **Workflow** block to the canvas. You then select the workflow to run and specify the execution mode:

| Run Until    | Description                                                                                                               |
| ------------ | ------------------------------------------------------------------------------------------------------------------------- |
| **Finished** | The current workflow execution is paused until the triggered workflow run has completed.                                  |
| **Queued**   | The current workflow execution continues and the triggered workflow run is queued. Both workflows may run simultaneously. |

Specify the parameters to pass into the workflow:

| Parameters | Description                                                                                                               |
| ---------- | ------------------------------------------------------------------------------------------------------------------------- |
| **Raw**    | Reference raw data. This can be any value, such as a string or all JSON data from a previous block (e.g., `query1.data`). |
| **JSON**   | Create a set of key-value pairs using input fields.                                                                       |
| **None**   | Do not pass any data.                                                                                                     |

You do not have to pass any data to the specified workflow. For example, you may want to run a workflow that retrieves data for your primary workflow to then process.

## Run the specified workflow

import Responsesvg from "/img/icons/workflows/blocks/response.svg";

Workflows run from within a workflow operate in a similar way as a [webhook-triggered](../triggers/webhooks.mdx) workflow. When run, data passed from the primary workflow is made available on `startTrigger.data`. You must include a  [Response](response.mdx) block in the workflow to be run—this returns data back to the primary workflow.

## Configure block settings

Refer to the [Workflow block reference](../../reference/objects/block/run-workflow.mdx) for information about the block's settings.

---

## Pause workflow execution with the Wait block

import Wait from "/img/icons/workflows/blocks/wait.svg";

Use the  **Wait** block to pause workflow execution for a specified amount of time. This block is useful for creating a single process that might need to take several hours, days, or weeks. For example, you could use the Wait block to:

- Grant a temporary access token, and revoke it after 24 hours.
- Send abandoned cart emails 1 hour after a customer leaves the website, and again after two days.
- Add a delay between several API calls in a loop to avoid hitting rate limits.

export function ArcadeEmbed() {
  return (
    
      
    
  )
}

Define the duration of the Wait in seconds, minutes, hours, or days. You can use a number or a JavaScript expression that evaluates to a number to define the duration. The maximum wait duration is 60 days.

:::note

Workflows with Wait blocks are still subject to [timeouts](../../concepts/limits.mdx#timeout). For asynchronous workflow runs with Wait blocks, the timeout is 60 days. For synchronous workflows runs, the timeout is 15 minutes up to executing the first webhook Response block. The remainder of the workflow follows the *asynchronous* timeout.

:::

After the Wait block finishes executing, the workflow continues execution of the blocks downstream. Wait blocks only pause execution for blocks that are directly downstream, and they not do not affect blocks that are executed in parallel.

## Configure block settings

Refer to the [Wait block reference](../../reference/objects/block/wait.mdx) for information about the block's settings.

---

## Create a README file with workflows

:::note

This feature is available on Retool Cloud and will be available in subsequent releases of self-hosted Retool. Reach out to your account manager to enable README for workflows. 

:::

You can create a README from the workflow IDE to help clarify important information about your workflow for collaborators. For example, if a workflow requires certain permissions on a resource, or if it's only scheduled to run once a week. 

## Create a README

To add a README to your workflow, click the title of the workflow and add your README content to the **Editor README** field. GitHub-flavored markdown is supported. 

Click the **Preview** link to view the README content in HTML. 

export function WFRM() {
  return (
    
      
    
  )
}

READMEs are visible from the **Edit workflow details** dialog box, in workflows JSON exports, and in a markdown file from Source Control. 

## View a README for a protected workflow

README files can also be viewed from workflows protected with Source Control. 

:::note
For more detailed information about Source Control, refer to [Protect workflows with Source Control](../../source-control/guides/protect/workflows.mdx).    
:::

Once you've [created the README](#create-a-readme) in the workflows IDE, protect the workflow, and create and merge the pull request to view the `README.md` file in your repository. 

export function WFRMSC() {
  return (
    
      
    
  )
}

---

## Configure workflow error handlers

As you build workflows of increasing complexity that interact with more data sources, it's important to handle errors and debug unexpected behavior effectively. You can configure blocks to function as error handlers and perform actions should any errors occur during the run.

## Supported block types

You can configure the following block types to operate as _global error handlers_ that execute if an error occurs anywhere during a workflow run. Note that workflows continue to run even after an error is triggered.

- [Resource query](blocks/resource-query.mdx)
- [AI action](blocks/ai-action.mdx)
- [JavaScript](blocks/code/javascript.mdx) or [Python](blocks/code/python.mdx) Code
- [Loop](blocks/logic/loop.mdx)
- [Response](blocks/response.mdx)

## Configure a block to be an error handler

To configure a block for error handling, click ••• and select **Add global error handler**. Error handlers do not need to be part of the control flow and will execute if the workflow detects an error.

## Retry failures automatically

For more granular error handling, you can configure error handlers to automatically execute again. This allows your workflow to automatically retry failed blocks with a specified schedule. Setting an exponential backoff can be especially useful for retrying blocks that query rate-limited APIs or data sources.

To configure retry settings, select **Settings** on the block. See the block settings, e.g., [Resource query settings](./blocks/resource-query.mdx#configure-block-settings), to learn more.

## Retrieve the workflow error details

You can reference [workflowContext](../reference/objects/workflowcontext.mdx) anywhere in your workflow to include workflow details. For example, you can get the workflow name using `{{ workflowContext.name }}`. This can be useful for distinguishing between workflows that send notifications or including details about when the workflow's actions were last completed.

If a workflow fails, Retool makes the error information available at `workflowContext.currentRun.error`. You can use this when configuring an error handler to log errors from the current run. For example, a Response block can return a `500` status with the error message.

## Review and debug runs

:::tip

You can use [Ask AI](../../queries/concepts/ask.mdx) to debug JavaScript, SQL, or GraphQL queries using AI-powered debugging.

:::

Retool logs every successful and failed run of a workflow. Click **Run history** in the status bar to debug workflows. The **Run history** panel contains a list of recent runs, each with their date, time, their status. You can then view the status of each block to see where a failure may have occurred.

Run logs can help troubleshoot problems with queries, such as malformed SQL statements or invalid JavaScript. You can filter log entries to show only **error**, **success**, or **info** entries. Keep in mind that queries are considered successful if they didn't return an error. A workflow cannot determine if its actions produced the results you expected.

Admins can enable and update the retention period for **Run History** data from **Settings** > **Advanced** > **Workflows: Enable Data Retention**. The default retention period is 30 days, and the maximum retention period is 90 days.

## Monitor usage with run logs

Retool calculates [workflow usage and billing](/support/billing-usage/workflows) based on _runs_. You can view information about workflow runs by reviewing the logs.

---

## Create functions to reuse queries and logic in workflows

*Functions* enable you to create reusable queries or complex logic. You can call functions anywhere in a workflow as function blocks, using JavaScript, or within a [Loop](./blocks/logic/loop.mdx) block. Functions support optional parameters and have access to the global workflow scope.

## Single-step and multi-step functions

You can choose whether a function is *single-step* or *multi-step*. The type you choose depends on the complexity or functionality required.

- **Single-step**: The function contains a single query that runs with optional parameters when called. Single-step functions are useful for simple logic, such as retrieving database records or transforming results.
- **Multi-step**: The function operates as a self-contained workflow with optional parameters. It has its own control flow and is assembled in the same way as its parent workflow. It supports most available block types, including logic and response blocks, and emits block-level logs. Multi-step functions are useful for complex operations with many steps.

:::note

Multi-step functions do not support [user task blocks](./user-tasks.mdx).

:::

## 1. Add a function

import Functionssvg from "/img/icons/workflows/functions.svg";
import Codesvg from "/img/icons/workflows/blocks/code.svg";

First, click  **Functions** in the left panel, then create a function. Then, configure the function. 

1. Define any optional parameters to pass into a function when called. Click **+ Add parameter** and set the parameter name. You can optionally provide a test value that is used only when the function is tested.
2. Select whether to use a single-step or multi-step function type.

:::danger 

Changing a function from multi-step to single-step permanently deletes the multi-step function logic.

:::

export function CreateFunction() {
  return (
    
      
    
  )
}

## 2. Configure function logic

The **Query** section contains the query or code that runs when the function is called. You can select a resource to query, **Run JS Code** or **Run Python Code** to execute JavaScript or Python code, or **Retool Workflow** to trigger another workflow.

For example, you could create a function that sends a trial expiration notification to customers when the trial is three days and one day from expiration. Instead of adding two separate blocks to send emails, you can create a single function and reuse it. The workflow first filters the customer data to determine which trials expire in 24 hours or in three days, then calls the same function. The function receives the customer's name and email address as parameters, along with either `tomorrow` or the trial expiration date.

Configure the resource or code that the function runs when called. 

A multi-step function has its own blocks and control flow. Click **Edit function** to edit the function and assemble its blocks. You can also call other functions from within multi-step functions.

When you edit the function, the canvas switches to **Function** mode so you can assemble the function's blocks directly on the canvas. When in this mode, the canvas background is blue. Click **Return to workflow** at any time to go back to the workflow.

For example, you could create a function that sends a trial expiration notification to customers when the trial is three days and one day from expiration. In the single-step function example, the workflow contained the logic to determine which trials expired in 24 hours and in three days. With a multi-step function, the workflow can be streamlined by offloading the logic to the function. 

A multi-step function with branched logic.

The customer data is passed as the function parameter, and the multi-step workflow handles the remaining logic. Since there is already a single-step function to send emails, the multi-step function can reference it in the same way as the workflow. As a result, the workflow only needs to call the multi-step function after retrieving the customer data.

:::tip 

Logs for multi-step functions are available in the **Run history** panel. These block-level logs are nested under whichever block or loop iteration called the function.

:::

By default, multi-step functions return the results of their final block to their parent workflow. These results can be accessed by referencing the `.data` property of the block that invoked the multi-step function.

## 4. Test the function

Click **▶︎ Test** to run the function. If your function makes use of parameters, any test values you provided are used.

## 5. Call the function from the workflow

import Addsvg from "/img/icons/workflows/add-block.svg";

You can call a function using the following methods:

-  Using the **Function** block to the workflow.
-  Within a JavaScript [Code](./blocks/code/javascript.mdx) block.
-  As the loop runner for a [Loop](./blocks/logic/loop.mdx) block.

:::note

Loop blocks and Function blocks that call multi-step functions do not support custom timeouts. Instead, these blocks use a total timeout that is calculated by adding the timeouts of each block in the multi-step function.

If a timeout is necessary for a multi-step function, you can call the function within a Code blocks and set a timeout on the Code block itself.

The [workflow timeout](../concepts/limits.mdx#timeout) always takes precedence over timeouts set within blocks or functions.

:::

Click  in the left panel to open the **Blocks** tab, then drag the **Function** block type to the canvas. You can also right-click anywhere on the canvas to display the **Add block** contextual menu.

If you defined any parameters for the selected function, the block displays input fields for you to populate. You can then reference any data using `{{ }}` expressions, such as `{{ getCustomersOnTrial.data }}`. 

A streamlined workflow using a multi-step function.

You can call a function from a JavaScript Code block using `await` and the function name. For example:

```javascript
const email = getUsers.data[0].email;
const name = getUsers.data[0].name;

await function1(name, email);
```

Loop blocks can call a function so that it is used for every loop iteration. Set the **Loop runner** to the desired function and use `{{value}}` or `{{index}}` to reference evaluated items in parameters.

A Loop block that calls a function.

The Function block returns its output back to the block from which it was called, allowing the output to be used in the rest of the workflow.

The [Run History menu](../concepts/logs.mdx#run-history) panel shows the workflow run. Click multi-step functions to expand them and see the run logs of the blocks within them.

Run History that includes a multi-step function.

## Convert a group of blocks to a multi-step function

You can convert a group of blocks within an existing workflow to a multi-step function. First, press and hold shift while click-and-dragging on the canvas to select a group of blocks. Next, right-click on the selection area and click **Create function**.

Convert a group of blocks to a function.

The selected blocks are then replaced in the workflow by a Function block that references the newly created function.

---

## Generate a workflow using Retool AI

import RocketSvg from '/img/icon-packs/line/shopping-business-startup.svg';
import Default from "/docs/_shared/_ai-default-model.mdx"

You can use AI to generate a fully functional workflow without the need to build it manually. You provide some instructions and Retool assembles the required blocks with any necessary logic. You can also select a database resource and provide context about table schema.

AI-generated workflows support: 

- Schedule or webhook triggers to automatically run the workflow.
- Resource query blocks that interact with data sources, such as SQL databases.
- JavaScript code that transform data.
- Slack and Retool Email queries that can send notifications. 
- Webhook response blocks to send data as webhook events.

## Generate a new workflow

:::caution Generating with AI overwrites the current workflow

Generating a workflow with AI overwrites any existing workflow blocks and logic you may have already created.

:::

Click  **Get started** in the left panel and select **Generate with AI**. 

Retool provides a selection of sample prompts for you to get started. click **Give example** to cycle through different examples. 

AI-generated workflows are not published automatically. Review and test the workflow to ensure it functions correctly.

## Generate with a resource

You can specify a connected database resource, such as [PostgreSQL](../../data-sources/guides/integrations/database/postgresql.mdx) or [BigQuery](../../data-sources/guides/integrations/database/bigquery.mdx). Click the **Generate with a Resource** dropdown, select the resource to use, then select the tables to reference.

Selecting multiple tables enables you to generate workflows that may require using data spread across different locations. For example, if you want to generate a daily report about inventory at a specific warehouse using its address, including relevant tables allows Retool to generate a workflow that looks up the warehouse ID and filter the inventory data. 

## Generated workflow notifications

You can specify Slack or email in the AI instructions if you want a workflow to send notifications. AI-generated workflows can add Resource query blocks for Slack and Retool Email automatically. If you do not have a Slack resource, you must connect one before you can send Slack notifications. 

## Generated workflow triggers

You can include instructions for triggering a workflow on a schedule or in response to a webhook. Provide details about the triggers and Retool generates a workflow with them automatically configured.

Webhook trigger instructions can also include details about any parameters to use. For example, you could generate a workflow to send a customer survey only if the webhook event contains `"approved": true`.

---

## Import and export workflows

You can import and export workflows to share them across your organization. Importing and exporting is also useful for copying workflows between cloud and self-hosted Retool instances.

The JSON export of a workflow contains its blocks, queries, etc. After importing a workflow, verify that blocks, queries, and their resources are correctly configured.

:::info Import into a different organization

If you export a workflow and import it into a different Retool organization, you must create any required [resources](../../data-sources/quickstarts/resources.mdx).

:::

import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";

To import a workflow:

- **From the Workflows page**: Click **Create new** and select **From JSON**.
- **In the workflow IDE**: Click **•••** and select **Import from JSON**.

To export a workflow, either:

- **From the Workflows page**: Click **•••** and select **Export and download**.
- **In the workflow IDE**: Click **•••** and select **Export to JSON**.

When you export a workflow, a JSON file is downloaded by your browser.

---

## Report app errors using Retool Events

In addition to built-in support for [reporting app errors](../../../apps/guides/observability/error-reporting.mdx) to Datadog or Sentry, you can use [Retool Events](../retool-events.mdx) to build workflows that automatically trigger whenever an app error occurs. This can be useful for automatically notifying third-party observability providers.

A workflow to send app errors to Sentry using a JavaScript SDK.

## Add a workflow for the event

First, navigate to the  settings in your organization. Click **Add a workflow** to select or create a workflow for the **Report App Errors** event.

  Add a workflow for the **Report App Errors** event.

You can download an example workflow for Datadog, Rollbar, or Sentry as a JSON file and [import it as a new workflow](../import-and-export.mdx). Each workflow contains sample error data for you to use during testing.

## Transform error data

The [App Error](../../../apps/guides/observability/error-reporting.mdx#error-types) event contains detailed information about the error. Observability providers require data to be in a particular shape. You can use the **Code** block within a workflow to transform the error data into a suitable payload before using a [Resource query](../blocks/resource-query.mdx) block to send it.

The following app error example uses JavaScript to transform the error into a digestible format for Datadog.

```json title="App error example" showLineNumbers
{
  "errors": [
    {
      "id": "50f1ffbf-58e7-44f1-9a6b-5bed45fee232",
      "timestamp": 18,
      "origin": "runtime",
      "appUuid": "91e36cb2-2d20-11ef-9a6c-378c1a9d5665",
      "callstack": [
        {
          "type": "error",
          "message": "workflow error",
          "lineNumber": 2,
          "id": "0/transformerRun-1718685005587-0/transformerFailure-1718685005588-1/error-1718685005588-2",
          "timestamp": 1718685005588
        },
        {
          "type": "transformerFailure",
          "transformerId": "transformer1",
          "id": "0/transformerRun-1718685005587-0/transformerFailure-1718685005588-1",
          "timestamp": 1718685005588
        },
        {
          "type": "transformerRun",
          "transformerId": "transformer1",
          "id": "0/transformerRun-1718685005587-0",
          "timestamp": 1718685005587
        },
        { "type": "loadEvent", "id": "0", "timestamp": 1718685005587 }
      ],
      "tags": {}
    }
  ]
}
```

```js title="Transform data" showLineNumbers
console.log("errors", startTrigger.data.errors);

const errors = [];

for (const error of startTrigger.data.errors) {
  errors.push({
    timestamp: error.timestamp,
    ddsource: error.origin,
    status: "error",
    ddtags: JSON.stringify(error.tags),
    service: "retool-error-monitor",
    message: error.errorMessage,
    error: {
      stack: error.callstack.map((node) => node.message).join("\n"),
      message: error.errorMessage,
      type: error.origin,
    },
    ...error.tags,
    callstack: error.callstack,
  });
}
return JSON.stringify(errors);
```

```js title="Result" showLineNumbers
[
  {
    timestamp: 18,
    ddsource: "runtime",
    status: "error",
    ddtags: "{}",
    service: "retool-error-monitor",
    error: { stack: "workflow error\n\n\n", type: "runtime" },
    callstack: [
      {
        type: "error",
        message: "workflow error",
        lineNumber: 2,
        id: "0/transformerRun-1718685005587-0/transformerFailure-1718685005588-1/error-1718685005588-2",
        timestamp: 1718685005588,
      },
      {
        type: "transformerFailure",
        transformerId: "transformer1",
        id: "0/transformerRun-1718685005587-0/transformerFailure-1718685005588-1",
        timestamp: 1718685005588,
      },
      {
        type: "transformerRun",
        transformerId: "transformer1",
        id: "0/transformerRun-1718685005587-0",
        timestamp: 1718685005587,
      },
      { type: "loadEvent", id: "0", timestamp: 1718685005587 },
    ],
  },
];
```

## Send errors to an observability provider

Observability providers typically receive error logs using an API or SDK. The method you use depends on the provider.

- Using an API (e.g., Datadog or Rollbar): Create a resource that's configured with the necessary settings. You can then select this resource in the workflow.
- Using a JavaScript SDK (e.g., Sentry): If necessary, use [configuration variables](../../../org-users/guides/configuration/config-vars.mdx) to store secret keys. You can then reference them when using custom [JavaScript](../blocks/code/javascript.mdx#add-a-library) or [Python](../blocks/code/python.mdx#add-custom-libraries) libraries to send app errors without exposing sensitive information in the workflow.

You can send app errors to most observability providers with an API or SDK. The following examples explain how to send errors to Datadog, Rollbar, and Sentry.

Add a **Resource query** block to the workflow and select the **REST API** resource. To send error logs, use the **POST /api/v2/logs** endpoint, add your Datadog API Key to the header, and provide the transformed data as the raw body in the request.

  A Resource query block to send app errors to Datadog.

First, create a resource for the [Rollbar API](https://docs.rollbar.com/reference/getting-started-1) using the [REST API](../../../data-sources/guides/integrations/api/rest.mdx) integration. You then configure the resource with the Rollbar API base URL (`https://api.rollbar.com/api/1`) and a `X-Rollbar-Access-Token` header with a valid access token.

  Rollbar resource configuration.

Next, add a **Resource query** block to the workflow and select the Rollbar resource. To send error logs, specify the `/item` API endpoint and and provide the transformed data as the raw body in the request.

You can use Sentry's JavaScript SDK to send app error logs by adding the **@sentry/node** [custom JavaScript library](../blocks/code/javascript.mdx#use-javascript-libraries) to the workflow. Once added, you can write JavaScript code that uses the SDK.

To add the SDK:

1. Open the **Libraries** tab.
2. Click **≠** and select **Add JavaScript library**.
3. Select the **@sentry/node** library.
4. Click **Add selected libraries**.

Once you add the SDK, you need to include it in the workflow's setup scripts. This enables the SDK for use in the worfklow. Navigate to the **Settings** tab and select **JavaScript configuration**. Add the requirement for **@sentry/node**:

```js
const Sentry = require("@sentry/node");
```

You can then add a **Code** block to the workflow and write the script to sends app errors to Sentry.

The Sentry SDK uses a [Data Source Name](https://docs.sentry.io/concepts/key-terms/dsn-explainer/) (DSN) to identify the source of reported logs. While this does not need to be kept secret, you should still store it in a configuration variable. You can then reference it across multiple workflows, should it be necessary, which makes it easier to manage and update.

## Publish the workflow

Once you create a workflow, you must [publish](../version-and-publish.mdx) it to begin automatically sending app error reports to providers. Whenever you make changes to a workflow, you must also publish those changes for them to take effect.

After publishing the workflow, create an app and perform actions that would cause an error. For example:

- Write an invalid query that fails to run.
- Create a test resource, reference it in the app in a query, then delete the resource.

---

## Observability



---

## Workflow observability and error reporting

import Observability from "../../../_shared/_observability.mdx";
import Filtering from "../../../_shared/_filtering-routing.mdx";

## Run log and error types

Datadog categorizes errors into `info`, `warn`, or `error` logs. Sentry reports only `error` logs.

## Run log and error tags

Tags can be used to filter, categorize, or route errors to different owners using the following format: `tag:value`.

| Datadog Tag              | Details                                                                                 | Example                                |
| ------------------------ | --------------------------------------------------------------------------------------- | -------------------------------------- |
| `blockName`              | The name of block run.                                                                  | `errorCodeBlock`                       |
| `blockType`              | The type of block run.                                                                  | `code`                                 |
| `duration`               | The duration of the run in seconds.                                                     | `6.821332`                             |
| `endTimestamp`           | The end timestamp in UTC milliseconds.                                                  | `1746058640525`                        |
| `environmentId`          | The environment ID.                                                                     | `production`                           |
| `error`                  | The error or event.                                                                     | `Error: Example error (line1)`         |
| `eventType`              | The workflow action taken (`WORKFLOW_RUN_START`, `BLOCK_RUN_END`, etc).                 | `BLOCK_RUN_END`                        |
| `multiStepFunctionId`    | The ID of a multi-step function used in a block or workflow.                            | `35bf8cf3-5000-4f93-81d1-af23po5k24d3` |
| `multiStepFunctionName`  | The name of a multi-step function used in a block or workflow.                          | `function2`                            |
| `multiStepFunctionRunId` | The ID of multi-step function run of a block or workflow.                               | `30117fb3-9720-4c0a-927c-a54994c72cd0` |
| `organizationId`         | The organization ID.                                                                    | `1`                                    |
| `release`                | The Retool version.                                                                     | `3.198.0`                              |
| `runStatus`              | The status for block and workflow runs (`SUCCESS`, `FAILURE`, `NOT_ENOUGH_TIME`, etc).  | `FAILURE`                              |
| `startTimestamp`         | The start timestamp in UTC milliseconds.                                                | `1746058958620`             |
| `status`                 | The status for the run (`info`, `error`, or `warn`).                                    | `error`                                |
| `triggerType`            | The method in which the workflow was triggered (`manual`, `scheduled`, `webhook`, etc). | `manual`                               |
| `workflowId`             | The workflow ID.                                                                        | `4634f898-97b1-4be5-bc73-c417d90b00be` |
| `workflowName`           | The worflow name.                                                                       | `Workflow Name`                        |
| `workflowRunId`          | The workflow run ID.                                                                    | `6ccaaa77-ca91-8fd9-5ca38-ec41d524m32` |

| Sentry Tag            | Details                                                                                 | Example                                |
| --------------------- | --------------------------------------------------------------------------------------- | -------------------------------------- |
| `blockName`           | The name of the block.                                                                  | `errorCodeBlock`                       |
| `blockType`           | The type of block.                                                                      | `code`                                 |
| `environment`         | The environment ID.                                                                     | `production`                           |
| `eventType`           | The workflow action taken (`WORKFLOW_RUN_START`, `BLOCK_RUN_END`, etc).                 | `BLOCK_RUN_END`                        |
| `level`               | The type of log.                                                                        | `error`                                |
| `multiStepFunctionId` | The ID of a multi-step function used in a block or workflow.                            | `35bf8cf3-5000-4f93-81d1-af23po5k24d3` |
| `release`             | The Retool version.                                                                     | `3.198.0`                              |
| `runtime`             | The runtime version.                                                                    | `node v20.18.1`                        |
| `runtime.name`        | The runtime name.                                                                       | `node`                                 |
| `server_name`         | The server name.                                                                        | `MacBook-H1TTF4FRWR`                   |
| `triggerType`         | The method in which the workflow was triggered (`manual`, `scheduled`, `webhook`, etc). | `manual`                               |
| `workflowId`          | The workflow ID.                                                                        | `4634f898-97b1-4be5-bc73-c417d90b00be` |
| `workflowName`        | The worflow name.                                                                       | `Workflow Name`                        |
| `workflowRunId`       | The workflow run ID.                                                                    | `6ccaaa77-ca91-8fd9-5ca38-ec41d524m32` |

---

## Trigger workflows with Retool Events

:::info

You must be an admin to configure Retool Events. Access to the workflows triggered by Retool Events is determined by existing [workflow permissions](../../permissions/guides.mdx).

:::

With [Retool Events](../reference/retool-events.mdx), organization admins can build workflows that run automatically in response to certain events in Retool. When an event is triggered, any associated workflows with pre-defined parameters are triggered.

You can even [configure branding options](../../org-users/concepts/branding.mdx) to replace Retool's built-in email notifications with workflows that send custom transactional emails with a reply-to address at your domain. When doing so, you should set up appropriate alerts as error handlers in your workflows.

## Configure Retool Event workflows

Navigate to the [Retool Events](https://login.retool.com/auth/login?source=docs&redirectOnLogin=settings/events) settings in your organization. From here, you can browse available events and add workflows that trigger in response to them.

| Event                                   | Description                                                                                                                                                                           |
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Password Reset Required**             | Triggered when a user requests their password reset.                                                                                                                                  |
| **Report App Errors**                   | Triggered whenever an app error is reported.                                                                                                                                          |
| **User Invited**                        | Triggered whenever a user is invited to your organization.                                                                                                                            |
| **User Created**                        | Triggered whenever a new user is added to your organization. Does not trigger on user invites or access requests, until the user accepts and/or is approved to join the organization. |
| **User Requested Access**               | Triggered whenever a user requests access to your organization.                                                                                                                       |
| **Source Control Deployment Completed** | Triggered whenever a source control deployment is completed.                                                                                                                          |
| **Login Attempted**                     | Triggered when users attempt to log in. Runs after a user is authenticated, but before they complete the login process.                                                               |

Click **Add a workflow** to select or create a workflow that runs whenever the respective event is triggered. If you add a new workflow, Retool creates one using a starter template for common use cases related to the event.

export function AddWorkflow() {
  return (
    
      
    
  );
}

### Reference event properties

Retool Event-triggered workflows display the related event in the **Enabled triggers** of the [Start block](../quickstart.mdx). These triggers are only available when you add workflows from the Retool Events settings page.

Data for the event is made available from the Start block. You can reference this anywhere in the workflow using `{{ startTrigger.data }}`. This contains different properties depending on the event type.

Refer to the [Retool Events reference](../reference/retool-events.mdx) to learn about the available properties for each event type.

### Configure workflow actions

Many Retool lifecycle events are related to organization and user administration, so workflows triggered from Retool Events often involve sending emails. The **User Invited** template is pre-populated with a [Resource query block](./blocks/resource-query.mdx) using Retool Email, but you can configure this to use SendGrid or another email provider.

The `sendUserInvite` block of the **User Invited Template** sends an email to the user invited to Retool, using the `invitedUserEmail` parameter as the **To** address. The email's body also contains links to the `inviteLink` parameter. When you construct your email blocks and templates, ensure you use the provided parameters to direct your users to the correct actions.

#### Login Attempted response structure

When using the Login Attempted event to trigger a workflow, you can use one of the following three redirect variables, which trigger custom redirect behavior upon successful authentication.

A response with `logoutErrorMessage` prevents a user from logging in and navigates the user back to the login page with the given error message displayed.

A response with `appId` redirects the user to a Retool app, identified by either a custom URL slug or the app's ID.

A response with `url` logs in the user and redirects them to a URL.

Using these redirects overrides any default landing page redirects set for the user’s permissions groups.

:::note

If your workflow involves sending emails, remember to disable Retool's default emails. Navigate to **Settings** > **Branding**. Select the appropriate email from the dropdown, and click **Manage**. Check the **Disable the Retool-managed email** setting.

:::

### Configure error handlers for failed events

To ensure workflows run as expected, you can configure [error handlers](./error-handlers.mdx). This enables you to perform additional actions, such as receive a notification, if an error occurs and the workflow could not complete successfully.

#### Handling errors for Login Attempted workflows

If a global error handler is not enabled in the login workflow and a failure occurs, users will still be able to log into their Retool accounts, but any configured redirects will not be applied. To ensure proper handling of failures, Retool recommends enabling a [global error handler](./error-handlers.mdx#configure-a-block-to-be-an-error-handler) in the login workflow. This handler determines the behavior when a failure occurs.

If the error handler response includes an `appId` or `url`, users will still be able to log in, and the error handler’s redirect will be applied.

If it is critical for the workflow to succeed in order for users to log in, you can return a `logoutErrorMessage` upon failure. This prevents users from logging in if the workflow encounters an error. However, this should only be used if the workflow contains security-critical logic, as it can easily lead to locking out all users.

---

## Configure triggers in Retool Workflows



---

## Trigger workflows with queries

You can trigger a workflow using messages polled from [Amazon SQS](../../../data-sources/guides/integrations/streaming/amazon-sqs.mdx) or [Kafka](../../../data-sources/guides/integrations/streaming/kafka.mdx) streaming services. Query triggers poll a topic or queue on a configured interval and trigger a workflow run for each message retrieved.

## Add a query trigger

import Startsvg from "/img/icons/workflows/blocks/start.svg";
import Triggerssvg from "/img/icons/workflows/triggers.svg";

To add a trigger, click the  **startTrigger** block or open the  **Triggers** tab in the left panel.

You can create multiple query triggers to poll for messages from different queues and at different intervals, in addition to using a [webhook](./webhooks.mdx) or [schedule](./schedule.mdx) trigger. The highest frequency at which you can poll for messages is once per minute.

If you use an Amazon SQS resource, the **Action type** is automatically set to **Receive messages from a queue**. For a Kafka resource, the **Action type** is automatically set to **Consume messages from a topic**.

## Set polling options

Expand the **Advanced Settings** section to configure how often you want Retool to poll for new messages. You can schedule polling using **Interval** mode to specify the frequency using a set of options. For more granularity, use **Cron** mode to provide [cron syntax](https://en.wikipedia.org/wiki/Cron).

### Configure querying on an interval

Use the **Schedule** option to configure a Retool to poll for messages periodically. You can configure a schedule that triggers every `n` minutes, hours, days, weeks, or months.

### Configure querying on a cron schedule

:::tip Cron examples
Check out some [examples](https://crontab.guru/examples.html) to learn more about cron syntax.
:::

Retool Workflows supports [cron syntax](https://en.wikipedia.org/wiki/Cron) when configuring a query trigger. The bottom of the **Advanced Settings** section displays a human-readable version of the values you provide. You can also hover the cursor over the label for each input to display a tooltip with syntax reference.

## Configure workflow run behavior

Workflows function differently depending on whether you use them with Amazon SQS or Kafka.

- Retool ensures that each unique message is used by only one workflow run at a time. Two workflows cannot action on the same message at the same time.
- Query triggers can receive up to 20 SQS messages at one time, and Retool triggers a unique workflow run per message. The workflow runs execute in parallel.
- Because SQS supports only one consumer group per queue, you cannot have more than one workflow consuming from the same SQS queue.

- Retool ensures that each unique message is used by only one workflow run at a time. Two workflows cannot action on the same message at the same time.
- Query triggers can consume up to 100 Kafka messages at one time, and Retool triggers a unique workflow run per message. The workflow runs execute in parallel.
- Because Kafka supports multiple consumer groups, you can have multiple workflows consuming from the same Kafka topic. Each query trigger must specify a distinct consumer group.

### Conclude the workflow run

At the end of each workflow run, you must mark the message as deleted (Amazon SQS) or committed (Kafka) to prevent it from being retrieved by the next query trigger. Complete the following steps to ensure that each message is consumed only once.

1. Add a resource query block to your workflow that accesses your Amazon SQS resource.
2. Set the **Action type** to `Delete a message from a queue`.
3. Set the **Queue** to the same queue that you defined in the query trigger.
4. Set the **Receipt Handle** to `{{startTrigger.data.receiptHandle}}`.

1. Add a resource query block to your workflow that accesses your Kafka resource.
2. Set the **Action type** to `Commit messages in a topic`.
3. Set the **Topic Name** and **Consumer Group** to the same topic name and consumer group that you defined in the query trigger.
4. Set the **Partition** to `{{startTrigger.data.partition}}`.
5. Set the **Offset** to ` {{startTrigger.data.offset}}`.

---

## Trigger workflows periodically

You can add _schedule_ triggers that run workflows automatically at regular intervals. You schedule workflows using **Interval** mode to specify the frequency using a set of options. For more granularity, use **Cron** mode to provide [cron syntax](https://en.wikipedia.org/wiki/Cron). You can also specify a timezone so that the workflow schedule runs based on local time.

## Add a schedule trigger

import Startsvg from "/img/icons/workflows/blocks/start.svg";
import Triggerssvg from "/img/icons/workflows/triggers.svg";

To add a trigger, click the  **startTrigger** block or open the  **Triggers** tab in the left panel.

You can create multiple schedule triggers to run workflows at different intervals, in addition to using a [webhook](webhooks.mdx) trigger.

## Configure an interval schedule

Add a **Schedule** trigger to configure a workflow to run periodically. You can configure a schedule that triggers every `n` minutes, hours, days, weeks, or months.

## Configure a cron schedule

:::tip Cron examples
Check out some [examples](https://crontab.guru/examples.html) to learn more about cron syntax.
:::

Retool Workflows supports [cron syntax](https://en.wikipedia.org/wiki/Cron) when configuring a schedule. The block displays a human-readable version of the values you provide. You can also hover the cursor over the label for each input to display a tooltip with syntax reference.

---

## Trigger workflows with webhooks

You can add _webhook_ triggers that run workflows automatically whenever it receives a webhook event, such as a service monitoring alert or when a payment is received. The workflow can reference data in the event's JSON payload in the same way as other block data. If necessary, you can also configure a workflow to respond to webhook events using the **Response** block, returning data back to the webhook origin.

## Add a webhook trigger

import Startsvg from "/img/icons/workflows/blocks/start.svg";
import Triggerssvg from "/img/icons/workflows/triggers.svg";

To add a trigger, click the  **startTrigger** block or open the  **Triggers** tab in the left panel.

export function CreateTrigger() {
  return (
    
      
    
  )
}

### Create webhook alias and path parameters

Use the **Alias** setting to create a custom URL path for your webhook trigger. The resulting webhook URL is formatted as `https://{domain}/url/{alias}`. For example: `https://example.retool.com/url/test-webhook`. Enable the **Public** setting if you want it to be possible to call your webhook URL without the API key.

You can also configure your custom URL path to include dynamic path parameters. This allows you to capture variable data directly from the request path and pass it to the workflow as input. Reference path parameters using `startTrigger.pathParams`.

For example, you could set **Alias** to `/orders/:id/status`. A user would send a request to `https://example.retool.com/url/my-custom-alias/orders/12345/status`. The path parameter `:id` is dynamically resolved to `12345`, and this value is then passed to the workflow as part of the execution input.

Dynamic path parameters have a few limitations that you must keep in mind:

- Only required, string parameters are supported. No wildcard parameters.
- Paths much be unique. For example, Retool disallows the creation of `/orders/:id` if `/orders/:status` already exists.
- Do not use duplicate parameter names, such as `/orders/:id/status/:id`.
- The first segment of the alias must be static.
- Do not include trailing slashes.

## Test headers and parameters

Use the **Test Headers**, **Test Path Parameters** and **Test JSON Parameters** settings in the Trigger block of your workflow to test your trigger without needing to manually call the endpoint.

You can reference the `startTrigger.headers`, `startTrigger.pathParams`, and `startTrigger.data` properties in the rest of your workflow.

Test headers and parameters are used only for manual runs. For actual runs triggered via webhook, real data is used instead.

## Send a webhook event

:::danger Do not publicize endpoint URLs

Any request sent to the specified endpoint will trigger the workflow. Do not share or publicize this information.

:::

Copy the endpoint URL and use it as the webhook event destination to trigger webhook events. Webhook events sent to the workflow endpoint must use `Content-Type: application/json`. A JSON payload is optional.

You can also copy the provided cURL command to send an event as you build and test your workflow.

```shell title="Example cURL command"
curl -X POST --url "https://api.retool.com/v1/workflows/43rewfs-7da9-ddgfet32-fddfr3-43rewf344gre/startTrigger" --data '{"hello":"world"}' -H 'Content-Type: application/json' -H 'X-Workflow-Api-Key: retool_wk_fdsedgf3t4t3grg34354656'
```

### Authenticate webhook events

You can authenticate webhook events by using one of the following in the request:

- The `X-Workflow-Api-Key` header.
- The `workflowApiKey` query parameter.

Retool recommends using `X-Workflow-Api-Key` header unless your intended usage only supports query parameters (e.g., third-party integrations). Be wary when including the secret API key as a query parameter as it can be logged since it is part of the URL.

## Retrieve event payload

Workflows can read data from a webhook event's JSON payload (e.g., error message or confirmation number) and make it available to your workflow. The following example of a Stripe event that occurs when a [Setup Intent](https://docs.stripe.com/api/setup_intents) is created. This event payload includes details about the event, including the Setup Intent's properties.

```json title="Stripe event"
{
  "id": "evt_1NG8Du2eZvKYlo2CUI79vXWy",
  "object": "event",
  "api_version": "2019-02-19",
  "created": 1686089970,
  "data": {
    "object": {
      "id": "seti_1NG8Du2eZvKYlo2C9XMqbR0x",
      "object": "setup_intent",
      "application": null,
      "automatic_payment_methods": null,
      "cancellation_reason": null,
      "client_secret": "seti_1NG8Du2eZvKYlo2C9XMqbR0x_secret_O2CdhLwGFh2Aej7bCY7qp8jlIuyR8DJ",
      "created": 1686089970,
      "customer": null,
      "description": null,
      "flow_directions": null,
      "last_setup_error": null,
      "latest_attempt": null,
      "livemode": false,
      "mandate": null,
      "metadata": {},
      "next_action": null,
      "on_behalf_of": null,
      "payment_method": "pm_1NG8Du2eZvKYlo2CYzzldNr7",
      "payment_method_options": {
        "acss_debit": {
          "currency": "cad",
          "mandate_options": {
            "interval_description": "First day of every month",
            "payment_schedule": "interval",
            "transaction_type": "personal"
          },
          "verification_method": "automatic"
        }
      },
      "payment_method_types": [
        "acss_debit"
      ],
      "single_use_mandate": null,
      "status": "requires_confirmation",
      "usage": "off_session"
    }
  },
  "livemode": false,
  "pending_webhooks": 0,
  "request": {
    "id": null,
    "idempotency_key": null
  },
  "type": "setup_intent.created"
}
```

You can then reference event properties anywhere in your workflows with the `startTrigger.data` object. For the example above, you can reference the Setup Intent ID within the payload using `{{ startTrigger.data.data.object.id }}`, which is `seti_1NG8Du2eZvKYlo2C9XMqbR0x`.

## Rotating API Key

To rotate the API key, click the **Rotate** button from the webhook trigger panel. This action generates a new API key and immediately invalidates the previous one. Update any callsites with the new API key to continue triggering the workflow successfully. 

Key rotation is immediate and the previous key will no longer work. Depending on your use case, it may take time to update your services and integrations, which can cause downtime. Since each workflow has a unique API key, an alternative approach is to duplicate an existing workflow and update your services or integrations to use it. 
 1. Navigate to your organization's list of workflows.
 1. Click *•••*  and select **Duplicate workflow**.
 1. Deploy the duplicated workflow. 
 
 Update all services and integrations to use the new workflow. Once completed, you can safely delete the old workflow.

 ### Best practices for key rotation

To minimize disruption during webhook API key rotation, especially in enterprise and multi-instance environments, follow these best practices:
- **Plan for manual rotation:** A developer or admin must click **Rotate** in production currently. In multi-instance deployment, developers may not have production access. Teams should explicitly define who is responsible for rotating keys. 
- **Avoid breaking dependencies:** Duplicating or moving workflows can break app or workflow triggers, UUID-based references, and git history.
- **Keep external webhooks thin:** Use webhook workflows only as entry points and forward requests to internal, triggered workflows.
- **Adopt clear naming conventions:** Distinguish between external and internal workflows (e.g., name the workflow `External Webhook: My Key Action`), and use aliases to keep external URLs stable.
- **Validate end-to-end:** After rotation, confirm all triggers and downstream workflows still function correctly.

---

## Configure user tasks

User Tasks enable you to build human-in-the-loop workflows that require a user to take action before the workflow proceeds. The **User Task** block pauses a workflow and creates a user task within the organization. Users then review and action tasks using Retool apps. Only when a user submits task completion using a Retool app does the workflow containing the user task block resume.

Workflows containing a User Task block provide context data for a user task which is accessible from the Retool app. When actioned, the user task can provide output data back to the workflow.

## Create user tasks from a workflow

:::info Provision use access to the associated app

User groups assigned to a user task must also have use access to the associated app where the user tasks are exposed to users. If not, users cannot use the app to complete user tasks.

:::

To create user tasks, add the **User Task** block to the control flow of a workflow. You then configure the following block fields:

| Setting          | Description                                                          |
| ---------------- | -------------------------------------------------------------------- |
| **Assign To**  | The users or groups that can action user tasks created by this workflow. |
| **App**          | The Retool app with which to review and action tasks.                |
| **Task Context** | Additional data from the workflow to include with the user task.     |
| **Expiry Time (days)** | The length of time after which the task expires. |

The User Task block.

All user tasks initially have `status` of `pending`. This changes to `submitted` once a user actions the task. When the User Task block runs, no further blocks along the control flow run until the task is actioned.

### Set user task assignments

:::info

User task permissions are optional. If unset, any user in the organization can view and action the user task.

:::

You can specify which users and [permission groups](../../permissions/guides/configure-permission-groups) are allowed to action tasks created by the User Task block. This is useful for restricting processes to a particular set of users, such as only allowing the finance team to administer refund requests.

Select a user or permission group.

You can also dynamically assign user tasks to users and groups using conditional logic. Click  for the **Assignments** field and then specify custom JavaScript logic to dynamically evaluate assignments.

:::caution

If your organization uses Source Control and has multiple instances of self-hosted Retool, ensure that user emails and group names are the same across instances.

:::

When dynamically assigning tasks, the **Assignments** field expects an `assignment` object with the following properties.

export const userAssignmentObject = {
  email: {},
  name: {
    "description": "The user name."
  },
  type: {
    "setting": "",
    "type": "enum",
        "values": [
      {
        "description": "The user to assign.",
        "value": "user"
      },
      {
        "description": "The group to assign.",
        "value": "group"
      },
    ],
    "description": "The assignment type."
  },
};

export const groupAssignmentObject = {
  name: {
    "description": "The group name."
  },
  type: {
    "setting": "",
    "type": "enum",
        "values": [
      {
        "description": "The user to assign.",
        "value": "user"
      },
      {
        "description": "The group to assign.",
        "value": "group"
      },
    ],
    "description": "The assignment type."
  },
};

The [retoolContext](../../apps/reference/objects/retoolcontext.mdx) global object contains `groups` and `users` properties with the necessary information.

```json
if (getCaseload.data >= 5) {
  return retoolContext.users['jenny@example.com']
} else {
  return retoolContext.users['admin@example.com']
}
```

The [retoolContext](../../apps/reference/objects/retoolcontext.mdx) global object contains `groups` and `users` properties with the necessary information.

```json
if (getCaseload.data >= 5) {
  return retoolContext.groups['Operations']
} else {
  return retoolContext.groups['Escalation Team']
}
```

Dynamically assign users or permission groups.

You can also use a combination of users and groups when determining assignment.

```json title="Group and user assigmment"
if (getCaseload.data >= 5) {
  return [retoolContext.users['jenny@example.com'], retoolContext.groups['Operations']]
} else {
  return [retoolContext.users['admin@example.com'],retoolContext.groups['Escalation Team']
}
```

### Create the user tasks app

:::info Use the same permissions for the workflow and app

Workflows that create user tasks and their associated apps must both use the same permission groups. If not, users cannot use the app to action user tasks.

:::

You create a Retool app from the User Task block for users to action tasks. Once you add a **User Task** block, click **Create User Task** to create a task review app using the default template. This starter app includes basic functionality for managing user tasks. If you have **Edit** or **Own** permissions to app folders, you can create your app within any of those folders. The app inherits any folder-level permissions, if set.

You can run the User Task block as you build and test your workflow. When manually run, the Workflow:

- Displays a modal in the Workflows IDE that contains the Retool app.
- Creates a test user task for testing the workflow and app whilst it runs.

### Provide task context

The User Task block can include additional data to use as context for the user task (e.g., order details). You can reference this data within Retool apps from a task's `context.result.data` property to populate component values, reference in queries, etc.

Task context is evaluated as JavaScript and must either reference or return a valid value (e.g., `startTrigger.data`).

### Set task expiration

Use the **Expiry Time** field to specify length of time, in days, after which the task automatically expires. If no value is provided, the task does not expire. When a task expires, its `status` automatically updates to `expired`. Use integer or float numbers to specify the duration, such as `1` or `1.5`.

After the task runs, you can reference the `expiryMetadata` property of the User Task block to reference expiration details. For example, to send a time-sensitive [notification](#send-new-task-notifications). `expiryMetadata` is an object with the following properties.

export const expiryMetadata = {
  "expiryTimeMs": {
    "description": "The time, in milliseconds, until expiration.",
    "type": "number"
  },
  "endDateEpochMs": {
    "description": "The end date, in milliseconds since the UNIX epoch.",
    "type": "number"
  },
  type: {
    "setting": "",
    "description": "The expiration type."
  },
};

Within an app, you can reference a task's `expiresAt` property to retrieve expiration details.

### Specify task admins

Users in the permission groups you select can review and action tasks using the Retool app. You can optionally specify **Task admin** permission groups in the block's **Settings** which allow those users to manage the tasks in the [Tasks Console](#manage-tasks-with-the-tasks-console).

### Send new task notifications

import Bell from "/img/icon-packs/bold/interface-alert-alarm-bell-2.svg";

The User Task block includes an additional connection to trigger another block when it creates a user task. Click  and select a block to use. For example, you could add a **Resource query** block to send messages using Retool Email, Slack, etc.

When the User Task block creates a task, it generates a direct URL for the Retool app with a reference to a unique task ID in a URL parameter. You can reference the `taskURL` property when sending notifications so that users can navigate directly to app and view the newly created task.

Send a Slack notification when a new task is created.

## Review and action user tasks from an app

:::tip

Retool organizations on the Enterprise plan can use the [Retool API](../../api/index.mdx) to programmatically manage tasks.

:::

The starter app you create from the User Task block includes basic functionality for reviewing and actioning user tasks. You can modify the starter app based on your needs.

### Write queries to interact with user tasks

Queries interact with user tasks within Retool apps. The user tasks app created by the workflow is preloaded with queries to get user tasks created by the workflow, get details of a specific task, and mark the user task as `submitted`.

The starter app includes `getUserTasks`, `getSingleUserTask`, and `submitUserTask` queries for you to use. To create these queries yourself:

1. Open the **Code** tab.
2. Create a **Resource query**.
3. Select the **User Action** resource.
4. Select the **User Task** type.

There are three actions from which you can choose when querying user tasks.

The **Get All User Tasks** action retrieves a list of all user tasks for which the current user has access. You can specify the following options:

| Option               | Description                                                                                                            |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| **Workflow**         | The workflow that created the user task.                                                                               |
| **User task**        | The name of the User Task block within the workflow. A workflow can create user tasks using multiple User Task blocks. |
| **Status**           | The task status.                                                                                                       |
| **Created After**    | User tasks created after the specified date.                                                                           |
| **Created Before**   | User tasks created before the specified date                                                                           |
| **Submitted After**  | User tasks actioned as submitted after the specified date.                                                             |
| **Submitted Before** | User tasks actioned as submitted before the specified date.                                                            |
| **Next Token**       | The next token from the previous API response. This is used for pagination for an arbitrary number of tasks.           |

Use the **Get User Task** action to retrieve a specific user task using the provided task ID. When you run a workflow containing User Task blocks, the `taskURL` of the associated block includes the the user Task ID as a URL parameter (`urlparams.hash.userTaskId`) to direct a user to that specific task.

You can specify the following options:

| Option           | Description                          |
| ---------------- | ------------------------------------ |
| **User Task ID** | The ID of the user task to retrieve. |

Use the **Submit User Task** action to mark the user task as `submitted`. The workflow resumes its run and receives any specified output from the app.

You can specify the following options:

| Option           | Description                             |
| ---------------- | --------------------------------------- |
| **User Task ID** | The ID of the user task to action.      |
| **Task Output**  | A value that is passed to the workflow. |

Use the **Reassign User Task** action to reassign the task to a different permission group.

You can specify the following options:

| Option           | Description                                           |
| ---------------- | ----------------------------------------------------- |
| **User Task ID** | The ID of the user task to action.                    |
| **Reassign To**  | A list of permission groups for which you can assign. |

Use the **Cancel User Task** action to cancel the task. Canceled tasks have a status of `canceled`.

You can specify the following options:

| Option                  | Description                             |
| ----------------------- | --------------------------------------- |
| **User Task ID**        | The ID of the user task to action.      |
| **Cancellation Reason** | The reason for canceling the user task. |

### Task context and output

Each user task has state. A user task's `context` property contains the information provided by the workflow, such as the task context, which you can reference anywhere in the app.

The **Complete User Task** action includes an optional output that is saved to `output`. This data is passed back to the workflow to use as it resumes.

You can build workflows with conditional logic that perform different actions based on this output. For example, you can use the **Branch** block to determine whether to reimburse an expense based on whether the output contains `"approved": true` or `"approved": false`.

## Manage tasks with the Tasks Console

:::info Action tasks using Retool apps

The Tasks Console cannot be used to action user tasks. Use a [Retool app](#select-the-review-app) to review and action user tasks.

:::

The **Tasks** tab of the organization contains the **Tasks Console**. From here, users can search for tasks, view task details, change the assigned permission group, or cancel tasks.

Admins can view all tasks across the organization. Task admins—users in permission groups specified in the User Task block settings—can only view tasks for which they are designated as task admins. All other users cannot access the Tasks Console.

### View task information

The Tasks Console displays a table of all user tasks. Each column corresponds to a property of the User Task object. To view more details about a specific task, click **>** to expand the row.

### Cancel a task

User tasks are initially created with a `pending` status. Tasks that are no longer required can be canceled by clicking **Cancel Task**.

### Reassign a task

Click **Reassign Task** to change the permission groups for a specific user task. As with [User Task block permissions](#set-permissions), you can select users and permission groups from a list or dynamically assign them.

## User task storage for self-hosted deployments

Retool stores all data related to user tasks, including user task context and outputs, in your deployment's externalized `postgres` database.

---

## Version and publish workflows

You must publish a workflow before it can run automatically using a [schedule](triggers/schedule.mdx) or with [webhooks](triggers/webhooks.mdx). The workflow IDE automatically saves changes to your workflow but you must publish them to take effect. This allows you to build a new workflow, or make changes to an existing one, without disruption.

When you publish a workflow, Retool creates a versioned release. Each release of a workflow has its own version number and reflects the workflow's state at that specific point in time. Only the published version is used by Retool. This allows you to safely test and build changes without disruption.

:::info Current working version
Any changes you make to a Retool workflow are automatically saved to the current working version. This version contains any changes that have not yet been published.
:::

## Publish a workflow release

Click **Publish release** in the toolbar to create and publish a new version of the workflow. Retool uses [Semantic Versioning](https://semver.org/) and automatically increments the version number based on your selection of **Major**, **Minor**, or **Patch** versioning.

:::note

For [protected workflows](../../source-control/guides/protect/workflows.mdx), create a new release from the  **Releases** tab in the left-hand menu.

:::

:::info Multi-instance releases

If you're using [multi-instance releases](../../source-control/guides/multi-instance-releases.mdx), you can manage different release versions of protected workflows across multiple deployment instances. This allows you to test newer versions on development or staging instances before promoting to production.

:::

import Workflowsvg from "/img/icons/workflows/blocks/workflow.svg";

If your workflow [calls another workflow](./blocks/run-workflow.mdx) using a  **Workflow** block, you can also choose to publish changes to the referenced workflow.

export function Publish() {
  return (
    
      
    
  )
}

## Manage releases

The  **Releases** tab contains a list of every published version of a workflow. The currently published version includes a **Live** tag.

| Property       | Description                                |
| :------------- | :----------------------------------------- |
| Version number | The version number (e.g., `1.0.2`).        |
| Description    | The description of the version.            |
| Author         | The Retool user who published the version. |
| Publish date   | When the version was last published.       |

## Unpublish a release
:::note

This feature is currently rolling out on Retool Cloud and will be available in subsequent releases of self-hosted Retool. Reach out to your account manager to enable unpublish for workflows.

:::

To unpublish a published release, click the **•••** menu of the **Live** release in the  **Releases** tab, and select **Unpublish release**. Until a new release is published, the latest save shown on the canvas will be live to users. 

export function Unpublish() {
  return (
    
      
    
  )
}

:::note
Published releases on [protected workflows](../../source-control/guides/protect/workflows.mdx) can also be unpublished. When unpublishing a release on a protected workflow, the latest saved version on the main branch will be live to users.
:::

## Preview a release

From the **Releases** tab, click the **•••** menu and select **Preview release** to preview it in the workflow IDE.

## Publish an existing release

You can publish an existing version of a workflow at any time. Open the **•••** menu for the version to publish, then select **Publish release**.

If you want to remove any releases and are certain they are no longer needed, you can also delete them permanently.

## Revert to a previous release

You can revert the current working version of your app to the state from a previous version. All changes since this version are discarded but still remain in the history. This does not affect any other releases and doesn't change the published release.

This is useful if you've made a series of changes in the current working version but want to revert the app to an earlier point.

---

## Workflows how-to guides

import Howto from '/docs/_partials/_doctypes/_howto.mdx';

:::tip Get started with workflows

If you are unfamiliar with Retool Workflows and want to get started: 

- Read the [quickstart](./quickstart) to learn about the fundamental concepts.
- Follow a [tutorial](./tutorial/) to build your first workflows.

:::

---

## Retool Workflows documentation

[Retool Workflows](https://retool.com/products/workflows) enables you to build complex automations that interact with your data sources. Connect pre-built query blocks together that can interact with resources, transform data, and run additional logic. Trigger workflows automatically with custom schedules or using webhook events.

---

## Retool Workflows quickstart

import Filtersvg from "/img/icons/workflows/blocks/filter.svg";
import Startsvg from "/img/icons/workflows/blocks/start.svg";
import Querysvg from "/img/icons/workflows/blocks/resource-query.svg";
import Branchsvg from "/img/icons/workflows/blocks/branch.svg";

This guide serves as an introduction to [Retool Workflows](https://retool.com/products/workflows). It covers many of the concepts and terminology you would come across as you build workflows to automate tasks. After reading this page, you should have a good understanding of the fundamentals for building workflows.

## Introduction

A workflow is an automation that interacts with your data sources. It contains a series of connected pre-built _blocks_ that interact with resources, transform data, and run additional logic. Each workflow runs automatically using a schedule or triggered with webhook events.

## Assemble blocks that interact with data {#blocks}

import Browsers from '/docs/_shared/_supported-browsers.mdx';

Workflows are comprised of _blocks_. A block is a type of [query](../queries/quickstart.mdx#resource-queries) that interacts with data (e.g., querying a database) or performs an action (e.g., execute JavaScript code). Blocks are modular and have internal state—you can reference block properties in any subsequent block. For example, a block named `findUser` can be referenced using `findUser.data`.

You drag and drop blocks in the [workflow IDE](concepts/ide.mdx), then configure them to interact with data or perform actions. You connect blocks together to define the control flow which determines the order that each block executes. Blocks can reference the results of previous blocks that have already executed, passing along the results to the next block.

## Set the order of operation {#control-flow}

Blocks connect together and execute sequentially during a run. This defines the order of operation and flow of data, known as the control flow. The connecting line between blocks visually represents the control flow, which can be a single path or branch to perform parallel operations.

Any block that is part of the control flow is executed during a workflow run, beginning with the  **startTrigger** block.

:::note

Blocks that have two connected inputs are executed after *both* inputs complete execution.

:::

## Use JavaScript to connect blocks together

You can [write JavaScript](../queries/quickstart.mdx) almost anywhere in a workflow using `{{ }}`. Each block can access data from any other block that has already executed. For example, the `getDataFromGithub`  [Resource query](guides/blocks/resource-query.mdx) retrieves data which the `processData` block uses. The output from `processData` is then used to run the `upsertToPostgres` block to update a database.

The control flow is not limited to a single path. A block can output data to multiple blocks that perform parallel actions, creating multiple paths. The  [Branch](guides/blocks/logic/branch.mdx) block can also perform conditional logic that outputs different results along different paths.

The following example illustrates a workflow that retrieves a list of customers. It then iterates through the list of customers to check whether they were a sales lead. If they were, it notifies the Sales team. If not, it sends the customer a welcome email.

You can always reference a previous block that is not currently part of the same control flow path. The workflow IDE automatically connects the blocks together and displays the connecting line.

## Call functions outside the control flow

Functions are reusable blocks that run in a headless state. They operate outside of the control flow and do not appear on the canvas. You call a function from [JavaScript](guides/blocks/code/javascript.mdx) Code blocks which can pass data as parameters. This reduces the need for query duplication and enables you to perform certain tasks only when necessary.

## Trigger workflows automatically

Workflows automatically run based on _trigger_ conditions. You can configure a workflow to run at a regular [schedule](guides/triggers/schedule.mdx) or in response to [webhook events](guides/triggers/webhooks.mdx).

### Schedules

You can add multiple schedule triggers to a workflow so that it runs at different intervals. This makes it possible to run workflows at different times that cannot be expressed as a simple repeating schedule. For example, you can create multiple triggers to schedule a workflow to run at 9am every Monday, 11pm every Wednesday, and 3am on the first day of the month.

Schedule triggers also support _cron_ syntax, which enables you to define a schedule with more granularity. For example, you can schedule a workflow to trigger at 9.30am every Monday, Wednesday, and Friday, but only between January and September:

| Cron         | Value   | Description                         |
| ------------ | ------- | ----------------------------------- |
| Minutes      | `30`    | 30 minutes past the hour.           |
| Hours        | `9`     | 9am.                                |
| Day of month | `*`     | Any day of the month.               |
| Month        | `1-9`   | Only January to September.          |
| Day of week  | `1,3,5` | Only Monday, Wednesday, and Friday. |

### Webhooks

Workflows can run in response to _webhook events_, such as a payment notification from [Stripe](../data-sources/guides/integrations/finance/stripe.mdx) or an incident alert from PagerDuty. Each workflow has a unique webhook endpoint URL with which to receive webhook events. You use this URL when configuring webhook notifications from other services.

```shell title="Example cURL command"
curl -X POST --url "https://api.retool.com/v1/workflows/43rewfs-7da9-ddgfet32-fddfr3-43rewf344gre/startTrigger?workflowApiKey=retool_wk_fdsedgf3t4t3grg34354656" --data '{"hello":"world"}' -H 'Content-Type: application/json'
```

Workflows can read data from a webhook event's JSON payload (e.g., error message or confirmation number) and make it available to your workflow. This makes it possible for workflows to process received data, such as logging new orders to a database.

### Publishing

You must publish a workflow release before it can run automatically using a [schedule](guides/triggers/schedule.mdx) or with [webhooks](guides/triggers/webhooks.mdx). The [workflow IDE](concepts/ide.mdx) automatically saves changes to your workflow but you must publish them to take effect. This allows you to build a new workflow, or make changes to an existing one, without disruption.

When you publish a workflow, Retool creates a versioned release. Each release of a workflow has its own version number and reflects the workflow's state at that specific point in time. Only the published version is used by Retool. This allows you to safely test and build changes without disruption.

## Review run logs

Retool logs every successful and failed run of a workflow. The **Run history** panel contains a list of recent runs, each with their date, time, their status. You can then view the status of each block to see where a failure may have occurred.

Run logs can help troubleshoot problems with queries, such as malformed SQL statements or invalid JavaScript. You can filter log entries to show only **error**, **success**, or **info** entries. Keep in mind that queries are considered successful if they didn't return an error. A workflow cannot determine if its actions produced the results you expected.

## Error handling

Retool enables you to configure blocks to be [global error handlers](workflows/quickstart.mdx#configure-error-handling) that run whenever an error occurs, such as sending a Slack notification. These blocks do not have to be part of the control flow to run in the event of an error.

---

## Retool Workflows environment variables

Use environment variables to configure Retool Workflows for self-hosted deployments.

## Code executor

import CodeExecutor from "@site/docs/self-hosted/reference/environment-variables/code-executor.mdx";

import Workflows from "@site/docs/self-hosted/reference/environment-variables/workflows.mdx";

## Workflows

---

## Workflows glossary

import SharedGlossary from '../../_partials/_glossary.mdx';

---

## Workflow IDE keyboard shortcuts

Use Retool's built-in keyboard shortcuts to quickly perform certain actions or control the interface.

---

## The Invoke Agent block

## Properties

## Settings

import Blocksettings from "./_settings/_settings.mdx";
import Timeoutsettings from "./_settings/_timeout.mdx";
import Retrysettings from "./_settings/_retry.mdx";
import Intervalsettings from "./_settings/_interval.mdx";
import Exponentialsettings from "./_settings/_exponential.mdx";
import Finallysettings from "./_settings/_finally.mdx";
import Coefficientsettings from "./_settings/_coefficient.mdx";
import Maxintervalsettings from "./_settings/_max_interval.mdx";

---

## The AI Action block

## Properties

## Settings

import Blocksettings from "./_settings/_settings.mdx";
import Timeoutsettings from "./_settings/_timeout.mdx";
import Retrysettings from "./_settings/_retry.mdx";
import Intervalsettings from "./_settings/_interval.mdx";
import Exponentialsettings from "./_settings/_exponential.mdx";
import Finallysettings from "./_settings/_finally.mdx";
import Coefficientsettings from "./_settings/_coefficient.mdx";
import Maxintervalsettings from "./_settings/_max_interval.mdx";

---

## The Branch block

## Properties

## Settings

import Blocksettings from "./_settings/_settings.mdx";
import Timeoutsettings from "./_settings/_timeout.mdx";

---

## The Code block

## Properties

## Settings

import Blocksettings from "./_settings/_settings.mdx";
import Timeoutsettings from "./_settings/_timeout.mdx";
import Retrysettings from "./_settings/_retry.mdx";
import Intervalsettings from "./_settings/_interval.mdx";
import Exponentialsettings from "./_settings/_exponential.mdx";
import Finallysettings from "./_settings/_finally.mdx";
import Coefficientsettings from "./_settings/_coefficient.mdx";
import Maxintervalsettings from "./_settings/_max_interval.mdx";

---

## The Filter block

## Properties

## Settings

import Blocksettings from "./_settings/_settings.mdx";
import Timeoutsettings from "./_settings/_timeout.mdx";

---

## Block objects



---

## The Loop block

## Properties

## Settings

import Blocksettings from "./_settings/_settings.mdx";
import Timeoutsettings from "./_settings/_timeout.mdx";
import Retrysettings from "./_settings/_retry.mdx";
import Intervalsettings from "./_settings/_interval.mdx";
import Exponentialsettings from "./_settings/_exponential.mdx";
import Finallysettings from "./_settings/_finally.mdx";
import Coefficientsettings from "./_settings/_coefficient.mdx";
import Maxintervalsettings from "./_settings/_max_interval.mdx";

---

## The Resource query block

## Properties

## Settings

import Blocksettings from "./_settings/_settings.mdx";
import Timeoutsettings from "./_settings/_timeout.mdx";
import Retrysettings from "./_settings/_retry.mdx";
import Intervalsettings from "./_settings/_interval.mdx";
import Exponentialsettings from "./_settings/_exponential.mdx";
import Finallysettings from "./_settings/_finally.mdx";
import Coefficientsettings from "./_settings/_coefficient.mdx";
import Maxintervalsettings from "./_settings/_max_interval.mdx";

---

## The Response block

## Properties

## Settings

import Blocksettings from "./_settings/_settings.mdx";
import Timeoutsettings from "./_settings/_timeout.mdx";
import Retrysettings from "./_settings/_retry.mdx";
import Intervalsettings from "./_settings/_interval.mdx";
import Exponentialsettings from "./_settings/_exponential.mdx";
import Finallysettings from "./_settings/_finally.mdx";
import Coefficientsettings from "./_settings/_coefficient.mdx";
import Maxintervalsettings from "./_settings/_max_interval.mdx";

---

## The Workflow block

## Properties

## Settings

import Blocksettings from "./_settings/_settings.mdx";
import Timeoutsettings from "./_settings/_timeout.mdx";
import Retrysettings from "./_settings/_retry.mdx";
import Intervalsettings from "./_settings/_interval.mdx";
import Exponentialsettings from "./_settings/_exponential.mdx";
import Finallysettings from "./_settings/_finally.mdx";
import Coefficientsettings from "./_settings/_coefficient.mdx";
import Maxintervalsettings from "./_settings/_max_interval.mdx";

---

## The Wait block

## Properties

## Settings

import Blocksettings from "./_settings/_settings.mdx";
import Retrysettings from "./_settings/_retry.mdx";
import Intervalsettings from "./_settings/_interval.mdx";
import Exponentialsettings from "./_settings/_exponential.mdx";
import Finallysettings from "./_settings/_finally.mdx";
import Coefficientsettings from "./_settings/_coefficient.mdx";
import Maxintervalsettings from "./_settings/_max_interval.mdx";

---

## The Current User object

You can reference the `current_user` object when a workflow is manually run from the workflow IDE or triggered from an app by a user. The evaluated `current_user` in workflows is an admin in your organization's instance or space. This differs from apps and agents, where the `current_user` is evaluated as the current, logged-in user. Workflows are always run as an admin role to ensure that when workflows are run without access to the current, logged-in user (such as when triggered by schedule or webhook), sufficient access to any resource on the control flow is guaranteed.

## Run workflow as the current user

There is a public beta feature that allows your organization to run workflows as the logged-in user when workflows are manually run from the workflow IDE or triggered from an app by a user. Please be aware that, even with this beta feature activated, any workflow invoked via a webhook, query, or app trigger or by another workflow still assigns `current_user` as an admin user.

:::note

To set the `current_user` to `null` on automatic workflow run, reach out to your account manager.

:::

## Properties

---

## The Start Trigger object

You can reference the `startTrigger` object when a workflow is manually run from the workflow IDE or [triggered with a webhook](../../guides/triggers/webhooks.mdx). `startTrigger` is `null` when a workflow is configured to use a schedule trigger.

## Properties

---

## The Workflow Context object

## Properties

---

## Retool Events reference

Use Retool Events to build workflows that run whenever certain events occur within your organization. Event payloads contain properties with information related to the event. You can reference these within Retool Event-related workflows to perform actions or send notifications automatically.

[Learn more](../guides/retool-events.mdx) about setting up Retool Events.

## Password reset required

A user must reset their password.

## Source Control deployment completed

Source Control has completed a deployment.

## User created

A user was created.

## User invited

A user was invited to join the organization.

## User requested access

A user requested access to join the organization.

## Login attempted

A user attempted to log in. This event runs after a user has been authenticated, but before they complete the login process.

---

## Retool Workflows reference

import Reference from '/docs/_partials/_doctypes/_reference.mdx';

---

## Retool Workflows tutorial

[Retool Workflows](./index.mdx) enables you to build, schedule, and monitor jobs, alerts, and ETL tasks. You can automate processes without the need for user interaction and take action based on conditions.

This guide explains how to build a workflow that performs a weekly check for new features added in the [latest edge release](../releases/edge/index.mdx) of Retool. If new features have been added, the workflow sends a notification using [Retool Email](../data-sources/quickstarts/retool-email.mdx). You can also trigger the workflow to check at any time with a webhook event.

## 1. Create a new workflow

Sign in to your Retool organization, select the **Workflows** tab in the navigation bar, then click **Create new > Workflow**.

A workflow's name is initially set to `My First Workflow - Name`. Click the name of this workflow in the toolbar and set it to `Subscribe to the Retool Release Notes`.

## 2. Configure triggers

import Triggerssvg from '/img/icons/workflows/triggers.svg';

You can trigger workflows on a [schedule](./guides/triggers/schedule.mdx), by sending a [webhook event](./guides/triggers/webhooks.mdx), or by polling a streaming service with a [query trigger](./guides/triggers/query.mdx). This workflow uses two types of triggers:

- A schedule trigger to run every week.
- A webhook trigger to manually run the workflow. This allows you to run the workflow at any time.

Click **Edit triggers** in the **startTrigger** block to open the **Triggers** tab.

### Create schedule

First, enable the **Schedule** trigger. Configure it to run every Friday at 5 PM, then click **Save changes**.

### Configure webhooks

Next, enable the **Webhook** trigger.

## 3. Fetch release notes

import Connectsvg from '/img/icon-packs/bold/interface-geometric-circle-alternate.svg';
import Startsvg from '/img/icons/workflows/blocks/start.svg';
import Resourcequerysvg from '/img/icons/workflows/blocks/resource-query.svg';
import Runsvg from '/img/icons/workflows/run.svg';

Workflows are comprised of [blocks](./guides/blocks/blocks.mdx) that perform specific actions, such as interacting with data or performing custom logic. You connect blocks together to define their control flow, which represents the order of operation in which they run.

This workflow uses the [Resource query](./guides/blocks/resource-query.mdx) block. You would primarily use this to query [resources](../data-sources/quickstarts/resources.mdx)—data sources you've connected to Retool—but you can also use it to interact with APIs and fetch JSON data with the **REST API** resource.

Retool releases a [new edge release](../releases/index.mdx) for self-hosted deployments every week and publishes the corresponding [release notes](../releases/edge/index.mdx). You can use the [JSON feed](https://docs.retool.com/data/releases/edge.json) of the edge release notes to subscribe to notifications. In this section, you'll subscribe to the release notes and get a list of everything in the `added` category for the most recent release.

Click and drag  from the  **startTrigger** block to create a new block, then select the  **Resource query** block type. Click on the block name and change it to `fetchPosts`.

This block defaults to the **REST API** resource with a **GET** action type. Specify the JSON feed in the **URL** field.

```url title="JSON feed"
https://docs.retool.com/data/releases/edge.json
```

Click  **Run** to retrieve the JSON feed data. The JSON feed contains an array of objects, `items`, that contain information about each release.

Next, add a JavaScript **Code** block that gets the most recent post. Click or drag from  to create a new **Code** block. Name this block `fetchLatest`, and copy and paste `return _.first(Object.values(fetchPosts.data));` to return the most recent post.

Finally, add one more JavaScript **Code** block that returns an array of every change in the `added` category. Title this block `fetchAdded`, and paste `return fetchLatest.data.versions[0].added` into the code block.

## 4. Check for added functionality

Use the **Branch block** to perform different actions based on whether the input value meets a truthy condition. Branch blocks allow you to create two different paths for a workflow to take, depending on whether the condition evaluates as `true` or `false`. In this case, the workflow needs to check if the most recent release notes contain any features labeled `added`.

First, click or drag  from `fetchAdded` to add a **Branch** block, then rename it to `containsAdded`. In the **If** statement, specify the following condition: `fetchAdded.data.length`. This condition evaluates to `true` if there are any features in the `added` array.

## 5. Summarize changes using AI

You can leverage AI in workflows to perform actions, such as summarizing text or generating images. This workflow uses an AI action to generate a summary of changes.

Click and drag  from the **If** statement in `containsAdded` to add an **AI action** block, then rename it to `summarizeChanges`. Select the **Generate text** action and instruct the AI model to generate a summary.

import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";

```text title="Instructions"
{{ fetchAdded.data }} contains a list of features that Retool added this week. Write me a summary of the added features. Link to the latest edge release notes at https://docs.retool.com/releases/edge for more information.
```

:::info Differences in generated AI content

AI-generated content may differ from the example below.

:::

```text title="Generated summary"

This week, Retool introduced several new features and enhancements:

1. **Typography Enhancements**: New rebrand fonts were added, alongside newly created typography components specifically for the sign-in page.
2. **NFC Tag Support**: Users can now read and write NFC tags within the platform.
3. **Semgrep Testing Support**: The platform now supports testing for writing `semgrep` rules.
4. **Action Mirroring**: Functionality was added to support mirroring actions that affect the app template.
5. **Global Split Pane Rendering**: Children of a global split pane can now be rendered across all pages.
6. **Cypress Enhancements**: New tabs have been added to the Cypress testing editor for improved usability.
7. **Global Component Tree Update**: A split pane feature was added to the global component tree.
8. **SSO Test Connection Management**: Users can now open the SSO test connection page in a new tab.
9. **Session-Specific State Management**: The platform now retains session-specific state variables like `current_user` and `viewport` during test generation.
10. **App Template Copying**: New support for copying app templates to mirror after slice loads was introduced.
11. **Enhanced App Testing UI**: A new user interface for app testing has been implemented, featuring expanded value code editors for query mocks and preconditions.
12. **Database Archiving Safeguards**: Safeguards have been integrated for Retool Database archiving processes.

For detailed information on these features, please check the [latest edge release notes](https://docs.retool.com/releases/edge).

```

## 6. Send an email notification

:::tip Use other notification methods

You can connect many communication platforms and services to Retool, such as [Slack](../data-sources/guides/integrations/messaging/slack.mdx), [Twilio](../data-sources/guides/integrations/messaging/twilio.mdx), [SendGrid](../data-sources/guides/integrations/messaging/sendgrid.mdx), and [Microsoft Teams](../data-sources/guides/integrations/messaging/microsoft-teams.mdx).

:::

Finally, the workflow must send an email with:

- The Self-hosted Retool version number.
- The AI-generated summary.

You can use [Retool Email](../data-sources/quickstarts/retool-email.mdx) to send emails without any extra configuration. Click and drag  from the AI action block to add a new Resource query block, then rename it to `sendEmail`. Configure the block with the following values:

| Property    | Value                                                              |
| ----------- | ------------------------------------------------------------------ |
| **To**      | The email address to receive the notification.                     |
| **Subject** | `Retool Self-hosted version {{ Object.keys(fetchPosts.data)[0] }}` |

Finally, set **Body** to **Markdown** mode, and include the summary:

```text title="Message body"
{{ summarizeChanges.data }}
```

import Runprevioussvg from '/img/icons/workflows/run-with-previous.svg';

Next, create a new Retool Email block that sends an email if there are no changes in the `added` category. Click and drag  from the `Else` branch of the `containsAdded` block. Set the same **To** and **Subject** field, and include a summary stating that nothing was added this week: `Retool did not add any features in this week's release.`

## 7. Add an error handler

import AddBlocksvg from '/img/icons/workflows/add-block.svg';
import Errorsvg from '/img/icons/workflows/global-error-handler.svg';

If an error occurs at any point in the workflow, it will fail and you won't receive your weekly update email. Set up a [global error handler](./guides/error-handlers) that sends you an email if something goes wrong. You can use various types of block types as error handlers—in this example, you'll use a **Resource query** block to send an email.

Open the Blocks  menu and drag the **Resource query**  block onto the canvas. The block does not need to be connected to other blocks. Rename the block `errorHandler`.

Click the ••• menu and select  **Add global error handler**. Next, change the resource type to **Retool Email**, and configure the block to send an email that tells the recipient that the workflow failed.

## 8. Test and publish the workflow

Now that the workflow is complete, you can manually run it to test. Click  **Run** in the toolbar to run the workflow. You can also test the workflow by calling the webhook trigger. To do so, open the  **Triggers** tab and select the **Webhook** trigger. Click **Copy > Copy as cURL** to copy a cURL command to run in the CLI.

Run the command to trigger the workflow:

```curl title="Example cURL command
curl -X POST --url "https://api.retool.com/v1/workflows/090bb051-1534-4279-b863-35gfk8223/startTrigger" \
-H 'Content-Type: application/json' \
-H 'X-Workflow-Api-Key: retool_wk_xxxxxxxx'
```

Workflows run automatically once published. If you want to test the schedule trigger and receive notifications automatically, click **Publish release** in the toolbar.

## Wrap up

At this point, you have successfully created a workflow that will keep you informed about the features that Retool adds in every week's edge release.

To continue learning about workflows, explore the guides for other common features:

- [Retool Events](./guides/retool-events.mdx)
- [Loops](./guides/blocks/logic/loop.mdx)
- [Webhook responses](./guides/blocks/response.mdx)
- [Best practices](./concepts/best-practices.mdx)