New-Sync-Operation handler function
New-Sync-Operation runs a synchronous Operation by invoking a Query, Signal, or Update, or executing other reliable code using the Temporal SDK Client. It is used on the handler side to define synchronous Nexus Operations.
Asynchronous Operation lifecycle steps
The asynchronous Operation lifecycle: (1) Caller Workflow executes a Nexus Operation. (2) Caller Worker issues a ScheduleNexusOperation command. (3) Caller Namespace records a NexusOperationScheduled event. (4) Caller Nexus Machinery sends the start request. (5) Handler Nexus Machinery sync-matches the request to a handler Worker. (6) Handler Worker receives a Nexus Task by polling the Endpoint's target Task Queue. (7) Handler processes the task using New-Workflow-Run-Operation. (8) Handler responds with the start Operation response. (9) Caller Namespace records a NexusOperationStarted event. (10) Handler Workflow completes and a Nexus Completion Callback is delivered to the caller's Nexus Machinery. (11) Caller Namespace records a Completed or Failed event. (12) Caller Worker polls for a Workflow Task. (13) Caller Workflow receives the result.
Multi-level Nexus calls and service composition
Nexus Operations can be composed across multiple services and teams. A handler Workflow can call another Nexus Operation, forming a chain like: Workflow A -> Nexus Op 1 -> Workflow B -> Nexus Op 2 -> Workflow C. Each step is a separate, durable Operation with its own retries and failure handling, enabling service composition across Namespaces without requiring direct connectivity or shared configuration between teams.
When to use Standalone Nexus Operations
Use a Standalone Nexus Operation if you just need to execute a single Nexus Operation across Namespace boundaries. If you need to orchestrate multiple Nexus Operations or have business logic between calls, call them from a Workflow instead.
Standalone Nexus Operation use cases
Standalone Nexus Operations let you make Nexus calls from anywhere using the Temporal SDK Client. Any code that can construct a Temporal Client can durably hand off a Nexus Operation to Temporal, such as a UI backend or BFF, a non-Temporal microservice, an HTTP handler, or a script. You still get automatic retries, built-in rate limiting and circuit breaking, and full execution visibility.
Standalone Nexus Operation key features
Standalone Nexus Operations support: executing any Nexus Operation as a top-level primitive without the overhead of a caller Workflow; same Service contract, Operation handlers, and Worker setup as Workflow-driven Operations; both synchronous and asynchronous (Workflow-backed) Nexus Operations; at-least-once execution with automatic retries by the Nexus Machinery; getting a handle to retrieve results, with the Operation token for asynchronous Operations; listing and counting Standalone Nexus Operation Executions using List Filter queries; executing the same Operation from a Workflow or standalone with no handler code changes.
Migrating from caller Workflow to Standalone Nexus Operation
A common pattern is a caller Workflow whose only purpose is to invoke a single Nexus Operation. When that is all the Workflow does, you can drop the caller Workflow entirely and call the Operation as a Standalone Nexus Operation. The handler Namespace's Service contract, Operation handlers, and Workers do not change — only the caller side does.
Considerations when migrating to Standalone Nexus Operations
When migrating from a caller Workflow: if the caller Workflow does more than one Operation call or has business logic between calls, keep the Workflow; Standalone Nexus Operations are only a fit when a single top-level Operation is all the Workflow did. Pick a stable Operation ID if you previously relied on Workflow ID reuse semantics for deduplication. Update Visibility queries that filtered by caller Workflow attributes.
Standalone vs Workflow-driven Nexus Operations
Standalone Nexus Operations execute a single Operation across Namespaces as a top-level primitive, while Workflows orchestrate multiple Nexus Operation steps. Standalone Nexus Operations use the same Service contract, Operation handlers, and Worker setup as Workflow-driven Operations — only the caller side differs.
Standalone Nexus Operations pre-release limitations
Standalone Nexus Operations are at Pre-release. The following features are not supported yet: Delete and reset; Batch --query support for cancel, terminate, and delete; UI list-page operator actions; HA / multi-region (global) namespaces using selective API forwarding.
Temporal CLI support for Standalone Nexus Operations
Standalone Nexus Operations require a Pre-release build of the Temporal CLI (v1.7.4-standalone-nexus-operations or later) that includes the temporal nexus operation command family. All commands are Experimental. The temporal nexus operation subcommand supports start, execute, result, list, count, describe, cancel, and terminate. The Nexus Endpoint must already exist on the server — create it with temporal operator nexus endpoint create.
Standalone Nexus Operations setup steps
To make your first standalone Nexus call: Create a caller Namespace where durable Nexus Operations will be started. Import the Temporal SDK in your language of choice. Execute a Nexus Operation through your caller Namespace using the Temporal SDK Client.
Temporal Cloud support for Standalone Nexus Operations
Standalone Nexus Operations in Temporal Cloud is available as a Pre-release feature.
Observability for Standalone Nexus Operations
You can use List Filters to query Standalone Nexus Operation Executions by Endpoint, Service, Operation, status, and other attributes using the SDK. CountNexusOperations returns the total number of Standalone Nexus Operation Executions matching a filter. This is the total count of executions (running, completed, failed, etc.) — not the number of queued tasks.
Non-retryable error hardcoding philosophy
If responsibility for an application is distributed across multiple maintainers, or if developing a library to integrate into somebody else's application, the decision to hardcode non-retryable errors follows a 'caller vs. implementer' dichotomy. Callers can make decisions about their Retry Policy, but only the implementer can decide whether an error should never be retryable out of the box.
Retry Policy definition and purpose
A Retry Policy is a collection of settings that tells Temporal how and when to try again after something fails in a Workflow Execution or Activity Task Execution. It is declarative—you specify the desired behavior and Temporal provides it, rather than implementing custom retry logic yourself.
Activity default retry behavior
Activities are associated with a Retry Policy by default. Temporal automatically retries a failed Activity with exponential backoff until it either succeeds or is canceled. When a subsequent request succeeds, the Workflow code resumes as if the failure never occurred.
Workflow execution retry not default
Unlike Activities, Workflow Executions do not retry by default. When a Workflow Execution is spawned, it is not associated with a default Retry Policy and thus does not retry by default.
Why workflows should not retry
Retrying an entire Workflow Execution is not recommended due to the deterministic nature of Workflow replay. Since Workflows replay the same sequence of events to reach the same state, retrying the whole Workflow would repeat the same logic without resolving the underlying issue that caused the failure. This repetition doesn't address problems related to external dependencies or unchanged conditions and can lead to unnecessary resource consumption and higher costs. Instead, retry failed Activities within the Workflow.
Retry Policy does not apply to Workflow Task Executions
Retry Policies do not apply to Workflow Task Executions, which retry until the Workflow Execution Timeout (which is unlimited by default) with an exponential backoff and a max interval of 10 minutes.
Activity Task Execution retry behavior places new task in queue
When an Activity Task Execution is retried, the Temporal Service places a new Activity Task into its respective Activity Task Queue, which results in a new Activity Task Execution.
Initial Interval property
Initial Interval specifies the amount of time that must elapse before the first retry occurs. The default value is 1 second. This is used as the base interval time for the Backoff Coefficient to multiply against.
Backoff Coefficient property
Backoff Coefficient dictates how much the retry interval increases. The default value is 2.0. A backoff coefficient of 1.0 means that the retry interval always equals the Initial Interval. Use this attribute to increase the interval between retries—a coefficient greater than 1.0 causes the first few retries to happen relatively quickly to overcome intermittent failures, but subsequent retries happen farther and farther apart to account for longer outages.
Maximum Interval property
Maximum Interval specifies the maximum interval between retries. The default value is 100 times the Initial Interval. This attribute is useful for Backoff Coefficients greater than 1.0 because it prevents the retry interval from growing infinitely.
Maximum Attempts property
Maximum Attempts specifies the maximum number of execution attempts that can be made in the presence of failures. The default is unlimited. If this limit is exceeded, the execution fails without retrying again and an error is returned. Setting the value to 0 also means unlimited. Setting the value to 1 means a single execution attempt and no retries. Setting the value to a negative integer results in an error when the execution is invoked.
Non-Retryable Errors property
Non-Retryable Errors specify errors that shouldn't be retried. By default, none are specified. Errors are matched against the type field of the Application Failure. If one of those errors occurs, a retry does not occur. Errors marked as non-retryable will not be retried, regardless of the Retry Policy.
Permanent vs transient and intermittent failures
There are three types of failures: transient, intermittent, and permanent. Transient and intermittent failures may resolve themselves upon retrying without further intervention, but permanent failures will not. Permanent failures, by definition, require changes to logic or input. It is better to surface permanent failures than to retry them.
Retry interval formula
The wait time before a retry is the retry interval. A retry interval is the smaller of two values: (1) the Initial Interval multiplied by the Backoff Coefficient raised to the power of the number of retries, or (2) the Maximum Interval.
Per-error next retry delay override
When an Activity or Workflow raises an Application Failure with the next Retry delay field set, this value will replace and override whatever the retry interval would be on the Retry Policy. Note that retries will still cap out under the Retry Policy's Maximum Attempts, as well as overall timeouts (Schedule-to-Close Timeout for Activities, Execution Timeout for Workflows).
Custom Retry Policy application scenarios
Only certain scenarios merit starting a Workflow Execution with a custom Retry Policy, such as: (1) a Temporal Cron Job or some other stateless, always-running Workflow Execution that can benefit from retries, or (2) a file-processing or media-encoding Workflow Execution that downloads files to a host.
Use non-retryable errors sparingly
Non-retryable errors should be used sparingly. They are appropriate when checking for bad input data—if the Activity cannot proceed with the input it has, that error should be surfaced immediately so that the input can be corrected on the next attempt. Errors marked as non-retryable in the error definition will always be non-retryable, even if the error type is not marked as non-retryable in the Activity's Retry Policy.
Search Attributes better for filtering than business logic
Search Attributes are most effective for search purposes or tasks requiring collection-based result sets. For business logic requiring high throughput or low latency, store and fetch data through Activities instead. Consider storing state in a local variable and exposing it with a Query, or storing state in an external datastore through Activities.
Continue-As-New with Worker Versioning
By default, a versioned Task Queue's Continue-as-New function starts the continued Workflow on the same compatible set as the original Workflow. If you continue-as-new onto a different Task Queue, the system doesn't assign any particular version. You also have the option to specify that the continued Workflow should start using the Task Queue's latest default version.
Main use case for Worker Versioning
The main reason to use Worker Versioning is to deploy incompatible changes to short-lived Workflows. On Task Queues using this feature, the Workflow starter doesn't have to know about the introduction of new versions. New code in newly deployed Workers executes new Workflow Executions, while only Workers with an appropriate version process old Workflow Executions.
Compatible version changes for bug fixes
You can implement compatible changes or prevent buggy code paths from executing on currently open Workflows by adding a new version to an existing set and defining it as compatible with an existing version. The new version processes existing Event Histories and must adhere to deterministic constraints. You may need to use versioning APIs to accomplish this.
Activity and Child Workflow versioning with incompatible changes
Worker Versioning lets you make incompatible changes to Activity Definitions in conjunction with incompatible changes to Workflow Definitions that use those Activities. Any Activity that a Workflow schedules on the same Task Queue gets dispatched by default only to Workers compatible with the Workflow that scheduled it. The same principle applies to Child Workflows. You can override the default behavior and run the Activity or Child Workflow on the latest default version.
Public-facing Workflow signature changes
Public-facing Workflows on a versioned Task Queue shouldn't change their signatures because doing so contradicts the purpose of Workflow-launching Clients remaining unaware of changes in the Workflow Definition. If you need to change a Workflow's signature, use a different Workflow Type or a completely new Task Queue.
Activity and Child Workflow on different Task Queue
If you schedule an Activity or a Child Workflow on a different Task Queue from the one the Workflow runs on, the system doesn't assign a specific version. This means if the target queue is versioned, they run on the latest default, and if it's unversioned, they operate as they would have without the Worker Versioning feature.
Migration from unversioned to versioned Task Queue
To migrate from an unversioned Task Queue, add a new default Build ID to the Task Queue, then deploy Workers with the same Build ID. Unversioned Workers will continue processing open Workflows, while Workers with the new Build ID will process new Workflow Executions.
Stopping Workflows by adding incompatible version without deployment
If you want to make sure that all Workflows currently being processed by a version stop processing, you can add a new version to the sets marked as compatible with the old version. New tasks will target the new version, but because you haven't deployed any Workers with that new version, they won't make any progress. This intentionally halts progress on those Workflows.
Serverless Worker Activity isolation pattern
By default, a single Worker may run multiple Activity slots, and resource exhaustion in one Activity can affect others on the same Worker. To isolate Activities from each other, split Workflow Workers and Activity Workers into separate Worker Deployments and set Activity slots to 1 per Worker. With single-slot configuration, each Activity gets a dedicated execution environment.
Task Queue constant definition - .NET example
In C# and .NET, define a Task Queue name constant in a constants class: public const string TaskQueueName = "translation-tasks";. Reference this constant as WorkflowConstants.TaskQueueName in both the workflow client code (in WorkflowOptions constructor taskQueue parameter) and worker configuration (in TemporalWorkerOptions constructor).
Task Queue naming best practice: use a shared constant
To ensure the Client and Worker always use the same Task Queue name, define the Task Queue name in a constant that is referenced by both the Client and Worker if possible. This prevents mismatches that would cause workflow execution to stall.
Three message types for Workflow interaction
Temporal supports three types of messages for Workflow interactions: Queries (read-only), Signals (asynchronous writes), and Updates (synchronous writes).
Signals characteristics
Signals are asynchronous write requests that cause changes in the running Workflow, but senders cannot await any response or error.
Updates characteristics
Updates are synchronous, tracked write requests where the sender can wait for a response on completion or an error on failure. Updates must be synchronous and must wait for the Worker running the Workflow to acknowledge the request.
Use Signals for asynchronous fire-and-forget messaging
Use Signals when clients want to quickly move on after sending an asynchronous message with no result or exception needed. Signals are appropriate when the Worker availability is not a concern and you do not want to limit the number of messages processed concurrently by a single Workflow.
Use Updates for synchronous write requests with validation
Use Updates when clients want to track the completion of the message, need a result or exception, want to validate the Update before accepting it into the Workflow and its history, have reasonable concurrent message limits, or want a low-latency end-to-end operation.
Use Queries for efficient read requests
Use Queries for read requests because they are efficient and never add entries to the Workflow Event History, unlike Updates. Queries can also operate on completed Workflows.
Alternative to Queries with blocking: polling versus Updates
When your goal is to read once the Workflow achieves a certain desired state and Queries cannot block, you have two options: poll periodically with Queries until the Workflow is ready, or write your read operation as an Update which will give better efficiency and latency but will write an entry to the Workflow Event History.
Strategy for asynchronous read/write requests
For asynchronous read/write requests, consider sending a Signal followed by polling with a Query.
Queries characteristics
Queries are read requests that can read the current state of the Workflow but cannot block in doing so. Queries never add entries to the Workflow Event History and can operate on completed Workflows.
Sending updates to child workflows uses activities
If you want to send an Update to another Workflow such as a Child Workflow from within a Workflow, you should do so within an Activity and use the Temporal Client as normal.
Signals can be sent from client, CLI, or workflow
Signals can be sent from any Temporal Client, the Temporal CLI, or from one Workflow to another.
Signal-With-Start lazy initialization
Signal-With-Start is used for lazy initialization of Workflows. If a running Workflow Execution with the given Workflow ID exists, it will be signaled. Otherwise, a new Workflow Execution starts and is immediately sent the Signal.
Updates are synchronous message passing
Updates can be sent from a Temporal Client or the Temporal CLI to a Workflow Execution. This call is synchronous and will call into the corresponding Update handler. If you want an asynchronous request, use Signals instead.
Update execution modes: Accepted vs Completed
When sending an Update in most languages (except Go), you can call executeUpdate to complete an Update and get its result. Alternatively, call startUpdate and pass in the Workflow Update Stage: Accepted waits until the Worker is contacted ensuring the Update is persisted, or Completed waits until the handler finishes and returns a result (equivalent to executeUpdate).
Update limits per Workflow Execution
There are limits on the total number of Updates that may occur during a Workflow Execution run, and also on the number of concurrent in-progress Updates that a Workflow Execution may have. Use Update Validators and Update IDs to stay within system limits in both Cloud and Self-Hosted environments.
Update-With-Start sends Update request with workflow start
Update-With-Start sends an Update request and starts a Workflow if necessary. A WorkflowIDConflictPolicy must be specified. Workflow ID and Update ID can be used as idempotency keys. If the Workflow exists and you provided an Update ID and the Update exists in the latest Workflow Run, Update-With-Start attaches to the existing Update regardless of WorkflowIDConflictPolicy. If the Workflow is closed, it attaches only if the Update has completed.
Update-With-Start lazy initialization pattern
Update-With-Start enables lazy initialization where a shopping cart can be modeled using the pattern. Updates let you add and remove items from the cart, and Update-With-Start lets the customer start shopping whether the cart already exists or they have just started shopping. Set WorkflowIDConflictPolicy to USE_EXISTING for this pattern.