Nexus lets different teams share Temporal workflows across team, namespace, region, and cloud boundaries — without exposing internal implementation details.
Team A can call Team B's workflows like a typed API — with full durability, retries, and fault-tolerance built in — without needing access to Team B's namespace or knowing how their code works.
Each team works in their own Temporal namespace. There's no clean way for the Payments team to trigger an action in the Fraud Detection team's workflow without tight coupling or shared credentials.
Teams either share namespaces (messy) or build fragile HTTP bridges (unreliable).
The Fraud Detection team publishes a Nexus Service with a clear contract. The Payments team calls it from their workflow like any other operation.
Nexus handles routing, retries, security, and observability automatically — across any boundary.
fraud.v1/checkTransactionfraud.v1Async Nexus Operations can run for up to 60 days in Temporal Cloud. The caller workflow is suspended until the result arrives.
Nexus Machinery retries failed operations automatically. Pair with workflow ID policies to get exactly-once semantics.
mTLS encryption and namespace allowlists control who can call what. No API keys flying around in code.
Works across teams, namespaces, regions (AWS & GCP), and clouds — with the same developer experience everywhere.
Nexus has four key concepts. Understanding these unlocks everything else.
The address you publish and route through
Think of an Endpoint like a URL for your team's services. It's a named entry point registered in the Nexus Registry that routes requests to a specific namespace and task queue.
It's not a general HTTP proxy — it's specifically designed for Nexus, with built-in auth, retries, and observability.
The contract you publish for others to consume
A Service is a named collection of Nexus Operations — like an API interface. Multiple services can run in the same worker. Callers import the service definition to get type safety.
Example: fraud.v1 service exposes checkTransaction, flagUser, and getScore operations.
The individual action — sync or async
Synchronous operations complete in under 10 seconds. The result comes back in the same HTTP round-trip. Great for quick lookups, scoring, or validations.
Asynchronous operations start a Workflow and return an operation token. The caller workflow is suspended. When the handler workflow completes, a callback delivers the result.
The directory of all Endpoints in your account
Scoped to your Temporal Cloud account or self-hosted cluster. Teams register Endpoints here. The Registry is the source of truth for endpoint discovery, access control, and audit logging.
Walk through the step-by-step lifecycle of a Nexus Operation. Choose sync or async.
Nexus protects callers from handler failures through automatic retries, circuit breaking, and cancellation propagation.
If a handler returns 5 consecutive retryable errors, Nexus trips the circuit breaker for that caller↔endpoint pair. This prevents overloading a struggling handler with a flood of retries.
| Feature | Raw HTTP Call | Nexus Operation |
|---|---|---|
| Automatic retries on transient failure | Manual | Built-in |
| Caller workflow survives worker restart | No | Yes (durable) |
| Circuit breaking | Manual | Automatic |
| Cancel propagates to handler | No | Yes |
| Cross-namespace auth | DIY | mTLS + Allowlists |
| Bi-directional observability links | No | Yes |
| Max operation duration | Request timeout | Up to 60 days |
When the caller workflow is canceled, Nexus automatically propagates the cancel signal to any pending Nexus Operations. The handler workflow receives the cancel request and the caller gets a CanceledFailure result.
Note: Terminating a caller workflow abandons (does not cancel) pending operations.
Here's what using Nexus actually looks like in TypeScript. Three steps: define, implement, call.
The service definition is the contract. Both teams share this — usually as a package.
// shared/fraud-service.ts (Team B publishes this) import { defineNexusServiceContract, WorkflowHandle } from '@temporalio/nexus'; export interface CheckTransactionInput { transactionId: string; amount: number; userId: string; } export interface FraudCheckResult { approved: boolean; riskScore: number; reason?: string; } // This defines the "contract" both teams agree on export const fraudService = defineNexusServiceContract({ name: 'fraud.v1', operations: { // async: runs a full Workflow, can take hours checkTransaction: { input: WorkflowInput<CheckTransactionInput>, output: WorkflowHandle<FraudCheckResult>, }, // sync: quick score lookup, <10 seconds getQuickScore: { input: SyncInput<{ userId: string }>, output: SyncOutput<{ score: number }>, } } });
Team B implements the handler in their own worker — using their own workflows internally.
// fraud-team/worker.ts (Team B's implementation) import { NexusWorker, NewWorkflowRunOperation, NewSyncOperation } from '@temporalio/nexus'; import { fraudService } from '../shared/fraud-service'; const fraudHandler = fraudService.implement({ // Async: delegate to internal Workflow checkTransaction: NewWorkflowRunOperation({ async start(input, ctx) { const client = ctx.getClient(); // Team B starts their OWN workflow internally return await client.workflow.start(FraudCheckWorkflow, { args: [input], taskQueue: 'fraud-task-queue', workflowId: `fraud-${input.transactionId}`, }); } }), // Sync: fast lookup, no Workflow needed getQuickScore: NewSyncOperation({ async handler(input) { const score = await scoreCache.get(input.userId); return { score }; } }), }); // Run a Worker that polls the Endpoint's task queue const worker = await NexusWorker.create({ taskQueue: 'fraud-task-queue', nexusServices: [fraudHandler], });
Team A calls the Nexus Service from their Workflow — it looks just like calling any other activity.
// payments-team/workflow.ts (Team A's caller) import { proxyActivities, executeNexusOperation } from '@temporalio/workflow'; import { fraudService } from '../shared/fraud-service'; // shared package export async function PaymentWorkflow(payment: Payment): Promise<Result> { // 1. Call the Nexus Operation — fully durable, typed const fraudResult = await executeNexusOperation({ endpoint: 'fraud-detection-prod', // Nexus Endpoint name service: fraudService, operation: fraudService.operations.checkTransaction, input: { transactionId: payment.id, amount: payment.amount, userId: payment.userId, }, // Total duration caller will wait (up to 60 days) scheduleToCloseTimeout: '24h', }); // 2. Caller workflow was suspended during the check // Now it has the result — fully typed! if (!fraudResult.approved) { throw new Error(`Fraud detected: ${fraudResult.reason}`); } return processApprovedPayment(payment); }
The caller never imports Team B's internal code. If Team B changes their implementation — swapping FraudCheckWorkflow for a new ML model — Team A's code doesn't change at all.
Check what you've learned. Click an answer to see if you're right.