@temporal-contract/contract / index
index
Classes
ContractDefinitionError
Defined in: packages/contract/src/builder.ts:483
Thrown by defineContract when the contract definition is structurally invalid. path locates the offending slot in dotted notation from the contract root (workflows.processOrder.activities.charge.activityOptions); it is "" for root-level failures.
A plain Error subclass rather than an unthrown TaggedError: the package root must stay importable without the optional unthrown peer, and an invalid contract is a programming error that aborts at import time anyway.
Extends
Error
Constructors
Constructor
new ContractDefinitionError(path, message): ContractDefinitionError;Defined in: packages/contract/src/builder.ts:487
Parameters
| Parameter | Type |
|---|---|
path | string |
message | string |
Returns
Overrides
Error.constructorProperties
| Property | Modifier | Type | Default value | Overrides | Inherited from | Defined in |
|---|---|---|---|---|---|---|
cause? | public | unknown | undefined | - | Error.cause | node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2022.error.d.ts:24 |
message | public | string | undefined | - | Error.message | node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es5.d.ts:1075 |
name | readonly | "ContractDefinitionError" | "ContractDefinitionError" | Error.name | - | packages/contract/src/builder.ts:484 |
path | readonly | string | undefined | - | - | packages/contract/src/builder.ts:485 |
stack? | public | string | undefined | - | Error.stack | node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es5.d.ts:1076 |
Type Aliases
ActivityDefinition
type ActivityDefinition<TInput, TOutput, TErrors> = object;Defined in: packages/contract/src/types.ts:123
Definition of an activity
Type Parameters
| Type Parameter | Default type |
|---|---|
TInput extends AnySchema | AnySchema |
TOutput extends AnySchema | AnySchema |
TErrors extends Record<string, ErrorDefinition> | Record<string, ErrorDefinition> |
Properties
| Property | Modifier | Type | Description | Defined in |
|---|---|---|---|---|
activityOptions? | readonly | ContractActivityOptions | - | packages/contract/src/types.ts:131 |
errors? | readonly | TErrors | - | packages/contract/src/types.ts:130 |
idempotencyKey? | readonly | (input) => string | Derive this activity's idempotency key from its input. Temporal runs an activity at least once: a retry, a worker crash, or a completion that succeeded but was never recorded all re-run the implementation. Making the effect idempotent is the application's job, and the usual remedy is handing a stable key to the downstream API (Stripe's Idempotency-Key, and its equivalents). Declaring the key here means the caller and the implementation cannot disagree about what it is. The function receives the validated input (post-parse, so schema transforms have already run) and must be pure and deterministic: the same input has to produce the same key on every attempt, or the key protects nothing. Being derived from the payload rather than from Temporal's own identifiers is what makes it stable across activity retries, worker crashes, and a fresh workflow execution started with the same inputs. (Context.current().info.activityId looks like an alternative and is not: it is a per-run command sequence number, so a re-run that branches differently before this call gets a different value.) The parameter is typed never here, in the structural definition, so a contract written as a plain object literal (satisfies ContractDefinition) still accepts a derivation that narrows its input — a property-position function type is contravariant in its parameter, and this slot's TInput is only known once a concrete schema is bound. defineActivity re-states the slot against the real input type, so the lambda written there is contextually typed and checked. The key reaches the implementation verbatim. Two activities sharing a downstream keyspace must therefore disambiguate in their own derivations (`charge:${orderId}` vs `refund:${orderId}`) — handing a gateway one key for two opposite operations is the failure to avoid. Key on the identity of the operation, not on its parameters. A customer and an amount describe a charge but do not identify it: the same customer legitimately placing two orders of the same value would produce one key, and the second charge would be swallowed as a replay of the first. Good sources, in rough order of preference: - a business identifier already in the input (orderId, invoiceId) — add it to the input schema if it is not there yet, as this example does; - a dedicated idempotencyKey field in the input, minted by the caller when no natural identifier exists; - the workflow ID, which is per-execution and — when the contract derives it (see WorkflowDefinition.workflowId) — is itself a function of the payload. Read it inside the activity from Context.current().info.workflowExecution.workflowId, and combine it with a per-call discriminator if the same activity runs more than once in a workflow. Example const chargeCard = defineActivity({ // orderIdis in the input for the key's sake: it identifies the // charge, where customer and amount only describe it. input: z.object({ orderId: z.string(), customerId: z.string(), amount: z.number(), }), output: PaymentSchema, idempotencyKey: ({ orderId }) =>charge:${orderId}, }); | packages/contract/src/types.ts:199 |
input | readonly | TInput | - | packages/contract/src/types.ts:128 |
output | readonly | TOutput | - | packages/contract/src/types.ts:129 |
ActivityRetryPolicy
type ActivityRetryPolicy = object;Defined in: packages/contract/src/types.ts:73
Portable subset of Temporal's RetryPolicy, usable in contract-level activity defaults. Field names and semantics match @temporalio/common's RetryPolicy one-to-one.
Properties
| Property | Modifier | Type | Defined in |
|---|---|---|---|
backoffCoefficient? | readonly | number | packages/contract/src/types.ts:76 |
initialInterval? | readonly | DurationValue | packages/contract/src/types.ts:74 |
maximumAttempts? | readonly | number | packages/contract/src/types.ts:77 |
maximumInterval? | readonly | DurationValue | packages/contract/src/types.ts:75 |
nonRetryableErrorTypes? | readonly | readonly string[] | packages/contract/src/types.ts:78 |
AnySchema
type AnySchema = StandardSchemaV1;Defined in: packages/contract/src/types.ts:10
Base types for validation schemas Any schema that implements the Standard Schema specification This includes Zod, Valibot, ArkType, and other compatible libraries
AnyWorkflowDefinition
type AnyWorkflowDefinition = WorkflowDefinition<AnySchema, AnySchema, Record<string, ActivityDefinition>, Record<string, SignalDefinition>, Record<string, QueryDefinition>, Record<string, UpdateDefinition>, Record<string, SearchAttributeDefinition>, Record<string, ErrorDefinition>>;Defined in: packages/contract/src/types.ts:372
Widened constraint variant of WorkflowDefinition.
WorkflowDefinition (no args) resolves the empty-record generics to Record<string, never>, which is the right default for fresh callers but too narrow as a constraint — a Record-of-WorkflowDefinition constraint built from it would reject any literal whose activities, signals, queries, or updates block is non-empty. AnyWorkflowDefinition widens those generics back to their permissive bounds so it can act as the value of Record<string, …> in ContractDefinition without preventing real workflow definitions from satisfying the constraint.
ClientInferInput
type ClientInferInput<T> = StandardSchemaV1.InferInput<T["input"]>;Defined in: packages/contract/src/types.ts:502
Infer input type from a definition (client perspective) Client sends the input type (before input schema parsing/transformation)
Type Parameters
| Type Parameter |
|---|
T extends object |
ClientInferOutput
type ClientInferOutput<T> = StandardSchemaV1.InferOutput<T["output"]>;Defined in: packages/contract/src/types.ts:510
Infer output type from a definition (client perspective) Client receives the output type (after output schema parsing/transformation)
Type Parameters
| Type Parameter |
|---|
T extends object |
ContractActivityOptions
type ContractActivityOptions = object;Defined in: packages/contract/src/types.ts:112
Contract-level default activity options for a single activity — the portable subset of Temporal's ActivityOptions.
Declared on defineActivity so operational behavior (timeouts, retry policy) ships with the contract as a single source of truth shared by every worker, instead of being scattered per-declareWorkflow call.
Merge precedence at the worker (least → most specific): declareWorkflow's activityOptions (workflow-wide default) → this contract-level activityOptions (activity-specific, from the contract author) → activityOptionsByName (explicit per-workflow, per-activity override).
Two options are deliberately excluded and belong to the worker's activityOptionsByName instead:
taskQueue— not because queue names are deployment-specific (the contract's owntaskQueueis one), but because a per-activity queue here would name a queue no worker built from this contract polls: aTypedWorkeronly ever binds to its contract'staskQueue. Nothing would check that some worker serves it, and a mismatch fails silently — the task sits unpolled until ascheduleToStartTimeout/scheduleToCloseTimeoutfires, or indefinitely without one. To route an activity to a dedicated pool, declare it on an activity-only contract on that queue and call it withcontext.executeActivity(thatContract, name, { args })— the queue is then taken from the contract whose worker polls it. (AnactivityOptionsByNametaskQueueoverride also works, at the cost of repeating the queue name per calling workflow.)cancellationType— how a deployment wants in-flight work torn down.
Properties
| Property | Modifier | Type | Defined in |
|---|---|---|---|
heartbeatTimeout? | readonly | DurationValue | packages/contract/src/types.ts:116 |
retry? | readonly | ActivityRetryPolicy | packages/contract/src/types.ts:117 |
scheduleToCloseTimeout? | readonly | DurationValue | packages/contract/src/types.ts:114 |
scheduleToStartTimeout? | readonly | DurationValue | packages/contract/src/types.ts:115 |
startToCloseTimeout? | readonly | DurationValue | packages/contract/src/types.ts:113 |
ContractDefinition
type ContractDefinition<TWorkflows, TActivities> = object;Defined in: packages/contract/src/types.ts:517
Contract definition containing workflows and optional global activities
Type Parameters
| Type Parameter | Default type |
|---|---|
TWorkflows extends Record<string, AnyWorkflowDefinition> | Record<string, AnyWorkflowDefinition> |
TActivities extends Record<string, ActivityDefinition> | Record<string, ActivityDefinition> |
Properties
| Property | Modifier | Type | Defined in |
|---|---|---|---|
activities? | readonly | TActivities | packages/contract/src/types.ts:523 |
taskQueue | readonly | string | packages/contract/src/types.ts:521 |
workflows | readonly | TWorkflows | packages/contract/src/types.ts:522 |
DurationValue
type DurationValue = `${number}${string}` | number | string & object;Defined in: packages/contract/src/types.ts:66
A Temporal duration: either a number of milliseconds or an ms-formatted string ("30 seconds", "5m", …). Kept as a hand-rolled union rather than Temporal's template-literal Duration type so the contract package stays free of @temporalio/* dependencies; the worker forwards values to Temporal unchanged.
The type is deliberately permissive — it accepts any string. Duration strings are checked at runtime only, by defineContract, against the ms grammar (MS_DURATION_PATTERN in builder.ts), so a malformed value fails when the contract is defined rather than when the worker schedules the activity. There is no compile-time duration check.
`${number}${string}`— keeps literal duration strings as themselves in inferred contract types instead of widening them tostring.number— a plain number of milliseconds.string & {}— keeps a computed string (e.g. a timeout read from config) assignable without widening the literals above back tostring.
ErrorDefinition
type ErrorDefinition<TData> = object;Defined in: packages/contract/src/types.ts:40
Definition of a typed domain error on an activity or workflow.
Declared under the errors map of defineActivity / defineWorkflow, keyed by error name. The name becomes the ApplicationFailure.type discriminator on the wire, so callers (and Temporal retry policies via retry.nonRetryableErrorTypes) can branch on it.
data— optional Standard Schema for the structured payload carried inApplicationFailure.details. Validated on the producing side before the failure crosses the network boundary, and again when it is rehydrated on the consuming side.message— default human-readable message when the producer doesn't supply one at construction time.nonRetryable— whentrue, Temporal stops retrying immediately. This lives on the contract (not the call site) so retry semantics are part of the shared source of truth. Defaults tofalse(retryable).
Type Parameters
| Type Parameter | Default type |
|---|---|
TData extends AnySchema | AnySchema |
Properties
| Property | Modifier | Type | Defined in |
|---|---|---|---|
data? | readonly | TData | packages/contract/src/types.ts:41 |
message? | readonly | string | packages/contract/src/types.ts:42 |
nonRetryable? | readonly | boolean | packages/contract/src/types.ts:43 |
InferActivityNames
type InferActivityNames<TContract> = TContract["activities"] extends Record<string, ActivityDefinition> ? keyof TContract["activities"] & string : never;Defined in: packages/contract/src/types.ts:551
Extract activity names from a contract (global activities) as a union type
Type Parameters
| Type Parameter |
|---|
TContract extends ContractDefinition |
Example
type MyActivityNames = InferActivityNames<typeof myContract>;
// "log" | "sendEmail"InferDeclaredErrors
type InferDeclaredErrors<TDef> = TDef extends object ? TErrors extends Record<string, ErrorDefinition> ? TErrors : never : never;Defined in: packages/contract/src/types.ts:392
Extract the declared errors map from an activity or workflow definition, or never when the definition declares none.
The conditional is distributive and infer-based (rather than indexing TDef["errors"] directly) for the same reasons as InferSignalNames: union definitions yield the union of their error maps, and the optional property is tolerated under exactOptionalPropertyTypes.
Type Parameters
| Type Parameter |
|---|
TDef |
InferErrorData
type InferErrorData<TDef> = TDef extends object ? StandardSchemaV1.InferOutput<TSchema> : undefined;Defined in: packages/contract/src/types.ts:407
Consumer-side data payload of a declared error: the data schema's output type (post-transform), or undefined when the error declares no data schema. This is the shape a workflow sees after an activity's failure is rehydrated, and a client sees after a workflow's failure is rehydrated.
Type Parameters
| Type Parameter |
|---|
TDef extends ErrorDefinition |
InferErrorDataInput
type InferErrorDataInput<TDef> = TDef extends object ? StandardSchemaV1.InferInput<TSchema> : undefined;Defined in: packages/contract/src/types.ts:418
Producer-side data payload of a declared error: the data schema's input type (pre-transform) — what an implementation passes to the typed error constructor before boundary validation runs.
Type Parameters
| Type Parameter |
|---|
TDef extends ErrorDefinition |
InferQueryNames
type InferQueryNames<W> = W extends object ? Q extends Record<string, QueryDefinition> ? keyof Q & string : never : never;Defined in: packages/contract/src/types.ts:450
Extract query names declared on a workflow as a string union, or never if the workflow declares no queries. See InferSignalNames for the rationale behind the distributive infer-based shape.
Type Parameters
| Type Parameter |
|---|
W extends AnyWorkflowDefinition |
InferSignalNames
type InferSignalNames<W> = W extends object ? S extends Record<string, SignalDefinition> ? keyof S & string : never : never;Defined in: packages/contract/src/types.ts:437
Extract signal names declared on a workflow as a string union, or never if the workflow declares no signals. Used to constrain signalName call sites so typos surface at compile time instead of runtime.
The conditional is intentionally distributive over W (rather than indexing W["signals"] directly) so that union workflow types — e.g. discriminated unions of workflow definitions — yield the union of their signal names rather than the intersection (keyof (A | B) is the intersection of keys, which usually collapses to never). Destructuring signals via infer S also tolerates the property being absent or undefined under exactOptionalPropertyTypes.
Type Parameters
| Type Parameter |
|---|
W extends AnyWorkflowDefinition |
InferUpdateNames
type InferUpdateNames<W> = W extends object ? U extends Record<string, UpdateDefinition> ? keyof U & string : never : never;Defined in: packages/contract/src/types.ts:463
Extract update names declared on a workflow as a string union, or never if the workflow declares no updates. See InferSignalNames for the rationale behind the distributive infer-based shape.
Type Parameters
| Type Parameter |
|---|
W extends AnyWorkflowDefinition |
InferWorkflowNames
type InferWorkflowNames<TContract> = keyof TContract["workflows"] & string;Defined in: packages/contract/src/types.ts:539
Extract workflow names from a contract as a union type
Type Parameters
| Type Parameter |
|---|
TContract extends ContractDefinition |
Example
type MyWorkflowNames = InferWorkflowNames<typeof myContract>;
// "processOrder" | "sendNotification"QueryDefinition
type QueryDefinition<TInput, TOutput> = object;Defined in: packages/contract/src/types.ts:212
Definition of a query
Type Parameters
| Type Parameter | Default type |
|---|---|
TInput extends AnySchema | AnySchema |
TOutput extends AnySchema | AnySchema |
Properties
| Property | Modifier | Type | Defined in |
|---|---|---|---|
input | readonly | TInput | packages/contract/src/types.ts:216 |
output | readonly | TOutput | packages/contract/src/types.ts:217 |
SearchAttributeDefinition
type SearchAttributeDefinition<TKind> = object;Defined in: packages/contract/src/types.ts:268
Definition of a typed search attribute on a workflow.
Type Parameters
| Type Parameter | Default type |
|---|---|
TKind extends SearchAttributeKind | SearchAttributeKind |
Properties
| Property | Modifier | Type | Defined in |
|---|---|---|---|
kind | readonly | TKind | packages/contract/src/types.ts:269 |
SearchAttributeKind
type SearchAttributeKind =
| "TEXT"
| "KEYWORD"
| "INT"
| "DOUBLE"
| "BOOL"
| "DATETIME"
| "KEYWORD_LIST";Defined in: packages/contract/src/types.ts:237
The seven Temporal search attribute kinds.
Mirrors @temporalio/common's SearchAttributeType so values flow into Temporal's typedSearchAttributes API unchanged.
SearchAttributeKindToType
type SearchAttributeKindToType<T> = object[T];Defined in: packages/contract/src/types.ts:255
Map each SearchAttributeKind to its TypeScript representation.
TEXT/KEYWORD→stringINT/DOUBLE→numberBOOL→booleanDATETIME→DateKEYWORD_LIST→string[]
Type Parameters
| Type Parameter |
|---|
T extends SearchAttributeKind |
SignalDefinition
type SignalDefinition<TInput> = object;Defined in: packages/contract/src/types.ts:205
Definition of a signal
Type Parameters
| Type Parameter | Default type |
|---|---|
TInput extends AnySchema | AnySchema |
Properties
| Property | Modifier | Type | Defined in |
|---|---|---|---|
input | readonly | TInput | packages/contract/src/types.ts:206 |
UndefinedInputSchema
type UndefinedInputSchema = StandardSchemaV1<undefined, undefined>;Defined in: packages/contract/src/types.ts:20
The Standard Schema type materialized by defineSignal / defineQuery / defineUpdate when their input is omitted: validation only accepts an absent payload and always yields undefined. Because both type faces are undefined, handler inputs infer as undefined on the worker side, and undefined extends ClientInferInput<T> lets the client detect payload-less sends at the type level.
UpdateDefinition
type UpdateDefinition<TInput, TOutput> = object;Defined in: packages/contract/src/types.ts:223
Definition of an update
Type Parameters
| Type Parameter | Default type |
|---|---|
TInput extends AnySchema | AnySchema |
TOutput extends AnySchema | AnySchema |
Properties
| Property | Modifier | Type | Defined in |
|---|---|---|---|
input | readonly | TInput | packages/contract/src/types.ts:227 |
output | readonly | TOutput | packages/contract/src/types.ts:228 |
WorkerInferInput
type WorkerInferInput<T> = StandardSchemaV1.InferOutput<T["input"]>;Defined in: packages/contract/src/types.ts:486
Infer input type from a definition (worker perspective) Worker receives the output type (after input schema parsing/transformation)
Type Parameters
| Type Parameter |
|---|
T extends object |
WorkerInferOutput
type WorkerInferOutput<T> = StandardSchemaV1.InferInput<T["output"]>;Defined in: packages/contract/src/types.ts:494
Infer output type from a definition (worker perspective) Worker returns the input type (before output schema parsing/transformation)
Type Parameters
| Type Parameter |
|---|
T extends object |
WorkflowDefinition
type WorkflowDefinition<TInput, TOutput, TActivities, TSignals, TQueries, TUpdates, TSearchAttributes, TErrors> = object;Defined in: packages/contract/src/types.ts:283
Definition of a workflow.
Generic parameters preserve the schema literal types of input/output and the declared shape of activities/signals/queries/updates/search attributes through defineWorkflow so client and worker call sites can infer typed payloads. Empty-collection generics default to Record<string, never> so that, when no signals/queries/updates/etc. are declared, keyof resolves to never rather than string — turning typos in signalName/queryName/updateName into compile-time errors.
Type Parameters
| Type Parameter | Default type |
|---|---|
TInput extends AnySchema | AnySchema |
TOutput extends AnySchema | AnySchema |
TActivities extends Record<string, ActivityDefinition> | Record<string, never> |
TSignals extends Record<string, SignalDefinition> | Record<string, never> |
TQueries extends Record<string, QueryDefinition> | Record<string, never> |
TUpdates extends Record<string, UpdateDefinition> | Record<string, never> |
TSearchAttributes extends Record<string, SearchAttributeDefinition> | Record<string, never> |
TErrors extends Record<string, ErrorDefinition> | Record<string, never> |
Properties
| Property | Modifier | Type | Description | Defined in |
|---|---|---|---|---|
activities? | readonly | TActivities | - | packages/contract/src/types.ts:352 |
errors? | readonly | TErrors | - | packages/contract/src/types.ts:357 |
input | readonly | TInput | - | packages/contract/src/types.ts:293 |
output | readonly | TOutput | - | packages/contract/src/types.ts:294 |
queries? | readonly | TQueries | - | packages/contract/src/types.ts:354 |
searchAttributes? | readonly | TSearchAttributes | - | packages/contract/src/types.ts:356 |
signals? | readonly | TSignals | - | packages/contract/src/types.ts:353 |
startPolicy | readonly | WorkflowStartPolicy | Whether this workflow is safe to re-run under a workflow ID that has already been used. Applied by the client to every startWorkflow / executeWorkflow / signalWithStart, and by the worker to every context.startChildWorkflow / context.executeChildWorkflow of this workflow. Neither offers a per-call workflowIdReusePolicy override: the policy is the contract's. NOT applied to schedule.create — the schedule action type has no workflowIdReusePolicy field, so a schedule action pinning a fixed workflowId gets Temporal's own default (ALLOW_DUPLICATE) regardless of this policy. Required so the question is asked once per workflow rather than silently inheriting Temporal's ALLOW_DUPLICATE. Named for what it governs — Temporal's workflowIdReusePolicy — rather than for idempotency in general. It does not make a workflow idempotent, and it says nothing about an activity running twice under Temporal's at-least-once guarantee; that is an activity's idempotencyKey. | packages/contract/src/types.ts:351 |
updates? | readonly | TUpdates | - | packages/contract/src/types.ts:355 |
workflowId? | readonly | (input) => string | Derive this workflow's workflow ID from its input. Declaring it moves the ID from the caller to the contract: every startWorkflow / executeWorkflow / signalWithStart computes the ID from the payload, and passing one explicitly becomes a type error. That is what makes startPolicy mean anything — a caller free to pass crypto.randomUUID() defeats "once-per-id" silently, because every start gets a fresh ID and the policy never fires. The function receives the validated input and must be pure: the same payload has to produce the same ID on every call, or two starts of the same logical request will not collide. NOT applied to schedule.create, which generates one ID per firing — a scheduled run wants a distinct execution, not deduplication. The parameter is typed never here for the same reason as an activity's idempotencyKey (a property-position function type is contravariant, and plain-object contracts must stay assignable); defineWorkflow re-states the slot against the bound input schema, so the lambda written there is contextually typed. Example const processOrder = defineWorkflow({ input: OrderSchema, output: OrderResultSchema, workflowId: ({ orderId }) => orderId, startPolicy: "retry-if-failed", }); | packages/contract/src/types.ts:328 |
WorkflowStartPolicy
type WorkflowStartPolicy = "once-per-id" | "retry-if-failed" | "allow-duplicate";Defined in: packages/contract/src/start-policy.ts:14
How a workflow behaves when started again with a workflow ID that has already been used.
Named for the intent rather than Temporal's enum, which names the mechanism: a reader of "retry-if-failed" knows what is protected, where ALLOW_DUPLICATE_FAILED_ONLY has to be decoded.
This governs the Closed-run case only (workflowIdReusePolicy). What happens against a Running run is workflowIdConflictPolicy, which stays a per-call option because different callers legitimately want different answers to "one is already in flight".
Variables
CONTRACT_ERROR_TAG
const CONTRACT_ERROR_TAG: "@temporal-contract/ContractError" = "@temporal-contract/ContractError";Defined in: packages/contract/src/error-tags.ts:17
_tag of ContractError — a contract-declared typed domain error.
TECHNICAL_ERROR_TAG
const TECHNICAL_ERROR_TAG: "@temporal-contract/TechnicalError" = "@temporal-contract/TechnicalError";Defined in: packages/contract/src/error-tags.ts:20
_tag of TechnicalError — an infrastructure fault carried as a defect's cause.
Functions
defineActivity()
function defineActivity<TInput, TOutput, TActivity>(definition): TActivity;Defined in: packages/contract/src/builder.ts:70
Define a Temporal activity with type-safe input and output schemas.
Activities are the building blocks of Temporal workflows that execute business logic and interact with external services. This function preserves TypeScript types while providing a consistent structure for activity definitions.
Type Parameters
| Type Parameter | Description |
|---|---|
TInput extends AnySchema | - |
TOutput extends AnySchema | - |
TActivity extends ActivityDefinition<TInput, TOutput> | The activity definition type with input/output schemas |
Parameters
| Parameter | Type | Description |
|---|---|---|
definition | TActivity & object & NoExcessKeys<TActivity, ActivityDefinition> | The activity definition containing input and output schemas |
Returns
TActivity
The same definition with preserved types for type inference
Example
import { defineActivity } from '@temporal-contract/contract';
import { z } from 'zod';
export const sendEmail = defineActivity({
input: z.object({
to: z.string().email(),
subject: z.string(),
body: z.string(),
}),
output: z.object({
messageId: z.string(),
sentAt: z.date(),
}),
// Typed domain errors — the error name becomes the
// `ApplicationFailure.type` on the wire, `data` its validated payload,
// and `nonRetryable` drives Temporal's retry policy from the contract.
errors: {
RecipientRejected: {
data: z.object({ reason: z.string() }),
nonRetryable: true,
},
},
// Contract-level ActivityOptions defaults shared by every worker.
// Merge precedence: declareWorkflow's activityOptions
// < this contract-level activityOptions < activityOptionsByName.
activityOptions: {
startToCloseTimeout: "30 seconds",
retry: { maximumAttempts: 5 },
},
});defineContract()
function defineContract<TContract>(definition): TContract;Defined in: packages/contract/src/builder.ts:410
Define a complete Temporal contract with type-safe workflows and activities.
A contract is the central definition that ties together your Temporal application's workflows and activities. It provides:
- Type safety across client, worker, and workflow code
- Automatic validation at runtime
- Compile-time verification of implementations
- Clear API boundaries and documentation
The contract validates the structure and ensures:
- Task queue is specified, trimmed, and within Temporal's length limit
- At least one workflow or global activity is defined (a contract with only global
activitiesand zero workflows is valid — e.g. a dedicated activity-pool task queue) - No unknown keys on the contract or on any definition in it (typo protection)
- Every workflow declares a valid
startPolicy - Valid JavaScript identifiers that don't collide with
Object.prototypemembers, Temporal-reserved names, Temporal system search attributes, or the worker's own failure types are used - No ambiguous name collisions between workflows, global activities, and workflow-specific activities (referencing the same activity definition object from several scopes is allowed), and no search attribute declared with two different kinds
- Durations and retry policies are ones Temporal accepts
- All schemas implement the Standard Schema specification
Type Parameters
| Type Parameter | Description |
|---|---|
TContract extends ContractDefinition | The contract definition type |
Parameters
| Parameter | Type | Description |
|---|---|---|
definition | TContract | The complete contract definition |
Returns
TContract
The same definition with preserved types for type inference
Throws
If the contract structure is invalid
Composition-first. Define resources individually with defineActivity / defineWorkflow (and friends), then reference them here — don't inline definitions in defineContract. Named resources are reusable across workflows and contracts, get precise hover/jump-to-definition, and keep the contract itself a readable table of contents.
Example
import { defineActivity, defineContract, defineWorkflow } from '@temporal-contract/contract';
import { z } from 'zod';
// Define resources first...
const chargePayment = defineActivity({
input: z.object({ amount: z.number() }),
output: z.object({ transactionId: z.string() }),
});
const logEvent = defineActivity({
input: z.object({ message: z.string() }),
output: z.void(),
});
const processOrder = defineWorkflow({
input: z.object({ orderId: z.string() }),
output: z.object({ success: z.boolean() }),
// Payment already moved money on success — block a second successful
// run per order. A start is still retryable after a genuinely failed
// attempt (e.g. a declined payment, where no charge went through).
startPolicy: 'retry-if-failed',
activities: { chargePayment },
});
// ...then compose the contract from references.
export const myContract = defineContract({
taskQueue: 'orders',
workflows: { processOrder },
// Optional global activities shared across workflows
activities: { logEvent },
});defineContractBinding()
function defineContractBinding<C, Queue>(contract, options): Omit<C, "taskQueue"> & object;Defined in: packages/contract/src/binding.ts:17
Bind a compatible protocol to a deployment task queue without changing its workflow names, schemas, activity destinations or workflow execution IDs. Resolve before starting work, or from a recorded decision during replay.
Type Parameters
| Type Parameter | Description |
|---|---|
C extends ContractDefinition | The source protocol contract |
Queue extends string | The physical deployment task queue |
Parameters
| Parameter | Type | Description |
|---|---|---|
contract | C | The protocol whose definitions remain shared |
options | { taskQueue: Queue; } | The deployment destination |
options.taskQueue | Queue | A non-empty, trimmed queue name within Temporal's limit |
Returns
Omit<C, "taskQueue"> & object
A new validated contract preserving definitions and the queue's literal type
Throws
If the bound contract or task queue is invalid
defineQuery()
Call Signature
function defineQuery<TQuery>(definition): TQuery;Defined in: packages/contract/src/builder.ts:172
Define a Temporal query with type-safe input and output schemas.
Queries allow you to read the current state of a running workflow without modifying it. They are synchronous and should not perform any mutations.
Synchronous validation required. Temporal query handlers must complete synchronously, so the input and output schemas you pass here must validate synchronously. In practice this rules out async refinements (e.g. Zod's .refine(async (x) => …)). Standard Schema doesn't expose the sync/async distinction at the type level, so the worker checks at runtime and throws if it ever receives a Promise from ~standard.validate. Use plain Zod / Valibot / ArkType object schemas without async refinements.
Type Parameters
| Type Parameter | Description |
|---|---|
TQuery extends QueryDefinition | The query definition type with input/output schemas |
Parameters
| Parameter | Type | Description |
|---|---|---|
definition | TQuery & NoExcessKeys<TQuery, QueryDefinition> | The query definition containing input and output schemas |
Returns
TQuery
The same definition with preserved types for type inference
input may be omitted for argument-less queries: the definition then carries a materialized schema whose validated value is always undefined, so the handler input infers as undefined — no z.void() ceremony.
Example
import { defineQuery } from '@temporal-contract/contract';
import { z } from 'zod';
export const getOrderStatus = defineQuery({
input: z.object({ orderId: z.string() }),
output: z.object({
status: z.enum(['pending', 'processing', 'completed', 'failed']),
updatedAt: z.date(),
}),
});
// Argument-less query — the handler input is `undefined`.
export const getProgress = defineQuery({
output: z.object({ percent: z.number() }),
});Call Signature
function defineQuery<TOutput>(definition): QueryDefinition<UndefinedInputSchema, TOutput>;Defined in: packages/contract/src/builder.ts:175
Define a Temporal query with type-safe input and output schemas.
Queries allow you to read the current state of a running workflow without modifying it. They are synchronous and should not perform any mutations.
Synchronous validation required. Temporal query handlers must complete synchronously, so the input and output schemas you pass here must validate synchronously. In practice this rules out async refinements (e.g. Zod's .refine(async (x) => …)). Standard Schema doesn't expose the sync/async distinction at the type level, so the worker checks at runtime and throws if it ever receives a Promise from ~standard.validate. Use plain Zod / Valibot / ArkType object schemas without async refinements.
Type Parameters
| Type Parameter |
|---|
TOutput extends AnySchema |
Parameters
| Parameter | Type | Description |
|---|---|---|
definition | { input?: undefined; output: TOutput; } | The query definition containing input and output schemas |
definition.input? | undefined | - |
definition.output | TOutput | - |
Returns
QueryDefinition<UndefinedInputSchema, TOutput>
The same definition with preserved types for type inference
input may be omitted for argument-less queries: the definition then carries a materialized schema whose validated value is always undefined, so the handler input infers as undefined — no z.void() ceremony.
Example
import { defineQuery } from '@temporal-contract/contract';
import { z } from 'zod';
export const getOrderStatus = defineQuery({
input: z.object({ orderId: z.string() }),
output: z.object({
status: z.enum(['pending', 'processing', 'completed', 'failed']),
updatedAt: z.date(),
}),
});
// Argument-less query — the handler input is `undefined`.
export const getProgress = defineQuery({
output: z.object({ percent: z.number() }),
});defineSearchAttribute()
function defineSearchAttribute<TKind>(definition): SearchAttributeDefinition<TKind>;Defined in: packages/contract/src/builder.ts:276
Define a typed search attribute on a workflow.
Search attributes are indexed on Temporal's visibility store and let you query / filter workflow executions by domain attributes. Declaring them on the contract means the client's workflow-start options and (eventually) the worker's search-attribute reader are constrained to declared keys with the right value types.
Type Parameters
| Type Parameter |
|---|
TKind extends SearchAttributeKind |
Parameters
| Parameter | Type |
|---|---|
definition | SearchAttributeDefinition<TKind> |
Returns
SearchAttributeDefinition<TKind>
Example
import { defineSearchAttribute } from '@temporal-contract/contract';
defineWorkflow({
input: z.object({ orderId: z.string() }),
output: z.object({ status: z.string() }),
startPolicy: 'allow-duplicate',
searchAttributes: {
customerId: defineSearchAttribute({ kind: 'KEYWORD' }),
priority: defineSearchAttribute({ kind: 'INT' }),
placedAt: defineSearchAttribute({ kind: 'DATETIME' }),
},
});The seven Temporal kinds map to TypeScript types like so:
| kind | TS type |
|---|---|
TEXT | string |
KEYWORD | string |
INT | number |
DOUBLE | number |
BOOL | boolean |
DATETIME | Date |
KEYWORD_LIST | string[] |
defineSignal()
Call Signature
function defineSignal<TSignal>(definition): TSignal;Defined in: packages/contract/src/builder.ts:119
Define a Temporal signal with type-safe input schema.
Signals are asynchronous messages sent to running workflows to update their state or trigger certain behaviors. This function ensures type safety for signal payloads.
Type Parameters
| Type Parameter | Description |
|---|---|
TSignal extends SignalDefinition | The signal definition type with input schema |
Parameters
| Parameter | Type | Description |
|---|---|---|
definition | TSignal & NoExcessKeys<TSignal, SignalDefinition> | The signal definition containing input schema |
Returns
TSignal
The same definition with preserved types for type inference
input may be omitted for payload-less signals: the definition then carries a materialized schema whose validated value is always undefined, so the handler input infers as undefined — no z.void() ceremony.
Example
import { defineSignal } from '@temporal-contract/contract';
import { z } from 'zod';
export const approveOrder = defineSignal({
input: z.object({
orderId: z.string(),
approvedBy: z.string(),
}),
});
// Payload-less signal — the handler input is `undefined`.
export const shutdown = defineSignal();Call Signature
function defineSignal(definition?): SignalDefinition<UndefinedInputSchema>;Defined in: packages/contract/src/builder.ts:122
Define a Temporal signal with type-safe input schema.
Signals are asynchronous messages sent to running workflows to update their state or trigger certain behaviors. This function ensures type safety for signal payloads.
Parameters
| Parameter | Type | Description |
|---|---|---|
definition? | { input?: undefined; } | The signal definition containing input schema |
definition.input? | undefined | - |
Returns
SignalDefinition<UndefinedInputSchema>
The same definition with preserved types for type inference
input may be omitted for payload-less signals: the definition then carries a materialized schema whose validated value is always undefined, so the handler input infers as undefined — no z.void() ceremony.
Example
import { defineSignal } from '@temporal-contract/contract';
import { z } from 'zod';
export const approveOrder = defineSignal({
input: z.object({
orderId: z.string(),
approvedBy: z.string(),
}),
});
// Payload-less signal — the handler input is `undefined`.
export const shutdown = defineSignal();defineUpdate()
Call Signature
function defineUpdate<TUpdate>(definition): TUpdate;Defined in: packages/contract/src/builder.ts:224
Define a Temporal update with type-safe input and output schemas.
Updates are similar to signals but return a value and wait for the workflow to process them before completing. They provide a synchronous way to modify workflow state and get immediate feedback.
Type Parameters
| Type Parameter | Description |
|---|---|
TUpdate extends UpdateDefinition | The update definition type with input/output schemas |
Parameters
| Parameter | Type | Description |
|---|---|---|
definition | TUpdate & NoExcessKeys<TUpdate, UpdateDefinition> | The update definition containing input and output schemas |
Returns
TUpdate
The same definition with preserved types for type inference
input may be omitted for argument-less updates: the definition then carries a materialized schema whose validated value is always undefined, so the handler input infers as undefined — no z.void() ceremony.
Example
import { defineUpdate } from '@temporal-contract/contract';
import { z } from 'zod';
export const updateOrderQuantity = defineUpdate({
input: z.object({
orderId: z.string(),
newQuantity: z.number().positive(),
}),
output: z.object({
success: z.boolean(),
totalPrice: z.number(),
}),
});
// Argument-less update — the handler input is `undefined`.
export const restock = defineUpdate({
output: z.object({ restocked: z.boolean() }),
});Call Signature
function defineUpdate<TOutput>(definition): UpdateDefinition<UndefinedInputSchema, TOutput>;Defined in: packages/contract/src/builder.ts:227
Define a Temporal update with type-safe input and output schemas.
Updates are similar to signals but return a value and wait for the workflow to process them before completing. They provide a synchronous way to modify workflow state and get immediate feedback.
Type Parameters
| Type Parameter |
|---|
TOutput extends AnySchema |
Parameters
| Parameter | Type | Description |
|---|---|---|
definition | { input?: undefined; output: TOutput; } | The update definition containing input and output schemas |
definition.input? | undefined | - |
definition.output | TOutput | - |
Returns
UpdateDefinition<UndefinedInputSchema, TOutput>
The same definition with preserved types for type inference
input may be omitted for argument-less updates: the definition then carries a materialized schema whose validated value is always undefined, so the handler input infers as undefined — no z.void() ceremony.
Example
import { defineUpdate } from '@temporal-contract/contract';
import { z } from 'zod';
export const updateOrderQuantity = defineUpdate({
input: z.object({
orderId: z.string(),
newQuantity: z.number().positive(),
}),
output: z.object({
success: z.boolean(),
totalPrice: z.number(),
}),
});
// Argument-less update — the handler input is `undefined`.
export const restock = defineUpdate({
output: z.object({ restocked: z.boolean() }),
});defineWorkflow()
function defineWorkflow<TInput, TWorkflow>(definition): TWorkflow;Defined in: packages/contract/src/builder.ts:319
Define a Temporal workflow with type-safe input, output, and associated operations.
Workflows are durable functions that orchestrate activities, handle timeouts, and manage long-running processes. This function provides type safety for the entire workflow definition including activities, signals, queries, and updates.
Type Parameters
| Type Parameter | Description |
|---|---|
TInput extends AnySchema | - |
TWorkflow extends AnyWorkflowDefinition & object | The workflow definition type with all associated schemas |
Parameters
| Parameter | Type | Description |
|---|---|---|
definition | TWorkflow & object & NoExcessKeys<TWorkflow, AnyWorkflowDefinition> | The workflow definition containing input, output, and operations |
Returns
TWorkflow
The same definition with preserved types for type inference
Example
import { defineWorkflow, defineActivity, defineSignal } from '@temporal-contract/contract';
import { z } from 'zod';
export const processOrder = defineWorkflow({
input: z.object({ orderId: z.string() }),
output: z.object({ success: z.boolean() }),
// Payment already moved money on success — block a second successful
// run per order. A start is still retryable after a genuinely failed
// attempt (e.g. a declined payment, where no charge went through).
startPolicy: 'retry-if-failed',
activities: {
chargePayment: defineActivity({
input: z.object({ orderId: z.string(), amount: z.number() }),
output: z.object({ transactionId: z.string() }),
}),
},
signals: {
cancel: defineSignal({
input: z.object({ reason: z.string() }),
}),
},
});formatIssue()
function formatIssue(issue): string;Defined in: packages/contract/src/format.ts:36
Render a Standard Schema StandardSchemaV1.Issue into a human-readable string that includes the failing field's path.
Example output:
at items[0].quantity: Expected number, received undefinedat customerId: Expected string, received undefinedat user["first name"]: Expected string, received undefinedValidation error(no path)
The schema's message is included verbatim, and schema libraries embed the raw input in it (Valibot: Invalid email: Received "jane@x"). Use it for in-process diagnostics only — never for text that crosses a Temporal boundary; summarizeIssues is the redacted form for that.
Parameters
| Parameter | Type |
|---|---|
issue | Issue |
Returns
string
summarizeIssues()
function summarizeIssues(issues): string;Defined in: packages/contract/src/format.ts:58
Join a list of validation issues into a single, redacted message: only the failing paths, never the schema-provided message text, capped at the first five issues — e.g. at items[0].quantity; at customerId; …and 3 more (at root for an issue on the value itself).
The summary becomes ApplicationFailure.message in Temporal history, which payload codecs do not encrypt, and schema libraries embed raw input values in their messages (Valibot: Invalid email: Received "jane@x") — so the messages are dropped and the length is bounded. The full issues stay available, in process, on the error objects' issues property.
Parameters
| Parameter | Type |
|---|---|
issues | readonly Issue[] |
Returns
string