ObjectOS
ErstellenAutomatisierung

Workflows

Model a record's lifecycle as a state machine — valid states, guarded transitions, and nothing a flow can do better.

Workflows

A workflow models a record's lifecycle as a finite state machine: the states the record can be in, the events that move it between them, and the guards that must hold for a move to happen. Use one when the core requirement is "this object can only move through these states by these events."

There is no standalone Salesforce-style Workflow Rule authoring type. The old "workflow" concept splits cleanly in two:

  • State machine metadata for strict lifecycle transitions — this page.
  • Flows for event-triggered or scheduled automation, including approval pauses.

Workflows vs flows

Workflow (state machine)Flow
ModelsState — where a record is in its lifecycleSteps — what happens when something occurs
Answers"Is this transition allowed right now?""What do we do about it?"
ShapeStates, transitions, guardsNodes and edges: triggers, actions, branches
Side effectsNone — it only constrainsAll of them — email, updates, HTTP, waits

They compose: the state machine constrains the transitions; flows perform the side effects around them when you need notifications, record updates, or external calls.

Define a state machine

A support case that must move new → assigned → resolved, with an escalation path:

import type { StateMachineConfig } from '@objectstack/spec/automation';

export const caseLifecycle: StateMachineConfig = {
  id: 'case_lifecycle',
  initial: 'new',
  states: {
    new: {
      on: {
        ASSIGN: { target: 'assigned' },
      },
    },
    assigned: {
      on: {
        RESOLVE: { target: 'resolved', cond: 'has_resolution' },
        ESCALATE: { target: 'escalated' },
      },
    },
    escalated: {
      on: {
        RESOLVE: { target: 'resolved', cond: 'has_resolution' },
      },
    },
    resolved: {
      type: 'final',
    },
  },
};

Read it as a contract: a new case can only be assigned. An assigned case can be resolved — but only if the has_resolution guard passes — or escalated. A resolved case is final; nothing moves it again.

Anatomy

KeyWhat it declares
idThe state machine's identifier
initialThe state every new record starts in
statesMap of state name → its outgoing transitions
onEvents this state responds to (ASSIGN, RESOLVE, …)
targetThe state an event moves the record to
condA guard that must hold for the transition to fire
type: 'final'Terminal state — no outgoing transitions

Guards

A guard (cond) makes a transition conditional: in the example above, RESOLVE only reaches resolved when has_resolution holds. Guards are how you encode "you can't close a case without a resolution" as a structural rule instead of a validation scattered through UI code.

Events, not field writes

Transitions fire on named events (ASSIGN, ESCALATE), not on arbitrary status-field edits. That's the point: the machine defines the complete set of legal moves, and anything not declared is impossible.

Tip: Keep the machine minimal — states, transitions, guards. The moment you want "and then send an email", you've left workflow territory: put the side effect in a flow that reacts to the transition.

Side effects belong in flows

State machines describe valid transitions and guards — nothing else. When a transition should do something (notify sales, stamp a closed date, call an external system), pair the machine with a flow triggered by the record change:

export const dealClosedWon = defineFlow({
  name: 'deal_closed_won',
  type: 'record_change',
  nodes: [
    {
      id: 'start',
      type: 'start',
      config: {
        triggerType: 'record-after-update',
        objectName: 'opportunity',
        condition: "record.stage == 'closed_won' && previous.stage != 'closed_won'",
      },
    },
    { id: 'set_closed_date', type: 'update_record', label: 'Set Closed Date' },
    { id: 'notify_sales', type: 'notify', label: 'Notify Sales' },
    { id: 'end', type: 'end' },
  ],
  edges: [
    { id: 'e1', source: 'start', target: 'set_closed_date' },
    { id: 'e2', source: 'set_closed_date', target: 'notify_sales' },
    { id: 'e3', source: 'notify_sales', target: 'end' },
  ],
});

The machine guarantees the deal reached closed_won legally; the flow handles what happens next. Conditions like the one above are CEL expressions — see CEL.

Migrating from workflow rules

If you're coming from a platform with Workflow Rules, here's the mapping:

Old conceptCurrent equivalent
Workflow RuleFlow
Time triggerScheduled flow
Field update actionupdate_record node
Email alertnotify node
HTTP callhttp node
Approval ProcessFlow with one or more approval nodes

The test: if the old rule was "when X happens, do Y", model it as a small flow. If it was "this record must move through controlled states", model the lifecycle as a state machine and use flows for the side effects.

Where to go next

PageWhy
FlowsSide effects around transitions — triggers, steps, error handling
ApprovalsHuman sign-off as a pause inside a flow
CEL expressionsThe language behind guards and flow conditions
Data modelingThe objects whose lifecycles you're constraining
Automation overviewChooser: flow vs workflow vs approval

On this page