# Triggering Workflow Runs — Kick off your registered tasks and flows.


> *Triggering one task run from another?*
>
> Use run chaining instead of the methods below. For details, see [Chaining task runs](/workflows-defining-tasks#chaining-task-runs).

After you [create a workflow](/workflows-tutorial) and register its tasks and flows, you can start triggering runs from your own apps and agents.

You can also [manually trigger runs](#running-manually) in the Render Dashboard and CLI to help with testing and debugging.

## First: Create an API key

Triggering runs from code requires a Render API key.

Create an API key from your [Account Settings page](https://dashboard.render.com/u/settings?add-api-key) in the Render Dashboard. Copy and securely store the key, which is displayed in full only on creation.

> *API keys are secret credentials!*
>
> Don't publicly post your API key, commit it to version control, or otherwise share it with anyone outside your organization. If you believe an API key has been compromised, revoke it.

## Running with the Render SDK

> *The Render SDK is currently available for TypeScript and Python.*
>
> SDKs for additional languages are planned for future releases. To trigger runs from other languages, [use the Render API](#running-with-the-render-api).

Follow these steps to trigger runs from your code using the Render SDK.

### 1. Install the SDK

**Tab: TypeScript**

From your TypeScript project directory:

```shell
npm install @renderinc/sdk@latest
```

(Or `pnpm install`, `bun add`, etc.)

*If you already have the SDK installed,* make sure you're using version `1.7.0` or later:

```shell
npm install @renderinc/sdk@latest
```

After installing, make sure `@renderinc/sdk` is listed as a dependency in your `package.json` file at version `^1.7.0` or later.

**Tab: Python**

From your Python project directory:

```shell
pip install render
```

*If you already have the SDK installed,* make sure you're using version `1.7.0` or later:

```shell
pip install --upgrade render
```

After installing, make sure to add `render>=1.7.0` as a dependency in your application's `requirements.txt`, `pyproject.toml`, or equivalent.

### 2. Set your API key

In your application's environment, set the `RENDER_API_KEY` environment variable to your [API key](#first-create-an-api-key):

```bash
export RENDER_API_KEY=rnd_abc123…
```

The SDK client automatically detects and uses the value of this environment variable.

Alternatively, you can provide your API key when initializing the client (see your language's SDK reference for details). *Do not hardcode your API key in your code.*

### 3. Initialize the client

Initialize the SDK client you’ll use to trigger and manage runs:

**Tab: TypeScript**

```typescript
import { Render } from '@renderinc/sdk'

const render = new Render()
```

**Tab: Python (async)**

> Use this client in *asynchronous* execution contexts (such as FastAPI route handlers).

```python
from render import RenderAsync

render = RenderAsync()
```

**Tab: Python (sync)**

> Use this client in *synchronous* execution contexts (such as a default Flask or Django app).

```python
from render import Render

render = Render()
```

### 4. Trigger a task run

Using the client initialized [above](#3-initialize-the-client), this code demonstrates triggering a task run and waiting for it to complete:

**Tab: TypeScript**

```typescript
async function triggerTaskRun() {
  // Kick off a task run
  const startedRun = await render.workflows.startTask(
    'my-workflow/calculateSquare',
    [2],
    { idempotencyKey: 'abc123' } // Optional
  )

  console.log('Task run started:', startedRun.taskRunId)

  // Wait for run to complete
  const finishedRun = await startedRun.get()

  console.log('Task run completed:', finishedRun.id)
  console.log('Final status:', finishedRun.status)
}

triggerTaskRun()
```

You trigger a task run by calling the client's [`workflows.startTask`](/workflows-sdk-typescript#starttask) method. This method has the following parameters:

------

###### *Function parameters*

---

###### Parameter

`taskSlug`

###### Description

*Required.* The *slug* indicating the task to run, available from your task's page in the [Render Dashboard](https://dashboard.render.com):

[image: Task slug in the Render Dashboard]

Always has the format `{workflow-slug}/{task-name}` (e.g., `my-workflow/calculateSquare`).

---

###### Parameter

`inputData`

###### Description

*Required.* An array containing the task's [input arguments](/workflows-defining-tasks#function-arguments).

Elements are positional based on the task's function signature. For a task that takes zero arguments, provide an empty array, `[]`.

---

###### Parameter

`options`

###### Description

An object containing configuration options for the task run. All fields are optional. See all supported options below.

---

###### *Supported `options`*

---

###### Parameter

`idempotencyKey`

###### Description

An optional string used to prevent duplicate task runs.

Added in SDK v1.2.0.

For details, see [Idempotency keys](/workflows-running#idempotency-keys).

---

###### Parameter

`signal`

###### Description

An optional `AbortSignal` used to cancel the initial request and any subsequent wait via [`TaskRunResult.get()`](/workflows-sdk-typescript#taskrunresult).

Canceling the client-side operation does not cancel the task run on Render. See [Canceling operations with `AbortSignal`](/workflows-sdk-typescript#canceling-operations-with-abortsignal).

------

The `workflows.startTask` method returns a `TaskRunResult` as soon as the run is created. This object provides the run's `taskRunId` immediately. You can call `await result.get()` to wait for the run to complete and obtain the full run details (including `status` and `results`).

For full options and details, see the [TypeScript SDK reference](/workflows-sdk-typescript#starttask).

**Tab: Python (async)**

```python
import asyncio

async def trigger_task_run():

  # Kick off a task run
  started_run = await render.workflows.start_task(
    "my-workflow/calculate_square",
    [2]
  )

  print(f"Task run started: {started_run.id}")
  print(f"Initial status: {started_run.status}")

  # Wait for run to complete
  finished_run = await started_run

  print(f"Task run completed: {finished_run.id}")
  print(f"Final status: {finished_run.status.value}")

if __name__ == "__main__":
  asyncio.run(trigger_task_run())
```

You trigger a task run by calling the async client's [`workflows.start_task`](/workflows-sdk-python#start-task) method. This method has the following parameters:

------

##### `task_slug`

*Required.* The *slug* indicating the task to run, available from your task's page in the [Render Dashboard](https://dashboard.render.com):

[image: Task slug in the Render Dashboard]

Always has the format `{workflow-slug}/{task-name}` (e.g., `my-workflow/calculate_square`).

##### `input_data`

*Required.* A list or dictionary containing the task's [input arguments](/workflows-defining-tasks#function-arguments).

- *If you provide a list,* elements are positional based on the task's function signature.
- *If you provide a dictionary,* each key maps to a parameter name in the task's function signature.

For a task that takes zero arguments, provide an empty list, `[]`.

##### `idempotency_key`

An optional string used to prevent duplicate task runs.

Added in SDK v1.2.0.

For details, see [Idempotency keys](/workflows-running#idempotency-keys).

------

The `workflows.start_task` method returns an [`AwaitableTaskRun`](/workflows-sdk-python#awaitabletaskrun) object as soon as the run is created. This object provides the run's `id` and initial `status`, which are both available immediately. You can `await` this object to wait for the run to complete and obtain a [`TaskRunDetails`](/workflows-sdk-python#taskrundetails) object.

For full options and details, see the [Python SDK reference](/workflows-sdk-python#start-task).

**Tab: Python (sync)**

```python
def trigger_task_run():

  # Kick off a task run
  started_run = render.workflows.start_task(
    "my-workflow/calculate_square",
    [2]
  )

  print(f"Task run started: {started_run.id}")
  print(f"Initial status: {started_run.status.value}")

  # Wait for run to complete
  for finished_run in render.workflows.task_run_events([started_run.id]):
    print(f"Task run completed: {finished_run.id}")
    print(f"Final status: {finished_run.status.value}")

if __name__ == "__main__":
  trigger_task_run()
```

You trigger a task run by calling the synchronous client's [`workflows.start_task`](/workflows-sdk-python#start-task) method. This method has the following parameters:

------

##### `task_slug`

*Required.* The *slug* indicating the task to run, available from your task's page in the [Render Dashboard](https://dashboard.render.com):

[image: Task slug in the Render Dashboard]

Always has the format `{workflow-slug}/{task-name}` (e.g., `my-workflow/calculate_square`).

##### `input_data`

*Required.* A list or dictionary containing the task's [input arguments](/workflows-defining-tasks#function-arguments).

- *If you provide a list,* elements are positional based on the task's function signature.
- *If you provide a dictionary,* each key maps to a parameter name in the task's function signature.

For a task that takes zero arguments, provide an empty list, `[]`.

##### `idempotency_key`

An optional string used to prevent duplicate task runs.

Added in SDK v1.2.0.

For details, see [Idempotency keys](/workflows-running#idempotency-keys).

------

The `workflows.start_task` method returns a [`TaskRun`](/workflows-sdk-python#taskrun) object as soon as the run is created. This object provides the run's `id` and initial `status`, which are both available immediately.

To wait for the run to complete, pass its ID to [`workflows.task_run_events`](/workflows-sdk-python#task-run-events). The iterator yields a [`TaskRunDetails`](/workflows-sdk-python#taskrundetails) object when the run reaches a terminal status (`completed`, `failed`, or `canceled`). You can also use [`workflows.run_task`](/workflows-sdk-python#run-task) instead of `start_task` to start _and_ wait in one blocking call.

For full options and details, see the [Python SDK reference](/workflows-sdk-python#start-task).

### 5. Trigger a flow run

Using the client initialized [above](#3-initialize-the-client), this code demonstrates triggering a flow run and waiting for it to complete:

**Tab: TypeScript**

```typescript
async function triggerFlowRun() {
  // Kick off a flow run
  const startedRun = await render.workflows.startFlow(
    'my-workflow/formatMessage',
    ['hello'],
    { idempotencyKey: 'format-message-request-123' } // Optional
  )

  console.log('Flow run started:', startedRun.id)

  // Wait for run to complete
  let finishedRun = await render.workflows.getFlowRun(startedRun.id)
  while (!['succeeded', 'failed', 'canceled'].includes(finishedRun.status)) {
    await new Promise(resolve => setTimeout(resolve, 1000))
    finishedRun = await render.workflows.getFlowRun(startedRun.id)
  }

  console.log('Flow run completed:', finishedRun.id)
  console.log('Final status:', finishedRun.status)
}

triggerFlowRun()
```

You trigger a flow run by calling the client's [`workflows.startFlow`](/workflows-sdk-typescript#startflow) method. This method has the following parameters:

------

###### *Function parameters*

---

###### Parameter

`flowSlug`

###### Description

*Required.* The *slug* indicating the flow to run, available from your flow's page in the [Render Dashboard](https://dashboard.render.com).

Always has the format `{workflow-slug}/{flow-name}` (e.g., `my-workflow/formatMessage`).

---

###### Parameter

`inputData`

###### Description

*Required.* An array containing the flow's [input arguments](/workflows-defining-flows#function-arguments).

Elements are positional based on the flow's function signature. Do not include a value for `ctx`. Render provides the `FlowContext` object automatically.

For a flow that takes no arguments besides `ctx`, provide an empty array, `[]`.

---

###### Parameter

`options`

###### Description

An object containing configuration options for the flow run. All fields are optional. See all supported options below.

---

###### *Supported `options`*

---

###### Parameter

`idempotencyKey`

###### Description

An optional string used to prevent duplicate flow runs.

For details, see [Idempotency keys](/workflows-running#idempotency-keys).

---

###### Parameter

`signal`

###### Description

An optional `AbortSignal` used to cancel the initial request.

Canceling the client-side operation does not cancel the flow run on Render. See [Canceling operations with `AbortSignal`](/workflows-sdk-typescript#canceling-operations-with-abortsignal).

------

The `workflows.startFlow` method returns a [`FlowRun`](/workflows-sdk-typescript#flowrun) object as soon as the run is created. This object provides the run's `id` and initial `status`, which are both available immediately.

To wait for the run to complete, poll [`workflows.getFlowRun`](/workflows-sdk-typescript#getflowrun) with its ID until the run reaches a terminal status (`succeeded`, `failed`, or `canceled`). This method returns a [`FlowRunDetails`](/workflows-sdk-typescript#flowrundetails) object, including `status` and `results`. You can also use [`workflows.runFlow`](/workflows-sdk-typescript#runflow) instead of `startFlow` to start _and_ wait in one call.

For full options and details, see the [TypeScript SDK reference](/workflows-sdk-typescript#startflow).

**Tab: Python (async)**

```python
import asyncio

async def trigger_flow_run():

  # Kick off a flow run
  started_run = await render.workflows.start_flow(
    "my-workflow/format_message",
    ["hello"]
  )

  print(f"Flow run started: {started_run.id}")
  print(f"Initial status: {started_run.status.value}")

  # Wait for run to complete
  finished_run = await render.workflows.get_flow_run(started_run.id)
  while finished_run.status.value not in ("succeeded", "failed", "canceled"):
    await asyncio.sleep(1)
    finished_run = await render.workflows.get_flow_run(started_run.id)

  print(f"Flow run completed: {finished_run.id}")
  print(f"Final status: {finished_run.status.value}")

if __name__ == "__main__":
  asyncio.run(trigger_flow_run())
```

You trigger a flow run by calling the async client's [`workflows.start_flow`](/workflows-sdk-python#start-flow) method. This method has the following parameters:

------

##### `flow_slug`

*Required.* The *slug* indicating the flow to run, available from your flow's page in the [Render Dashboard](https://dashboard.render.com).

Always has the format `{workflow-slug}/{flow-name}` (e.g., `my-workflow/format_message`).

##### `input_data`

*Required.* A list or dictionary containing the flow's [input arguments](/workflows-defining-flows#function-arguments).

- *If you provide a list,* elements are positional based on the flow's function signature.
- *If you provide a dictionary,* each key maps to a parameter name in the flow's function signature.

Do not include a value for `ctx`. Render provides the `FlowContext` object automatically.

For a flow that takes no arguments besides `ctx`, provide an empty list, `[]`.

##### `idempotency_key`

An optional string used to prevent duplicate flow runs.

For details, see [Idempotency keys](/workflows-running#idempotency-keys).

------

The `workflows.start_flow` method returns a [`FlowRun`](/workflows-sdk-python#flowrun) object as soon as the run is created. This object provides the run's `id` and initial `status`, which are both available immediately.

To wait for the run to complete, poll [`workflows.get_flow_run`](/workflows-sdk-python#get-flow-run) with its ID until the run reaches a terminal status (`succeeded`, `failed`, or `canceled`). This method returns a [`FlowRunDetails`](/workflows-sdk-python#flowrundetails) object, including `status` and `results`.

For full options and details, see the [Python SDK reference](/workflows-sdk-python#start-flow).

**Tab: Python (sync)**

```python
import time

def trigger_flow_run():

  # Kick off a flow run
  started_run = render.workflows.start_flow(
    "my-workflow/format_message",
    ["hello"]
  )

  print(f"Flow run started: {started_run.id}")
  print(f"Initial status: {started_run.status.value}")

  # Wait for run to complete
  finished_run = render.workflows.get_flow_run(started_run.id)
  while finished_run.status.value not in ("succeeded", "failed", "canceled"):
    time.sleep(1)
    finished_run = render.workflows.get_flow_run(started_run.id)

  print(f"Flow run completed: {finished_run.id}")
  print(f"Final status: {finished_run.status.value}")

if __name__ == "__main__":
  trigger_flow_run()
```

You trigger a flow run by calling the synchronous client's [`workflows.start_flow`](/workflows-sdk-python#start-flow) method. This method has the following parameters:

------

##### `flow_slug`

*Required.* The *slug* indicating the flow to run, available from your flow's page in the [Render Dashboard](https://dashboard.render.com).

Always has the format `{workflow-slug}/{flow-name}` (e.g., `my-workflow/format_message`).

##### `input_data`

*Required.* A list or dictionary containing the flow's [input arguments](/workflows-defining-flows#function-arguments).

- *If you provide a list,* elements are positional based on the flow's function signature.
- *If you provide a dictionary,* each key maps to a parameter name in the flow's function signature.

Do not include a value for `ctx`. Render provides the `FlowContext` object automatically.

For a flow that takes no arguments besides `ctx`, provide an empty list, `[]`.

##### `idempotency_key`

An optional string used to prevent duplicate flow runs.

For details, see [Idempotency keys](/workflows-running#idempotency-keys).

------

The `workflows.start_flow` method returns a [`FlowRun`](/workflows-sdk-python#flowrun) object as soon as the run is created. This object provides the run's `id` and initial `status`, which are both available immediately.

To wait for the run to complete, poll [`workflows.get_flow_run`](/workflows-sdk-python#get-flow-run) with its ID until the run reaches a terminal status (`succeeded`, `failed`, or `canceled`). This method returns a [`FlowRunDetails`](/workflows-sdk-python#flowrundetails) object, including `status` and `results`.

For full options and details, see the [Python SDK reference](/workflows-sdk-python#start-flow).

## Other supported methods

### Running with the Render API

The [Render API](api) provides endpoints for triggering runs, along with a variety of endpoints for retrieving workflow and run details. The Render SDK uses the Render API behind the scenes, and you can also use it directly from your own code.

**Tab: Tasks**

Start a task run by sending a POST request to the [Run task](https://api-docs.render.com/reference/createtask/) endpoint. The JSON body for this request includes the following properties:

```json
{
  "task": "my-workflow/calculate_square",
  "input": [2]
}
```

------

##### `task`

*Required.* An identifier specifying the task to run.

You can provide either of two identifiers, both of which are available from your task's page in the [Render Dashboard](https://dashboard.render.com):

[image: Task slug in the Render Dashboard]

- The task's *slug*
  - This has the format `{workflow-slug}/{task-name}` (for example, `my-workflow/calculate_square`)
- The task's *ID*
  - This has the format `tsk-abc123...`

##### `input`

*Required.* An array containing values for the task's [input arguments](/workflows-defining-tasks#function-arguments).

Elements are positional based on the task's function signature.

For a task that takes zero arguments, provide an empty array, `[]`.

##### `idempotencyKey`

An optional string used to prevent duplicate runs.

For details, see [Idempotency keys](#idempotency-keys).

------

**Tab: Flows**

Start a flow run by sending a POST request to the [Run flow](https://api-docs.render.com/reference/startflowrun/) endpoint. The JSON body for this request includes the following properties:

```json
{
  "flow": "my-workflow/format_message",
  "input": ["hello"]
}
```

------

##### `flow`

*Required.* An identifier specifying the flow to run.

You can provide either of two identifiers, both of which are available from your flow's page in the [Render Dashboard](https://dashboard.render.com):

- The flow's *slug*
  - This has the format `{workflow-slug}/{flow-name}` (for example, `my-workflow/format_message`)
- The flow's *ID*
  - This has the format `flw-abc123...`

##### `input`

*Required.* An array containing values for the flow's [input arguments](/workflows-defining-flows#function-arguments).

Elements are positional based on the flow's function signature.

Do not include a value for `ctx`. Render provides the `FlowContext` object automatically.

For a flow that takes no arguments besides `ctx`, provide an empty array, `[]`.

##### `idempotencyKey`

An optional string used to prevent duplicate runs.

For details, see [Idempotency keys](#idempotency-keys).

------

### Running manually

You can manually trigger runs directly from the Render Dashboard and CLI. This is handy for testing and debugging new tasks and flows.

**Tab: Dashboard**

#### Running manually in the Render Dashboard

1. From your workflow's *Definitions* page in the [Render Dashboard](https://dashboard.render.com), click any task or flow to open its *Runs* page.

2. Click *Run Task* / *Run Flow* in the top-right corner of the page:

   [image: Running a task in the Render Dashboard]

   A dialog appears for providing the run's input arguments:

   [image: Providing input arguments for a run in the Render Dashboard]

3. Provide the run's input arguments as a JSON array. Each array element maps to the task/flow function's corresponding positional argument.

   For example, you can provide `[5]` for a function that takes a single integer argument, or `[]` for a function that takes zero input arguments.

   You can click *Format* and *Validate* to cleanly structure your input and confirm that it's valid JSON.

4. Click *Run task* / *Run flow*.

   Your new run appears at the top of the *Runs* table.

**Tab: CLI (Tasks)**

#### Running tasks manually with the Render CLI

1. Make sure your development machine has version 2.35.0 or later of the Render CLI:

   ```shell
   render --version
   render version 2.35.0
   ```

   If it doesn't, install the latest version.

Use any of the following methods to install the Render CLI or upgrade to the latest version:

   **Subtab: Homebrew**

   ##### Homebrew installation

   Run the following commands:

   ```shell
   brew update
   brew install render
   ```

   **Subtab: WinGet**

   ##### WinGet installation

   Run the following command:

   ```shell
   winget install render.cli
   ```

   **Subtab: curl**

   ##### Installing with `curl`

   Run the following command:

   ```shell
   curl -fsSL https://raw.githubusercontent.com/render-oss/cli/refs/heads/main/bin/install.sh | sh
   ```

   **Subtab: Direct download**

   ##### Direct download

   1. Open the CLI's [GitHub releases page](https://github.com/render-oss/cli/releases/).
   2. Download the executable that corresponds to your system's architecture.

   If you use an architecture besides those provided, you can build from source instead.

   **Subtab: Build from source**

   ##### Building from source

> We recommend building from source only if no other installation method works for your system.

   1. [Install the Go programming language](https://golang.org/doc/install) if you haven't already.

   2. Clone and build the CLI project with the following commands:

      ```shell
      git clone git@github.com:render-oss/cli.git
      cd cli
      go build -o render
      ```

   After installation completes, open a new terminal tab and run `render` with no arguments to confirm.

2. Run the following command:

   ```shell
   render workflows tasks list
   ```

   The CLI opens an interactive menu of all workflow tasks in your workspace:

   [image: Listing tasks in the Render CLI]

3. Select a task and press *Enter*, then select the `run` command.

   The CLI prompts you to provide the task's input arguments as a JSON array:

   [image: Providing input arguments for a task run in the Render CLI]

4. Provide your desired arguments (or `[]` for a task that takes zero arguments) and press *Enter*. The CLI kicks off your task with a request to the Render API and begins tailing its logs.

   You can remain in this view to view live logs from your run.

5. Press *Esc* to navigate back up to the list of commands for your task. This time select the `runs` command.

   The CLI opens an interactive menu of the task's runs:

   [image: Viewing task runs in the Render CLI]

6. Select a run and press *Enter*, then select the `results` command.

   The CLI opens a view of the run's results:

   [image: Viewing task run details in the Render CLI]

**Tab: CLI (Flows)**

#### Running flows manually with the Render CLI

1. Make sure your development machine has version 2.35.0 or later of the Render CLI:

   ```shell
   render --version
   render version 2.35.0
   ```

   If it doesn't, install the latest version.

Use any of the following methods to install the Render CLI or upgrade to the latest version:

   **Subtab: Homebrew**

   ##### Homebrew installation

   Run the following commands:

   ```shell
   brew update
   brew install render
   ```

   **Subtab: WinGet**

   ##### WinGet installation

   Run the following command:

   ```shell
   winget install render.cli
   ```

   **Subtab: curl**

   ##### Installing with `curl`

   Run the following command:

   ```shell
   curl -fsSL https://raw.githubusercontent.com/render-oss/cli/refs/heads/main/bin/install.sh | sh
   ```

   **Subtab: Direct download**

   ##### Direct download

   1. Open the CLI's [GitHub releases page](https://github.com/render-oss/cli/releases/).
   2. Download the executable that corresponds to your system's architecture.

   If you use an architecture besides those provided, you can build from source instead.

   **Subtab: Build from source**

   ##### Building from source

> We recommend building from source only if no other installation method works for your system.

   1. [Install the Go programming language](https://golang.org/doc/install) if you haven't already.

   2. Clone and build the CLI project with the following commands:

      ```shell
      git clone git@github.com:render-oss/cli.git
      cd cli
      go build -o render
      ```

   After installation completes, open a new terminal tab and run `render` with no arguments to confirm.

2. List the flows available in your active workspace:

   ```shell
   render workflows flows list
   ```

   Note your flow's *Name* and *Workflow ID*. Then list workflows as JSON to obtain their slugs:

   ```shell
   render workflows list --output json
   ```

   Find the `workflow` object with the matching `id` and note its `slug`.

3. Start a run by providing the flow's slug and its input arguments as a JSON array:

   ```shell
   render workflows flows runs start my-workflow/format_message --input='["hello"]'
   ```

   - Replace `my-workflow/format_message` with `{workflow-slug}/{flow-name}`, using the workflow slug and flow name from the previous step.
   - For a flow that takes zero input arguments, provide `--input='[]'`.

   The CLI prints the new run's ID, which has the format `frn-abc123...`.

4. List runs for the flow:

   ```shell
   render workflows flows runs list --flow-slug my-workflow/format_message
   ```

5. Inspect a run's status and, after it finishes, its return value:

   ```shell
   render workflows flows runs show frn-abc123
   ```

   Replace `frn-abc123` with the ID of the run to inspect.

For all supported commands and options, see the [CLI reference](cli-reference#workflows-flows).

## Idempotency keys

You can provide an *idempotency key* when triggering a run with the Render [SDK](#running-with-the-render-sdk) or [API](#running-with-the-render-api). This helps prevent accidental creation of duplicate runs.

**Tab: TypeScript**

> Task idempotency keys were added in SDK v1.2.0.

Provide `idempotencyKey` in the options object for any of `startTask`, `runTask`, `startFlow`, or `runFlow`:

```typescript
const startedRun = await render.workflows.startTask(
  'my-workflow/calculateSquare',
  [2],
  { idempotencyKey: 'calculate-square-request-123' }
)
```

**Tab: Python**

> Task idempotency keys were added in SDK v1.2.0.

Provide the `idempotency_key` keyword parameter to any of `start_task`, `run_task`, `start_flow`, or `run_flow`. Both synchronous and asynchronous clients support this parameter:

```python
started_run = render.workflows.start_task(
    "my-workflow/calculate_square",
    [2],
    idempotency_key="calculate-square-request-123",
)
```

**Tab: REST API**

Include `idempotencyKey` in the JSON body of your request to the [Run task](https://api-docs.render.com/reference/createtask/) or [Run flow](https://api-docs.render.com/reference/startflowrun/) endpoint:

```json
{
  "task": "my-workflow/calculate_square",
  "input": [2],
  "idempotencyKey": "calculate-square-request-123"
}
```

Idempotency keys are useful when resubmitting a request after a timeout, or when handling an incoming event that might be delivered more than once.

### How idempotency works

If you include an idempotency key when triggering a run, your workflow service checks whether it has already created a run with the same idempotency key in the last 24 hours:

- If so, Render responds with the original run instead of creating a new one.
- If not, Render creates a new run and associates the idempotency key with it.

### Considerations

When creating and using idempotency keys, keep the following in mind:

- *Keys are scoped to a workflow service.*

    Two different workflow services in your workspace can use the same idempotency key independently.

- *The original task/flow and its inputs are always preserved.*

    If you provide the same idempotency key but specify a different task/flow or different arguments, Render still responds with the original run.

    This is true even if you originally ran a task and are now attempting to run a flow with the same idempotency key (or vice versa).

- *Idempotency is enforced for 24 hours from a run's creation.*

    Repeated trigger attempts do not extend this window. Completion, failure, or cancellation of a run does not shorten this window. After the window expires, reusing the key creates a new run, even if the original run is still executing.

- *Idempotency does not affect retry or replay logic.*

    Idempotency keys serve only to prevent duplicate creation of a root-level run:

    - A single task run might still execute multiple attempts according to its [retry configuration](/workflows-defining-tasks#retry-settings).

    - A single flow run might still replay if it's interrupted.

    Always design any side effects in your tasks and flows to handle repeated execution attempts.

---

##### Appendix: Glossary definitions

###### run chaining

Triggering a new *task run* directly from an in-progress run.

All runs in a chain belong to the same *workflow*.

Related article: https://render.com/docs/workflows-defining-tasks.md#chaining-task-runs

###### task

A function that executes on its own provisioned *instance*. Belongs to a *workflow*.

Each execution of a task is called a *run*.

Related article: https://render.com/docs/workflows-defining-tasks.md

###### flow

A function that executes a durable sequence of *steps* on its own *instance*. Belongs to a *workflow*.

If a flow's *run* is interrupted, it can reuse the results from completed steps when it replays.

Related article: https://render.com/docs/workflows-defining-flows.md

###### run

A single execution of a *task* or *flow*. Belongs to a *workflow*.

A run spins up in its own *instance*, executes, returns a value, and is deprovisioned.

Related article: https://render.com/docs/workflows-running.md