Workflows
Workflows run the durable body of one Action: steps, waits, retries and hand-offs that survive restarts.
Each step's result is recorded before the next step starts.
Contract
A Process names one or more Workflows and the Trigger that starts each: an Event, a schedule, or a Thing entering a state. Each step calls one Function and declares what happens when it fails.
| Guarantee | Detail |
|---|---|
| Durable | After a crash, the run resumes at the first unrecorded step; finished steps never run again |
| Exactly once outside | A step that reaches the outside world carries an idempotency key, so a retry never charges or sends twice |
| Free waits | sleep('30d') and waitForEvent consume nothing while waiting |
| Delay windows | A one-way step waits as pending with reason awaitingTime, so a watcher can intervene |
| Readable status | pending names its reason: queued, running, retrying, awaitingWorker, awaitingInput, awaitingSource, awaitingAction or awaitingTime |
| Final status | done, failed, canceled (compensation ran) or refused (a check stopped it first) |
| Steerable | Cancel, retry as a new linked Action, or redirect with new inputs |
A wait on a Worker is a Task in that Worker's inbox. A caller picks a Mode per call: do waits for the result, and send returns the pending Action at once. Functions covers what runs each step.
Verbs
| Verb | Object | Event |
|---|---|---|
| Workflow.create | Workflow | Workflow.created |
| Workflow.get | Workflow | None. |
Interfaces
Example
// minutely.app, a meeting-notes App, converts trials with one Workflow.
import { $, workflow } from 'startup-builder'
export default workflow({
name: 'trial-to-paid',
execute: async (ctx, { customer }) => {
await ctx.step('welcome', () => $.Message.create({ medium: 'email', direction: 'outbound', to: customer, purpose: 'onboarding', body: 'Welcome aboard.' }))
const paid = await ctx.waitForEvent('Order.paid', { where: { customer }, timeout: '14d' })
if (!paid) {
const task = await ctx.step('check-in', () => $.do.Task.create({ for: customer, priority: 1 }))
await ctx.step('route', () => $.Task.delegate({ task: task.$id, role: 'success-lead' }))
}
},
})