Skip to content

Execute once, every time

Write a workflow as a normal async function. When it waits for a person, IdentityFlow puts it to sleep and wakes it up later — across restarts, with a full record of what happened.

Here is a complete workflow. It looks up a person, then stops and asks a human to approve something:

import * as v from '@identity-flow/sdk/valibot';
import { defineWorkflow } from '@identity-flow/sdk';
import { Directory } from './directory.binding';
export const approval = defineWorkflow(
{ name: 'approval', version: '1.0.0', schema: v.object({ userId: v.string() }) },
async (flow) => {
const directory = flow.use(Directory);
const person = await flow.do('load person', () => directory.lookup(flow.params.userId));
const decision = await flow.dialog(
'approve access',
{ schema: v.object({ status: v.literal('approved') }) },
() => ({
params: { displayName: person.displayName },
assignees: [{ providerId: 'siam', subject: 'role:approver' }],
}),
);
return { person, decision };
},
);

The interesting line is await flow.dialog(...). It can wait for a week. The function is not sitting in memory holding a connection open — IdentityFlow suspends it, writes what happened so far to PostgreSQL, and lets the process exit. When someone with the role:approver membership answers, the function resumes on the next line with decision filled in. Redeploy in between, restart the machine, wait a month: the run continues from where it stopped, and it never re-runs a step it already finished.

Three things make that work, and each one is visible in the code above:

  • Every step is recorded. flow.do and flow.dialog write an immutable event before and after they run. Those events are the source of truth — the current state is derived from them, never the other way round. Afterwards you can ask what happened, in order, and who did it.
  • The outside world stays at the edge. flow.use(Directory) is the only way the workflow talks to another system. Everything else is deterministic, so replaying the events always produces the same result.
  • Who may answer is part of the workflow. assignees names a membership (role:approver at the provider siam), not a person. Someone without that membership cannot complete the step, and the attempt is recorded too.
  • Start here — what your machine needs before you run anything.
  • Tutorial — the guided path: build this workflow, test it, watch it survive a restart, then run it in CI.
  • Testing Workflows — the full runnable project around the snippet above, including its test.
  • Concepts — the handful of words this documentation uses.
  • Reference — API and configuration, quoted from the source.

You can run all of this on your own machine in the 0.2.0 release:

  • Workflows that wait for a person, a timer, or a child workflow, and survive a process restart.
  • Tests: workflowTest() is an ordinary Vitest test. It runs against real PostgreSQL, lets you act as different people, and lets you replace external systems with test doubles.
  • The full event history, step states, and instance state for any run.
  • Deciding who may act on a pending step, using prepared test identities.
  • A local CI run and a deployment exercise that copies a tested project to a second directory on the same machine.

Two caveats worth knowing up front. External calls are delivered at least once, so anything you call must tolerate seeing the same request twice. Authorization is labelled preview in this release: the boundary is real and you can watch it reject the wrong person, but it is not a finished, supported feature yet.

None of this ships in 0.2.0, and nothing on this site should be read as promising it:

  • A public package registry. You install from the release set you were given.
  • Staging, production, or any remote deployment target. The deployment exercise is one directory to another on the same machine.
  • A connection to a live customer identity system. The workshop uses prepared test identities throughout; the optional live adapter is preview only.
  • Acting on someone else’s behalf, or acting as someone else. Both are described under Preview and neither is available.
  • Traces, dashboards, or performance numbers.

Every page says which of the two lists it belongs to. Preview has the full status list, copied from the release note. When something on your machine doesn’t match what a page claims, go to Troubleshooting.