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
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:
role:— only users with that role may make the move.when:— a SQL condition on the row that must hold (e.g. a tracking number must exist before shipping).on_transition:— side effects fired when a record enters a state: webhooks, notifications, and the same actions hooks support.
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:
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.