Skip to content

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.

GuaranteeDetail
DurableAfter a crash, the run resumes at the first unrecorded step; finished steps never run again
Exactly once outsideA step that reaches the outside world carries an idempotency key, so a retry never charges or sends twice
Free waitssleep('30d') and waitForEvent consume nothing while waiting
Delay windowsA one-way step waits as pending with reason awaitingTime, so a watcher can intervene
Readable statuspending names its reason: queued, running, retrying, awaitingWorker, awaitingInput, awaitingSource, awaitingAction or awaitingTime
Final statusdone, failed, canceled (compensation ran) or refused (a check stopped it first)
SteerableCancel, 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

VerbObjectEvent
Workflow.createWorkflowWorkflow.created
Workflow.getWorkflowNone.

Interfaces

Example

TypeScript
// 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' }))
    }
  },
})