Steps
A step calls one Verb as one Role; it names its id, what it waits for, and what happens when it fails, and the Verb decides the rest.
A step is one Verb called as one Role, inside a Workflow. It declares its id, its wait and its failure rule; the Verb decides its Permission, its Event and its approval.
Fields
| Field | Holds | Default |
|---|---|---|
id | A stable slug, so an Action resumes on the right step after a deploy | Required |
text | The one sentence the Process page prints | Required |
does | The Verb it calls, such as Invoice.collect | None: a read or a branch |
by | The Role whose Permissions and Limits the call runs under | The accountable Role |
approval | Who says yes first: none, ask or mandate | Computed from the Verb |
waitsFor | An Event to wait for before the step runs, with an optional where | None |
timeout | How long it waits before its failure rule applies: 30m, 4h, 7d, 2w | None |
onFail | retry, escalate, compensate or fail | retry |
compensate | The Verb that undoes or remedies it; required by onFail: | None |
exceptions | Named outcomes that leave the happy path, each with when and an optional continueAt | None |
why | Why the step exists, for the runbook | None |
What a step can do
Every step is a Verb call, so the closed set of step kinds is the set of Verbs.
| To | Call | It waits as |
|---|---|---|
| Write a record | Any Verb, such as Invoice.collect | Nothing, or its delay window when one-way |
| Ask a person and wait for the answer | Action.ask, answered by Action.answer in any Interface | pending, awaitingInput |
| Get a yes past a Limit | Approval.create | pending, awaitingInput |
| Hand work to another Role | Task.delegate | pending, awaitingWorker |
| Hand a problem up | Task.escalate | pending, awaitingWorker |
| Send a message | Message.create | Its delay window |
| Wait for something to happen | waitsFor an Event, capped by timeout | pending, awaitingAction |
A waiting step costs nothing, survives deploys, and shows its reason on the Action. In a Workflow file, $.step, $.sleep and $.waitForEvent write the same steps in code.
Branches and failure
A step leaves the happy path in two ways. An exception is a known outcome: it names when it applies and the step to continue at, or ends the Action when it names none. A failure is anything else: the step retries with backoff, then follows its onFail.
Dunning shows both. When the card on file has expired, the retry step's cardExpired exception skips ahead to telling the Customer. When a retry fails for any other reason, the step tries again on the next day of the schedule.
onFail | When retries run out |
|---|---|
retry | Keeps retrying with backoff until timeout, then goes to the accountable Role |
escalate | Becomes a Task for the accountable Role, with the step's inputs |
compensate | Calls the compensate Verb, such as Order.refund after a failed redo |
fail | Ends the Action failed, with the reason on record |
Three steps, one wait
A Workflow on Quote.sent waits a week for the buyer to accept, then wins the Deal and starts onboarding. If the week passes, the Sales Lead gets the Deal as a Task.
import { $ } from 'startup-builder'
await $.Workflow.create({
steps: [
{ id: 'accepted', text: 'Wait up to seven days for the buyer to accept.', waitsFor: { event: 'Quote.accepted' }, timeout: '7d', onFail: 'escalate', by: 'sales-lead' },
{ id: 'win', text: 'Mark the Deal won.', does: 'Deal.win', by: 'sales-lead' },
{ id: 'welcome', text: 'Send the buyer the onboarding link.', does: 'Message.create', by: 'onboarding-specialist' },
],
})How a Workflow runs covers approvals, service levels and turning a Process on or off. A Trigger starts the first step, and Triggers lists every kind.