Workflow API
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… | Use | Read next |
|---|---|---|
| Define a process and validate its input | defineWorkflow | Definition shape |
| Call a service or record a calculation | flow.do | Tasks |
| Reuse a task function | defineTask | Extracted tasks |
| Wait for time to pass | flow.sleep | Timers |
| Ask an eligible person for an answer | flow.dialog | Dialogs |
| Poll an external job until it has a result | flow.request | Requests to external systems |
| Run another workflow and wait for its result | flow.start | Child workflows |
| Run steps concurrently or impose a deadline | Standard Promise methods | Promise composition |
| Call an external system, and replace it in tests | defineBinding, flow.use | Bindings |
| Map accounts to current principals | defineHooks | Hooks API |
| Decide who may start, view or operate workflows | defineAuthorization | Access control |
| Drive a workflow as different accounts in a test | workflowTest | Testing API |
| Build my own UI | Incoming GraphQL API | Build your own UI |
| Configure or deploy the host | Host configuration and startup | Configuration, 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 youawaitto get aT. Every step returns one; the workflow waits at theawait.A | Bis a union: the value has typeAor typeB.name?: Tmarks an optional field; it may be missing orundefined.readonlymarks a field or list you must not change.Partial<T>isTwith every field optional.<T>after a name is a type parameter, like[T any]in Go. TypeScript infers it in most calls.(context: C) => Ris a function type that takes aCand returns anR.MaybePromise<T>isTorPromise<T>: return the value directly or from anasyncfunction.
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.
| Option | Type | Meaning and constraint |
|---|---|---|
name | string, required | Stable workflow name; 1–256 UTF-16 code units, valid Unicode, no C0/C1 controls. |
schema | StandardSchema<Params>, optional | Validate start input before any step; invalid input refuses the start. |
version | string, optional | Set an explicit version such as 1.0.0. Without one, the version becomes 0.0.0+<bundle hash>. A deployed version is immutable. |
draft | boolean, default true | The host skips a definition, with a warning, unless it sets draft: false. Workflow Tests run a definition either way. |
releaseChannel | string, default latest | Alias for selecting a version. It is separate from the version number. |
label, description, changelog | string, optional | Display name, purpose and changes for this version. |
defaults | StepConfig, optional | Default 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. |
meta | Partial<WorkflowDefinitionMeta & SystemMeta>, optional | JSON metadata. Do not put secrets here. |
codecs | Codecs, optional | Named 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 }- stable workflow name
- raise on every change
- 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.
| Member | Type or signature | Purpose and constraint |
|---|---|---|
params | readonly Params | Validated start input. Treat it as immutable. |
vars | Partial<Variables> | Persisted JSON workflow variables. Assign deterministic values; a variable is not an external-service cache. |
definition | readonly WorkflowDefinition | Deployed definition, including id, name, version, releaseChannel, display metadata and resolved defaults. |
instance | readonly ActiveWorkflowInstance | Current instance identity, workflow identity, params, recipients, metadata, variables, defaults and creation/update information. |
use | <T>(binding: Binding<T>) => T | Resolve a binding; see Bindings. |
assert | (condition: unknown, message: string) => asserts condition | Throw 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[]) => void | Record a workflow log event at the selected level. Payloads persist; omit credentials and sensitive data. |
time, timeLog, timeEnd | (label?: string) => void | Start, 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 }) =>- stable step name
- three attempts in total
- 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 field | Type | Use |
|---|---|---|
flow | Flow<Params, Variables> | Read workflow input and state. |
step | ActiveWorkflowStep | Inspect this step’s stable identity, name and attempt count. |
use | UseAPI | Resolve a binding for this callback. |
signal | AbortSignal | Pass to cancellable I/O and stop work when aborted. Cancellation is cooperative. |
span | OpenTelemetry Span | Attach 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>:
| Field | Type | Meaning and constraint |
|---|---|---|
params | JSON object or null/undefined, required property | Information shown to the person. It is persisted, so keep it limited to the decision. |
assignees | One AssigneeRef or a non-empty array | Each ref has providerId and subject. Any account with a matching current principal can answer. |
assigneeSource | AssigneeSourceRef, optional | Provider, source key, JSON context and optional source version of an externally maintained group, resolved again when a change event arrives. |
until | Duration or Date, optional | Hard deadline. When it passes, the step fails with WF_TIMEOUT. |
escalateAt | Duration or Date, optional | Mark an unanswered dialog as escalated without completing or failing it. It does not choose a new assignee. |
message | string, optional | Text 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' }],- registered provider ID
- 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 input | Type | Meaning |
|---|---|---|
workflow | WorkflowDefinitionKey | Definition ID or name of the deployed child. |
params | Serializable child input, required | Validated by the child’s input schema. |
version, releaseChannel | string, optional | Select the child version or channel. Use an exact version for an exact dependency. |
label, description | string, optional | Display information for the child instance. |
recipient, recipients | Subject or one/many subjects, optional | Business recipients; these do not replace dialog assignees or grant access. |
defaults | StepConfig, optional | Child instance step defaults. |
until | Duration or Date, optional | Child instance deadline; when it passes, the step fails with WF_TIMEOUT. |
meta | JSON metadata, optional | Correlation 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 field | Default or behavior |
|---|---|
url?: string | GRAPHQL_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?: Fetch | Custom fetch implementation with the standard fetch signature. |
method?: HTTPMethodInput | POST; also accepts GET and lowercase variants. |
errorPolicy?: ErrorPolicy | none throws on GraphQL errors; ignore ignores them; all preserves errors and data. |
excludeOperationName?: boolean | Defaults to false; skip extraction only when appropriate for the service. |
Other RequestInit fields | Standard 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 export | Input and returned output | Constraint |
|---|---|---|
ProviderIdSchema | Provider string, unchanged | 1–128 UTF-16 code units, valid Unicode, no C0/C1 controls; the legacy unscoped ID is reserved. |
PrincipalSubjectSchema | Subject string, unchanged | Non-empty, at most 512 UTF-16 code units; interpretation belongs to the provider. |
PrincipalRefSchema, AssigneeRefSchema | Object containing provider ID and subject; returns those validated fields | Both fields must pass the validators above. One ref does not establish current account membership. |
JSONSchema | JSON primitive, array or object; returns validated JSON | Recursive values only; no functions, undefined or live clients. |
JSONObjectSchema | String-keyed object; returns validated object | Every value must pass JSONSchema. |
DurationStringSchema | Human-readable duration string; returns milliseconds | Non-negative safe integer after conversion. |
DurationSchema | Duration string or number by type; returns milliseconds | A number is read as milliseconds, 0 included. |
UntilSchema | Duration or Date; returns an absolute Date | At or after 2000-01-01; a relative duration is resolved against the clock when parsed. Keep that operation inside a recorded step. |
StepConfigSchema | Optional retry/timeout fields; returns normalized settings | false, 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'.
| Option | Meaning | Baseline default |
|---|---|---|
retries: false | Run once; do not retry after a failure. | |
retries: number | Total attempts, including the first. 3 runs the step at most three times; 0 and 1 both run it once. | 5 |
retries: DurationString | Delay between attempts; the other retry settings keep their defaults. | '1 minute' |
retries: RetryStrategy | Object 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: Duration | Stored 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' } },- three attempts in total
- 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.
| Export | Options/result | Constraint or meaning |
|---|---|---|
WorkflowError | WorkflowErrorOptions | Optional name, code, cause, retryable, instance, step. |
NonRetryableError | NonRetryableErrorOptions | Forces retryable: false; default code WF_NON_RETRYABLE. Use for a permanent refusal. |
ValidationError | ValidationErrorOptions, includes issues | Default WF_VALIDATION, not retryable unless explicitly set true. |
TimeoutError | Required TimeoutErrorOptions argument | Non-retryable; code WF_TIMEOUT. The engine throws it when a dialog, request, child workflow or instance passes until. |
AssertionError | WorkflowErrorOptions | Non-retryable; code WF_ASSERTION, also thrown by flow.assert. |
AuthorizationError | Optional message and NonRetryableErrorOptions | Non-retryable; code WF_AUTHORIZATION_DENIED. |
AuthorizationPrincipalLimitExceededError | (limit: number, traceId: string) | Non-retryable, exposes both fields and code AUTHORIZATION_PRINCIPAL_LIMIT_EXCEEDED. |
NotFoundError | Optional message and NonRetryableErrorOptions | WF_NOT_FOUND: missing or not discoverable. Do not distinguish these in a UI. |
ForbiddenError | Optional message and NonRetryableErrorOptions | WF_FORBIDDEN: discoverable, but the action is denied. |
ConflictError | Optional message and NonRetryableErrorOptions | WF_CONFLICT: action permitted, state stale or ineligible. |
LockedError | Required 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.
| Export | Shape and purpose |
|---|---|
ExecuteWorkflow<Params, Result, Variables> | (flow: Flow<Params, Variables>) => PromiseLike<Result>, with the same Flow as its typed this context. |
WorkflowInstanceVariables | String 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. |
WorkflowInstanceMeta | String-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.
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>.
| Type | Fields |
|---|---|
WorkflowDefinition | id: WorkflowDefinitionId; name, version, releaseChannel: strings; optional changelog, label, description, hash: strings; optional size, ratio: numbers; meta: Readonly<Partial<WorkflowDefinitionMeta & SystemMeta>>; defaults: ResolvedStepConfig; createdAt: Date. |
WorkflowDefinitionMeta | String-keyed JSONValue properties. The interface permits application-specific metadata fields. |
SystemMeta | JSON 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>. |
BaseWorkflowInstance | id: 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. |
ActiveWorkflowInstance | All base fields; status: 'ACTIVE'; optional serializable data; unset finish information. |
BaseWorkflowStep | Workflow 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. |
ConfigWorkflowStep | Base step fields plus config: ResolvedStepConfig. |
ActiveWorkflowStep | Configured 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. |
WorkflowTestStep | Base step fields, test step status, optional config, unknown data, optional deadline and finish date/actor. |
WorkflowTestPendingDialog | Test step fields with kind: 'DIALOG', status: 'PENDING', required config, token and JSON data; optional until, escalateAt, escalatedAt, assigneeSource and readonly assignees. |
WorkflowTestEvent | id: 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.