# Render Workflows — Rapidly spin up chains of long-running tasks on distributed compute.
> *Render Workflows is in public beta.*
>
> During the beta, bugs or changes in API/SDK behavior are possible as we continue refining the product. We welcome any and all feedback at *workflows-feedback@render.com*.
Use *Render Workflows* to rapidly distribute computational work across multiple independent instances:
Workflows are perfect for use cases that benefit from high-performance, distributed execution, such as AI agents, ETL pipelines, and data processing.
## How it works
1. Using the Render SDK, you can mark functions in your code as *tasks*.
**TypeScript**
Here's minimal TypeScript for defining a task named `calculateSquare`. First import `task` from the SDK, then call it with an options object and your task function:
```typescript:index.ts
import { task } from '@renderinc/sdk/workflows'
const calculateSquare = task(
{ name: 'calculateSquare' },
function calculateSquare(a: number): number {
return a * a
}
)
```
**Python**
Here's minimal Python for defining a task named `calculate_square`. First initialize a `Workflows` app, then apply the `@app.task` decorator:
```python:main.py
from render_sdk import Workflows
app = Workflows()
@app.task
def calculate_square(a: int) -> int:
return a * a
if __name__ == "__main__":
app.start()
```
2. In the Render Dashboard, you create a workflow service and link the repo containing your task definitions. Render automatically *registers* your defined tasks:
[image: Viewing registered tasks in the Render Dashboard]
3. You can now trigger *runs* of your registered tasks from anywhere (web apps, agents, etc.) using the Render SDK or API.
**TypeScript**
Here's minimal TypeScript for triggering a run of `calculateSquare` and passing the argument `2`:
```typescript:client_app.ts
import { Render } from '@renderinc/sdk'
const render = new Render()
const startedRun = await render.workflows.startTask(
'my-workflow/calculateSquare',
[2],
)
const finishedRun = await startedRun.get()
console.log(finishedRun.results)
```
**Python (async)**
Here's minimal async Python for triggering a run of `calculate_square` and passing the argument `2`:
```python:client_app.py
from render_sdk import RenderAsync
render = RenderAsync()
started_run = await render.workflows.start_task(
"my-workflow/calculate_square",
[2],
)
finished_run = await started_run
print(finished_run.results)
```
**Python (sync)**
Here's minimal synchronous Python for triggering a run of `calculate_square` and passing the argument `2`:
```python:client_app.py
from render_sdk import Render
render = Render()
finished_run = render.workflows.run_task(
"my-workflow/calculate_square",
[2],
)
print(finished_run.results)
```
4. Render spins up each triggered run in its own instance.
- This usually takes less than a second.
5. A run can trigger _additional_ runs simply by calling the corresponding task function. This is called *run chaining*.
**TypeScript**
Below, the `sumSquares` task chains two parallel runs of `calculateSquare`:
```typescript:index.ts
import { task } from '@renderinc/sdk/workflows'
const calculateSquare = task(
{ name: 'calculateSquare' },
function calculateSquare(a: number): number {
return a * a
}
)
const sumSquares = task(
{ name: 'sumSquares' },
async function sumSquares(a: number, b: number): Promise {
const [result1, result2] = await Promise.all([
calculateSquare(a),
calculateSquare(b)
])
return result1 + result2
}
)
```
**Python**
Below, the `sum_squares` task chains two parallel runs of `calculate_square`:
```python:main.py
from render_sdk import Workflows
import asyncio
app = Workflows()
@app.task
def calculate_square(a: int) -> int:
return a * a
@app.task
async def sum_squares(a: int, b: int) -> int:
result1, result2 = await asyncio.gather(
calculate_square(a),
calculate_square(b)
)
return result1 + result2
```
Runs execute alongside other Render service types, enabling fast and safe communication over your private network.
## Core features
| Feature | Description |
| --- | --- |
| *Automatic queuing and orchestration* | Render coordinates the entire task run lifecycle for you, from queuing to spin-up to deprovisioning. |
| *Long-running execution* | Each task run can execute for up to 24 hours. |
| *Configurable retry logic* | Define [retry behavior](/workflows-defining#retry-logic) for each task in the event of a failed run, with exponential backoff. |
| *Configurable timeout* | Specify a [timeout](/workflows-defining#timeout) for runs of each task, from 30 seconds to 24 hours. |
| *Configurable compute specs* | Specify which [instance type](/workflows-defining#instance-type-compute-specs) to use for runs of each task. |
| *Workflow-wide defaults* | Set the default retry logic, timeout, and instance type for all tasks in your workflow (and optionally override per task). |
| *Execution observability* | Track the progress and status of active and completed runs in the Render Dashboard. |
| *Outbound networking* | Runs can initiate network connections over both the public internet and your private network. Runs cannot receive _incoming_ network connections. |
| *Unified SDK* | Install a single lightweight SDK both to register tasks and to trigger runs from your code.
> *The Render SDK is currently available for [TypeScript](/workflows-sdk-typescript) and [Python](/workflows-sdk-python).* SDKs for additional languages are planned for future releases.
|
### Beta limitations
We'll address these limitations in future releases following beta:
- Workflows currently only support TypeScript and Python for defining tasks.
- SDKs for other languages are planned for future releases.
- Workflows do not provide built-in support for automatically triggering runs on a schedule.
- To schedule runs, you can create a [cron job](cronjobs) that runs your tasks on the desired schedule.
- If a workflow belongs to a [network-isolated environment](projects#blocking-cross-environment-traffic), its runs _cannot_ communicate with other services in that environment over its private network.
- Workflows do not yet support running tasks on [HIPAA-compliant](hipaa-compliance) hosts.
- To prevent accidental PHI exposure, it is not currently possible to create new workflows in a HIPAA-enabled workspace.
- If you enable HIPAA compliance for a workspace that already has workflows, *do not process PHI in your workflows.*
## Get started
Now that you know the basics, you're ready to [create your first workflow!](/workflows-tutorial)
## Billing
See [Limits and Pricing for Render Workflows](/workflows-limits).
## FAQ
###### How do I get started with Render Workflows?
Get started with [Your First Workflow](/workflows-tutorial).
###### Which languages can I use to define workflow tasks?
The Render SDK is available for [TypeScript](/workflows-sdk-typescript) and [Python](/workflows-sdk-python).
SDKs for additional languages are planned for future releases.
###### Can I trigger task runs without using the Render SDK?
*Yes.* For languages without Render SDK support, you can trigger runs by calling the Render API directly.
For details, see [Triggering Task Runs](/workflows-running).
###### Can my task runs receive incoming network connections?
*No.* Similar to background workers, task runs must initiate any required network connections.
---
##### Appendix: Glossary definitions
###### task
A function you can execute on its own compute as part of a *workflow*.
Each execution of a task is called a *run*.
Related article: https://render.com/docs/workflows-defining.md
###### run
A single execution of a workflow *task*.
A run spins up in its own *instance*, executes, returns a value, and is deprovisioned.
Related article: https://render.com/docs/workflows-running.md
###### instance
A virtual machine that runs your service's code on Render.
You can select from a range of *instance types* with different compute specs.
###### run chaining
Triggering a new *task run* by calling its function from an in-progress run.
All runs in a chain belong to the same *workflow*.
Related article: https://render.com/docs/workflows-defining.md
###### service type
When you deploy code on Render, you select a *service type* based on the capabilities you need.
For example, you create a *web service* to host a dynamic web app at a public URL.
Related article: https://render.com/docs/service-types.md
###### private network
Your Render services in the same *region* can reach each other without traversing the public internet, enabling faster and safer communication.
Related article: https://render.com/docs/private-network.md
###### background worker
Deploy this *service type* to continuously run code that does not receive incoming requests.
Ideal for processing jobs from a queue.
Related article: https://render.com/docs/background-workers.md