Skip to content

Workflow API

Define a workflow input and version Run or wait step functions Connect and control bindings and errors
Define a workflow input and version Run or wait step functions Connect and control bindings and errors
Choose the definition, step or integration API you need; the sections below retain their exact signatures and options.

Choose an API · Definition · Steps · Bindings · Retry and errors

This page lists the signatures, options, defaults and limits of the IdentityFlow 0.3.1 authoring packages. To build a workflow step by step, read Author workflows. Use this page to choose an API or to check an option. For the terms instance, step and binding, see Concepts.

I need to…UseRead next
Define a process and validate its inputdefineWorkflowDefinition shape
Call a service or record a calculationflow.doTasks
Reuse a task functiondefineTaskExtracted tasks
Wait for time to passflow.sleepTimers
Ask an eligible person for an answerflow.dialogDialogs
Poll an external job until it has a resultflow.requestRequests to external systems
Run another workflow and wait for its resultflow.startChild workflows
Run steps concurrently or impose a deadlineStandard Promise methodsPromise composition
Call an external system, and replace it in testsdefineBinding, flow.useBindings
Map accounts to current principalsdefineHooksHooks API
Decide who may start, view or operate workflowsdefineAuthorizationAccess control
Drive a workflow as different accounts in a testworkflowTestTesting API
Build my own UIIncoming GraphQL APIBuild your own UI
Configure or deploy the hostHost configuration and startupConfiguration, Install and run IdentityFlow

Import authoring functions from @identity-flow/sdk, which re-exports @identity-flow/api, and the supplied validator from @identity-flow/sdk/valibot. The GraphQL API your UI uses is separate from the SDK used inside a workflow. An outgoing GraphQL binding calls another service; it is not a client for starting or answering workflows in your UI.

The signatures on this page are TypeScript. If you come from Go:

  • Promise<T> is a value you await to get a T. Every step returns one; the workflow waits at the await.
  • A | B is a union: the value has type A or type B.
  • name?: T marks an optional field; it may be missing or undefined.
  • readonly marks a field or list you must not change.
  • Partial<T> is T with every field optional.
  • <T> after a name is a type parameter, like [T any] in Go. TypeScript infers it in most calls.
  • (context: C) => R is a function type that takes a C and returns an R.
  • MaybePromise<T> is T or Promise<T>: return the value directly or from an async function.

defineWorkflow(options, execute) returns ExportedWorkflowDefinition<Params, Result, Variables>. It describes a workflow; calling it does not start an instance. Make it the default export of its file: the host deploys only a workflow file’s default export, so one file holds one workflow. A definition that is only a named export is never deployed. Named exports next to it, such as a binding or the definition itself for a test to import, are fine.

execute is an async function receiving flow: Flow<Params, Variables>. flow.params has the output type of the input schema. The function’s returned value becomes the completed instance’s result. Most workflows need no explicit generic arguments. If you supply them, their order is Variables, Result, Params. DefineWorkflowOptions<Params> adds name and schema to WorkflowDefinitionOptions.

OptionTypeMeaning and constraint
namestring, requiredStable workflow name; 1–256 UTF-16 code units, valid Unicode, no C0/C1 controls.
schemaStandardSchema<Params>, optionalValidate start input before any step; invalid input refuses the start.
versionstring, optionalSet an explicit version such as 1.0.0. Without one, the version becomes 0.0.0+<bundle hash>. A deployed version is immutable.
draftboolean, default trueThe host skips a definition, with a warning, unless it sets draft: false. Workflow Tests run a definition either way.
releaseChannelstring, default latestAlias for selecting a version. It is separate from the version number.
label, description, changelogstring, optionalDisplay name, purpose and changes for this version.
defaultsStepConfig, optionalDefault retries and timeout for this workflow’s steps. Individual steps can override them. 0.3.1 stores timeout but does not enforce it; see Retry boundary.
metaPartial<WorkflowDefinitionMeta & SystemMeta>, optionalJSON metadata. Do not put secrets here.
codecsCodecs, optionalNamed custom serializers; see Data and schemas.

The options from Deploy a new version of a workflow, in one line:

{ name: 'onboarding', version: '1.1.0', draft: false, schema: Onboarding }
  1. stable workflow name
  2. raise on every change
  3. else the host skips it

This complete module loads a person, waits for a validated decision, and returns that decision. The named binding throws until you configure a directory client. The Workflow Test in Author workflows replaces it with a fixture, a stand-in for the binding that needs no network.

import * as v from '@identity-flow/sdk/valibot';
import { defineBinding, defineWorkflow } from '@identity-flow/sdk';
export const Directory = defineBinding('reference-directory', () => ({
lookup(_employeeId: string, _signal: AbortSignal): Promise<{ displayName: string }> {
throw new Error('Configure a directory client for this environment');
},
}));
export const approval = defineWorkflow(
{
name: 'reference-approval',
version: '1.0.0',
draft: false,
schema: v.object({ employeeId: v.string() }),
},
async (flow) => {
const person = await flow.do('load employee', { retries: 3 }, ({ use, signal }) =>
use(Directory).lookup(flow.params.employeeId, signal),
);
const answer = await flow.dialog(
'approve request',
{ schema: v.object({ approved: v.boolean() }) },
() => ({
params: { displayName: person.displayName },
assignees: [{ providerId: 'acme', subject: 'role:approver' }],
}),
);
return { employeeId: flow.params.employeeId, approved: answer.approved };
},
);
export default approval;

After changing behavior, increase version. A 0.3.1 host refuses a changed bundle under an already deployed version with WF_DEFINITION_BUNDLE_CHANGED. Running instances do not stay on the version they started with: each time an instance continues, IdentityFlow runs it with the newest deployed version in its release channel that satisfies ^<its current version>. Below 1.0.0 the range is narrower: ^0.3.0 does not include 0.4.0. A change that running instances cannot absorb, such as a stricter params schema or renamed steps, needs a new major version (for a 0.x version, a new minor version). Change the definition in place and raise version; running instances of older versions keep running from the copy the host stores in its database. See Versions and changed bundles and Deploy a new version of a workflow.

Flow<Params, Variables> is the context passed to execute. It is distinct from the driver called flow inside a Workflow Test.

MemberType or signaturePurpose and constraint
paramsreadonly ParamsValidated start input. Treat it as immutable.
varsPartial<Variables>Persisted JSON workflow variables. Assign deterministic values; a variable is not an external-service cache.
definitionreadonly WorkflowDefinitionDeployed definition, including id, name, version, releaseChannel, display metadata and resolved defaults.
instancereadonly ActiveWorkflowInstanceCurrent instance identity, workflow identity, params, recipients, metadata, variables, defaults and creation/update information.
use<T>(binding: Binding<T>) => TResolve a binding; see Bindings.
assert(condition: unknown, message: string) => asserts conditionThrow a non-retryable AssertionError when the condition is falsy. After the call, TypeScript treats the condition as true.
debug, log, info, warn, error, trace(...data: any[]) => voidRecord a workflow log event at the selected level. Payloads persist; omit credentials and sensitive data.
time, timeLog, timeEnd(label?: string) => voidStart, report or end a named diagnostic timer. Do not use these to decide workflow control flow.

Every step name must be 1–256 UTF-16 code units with valid Unicode and no C0/C1 control characters. Use a stable, unique name for each logical operation within a run. A loop can derive names from stable input data. Do not derive names from the current clock or randomness.

Step results must satisfy Serializable<T>; see Data and schemas. A recorded result is reused on replay. An external effect can still happen more than once if the process stops before recording its result. Give the receiver an idempotency key derived from stable workflow or business data. There is no idempotencyKey step option.

flow.do(name, task): Promise<T> and flow.do(name, options, task): Promise<T> execute a named task and persist its result. name identifies the step; task receives a TaskContext; options accepts TaskOptions, an alias for StepConfig.

The three arguments in the approval example:

flow.do('load employee', { retries: 3 }, ({ use, signal }) =>
  1. stable step name
  2. three attempts in total
  3. TaskContext fields

Adding schema to the options validates and optionally transforms the task’s returned result. For WithSchema<TaskOptions, Result, Intermediate>, the callback returns Intermediate and the Promise resolves to Result. It does not validate a separate callback input.

TaskContext fieldTypeUse
flowFlow<Params, Variables>Read workflow input and state.
stepActiveWorkflowStepInspect this step’s stable identity, name and attempt count.
useUseAPIResolve a binding for this callback.
signalAbortSignalPass to cancellable I/O and stop work when aborted. Cancellation is cooperative.
spanOpenTelemetry SpanAttach diagnostic attributes to this task’s span. Spans leave the host only when you export traces.

A Task<Result, Context> is a callback (context) => Result | PromiseLike<Result>. The approval example’s lookup call belongs inside this callback so a resume can use the recorded person instead of asking the directory again. See retrying a service call for failure handling.

defineTask(task): Task<Result, TaskContext<ActiveWorkflowStep, Params, Variables>> returns the same function with contextual types. It neither executes it nor assigns a durable step name. Call the returned function through flow.do(name, task). Explicit generic parameters are Result, Params, Variables.

The child workflow example defines check once and passes it to flow.do. Reusing it at several points requires different stable step names.

flow.sleep(name, until): Promise<void> persists a wait. flow.sleep(name, until, value): Promise<T> returns value when the wait ends. until accepts a Duration, a Date, or SleepOptions with required until and optional display message. A number is read as milliseconds. Duration strings such as '15 minutes' or '1 hour' are clearer. Durations must be non-negative safe integers after conversion. An absolute date must be at or after 2000-01-01.

Compute a deadline based on the current clock inside a task and reuse that recorded date. Use the deadline recipe to race a dialog against a timer. The wait is recorded in the database, so the program can stop and restart during it; the instance continues once the time has passed. In a Workflow Test a sleep takes real time, so keep it shorter than the flow.waitFor timeout.

flow.dialog(name, define): Promise<Result> or flow.dialog(name, options, define): Promise<Result> creates a dialog, a step that waits for a person’s answer, and resolves with that answer. The callback receives DialogContext, which extends TaskContext with token, until and optional message. token is an internal correlation value and does not authorize anyone to answer; clients answer a dialog by its ID with the GraphQL mutation completeDialog.

options is DialogOptions: retry and timeout settings, optional until, message and escalateAt. Add schema: StandardSchema<Result> to validate the answer and infer its result type. Without a schema or an explicit result type, the answer’s type is broad and nothing validates it.

completeDialog checks the answer against the schema before it records it. An answer that fails the schema is refused and nothing is recorded, so the dialog keeps waiting for a corrected answer. Over GraphQL the refusal is BAD_USER_INPUT with each rejected field in extensions.issues; a command caller gets a CommandInputError with the code WF_COMMAND_INPUT. The task form in IdentityFlow shows an issue at the field its path names; an issue without a path, such as a Valibot v.check on the whole answer, appears in the form’s list instead. To show such a check at a field, give its issue that field’s path, in Valibot with v.forward(v.check(…), ['reason']). The workflow validates the recorded answer again when it continues. An answer recorded without the check on submission, by 0.3.0 or earlier or because the check could not decide, can still fail there, and so can an answer that passed the check if the validation rules change before the workflow continues: the step becomes ERRORED with a ValidationError, and the instance follows if the workflow does not catch it.

The callback returns DialogInput<DialogParams>:

FieldTypeMeaning and constraint
paramsJSON object or null/undefined, required propertyInformation shown to the person. It is persisted, so keep it limited to the decision.
assigneesOne AssigneeRef or a non-empty arrayEach ref has providerId and subject. Any account with a matching current principal can answer.
assigneeSourceAssigneeSourceRef, optionalProvider, source key, JSON context and optional source version of an externally maintained group, resolved again when a change event arrives.
untilDuration or Date, optionalHard deadline. When it passes, the step fails with WF_TIMEOUT.
escalateAtDuration or Date, optionalMark an unanswered dialog as escalated without completing or failing it. It does not choose a new assignee.
messagestring, optionalText shown with the waiting dialog.

For a fixed role, list the role’s principal in assignees. Use assigneeSource only when the group is maintained outside IdentityFlow; see assignee sources.

assignees: [{ providerId: 'acme', subject: 'role:approver' }],
  1. registered provider ID
  2. compared with principals

An AssigneeRef is a PrincipalRef, { providerId, subject }. Provider IDs contain at most 128 UTF-16 code units, valid Unicode and no C0/C1 controls; __legacy_unscoped__ is reserved. Subjects are opaque strings, 1–512 UTF-16 code units. An assignee set is non-empty and limited to 1,024 refs. Equality uses both fields; subjects compare without regard to letter case. An account without a matching current principal is refused; hiding a button in your UI grants or removes nothing. See dialog with a role assignee.

flow.request(name, options, activity): Promise<Result> starts external work in activity and waits for its result; flow.request(name, activity) is the same without options. ActivityOptions adds until and message to StepConfig; adding schema validates the result. ActivityContext adds a token (nothing in 0.3.1 accepts it), until and optional message to TaskContext.

IdentityFlow 0.3.1 has no endpoint or GraphQL mutation that accepts a reply for a request token. A request step therefore completes only through poll, or fails with WF_TIMEOUT once until has passed. Set both.

import * as v from '@identity-flow/sdk/valibot';
import { type PollContext, defineBinding, defineWorkflow } from '@identity-flow/sdk';
export const Jobs = defineBinding('reference-jobs', () => ({
submit(_signal: AbortSignal): Promise<{ jobId: string }> {
throw new Error('Configure the job service for this environment');
},
status(_jobId: string, _signal: AbortSignal): Promise<{ reportUrl?: string }> {
throw new Error('Configure the job service for this environment');
},
}));
export const externalJob = defineWorkflow(
{ name: 'reference-external-job', version: '1.0.0', draft: false, schema: v.object({}) },
async (flow) =>
await flow.request(
'await job result',
{
until: '1 hour',
schema: v.object({ reportUrl: v.string() }),
poll: {
retries: '30 seconds',
callback: async ({ step, use, signal }: PollContext<{ jobId: string }>) => {
const job = await use(Jobs).status(step.data.jobId, signal);
if (!job.reportUrl) return { success: false } as const;
return { success: true, data: { reportUrl: job.reportUrl } } as const;
},
},
},
({ use, signal }) => use(Jobs).submit(signal),
),
);
export default externalJob;

The activity submits the job once and returns { jobId }, which is stored with the step. The poll callback reads it as step.data. It returns { success: false } to keep waiting or { success: true, data } to complete the step; data then passes through the result schema. The annotation PollContext<{ jobId: string }> names the type of step.data, and as const keeps success the literal true or false that the result type requires.

poll is a callback or PollOptions with callback and optional retries. Poll retries work like step retries: a duration string sets the delay between checks, a number limits the checks, and the default is { limit: false, delay: '1 minute', backoff: 'constant' }. When a finite limit is used up, polling stops and the step waits for until.

Workflow Tests do not run request steps. A test that reaches flow.request, also inside a child workflow, fails with WF_TEST_UNSUPPORTED_CALL.

flow.start(name, activity): Promise<Result> or flow.start(name, options, activity): Promise<Result> starts a child and waits for its result. options accepts ActivityOptions, optionally with a result schema. activity receives StartWorkflowContext with the task context and until, and returns StartWorkflowInput.

Child inputTypeMeaning
workflowWorkflowDefinitionKeyDefinition ID or name of the deployed child.
paramsSerializable child input, requiredValidated by the child’s input schema.
version, releaseChannelstring, optionalSelect the child version or channel. Use an exact version for an exact dependency.
label, descriptionstring, optionalDisplay information for the child instance.
recipient, recipientsSubject or one/many subjects, optionalBusiness recipients; these do not replace dialog assignees or grant access.
defaultsStepConfig, optionalChild instance step defaults.
untilDuration or Date, optionalChild instance deadline; when it passes, the step fails with WF_TIMEOUT.
metaJSON metadata, optionalCorrelation and other instance metadata.

The child and the parent are two workflows, so they live in two files. The child, reference-child.workflow.ts:

import * as v from '@identity-flow/sdk/valibot';
import { defineTask, defineWorkflow } from '@identity-flow/sdk';
const check = defineTask(() => ({ checked: true }));
export const child = defineWorkflow(
{ name: 'reference-child', version: '1.0.0', draft: false, schema: v.object({}) },
async (flow) => await flow.do('check request', check),
);
export default child;

The parent, in its own file, starts the child by name and version:

import * as v from '@identity-flow/sdk/valibot';
import { defineWorkflow } from '@identity-flow/sdk';
export const parent = defineWorkflow(
{ name: 'reference-parent', version: '1.0.0', draft: false, schema: v.object({}) },
async (flow) =>
await flow.start('run check', { schema: v.object({ checked: v.boolean() }) }, () => ({
workflow: 'reference-child',
version: '1.0.0',
params: {},
})),
);
export default parent;

Both definitions must be deployed. The parent result is the child’s result, validated by the optional parent step schema. A refusal the child returns as data is a normal result; an uncaught error in the child is a failure. For testing a parent and its child, see Child workflows in the Testing API.

defineBinding(name, factory): NamedBinding<Value, Name> gives a zero-argument Binding<Value> factory a stable name. flow.use(binding): Value or context.use(binding): Value resolves it on first use. Define each binding once at module level and reuse that value: a binding created inside execute is a new factory on every run and bypasses the cache. Call the service inside a step, even if you resolve the binding outside it.

Names are non-blank, trimmed, valid Unicode without C0/C1 controls, at most 120 Unicode code points. The returned factory exposes readonly bindingName. The older defineBinding(factory) overload returns its argument unchanged; use the named overload, because Workflow Test fixtures find a binding by name. Do not persist clients, sockets or credentials as step results or variables.

createGraphQLBinding(config?: RequestConfig): Binding<GraphQLClient> is exported by @identity-flow/binding-graphql and @identity-flow/sdk/bindings/graphql. client.request<Result>(document, variables?, requestHeaders?): Promise<Result> sends a query or mutation document with its variable object and optional per-call headers. The document may be a string or a typed document node. The object overload accepts { document, variables?, requestHeaders?, signal? }; typed documents can make variables required. A result generic types the reply but does not validate a service response at runtime.

gql(chunks: TemplateStringsArray, ...variables: unknown[]): string is a template tag that builds a document string without executing or schema-validating it. client.gql is the same tag as the top-level gql export.

RequestConfig fieldDefault or behavior
url?: stringGRAPHQL_ENDPOINT, then http://localhost:4000/-/graphql. This is an outgoing-service default, not the host’s GraphQL API.
headers?: MaybeLazy<HeadersInit>Static headers or a zero-argument function returning headers. Read credentials at the service boundary.
fetch?: FetchCustom fetch implementation with the standard fetch signature.
method?: HTTPMethodInputPOST; also accepts GET and lowercase variants.
errorPolicy?: ErrorPolicynone throws on GraphQL errors; ignore ignores them; all preserves errors and data.
excludeOperationName?: booleanDefaults to false; skip extraction only when appropriate for the service.
Other RequestInit fieldsStandard request options, excluding the redefined headers and method.

Wrap the client in a named domain binding, as in the complete GraphQL recipe. In a Workflow Test, register a fixture for the domain binding before starting; the test then never calls the real service.

StandardSchema<Out, In> is a vendor-neutral validation contract. The input schema determines flow.params; a step result schema determines the value after await. ValidationIssue is the validator’s issue shape. WithSchema<Options, Out, In> means options with a required schema. JSONStandardSchema<Out, In> restricts the values to JSON, as required by hooks.

The supplied Valibot export supports v.object, v.string, v.number, v.boolean, v.picklist, v.optional, v.pipe and validation rules. See the complete start-form recipe for labels, choices, date validation and numeric bounds. Type inference cannot replace runtime validation of input from a person or service.

Serializable<T> covers persisted step/instance values, including ordinary data, Date, errors, typed buffers, maps and sets whose members are serializable. JSONValue, JSONObject and JSONArray are the narrower types for variables, metadata and dialog display data. Do not infer that an arbitrary class, function or service client is safe to persist because TypeScript accepts a structural type.

Codec<U> has encode(value: unknown): false | U and decode(data: U): unknown. Codecs is a readonly name-to-codec map. Returning false from encode selects built-in serialization. Codecs must reconstruct their values across replay; definition codecs augment engine codecs. This is an advanced serialization hook, not a way to preserve a live resource between steps.

deriveInputContract(schema?: StandardSchema | null): JSONValue | null derives a start-form description from Valibot. Other schema vendors still validate, but in 0.3.1 they return null here. INPUT_CONTRACT_META_KEY is 'inputContract'. defineWorkflow derives it automatically. A missing description means input is not described; it does not mean there is no input or that validation is disabled.

@identity-flow/sdk/schemas and @identity-flow/api/schemas expose Valibot schemas for SDK boundaries. parse(schema, input, config?) returns the inferred output or throws ValidationError; assert(schema, input, config?) narrows the input or throws. The optional config carries Valibot parsing settings and optional workflow/step error context. Use your own Standard Schema for business input. Reusable public validators include PrincipalRefSchema, AssigneeRefSchema, ProviderIdSchema, PrincipalSubjectSchema, DurationSchema, DurationStringSchema, UntilSchema, StepConfigSchema, JSONSchema and JSONObjectSchema. Duration schemas produce milliseconds and UntilSchema produces a Date; they are not identity transforms.

UUIDv7Base64URL is a branded, 22-character unpadded base64url ID: a string type that only a validated value can have. isUUIDv7Base64URL(value: unknown): value is UUIDv7Base64URL validates its version, variant and canonical encoding, and tells TypeScript that value has that type when it returns true. AccountId, WorkflowDefinitionId, WorkflowInstanceId, WorkflowStepId and ActivityToken use that form. WorkflowDefinitionName, Subject, ProviderId and WorkflowStepKey are string aliases with their own boundary constraints. Do not cast an arbitrary string to an ID to bypass validation.

These exports are Valibot schema values, not functions to call directly. Pass one to parse(schema, input, config?) to validate and obtain the result below, or compose it in a Valibot schema. Invalid input produces ValidationError. They do not grant authority or apply engine configuration defaults.

Schema exportInput and returned outputConstraint
ProviderIdSchemaProvider string, unchanged1–128 UTF-16 code units, valid Unicode, no C0/C1 controls; the legacy unscoped ID is reserved.
PrincipalSubjectSchemaSubject string, unchangedNon-empty, at most 512 UTF-16 code units; interpretation belongs to the provider.
PrincipalRefSchema, AssigneeRefSchemaObject containing provider ID and subject; returns those validated fieldsBoth fields must pass the validators above. One ref does not establish current account membership.
JSONSchemaJSON primitive, array or object; returns validated JSONRecursive values only; no functions, undefined or live clients.
JSONObjectSchemaString-keyed object; returns validated objectEvery value must pass JSONSchema.
DurationStringSchemaHuman-readable duration string; returns millisecondsNon-negative safe integer after conversion.
DurationSchemaDuration string or number by type; returns millisecondsA number is read as milliseconds, 0 included.
UntilSchemaDuration or Date; returns an absolute DateAt or after 2000-01-01; a relative duration is resolved against the clock when parsed. Keep that operation inside a recorded step.
StepConfigSchemaOptional retry/timeout fields; returns normalized settingsfalse, count and duration retry shorthands become objects; omitted defaults remain unresolved until the engine applies configuration.

TaskOptions = StepConfig. ActivityOptions extends it with until and message; DialogOptions additionally supports escalateAt. Duration is a number of milliseconds or a DurationString such as '2 seconds'. Strings are clearer. Backoff is 'constant', 'linear' or 'exponential'.

OptionMeaningBaseline default
retries: falseRun once; do not retry after a failure.
retries: numberTotal attempts, including the first. 3 runs the step at most three times; 0 and 1 both run it once.5
retries: DurationStringDelay between attempts; the other retry settings keep their defaults.'1 minute'
retries: RetryStrategyObject with optional limit, delay, backoff. limit counts total attempts like the number form; limit: false retries without limit.{ limit: 5, delay: '1 minute', backoff: 'constant' }
timeout: DurationStored with the step configuration. 0.3.1 does not enforce it: a callback that runs longer is not stopped. For a deadline, set until on a dialog, request or child workflow.'15 minutes'

Baseline values come from defaultStepConfig; configured engine, definition, instance and per-step defaults can change the effective values. ResolvedStepConfig contains numeric millisecond delays/timeouts and a resolved retry object. Constant backoff uses the delay; linear uses delay times attempts already made; exponential uses delay times two to that power. Thus a two-second exponential delay waits four seconds after the first failed attempt and eight after the second. Linear and exponential delays are capped at one day between attempts. See the executable retry example and Errors and retries.

{ retries: { limit: 3, delay: '2 seconds', backoff: 'exponential' } },
  1. three attempts in total
  2. waits 4 s, then 8 s

Most constructors take (message: string, options?). All create error objects; they do not stop a workflow until thrown. Uncaught task failures use the retry policy. The base WorkflowError is retryable by default.

ExportOptions/resultConstraint or meaning
WorkflowErrorWorkflowErrorOptionsOptional name, code, cause, retryable, instance, step.
NonRetryableErrorNonRetryableErrorOptionsForces retryable: false; default code WF_NON_RETRYABLE. Use for a permanent refusal.
ValidationErrorValidationErrorOptions, includes issuesDefault WF_VALIDATION, not retryable unless explicitly set true.
TimeoutErrorRequired TimeoutErrorOptions argumentNon-retryable; code WF_TIMEOUT. The engine throws it when a dialog, request, child workflow or instance passes until.
AssertionErrorWorkflowErrorOptionsNon-retryable; code WF_ASSERTION, also thrown by flow.assert.
AuthorizationErrorOptional message and NonRetryableErrorOptionsNon-retryable; code WF_AUTHORIZATION_DENIED.
AuthorizationPrincipalLimitExceededError(limit: number, traceId: string)Non-retryable, exposes both fields and code AUTHORIZATION_PRINCIPAL_LIMIT_EXCEEDED.
NotFoundErrorOptional message and NonRetryableErrorOptionsWF_NOT_FOUND: missing or not discoverable. Do not distinguish these in a UI.
ForbiddenErrorOptional message and NonRetryableErrorOptionsWF_FORBIDDEN: discoverable, but the action is denied.
ConflictErrorOptional message and NonRetryableErrorOptionsWF_CONFLICT: action permitted, state stale or ineligible.
LockedErrorRequired LockedErrorOptions with resource: { id }WF_LOCKED, exposes resource; retryable only when explicitly true.

The code strings in that table are also exported constants. Use isNonRetryableError, isValidationError, isAuthorizationError, isNotFoundError, isForbiddenError, isConflictError, or isLockedError to narrow an unknown error to the corresponding public type. Each takes unknown and returns a boolean type guard. They operate on actual error instances, not arbitrary JSON from an HTTP response. What each code means when IdentityFlow reports it, and what to do, is listed in Error codes.

isAbortError(error: unknown) recognizes an Error with name: 'AbortError' or code: 'ABORT_ERR'. throwIfAborted(value: unknown): void rethrows such an error, or checks an AbortSignal and throws when aborted. A broad catch must preserve this control flow; suspension must not become a business refusal.

defineHooks, its registrations, the webhook protocol and createHookTestHost are in the Hooks API.

defineAuthorization, access functions, targets, scopes and activation are described in Access control.

workflowTest, fixtures, waits, observations, error codes and the test setup are in the Testing API.

Inline callbacks normally infer these types. Import them only when giving a reusable function or adapter an explicit contract. Type aliases add no runtime validation; the schemas and engine boundaries perform that check.

ExportShape and purpose
ExecuteWorkflow<Params, Result, Variables>(flow: Flow<Params, Variables>) => PromiseLike<Result>, with the same Flow as its typed this context.
WorkflowInstanceVariablesString keys with JSON values; flow.vars exposes a partial value of your variables type.
Activity<Result, Context>A Task whose context is an ActivityContext; same sync/async return contract.
DialogActivity<Params, Context>An activity returning DialogInput<Params>, the dialog description rather than the person’s answer.
StartWorkflowActivity<Params, Context>A task returning StartWorkflowInput<Params>, the child launch request.
PollContext<Data, Params, Variables>The poll callback’s TaskContext; step.data is the value the request activity returned.
WorkflowInstanceOptions<Params>Required params; optional child version/channel, display text, recipients, defaults and deadline, as listed under child workflows.
WorkflowInstanceMetaString-keyed JSON metadata for an instance.

Types for hook callbacks, such as AssigneeResolver and LifecycleStartup, are listed in the Hooks API.

BaseWorkflowInstance contains identity, workflow identity, labels/descriptions, recipients, parameters, deadline, metadata, variables, resolved defaults and creation/update actor IDs and dates. ActiveWorkflowInstance adds status: 'ACTIVE' and optional data. Do not mutate these records to control execution.

BaseWorkflowStep contains workflow and instance identity, step id, key, name, kind, message, attempts, metadata and creation/update information. ConfigWorkflowStep adds config: ResolvedStepConfig. ActiveWorkflowStep adds the active status; RequestingWorkflowStep<Data> has request data, token and until. WorkflowStepKind is TASK, SLEEP, DIALOG, REQUEST or SUBFLOW.

A step’s status moves as Figure 1 shows. A callback that throws makes the attempt ERRORED; when another attempt is allowed, the step becomes ACTIVE again at its retry time. A step that loses a Promise composition ends as CANCELED instead.

WAITS FOR A RESULT ACTIVE attempt runs SLEEPING flow.sleep PENDING flow.dialog REQUESTING flow.request RELYING flow.start CONTINUING checks result COMPLETED final ERRORED or retried sleeps returns, no schema returns, with schema waits time passes result valid fails
WAITS FOR A RESULT ACTIVE attempt runs SLEEPING flow.sleep PENDING flow.dialog REQUESTING flow.request RELYING flow.start CONTINUING checks result COMPLETED final ERRORED or retried sleeps returns, no schema returns, with schema waits time passes result valid fails
A waiting step parks in a waiting status; an arriving result, or a flow.do result with a schema, passes through CONTINUING before the step becomes COMPLETED or ERRORED.

The statuses a Workflow Test observes and its PostgreSQL setup options are in the Testing API. The fields of the records the test driver returns are listed below with the production records.

Record fields for typed helpers and assertions

These are read-only observations. A function that accepts a record may inspect it; writing a record does not update the engine. The property lists below group fields with the same type. An optional value may be null or undefined where the public type uses Optional<T>.

TypeFields
WorkflowDefinitionid: WorkflowDefinitionId; name, version, releaseChannel: strings; optional changelog, label, description, hash: strings; optional size, ratio: numbers; meta: Readonly<Partial<WorkflowDefinitionMeta & SystemMeta>>; defaults: ResolvedStepConfig; createdAt: Date.
WorkflowDefinitionMetaString-keyed JSONValue properties. The interface permits application-specific metadata fields.
SystemMetaJSON properties plus optional string causationId, correlationId, traceparent, tracestate, traceId, spanId; optional isRemote: boolean | string; traceFlags: number | string; optional traceState: string | string[]. Public metadata fields normally use Partial<SystemMeta>.
BaseWorkflowInstanceid: WorkflowInstanceId, workflowId: WorkflowDefinitionId; workflowName, workflowVersion, workflowReleaseChannel: strings; optional workflowLabel, workflowDescription, label, description, message: strings; optional recipients: readonly Subject[]; params: Serializable<unknown>; optional until: Date; readonly partial meta and vars; defaults: ResolvedStepConfig; createdAt, updatedAt: Date; optional createdBy, updatedBy: AccountId.
ActiveWorkflowInstanceAll base fields; status: 'ACTIVE'; optional serializable data; unset finish information.
BaseWorkflowStepWorkflow ID/name/version/channel and optional workflow label/description; instanceId and optional instance label/description; id: WorkflowStepId; key, name: strings; kind: WorkflowStepKind; optional message; attempts: number; partial SystemMeta; creation/update dates and optional actor IDs.
ConfigWorkflowStepBase step fields plus config: ResolvedStepConfig.
ActiveWorkflowStepConfigured step fields, status: 'ACTIVE' and optional serializable data.
RequestingWorkflowStep<Data>Configured step fields, kind: 'REQUEST', status: 'REQUESTING', optional until: Date, token: ActivityToken, data: Data.
WorkflowTestInstance<Result>Base instance fields, test instance status, optional result/serializable data, optional finishedAt: Date and finishedBy: AccountId. Await completion to narrow the result.
WorkflowTestStepBase step fields, test step status, optional config, unknown data, optional deadline and finish date/actor.
WorkflowTestPendingDialogTest step fields with kind: 'DIALOG', status: 'PENDING', required config, token and JSON data; optional until, escalateAt, escalatedAt, assigneeSource and readonly assignees.
WorkflowTestEventid: string, type: WorkflowTestEventType, createdAt: Date, optional createdBy; optional workflow/instance/step IDs, stepKind, log level, message, unknown data, info, meta.
WorkflowTestObservation<Workflow>Readonly instance: WorkflowTestInstance, steps: readonly WorkflowTestStep[], events: readonly WorkflowTestEvent[]; no implicit wait.

WorkflowTestEventType contains INSTANCE_STARTED, INSTANCE_PAUSED, INSTANCE_RESUMED, INSTANCE_TERMINATED, INSTANCE_COMPLETED, INSTANCE_ERRORED, INSTANCE_VARIABLES_UPDATED, STEP_STARTED, STEP_SLEEPING, STEP_PENDING, STEP_ESCALATED, STEP_ASSIGNEES_UPDATED, STEP_REQUESTING, STEP_RELYING, STEP_CONTINUING, STEP_COMPLETED, STEP_CANCELED, STEP_ERRORED and LOG. Use these names to inspect evidence of a particular transition. Payload shape still depends on the event kind; the public record leaves it unknown.

Dialog eligibility, host access and business recipients answer different questions. A provider-scoped assignee says who may answer a dialog. Host access says who may discover or operate a resource. A recipient is business context. Reading an instance does not grant any of these permissions.

For your own UI, read Build your own UI and the GraphQL API. For a workflow that calls an external system, use Bindings. For the host’s credentials and provider routing, use Configuration.