Start here
Choose the result you need, then open its first step.
IdentityFlow is provided to customers directly. If you don’t have the starter project and the IdentityFlow program yet, write to contact@kenoxa.de.
For exact names and defaults, open the Reference. If a term is unfamiliar, look it up in Concepts. Setup includes checking the prerequisites and installing the project’s dependencies.
Prerequisites and next steps for each path
Define a process that calls other systems, waits for people, and continues after a restart. Author workflows walks through a complete example you can adapt and explains the TypeScript syntax as you use it. To call another system from a workflow, write a binding, as Bindings and connectors shows.
You need Node.js 26.8.1 or later within Node 26 and the @identity-flow/*
npm packages to write and type-check a workflow, and the Workflow Test
distribution to run it; see Test a workflow.
Test workflows shows the install command.
The starter has all of this set up.
Use Recipes for common tasks and the Workflow API for steps, options and limits.
Check that a workflow produces the expected result, waits for the right person and handles failures. A Workflow Test runs a real instance against a PostgreSQL database, acts as the accounts you declare, and replaces each binding with a fixture, a stand-in your test controls.
You need the Workflow Test distribution and a running Docker, or a PostgreSQL server of your own; Test workflows lists the details.
Start with Test workflows and look up functions in the Testing API.
A hooks file tells IdentityFlow which
principals a signed-in
account has, such as its person, roles and teams. It can also resolve
assignee sources, groups
maintained in your directory, and receive change events from that directory.
Access rules in authorization.ts then decide which accounts may start, view,
operate and audit workflows.
You need an OIDC provider for sign-in and access to your directory. Hook tests run under plain Vitest, without the Workflow Test distribution and without a database.
Start with Identity and access, then Connect your identity provider and Access control. Look up signatures in the Hooks API.
Your UI calls the IdentityFlow GraphQL API to list and answer the signed-in account’s dialogs and to start workflows. IdentityFlow checks the account’s access on every read and every command, so your UI does not enforce access rules itself. In 0.3.1 the API accepts only the session cookie from IdentityFlow’s own browser sign-in, so your UI must run in the browser and be served from the same origin as IdentityFlow.
You need a running IdentityFlow with sign-in configured.
Start with Build your own UI and look up query roots, mutations and error codes in the GraphQL API reference. The GraphQL binding in Bindings and connectors is a separate thing: workflows use it to call other GraphQL services.
IdentityFlow 0.3.1 is released for production use; read its known limits before you plan a rollout. Install the program on a server, connect it to PostgreSQL and your OIDC provider, and deploy your Workflow Definitions. Operators then pause, resume and terminate instances and reassign pending dialogs in the web application or through GraphQL.
You need PostgreSQL 17 or 18, an OIDC provider with a confidential client for IdentityFlow, and HTTPS in front of the program; What the server needs has the details.
Start with Install and run IdentityFlow and Operate running workflows. Every configuration key is in the Configuration reference.
The tutorial uses the starter: you build and run a vacation approval, answer it as a second account, change a multi-step approval, and run the tests in CI. Its setup lists what your machine needs and sets up the starter.
IdentityFlow 0.3.1 is delivered to customers as three parts:
- The IdentityFlow program, one executable that runs your workflows and serves the web application and the GraphQL API.
- The Workflow Test distribution, the test runner for Workflow Tests against a real PostgreSQL database on developer machines and in CI.
- The starter, an example project with workflows, a hooks file and tests, which the tutorial uses.
The program and the Workflow Test distribution are built for macOS on Apple
Silicon and Linux x64; on Windows, use the Linux files inside WSL2.
Install and run IdentityFlow lists the
file names. Your own workflow project uses the npm packages
@identity-flow/api, @identity-flow/sdk, @identity-flow/binding-graphql and
@identity-flow/testing at version 0.3.1. Release 0.3.1 lists
everything the release contains and its
known limits.
Workflows can wait for a person, a timer or a child workflow, and poll an external system until it reports a result. Access control decides, for each workflow, who may start, view, operate and audit instances, based on the principals your provider reports for the signed-in account.
IdentityFlow is provided to customers directly. If you don’t have the starter project and the IdentityFlow program yet, write to contact@kenoxa.de.
How a complete approval works in code
Workflows are TypeScript modules. IdentityFlow runs them and stores their history in PostgreSQL. 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: 'acme', subject: 'role:approver' }], }), );
return { person, decision }; },);
export default approval;async (flow) => { … } is a function literal, like Go’s func(flow) { … },
that defineWorkflow receives; await waits for a step’s result.
export default approval makes it the file’s Workflow Definition, the one the
IdentityFlow host loads; the named export is what its test imports. This
definition has no draft: false. A Workflow Test runs it anyway; the
IdentityFlow host would skip it as a draft. Add draft: false before you deploy
a workflow; see Author workflows.
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 can let the process
exit. When an approver answers, the function continues with decision filled
in. Restart IdentityFlow in between or wait a month: the instance continues
where it stopped, and IdentityFlow never re-runs a step it already finished.
Calls to other systems are the exception: they are delivered at least once. If
the process stops after the other system accepted a request but before
IdentityFlow recorded the step’s result, the step runs again and the request
arrives twice, so every system you change must recognize a repeated request;
External effects shows how.
Continuing is a replay: IdentityFlow runs the function again from the top, and every step that already finished returns its recorded result instead of running again. Code outside a step therefore runs on every replay and must not call other systems.
flow.do('load person', () => directory.lookup(flow.params.userId))- records the result
- skipped once recorded
Three things make that work, and each one is visible in the code above:
- Every step is recorded. A step is a named unit
of work:
flow.doruns your code and records its result, andflow.dialogis a dialog, a step that waits for a person’s answer. Each step appends immutable events to the instance’s history. The current state is derived from those events, so afterwards you can read what happened, in order, and which account did it. - Other systems are reached through a binding.
flow.use(Directory)gives the workflow a binding, its named access to an external system, defined in./directory.binding. Call it inside a step, asdirectory.lookup(...)runs insideflow.doabove. - Who may answer is part of the workflow.
assigneesnames a principal (role:approverat the provideracme, your identity hook), not a person. An account without that principal cannot answer: IdentityFlow refuses the answer, and the dialog stays pending.
assignees: [{ providerId: 'acme', subject: 'role:approver' }]- the provider ID
- a role, not a person
- Concepts — what Workflow Definition, instance, step, replay, binding, principal, assignee and access function mean.
- Recipes — complete workflow and hooks files for common tasks, each with its test.
- Reference — the Workflow API, Hooks API, Testing API, configuration keys, command line, GraphQL API and error codes.
- Troubleshooting — find the cause of an error code or symptom and the command that fixes it.