Standalone Activities Interactive Demo
Temporal SDK support for Standalone Activities is at Pre-release.
All APIs are experimental and may be subject to backwards-incompatible changes.
Standalone Activities let you execute an Activity directly from a Temporal Client — no Workflow required. The Activity is durably enqueued on the Temporal Server, executed by a Worker, and its result is returned to the caller.
Use the interactive demo below to explore the API, experiment with failure scenarios, and see the generated SDK code and CLI command update in real time.
Configure Activity
Failure Simulation
SDK Code
activityOptions := client.StartActivityOptions{
ID: "my-activity-id",
TaskQueue: "my-task-queue",
StartToCloseTimeout: 10 * time.Second,
}
handle, err := c.ExecuteActivity(ctx, activityOptions,
helloworld.Activity, "Hello", "World")
if err != nil {
log.Fatalln("Unable to execute activity", err)
}
var result string
err = handle.Get(ctx, &result)
// result: "Hello, World!"
CLI Command
temporal activity execute \
--type Activity \
--activity-id my-activity-id \
--task-queue my-task-queue \
--start-to-close-timeout 10s \
--input '"World"'
Execution Flow
Activity Log
How it works
When you call client.ExecuteActivity() (or the equivalent in your SDK), the following happens:
- Connect — Your application connects to the Temporal Server.
- Schedule — The Server durably persists the Activity execution on the specified Task Queue.
- Poll — A Worker polling that Task Queue picks up the Activity Task.
- Execute — The Worker runs your Activity function with the provided arguments.
- Return — The result is stored by the Server and returned to the original caller via the
ActivityHandle.
Because the Server durably persists the Activity, it survives Worker restarts and network interruptions. If the Activity fails, the Server automatically retries it according to the Retry Policy you configure.
Standalone vs Workflow Activities
| Workflow Activity | Standalone Activity | |
|---|---|---|
| Orchestrated by | A Workflow Definition | Your application code |
| Started with | workflow.ExecuteActivity() | client.ExecuteActivity() |
| Retry policy | Configured per ActivityOptions | Configured per StartActivityOptions |
| Visibility | Workflow Event History | Activity Visibility (List/Count) |
| Use case | Complex, multi-step orchestration | Simple, independent jobs |
The Activity function and Worker registration are identical for both — only the execution path differs.
Setup
1. Install the Temporal CLI
Download the Standalone Activity prerelease build of the Temporal CLI:
# macOS (Homebrew)
brew install temporal
# Or download directly from GitHub
# https://github.com/temporalio/cli/releases/tag/v1.6.2-standalone-activity
2. Start a local development server
temporal server start-dev
The server starts at localhost:7233 and the Web UI at
http://localhost:8233. Standalone Activities appear under the Activities
nav item in the Web UI.
3. Install the SDK
- Go
- Python
- .NET
Requires Go SDK v1.41.0 or higher.
go get go.temporal.io/sdk@latest
Clone the samples repository to follow along:
git clone https://github.com/temporalio/samples-go.git
cd samples-go
Requires Python SDK v1.23.0 or higher.
uv add temporalio
# or: pip install temporalio
Clone the samples repository to follow along:
git clone https://github.com/temporalio/samples-python.git
cd samples-python
Requires .NET SDK v1.12.0 or higher.
dotnet add package Temporalio
Clone the samples repository to follow along:
git clone https://github.com/temporalio/samples-dotnet.git
cd samples-dotnet
4. Run the Worker
The Worker registers the Activity and polls the Task Queue. Start it in a dedicated terminal:
- Go
- Python
- .NET
go run standalone-activity/helloworld/worker/main.go
uv run hello_standalone_activity/worker.py
dotnet run --project src/StandaloneActivity worker
5. Execute the Activity
In a separate terminal, run the starter:
- Go
- Python
- .NET
go run standalone-activity/helloworld/starter/main.go
uv run hello_standalone_activity/execute_activity.py
dotnet run --project src/StandaloneActivity execute-activity
Or use the Temporal CLI directly:
temporal activity execute \
--type Activity \
--activity-id my-activity-id \
--task-queue my-task-queue \
--start-to-close-timeout 10s \
--input '"World"'
Key API concepts
ExecuteActivity — start and wait
The primary call. Durably enqueues the Activity, waits for execution, and returns the result:
- Go
- Python
- .NET
handle, err := c.ExecuteActivity(ctx, client.StartActivityOptions{
ID: "my-activity-id",
TaskQueue: "my-task-queue",
StartToCloseTimeout: 10 * time.Second,
}, helloworld.Activity, "World")
var result string
err = handle.Get(ctx, &result)
result = await client.execute_activity(
compose_greeting,
args=[ComposeGreetingInput("Hello", "World")],
id="my-activity-id",
task_queue="my-task-queue",
start_to_close_timeout=timedelta(seconds=10),
)
var result = await client.ExecuteActivityAsync(
() => MyActivities.ComposeGreetingAsync(
new ComposeGreetingInput("Hello", "World")),
new("my-activity-id", "my-task-queue")
{
StartToCloseTimeout = TimeSpan.FromSeconds(10),
});
StartActivity — fire and forget
Enqueues the Activity without waiting for it to finish. Useful when you want to kick off long-running work and check back later:
- Go
- Python
- .NET
handle, err := c.ExecuteActivity(ctx, options, helloworld.Activity, "World")
// handle.Get() can be called later
handle = await client.start_activity(
compose_greeting,
args=[ComposeGreetingInput("Hello", "World")],
id="my-activity-id",
task_queue="my-task-queue",
start_to_close_timeout=timedelta(seconds=10),
)
# Later:
result = await handle.result()
var handle = await client.StartActivityAsync(
() => MyActivities.ComposeGreetingAsync(
new ComposeGreetingInput("Hello", "World")),
new("my-activity-id", "my-task-queue")
{
StartToCloseTimeout = TimeSpan.FromSeconds(10),
});
// Later:
var result = await handle.GetResultAsync();
List and Count Activities
- Go
- Python
- .NET
// List
resp, err := c.ListActivities(ctx, client.ListActivitiesOptions{
Query: "TaskQueue = 'my-task-queue'",
})
for info, err := range resp.Results { ... }
// Count
resp, err := c.CountActivities(ctx, client.CountActivitiesOptions{
Query: "TaskQueue = 'my-task-queue'",
})
log.Println("Total:", resp.Count)
# List
async for info in client.list_activities(
query="TaskQueue = 'my-task-queue'"
):
print(info.activity_id, info.status)
# Count
resp = await client.count_activities(
query="TaskQueue = 'my-task-queue'"
)
print("Total:", resp.count)
// List
await foreach (var info in client.ListActivitiesAsync(
"TaskQueue = 'my-task-queue'"))
{
Console.WriteLine($"{info.ActivityId}: {info.Status}");
}
// Count
var resp = await client.CountActivitiesAsync(
"TaskQueue = 'my-task-queue'");
Console.WriteLine($"Total: {resp.Count}");
The Query parameter uses the same List Filter syntax as Workflow Visibility.
Next steps
For complete API reference and advanced usage, see the SDK-specific guides: