Nexus Standalone Activity
Activity-backed Nexus Operations are pre-release and built on Nexus SDK V2.
TemporalOperationHandler is marked experimental in the SDKs and may change in backwards-incompatible ways.
A Nexus Operation can be backed by a Standalone Activity as well as a Workflow. Starting the Operation starts an Activity Execution that has no parent Workflow, and the Operation completes when that Activity returns.
Reach for this shape when the work behind an Operation is a single durable step rather than a process: call an external API, run a computation, write to another system.
Two things combine here, and it is worth separating them. An Activity gives that step automatic retries, timeouts, and a durable record of what happened. Exposing it as a Nexus Operation puts a typed contract and a Namespace boundary in front of it, so another team can call it without sharing your code, your deployment, or write access to your Namespace. Because the Activity carries the durability, the Operation needs no Workflow behind it, and uses fewer Billable Actions in Temporal Cloud than running the same single step through one.
When to use an Activity-backed Operation
The shape fits work that is one durable step sitting behind a team boundary. You get all of the following without building any of it yourself:
- Durability per call. Retries on the policy you set, timeouts you control, and a record of every attempt including the last error.
- Deduplicated starts. A stable Activity Id means a retried or redelivered request targets the same Activity Execution instead of starting a second one, so you do not need a separate store of processed Ids.
- Protection from a failing dependency. Repeated retryable failures trip the circuit breaker rather than piling retries onto a system that is already struggling.
- A boundary between teams. Callers get scoped access to named Operations, not write access to your Namespace, and they never see your Task Queues, your Workers, or your implementation.
- Room to change your mind. As long as the contract holds you can rewrite what runs behind an Operation, including replacing the Activity with a Workflow later, and no caller changes.
Callers are free either way: a caller Workflow can invoke the Operation as one step of a larger process, or a Client can start it directly as a Standalone Nexus Operation with no Workflow on either side.
Here are some example use cases.
Durable webhook and event processing without running a queue
A service ingests webhooks from third-party providers — payment events, repository pushes, delivery receipts, CRM change notifications. Each one needs to trigger a single durable task: update a downstream system, kick off a notification, index into search, sync into a warehouse.
Providers retry aggressively if you do not return 200 within seconds, so the receiver has to accept fast and do the work elsewhere.
The usual way to get there is to assemble it yourself: a queue, a consumer fleet, hand-written retry and dead-letter handling, and a separate store holding processed event Ids for deduplication.
That is a lot of infrastructure whose only job is to run one function reliably.
An Activity-backed Operation replaces the whole assembly.
The receiver starts the Operation and returns 200 immediately; Temporal owns delivery from that point.
The provider's own event Id becomes the Activity Id, so a redelivered webhook targets the same Activity Execution instead of starting a second one, and the deduplication store disappears.
Retries, backoff, and the record of every attempt come from the Activity.
When a downstream dependency starts failing, the circuit breaker trips rather than letting retries pile up against it.
The team that owns the receiver is usually not the team that owns the processing, and the Nexus Endpoint is where that boundary lands: scoped access to specific Operations rather than write access to a Namespace.
Sandboxing a tool call
An agent needs to call a tool — an MCP tool, an internal API, something that executes untrusted input. You want that call to run somewhere with its own credentials, its own network reach, and its own blast radius, not inside the process orchestrating the agent.
An Activity-backed Operation puts a Namespace boundary between the two. The tool runs on Workers in the Namespace that owns it, under that Namespace's credentials, and the caller only ever sees the Operation contract. The call is still durable and retried, and it is still traceable end to end, but the caller cannot reach past the contract into the environment the tool runs in. See Build AI applications with Temporal for how this fits alongside the rest of the agent stack.
A durable front door to another system
Any system you call can fail, time out, or be down when you need it — a third-party API, a legacy internal service, an unreliable piece of infrastructure. Wrapping that call in an Activity-backed Operation makes every call against it durable: retried on your policy, bounded by your timeouts, and recorded whether it succeeded or not.
Written once and published as a Nexus Service, it becomes a connector that every team calls instead of each writing its own integration. The team that owns it can fix a bug or change the implementation behind the contract, without a coordinated rollout across every consumer.
Related patterns
The same shape fits anything that is an external trigger, one durable step, and a team boundary.
- Asynchronous user actions from a backend-for-frontend. A user clicks "export my data" or "revoke my sessions"; the BFF starts the Operation and hands the client a handle to poll.
- Consumer offload. A Kafka or event-stream consumer starts an Operation, commits its offset, and lets Temporal own durability from there. The event Id is the deduplication key.
- Platform actions triggered by CI/CD. A pipeline step requests a compliance scan, a canary step, or a credential rotation. The platform team owns the handler; consumer teams get scoped Endpoint access.
- Scheduled platform tasks. A scheduler fires an Operation and a shared platform team's Workers run the task.
How it works
Use TemporalOperationHandler and call startActivity on the injected Nexus-aware Client.
The handler returns an async result carrying an activity-execution Operation token, and the server delivers the Activity's result to the caller through the Nexus completion callback when the Activity finishes.
Click the language tabs to see example code in each language - Go, Java, .Net, Python, and Typescript.
This is still a rough draft for feedback. Not all languages are filled in yet.
Code coming in next draft
@ServiceImpl(service = GreetingNexusService.class)
public class GreetingNexusServiceImpl {
@OperationImpl
public OperationHandler<GreetingInput, GreetingOutput> greet() {
return TemporalOperationHandler.create(
(context, client, input) ->
client.startActivity(
GreetingActivities.class,
GreetingActivities::greet,
input,
StartActivityOptions.newBuilder()
.setId("greet-" + context.getRequestId())
.setTaskQueue(HandlerWorker.TASK_QUEUE_NAME)
.setStartToCloseTimeout(Duration.ofSeconds(10))
.build()));
}
}
Code coming in next draft
Code coming in next draft
Code coming in next draft
You write the Activity the same way whichever side calls it. The same Activity Function can be executed by a Workflow and started behind a Nexus Operation with no code changes — nothing in its definition is Nexus-specific. What differs is how it is started, not what it is.
Code coming in next draft
@ActivityInterface
public interface GreetingActivities {
@ActivityMethod
GreetingOutput greet(GreetingInput input);
}
Code coming in next draft
Code coming in next draft
Code coming in next draft
Required options
StartActivityOptions requires two values that a Workflow-called Activity does not need.
- An Activity ID, unique within the Namespace. There is no parent Workflow to scope it.
- A Task Queue. It does not have to be the Task Queue the Nexus Endpoint targets, so the Activity can run on its own Worker fleet.
Deriving the ID from the Nexus request ID makes the start idempotent. The server retries a Nexus start request using the same request ID, so each retry targets the same Activity ID rather than starting a second Activity.
Setting setIdConflictPolicy(ACTIVITY_ID_CONFLICT_POLICY_USE_EXISTING) attaches to an already-running Activity with that ID instead of failing.
Combined with an ID derived from the Operation input rather than the request ID, this lets several Nexus Operations share one Activity Execution and all receive its result.
Register the Worker
Register the Activity implementations and the Nexus Service implementation on a Worker polling the Endpoint's target Task Queue. There is no Workflow implementation to register.
Code coming in next draft
Worker worker = factory.newWorker(TASK_QUEUE_NAME);
worker.registerActivitiesImplementations(new GreetingActivitiesImpl());
worker.registerNexusServiceImplementation(new GreetingNexusServiceImpl());
Code coming in next draft
Code coming in next draft
Code coming in next draft
Cancellation requires heartbeating
This is the biggest behavioral difference from a Workflow-backed Operation, and the easiest thing to get wrong.
A Workflow is interrupted by a cancellation request: a blocking call throws and, if the failure propagates, the Workflow and its Operation both end as cancelled. An Activity is not interrupted. The server records the cancellation request, and the Worker only learns about it on the next heartbeat.
So an Activity that never heartbeats runs until it completes or hits its start-to-close timeout, no matter how many cancellation requests the caller sends. For a long-running Activity-backed Operation to be cancellable at all:
- Heartbeat from the Activity, and let the resulting completion exception propagate.
- Set a heartbeat timeout so the server notices a Worker that has stopped heartbeating.
- Set maximum attempts to 1, or a cancelled attempt is retried and the Operation stays running instead of ending as cancelled.
Code coming in next draft
StartActivityOptions.newBuilder()
.setId("greeting-" + context.getRequestId())
.setTaskQueue(HandlerWorker.TASK_QUEUE_NAME)
.setStartToCloseTimeout(Duration.ofMinutes(10))
.setHeartbeatTimeout(Duration.ofSeconds(5))
.setRetryOptions(RetryOptions.newBuilder().setMaximumAttempts(1).build())
.build();
Code coming in next draft
Code coming in next draft
Code coming in next draft
For a short Activity that finishes well inside its timeout, none of this applies.
Choose between an Activity and a Workflow
Back an Operation with an Activity when the work is a single step: one external call, one computation, one notification. You get retries, timeouts, and a durable record of the step, without an Event History tracking orchestration you are not doing.
Back an Operation with a Workflow when the work has more than one step, needs to wait for something, needs to receive messages, or needs durable intermediate state. For example, an approval that blocks for a human decision is a Workflow, not an Activity.
Sample code: {code not yet live}
- Nexus SDK V2 for
TemporalOperationHandlerand the Nexus-aware Client. - Standalone Activity for the underlying concept, and Java: Standalone Activities for the SDK API.
- Standalone Nexus Operation for starting Operations without a caller Workflow.
- Development Walkthrough uses an Activity-backed Operation in context.