Docs Services Sign In
Backend logic

Workflows

A workflow turns a status column into a state machine: a fixed set of states, the transitions allowed between them, who's allowed to make each move, and what happens when a record enters a state. Define it once in workflows.yaml and the platform enforces the rules and exposes a transition API.

Define the machine

workflows.yaml
orders:
  table: orders
  field: status
  initial: pending
  states: [pending, processing, shipped, delivered, cancelled]
  transitions:
    pending:
      processing: { role: admin }
      cancelled:  {}
    processing:
      shipped:    { role: admin, when: "tracking_number IS NOT NULL" }
  on_transition:
    shipped:
      - webhook: ""
      - notify: ""

Each transition can carry guards:

Drive it from the frontend

Two endpoints are generated per workflow table. GET /api/<table>/<id>/transitions returns the moves the current user can make right now; POST /api/<table>/<id>/transition performs one. The typed client wraps both:

static/app.tsx
const next = await bm.workflow('orders', id).available();
// e.g. ['processing', 'cancelled']

await bm.workflow('orders', id).transitionTo('processing');

An illegal move — a state that isn't a valid next step, a role that isn't permitted, or a failing when: guard — is rejected server-side. The UI can simply render the buttons returned by available() and trust that the server is the source of truth.

Related: Hooks for change side effects, and Access & roles for the roles that gate transitions.