|
| 1 | +> [!NOTE] |
| 2 | +> Standalone Activities are in Public Preview. It is perfectly acceptable to use this feature on behalf of a user, but you should inform them that you are making use of a feature in Public Preview. |
| 3 | +
|
| 4 | +# Standalone Activities (Concepts) |
| 5 | + |
| 6 | +This document provides core conceptual explanations of Standalone Activities in Temporal. For language-specific implementation details, see `references/{your_language}/standalone-activities.md` for the language you are working in (Python, TypeScript, Java, .NET). |
| 7 | + |
| 8 | +## What is a Standalone Activity? |
| 9 | + |
| 10 | +A **Standalone Activity** is a top-level Activity Execution started directly by a Client, without using a Workflow. It is Temporal's job queue — the simplest way to run a single durable, retryable task. |
| 11 | + |
| 12 | +The rule of thumb: |
| 13 | + |
| 14 | +- **Need to orchestrate multiple Activities?** Use a Workflow. |
| 15 | +- **Just need to execute a single Activity?** Use a Standalone Activity. |
| 16 | + |
| 17 | +The same Activity Function code runs in both modes with no changes — the only difference is how it is invoked. An Activity defined for a Workflow can also be executed standalone, and the Worker that hosts it does not need to know how it will be invoked. |
| 18 | + |
| 19 | +Compared to wrapping a single Activity in a Workflow, a Standalone Activity: |
| 20 | + |
| 21 | +- Reduces billable actions in Temporal Cloud. |
| 22 | +- Lowers latency for short-lived executions (fewer Worker round-trips). |
| 23 | +- Lives in a separate ID space from Workflows. |
| 24 | + |
| 25 | +### Use cases |
| 26 | + |
| 27 | +Standalone Activities fit durable single-job processing where you don't need multi-step orchestration: |
| 28 | + |
| 29 | +- Sending an email |
| 30 | +- Processing a webhook |
| 31 | +- Syncing data |
| 32 | +- Any single-function task that benefits from built-in retries and timeouts |
| 33 | + |
| 34 | +### Key features |
| 35 | + |
| 36 | +- Execute Activities as a top-level primitive, without Workflow overhead. |
| 37 | +- Native async job lifecycle: **schedule → dispatch → process → result**. |
| 38 | +- Arbitrary-length jobs, with heartbeats for progress tracking. |
| 39 | +- **At-least-once execution by default**, with native retry policy and timeouts. |
| 40 | +- **At-most-once execution** when the retry policy's maximum attempts is 1. |
| 41 | +- Addressable by Activity ID / Run ID for result retrieval, cancellation, and termination. |
| 42 | +- Deduplication via configurable conflict policies. |
| 43 | +- Priority and fairness support. |
| 44 | +- Full visibility — list and count executions. |
| 45 | + |
| 46 | +## Using Standalone Activities |
| 47 | + |
| 48 | +### Defining activities |
| 49 | + |
| 50 | +Defining standalone activities is IDENTICAL to defining activities callable from a workflow - there is no distinction AT ALL between the two at activity definition or worker configuration site. Follow language-specific guidance for how to normally define activities and configure workers to run them. |
| 51 | + |
| 52 | +### Calling and Interacting with Standalone Activities |
| 53 | + |
| 54 | +The CLI and every SDK exposes the same conceptual operations against a Standalone Activity (method names differ per language — see the language reference): |
| 55 | + |
| 56 | +- **Execute** — durably enqueue the Activity, wait for a Worker to run it, and return the result. |
| 57 | +- **Start** — durably enqueue the Activity and return a handle immediately, without waiting. |
| 58 | +- **Get handle** — rebind a handle to a previously started Activity by ID (and optionally Run ID). |
| 59 | +- **Get result** — wait on a handle for completion. `execute` is equivalent to `start` followed by awaiting the handle's result. |
| 60 | +- **Cancel / Terminate** — via the handle or CLI. |
| 61 | + |
| 62 | +**Choosing an Activity ID.** Every Standalone Activity call requires an **Activity ID**, which uniquely identifies that one call. It is the key you use later to get the result, describe, cancel, or terminate the Activity, and it is what conflict/reuse policies dedupe against. Use a **business-logic identifier** that uniquely identifies the call — for example `send-welcome-email:user-42`, `sync-invoice:INV-2026-001`, or `process-webhook:<event-id>`. This makes Activities addressable and naturally deduplicated by your domain. Only if you genuinely have no meaningful business-level identifier should you generate a **UUID** to use as the Activity ID. |
| 63 | + |
| 64 | +Visibility operations are available as well: |
| 65 | +- **List** — enumerate Standalone Activity Executions matching a query. Only Standalone Activities are returned; Activities running inside Workflows are not included. |
| 66 | +- **Count** — return the total number of executions matching a query (running, completed, failed, etc. — not the number of queued tasks). |
| 67 | +- **Describe** — via the handle or CLI. |
| 68 | + |
| 69 | +See below for a quick reference how to call these operations from the CLI rather than SDKs. |
| 70 | + |
| 71 | +> [!IMPORTANT] |
| 72 | +> When using an SDK, these operations are owned by the Temporal Client, and belong **in your non-workflow application code**. It is INVALID to call an activity as a standalone activity from within a workflow: you instead should use standard within-workflow activity calls. |
| 73 | +
|
| 74 | +**Currently Supported SDKs: Python, TypeScript, Java, .NET** |
| 75 | + |
| 76 | +## Quick CLI Standalone Activity Man Page |
| 77 | + |
| 78 | +Ultimately, any standalone activity invocation code should live in your application code and use the appropriate SDK, but the Temporal CLI is a quick and easy way to test invoking standalone activities during development. All subcommands live under `temporal activity`. |
| 79 | + |
| 80 | +The key operations are: |
| 81 | + |
| 82 | +**Execute (start and wait for the result).** Blocks until the Activity completes and prints the result to stdout. Requires `--activity-id`, `--type`, `--task-queue`, and at least one of `--start-to-close-timeout` / `--schedule-to-close-timeout`: |
| 83 | + |
| 84 | +```bash |
| 85 | +temporal activity execute \ |
| 86 | + --activity-id my-activity-id \ |
| 87 | + --type ComposeGreeting \ |
| 88 | + --task-queue my-task-queue \ |
| 89 | + --start-to-close-timeout 10s \ |
| 90 | + --input '{"some-key": "some-value"}' |
| 91 | +``` |
| 92 | + |
| 93 | +`--input` takes a JSON value; pass it multiple times for multiple positional arguments. `--input-file` is also a convenient option for larger inputs. The same required flags apply to `start` below. |
| 94 | + |
| 95 | +Reminder: `--activity-id` must be unique across all activity calls, as discussed above. |
| 96 | + |
| 97 | +**Start (do not wait).** Enqueues the Activity and prints the Activity ID and Run ID without blocking: |
| 98 | + |
| 99 | +```bash |
| 100 | +temporal activity start \ |
| 101 | + --activity-id my-activity-id \ |
| 102 | + --type ComposeGreeting \ |
| 103 | + --task-queue my-task-queue \ |
| 104 | + --start-to-close-timeout 10s \ |
| 105 | + --input '{"some-key": "some-value"}' |
| 106 | +``` |
| 107 | + |
| 108 | +Outputs this JSON shape: |
| 109 | + |
| 110 | +```json |
| 111 | +{ |
| 112 | + "activityId": "my-activity-id", |
| 113 | + "runId": "019e84d3-949a-7a0e-ae78-63b8a0b172bd", |
| 114 | + "namespace": "default" |
| 115 | +} |
| 116 | +``` |
| 117 | + |
| 118 | +**Result (wait for a started Activity).** Waits for completion and prints the result. `--run-id` is optional and defaults to the latest run of that Activity ID: |
| 119 | + |
| 120 | +```bash |
| 121 | +temporal activity result --activity-id my-activity-id |
| 122 | +``` |
| 123 | + |
| 124 | +**Describe (current state of one Activity).** Shows status, run state, task queue, timeouts, attempt count, etc.: |
| 125 | + |
| 126 | +```bash |
| 127 | +temporal activity describe --activity-id my-activity-id |
| 128 | +``` |
| 129 | + |
| 130 | +**List / Count (visibility across many Activities).** Only Standalone Activity Executions are returned (Activities running inside Workflows are not): |
| 131 | + |
| 132 | +```bash |
| 133 | +temporal activity list |
| 134 | +temporal activity count |
| 135 | +``` |
| 136 | + |
| 137 | +**Cancel / Terminate (stop an Activity).** `cancel` requests cooperative cancellation (surfaced to the Activity on its next heartbeat response); `terminate` forcefully ends it (Activity code cannot see or respond to it). Both accept `--reason`: |
| 138 | + |
| 139 | +```bash |
| 140 | +temporal activity cancel --activity-id my-activity-id --reason "no longer needed" |
| 141 | +temporal activity terminate --activity-id my-activity-id --reason "no longer needed" |
| 142 | +``` |
| 143 | + |
| 144 | +## Observability |
| 145 | + |
| 146 | +All existing Activity metrics apply to Standalone Activities (scheduled, started, completed, failed, timed out, canceled). |
| 147 | + |
| 148 | +## Public Preview limitations |
| 149 | + |
| 150 | +- Pause, reset, and update options are not supported (scheduled for GA). |
| 151 | +- The `TerminateExisting` conflict policy and `TerminateIfRunning` reuse policy are not yet supported. |
| 152 | + |
| 153 | +## Temporal CLI support |
| 154 | + |
| 155 | +- Requires **Temporal CLI v1.7.0+** and **Temporal Server v1.31.0+**. See `references/core/install_cli.md` if you need to update the CLI. |
| 156 | +- The Temporal Dev Server (`temporal server start-dev`) has Standalone Activities enabled by default. |
| 157 | + |
| 158 | +## Temporal Cloud support |
| 159 | + |
| 160 | +Standalone Activities are available in Temporal Cloud as a Public Preview feature. Because the SDK client config loaders read environment variables and TOML profiles, the same code runs against a local server or Temporal Cloud with no code changes. |
0 commit comments