@temporal-contract/contract / index
index
Type Aliases
ActivityDefinition
type ActivityDefinition<TInput, TOutput, TErrors> = object;Defined in: packages/contract/src/types.ts:127
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:135 |
errors? | readonly | TErrors | - | packages/contract/src/types.ts:134 |
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:203 |
input | readonly | TInput | - | packages/contract/src/types.ts:132 |
output | readonly | TOutput | - | packages/contract/src/types.ts:133 |
ActivityRetryPolicy
type ActivityRetryPolicy = object;Defined in: packages/contract/src/types.ts:90
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:93 |
initialInterval? | readonly | DurationValue | packages/contract/src/types.ts:91 |
maximumAttempts? | readonly | number | packages/contract/src/types.ts:94 |
maximumInterval? | readonly | DurationValue | packages/contract/src/types.ts:92 |
nonRetryableErrorTypes? | readonly | readonly string[] | packages/contract/src/types.ts:95 |
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:375
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:505
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:513
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:116
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).
Deployment-specific concerns (taskQueue routing, cancellation type) are deliberately excluded — those belong to the worker's activityOptionsByName, not the portable contract.
Properties
| Property | Modifier | Type | Defined in |
|---|---|---|---|
heartbeatTimeout? | readonly | DurationValue | packages/contract/src/types.ts:120 |
retry? | readonly | ActivityRetryPolicy | packages/contract/src/types.ts:121 |
scheduleToCloseTimeout? | readonly | DurationValue | packages/contract/src/types.ts:118 |
scheduleToStartTimeout? | readonly | DurationValue | packages/contract/src/types.ts:119 |
startToCloseTimeout? | readonly | DurationValue | packages/contract/src/types.ts:117 |
ContractDefinition
type ContractDefinition<TWorkflows, TActivities> = object;Defined in: packages/contract/src/types.ts:520
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:526 |
taskQueue | readonly | string | packages/contract/src/types.ts:524 |
workflows | readonly | TWorkflows | packages/contract/src/types.ts:525 |
DurationValue
type DurationValue = `${number}${string}` | number | string & object;Defined in: packages/contract/src/types.ts:83
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 union is three members instead of plain string | number so that literal duration strings survive inference into validate-contract.ts's compile-time CheckDuration, while every string the runtime accepts still type-checks:
`${number}${string}`— preserves every literal duration string as itself instead of widening it tostring, including a leading-dot literal like".5s"(the runtime regex,MS_DURATION_PATTERNinbuilder.ts, accepts the leading dot). One template-literal member anywhere in the union is enough to enable literal inference for every string literal candidate — a second, narrower template-literal member for the dot case specifically is not needed for inference and was removed (mutation-tested: deleting it, alone or together withCheckDuration's corresponding`.${number}${string}`branch, leaves the package green).CheckDuration's`.${number}${string}`branch (validate-contract.ts) is likewise not load-bearing today: removing it alone also leaves the package green, becauseIsMsDurationalready resolves a concrete literal like".5s"on its own — the branch only matters for the unresolved pattern caseIsExactlyguards against, which no currentDurationValueshape produces. SeeCheckDuration's doc comment for why the branch is kept anyway.number— a plain number of milliseconds.string & {}— deliberate, not a mistake: it is what keeps a computed string (e.g. a timeout read from config, which has no literal to preserve) accepting. Without it, any non-literalstringduration stops compiling — a regression the runtime does not have. Do not "simplify" this tostring, which would silently widen every literal above back tostringand defeat the whole point of this union.
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 |
IdempotencyMode
type IdempotencyMode = WorkflowStartPolicy;Defined in: packages/contract/src/idempotency.ts:59
Deprecated
Renamed to WorkflowStartPolicy, and the field that carries it from idempotency to startPolicy: it governs workflowIdReusePolicy — whether a workflow ID may be reused after a Closed run — and never made a workflow idempotent. For an activity running twice under Temporal's at-least-once guarantee, see an activity's idempotencyKey.
InferActivityNames
type InferActivityNames<TContract> = TContract["activities"] extends Record<string, ActivityDefinition> ? keyof TContract["activities"] & string : never;Defined in: packages/contract/src/types.ts:554
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:395
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:410
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:421
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:453
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:440
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:466
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:542
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:216
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:220 |
output | readonly | TOutput | packages/contract/src/types.ts:221 |
SearchAttributeDefinition
type SearchAttributeDefinition<TKind> = object;Defined in: packages/contract/src/types.ts:272
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:273 |
SearchAttributeKind
type SearchAttributeKind =
| "TEXT"
| "KEYWORD"
| "INT"
| "DOUBLE"
| "BOOL"
| "DATETIME"
| "KEYWORD_LIST";Defined in: packages/contract/src/types.ts:241
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:259
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:209
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:210 |
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:227
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:231 |
output | readonly | TOutput | packages/contract/src/types.ts:232 |
WorkerInferInput
type WorkerInferInput<T> = StandardSchemaV1.InferOutput<T["input"]>;Defined in: packages/contract/src/types.ts:489
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:497
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:287
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:355 |
errors? | readonly | TErrors | - | packages/contract/src/types.ts:360 |
input | readonly | TInput | - | packages/contract/src/types.ts:297 |
output | readonly | TOutput | - | packages/contract/src/types.ts:298 |
queries? | readonly | TQueries | - | packages/contract/src/types.ts:357 |
searchAttributes? | readonly | TSearchAttributes | - | packages/contract/src/types.ts:359 |
signals? | readonly | TSignals | - | packages/contract/src/types.ts:356 |
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; an explicit per-call workflowIdReusePolicy still wins. 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 mode. 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:354 |
updates? | readonly | TUpdates | - | packages/contract/src/types.ts:358 |
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:332 |
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:63
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 | 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:392
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
- 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 top-level keys (typo protection, like
activityOptions) - Valid JavaScript identifiers that don't collide with Temporal-reserved names 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)
- 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 },
});defineQuery()
Call Signature
function defineQuery<TQuery>(definition): TQuery;Defined in: packages/contract/src/builder.ts:163
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 | 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:164
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:263
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:112
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 | 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:113
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:213
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 | 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:214
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:306
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 | 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:40
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)
Path segments come either as bare PropertyKey values or as { key: PropertyKey } objects (per the spec); both are normalized.
- Numeric keys →
[N] - String keys that are valid JS identifiers → bare (first) or
.key - String keys that aren't valid identifiers →
["..."]with JSON-style escaping (handles dots, spaces, leading digits, the empty string, the literal string"0", embedded quotes, etc.) - Symbol / other
PropertyKey→[Symbol(name)]
Parameters
| Parameter | Type |
|---|---|
issue | Issue |
Returns
string
summarizeIssues()
function summarizeIssues(issues): string;Defined in: packages/contract/src/format.ts:66
Join a list of validation issues into a single message, with each issue rendered via formatIssue so field paths surface in the error text.
Parameters
| Parameter | Type |
|---|---|
issues | readonly Issue[] |
Returns
string