Flows
Guided screen flows and autolaunched processes built without code — run from records, lists, pages, a link, workflows or events, and through the API.
A flow is a process an admin builds from elements under Settings →
Automation → Flows (/dashboard/flows). A screen flow walks a person
through screens; an autolaunched flow has no screens and runs from a
workflow, an event or another flow. Flows are part of the Workflow Automation
feature (Professional plan).
Flows run on the server. SalesOS renders each screen in the runner's language, validates every submission, and keeps the run (an interview) so a reload, another tab or another device resumes where the person stopped.
Elements
| Element | What it does |
|---|---|
SCREEN | Inputs: text, textarea, number, currency, date, picklist (with dependent picklists), checkbox, lookup (record picker), choice (single or multiple); display and richtext show text. Each field can be required, have a default, a validation formula with its message, and visibleWhen conditions. |
DECISION | Outcomes in order; the first whose conditions (or formula) hold wins, else the default (next). |
ASSIGNMENT | set, add, subtract, append, remove, clear on variables. |
LOOP | Steps through a collection into an item variable; done after the last item. |
GET_RECORDS | One record by id, or records matching filters (lead, contact, account, opportunity, task, custom:<apiName>). |
CREATE_RECORD / UPDATE_RECORD / DELETE_RECORD | Writes through the entity services: validation rules, before-save hooks, field-level security, record locks, workflows, webhooks and the audit trail all apply. |
CUSTOM_ACTION | Calls a custom action with mapped inputs; outputs go into variables. |
FUNCTION | Runs a serverless function. |
HTTP_CALLOUT | Calls a named credential: credential, method, path and an optional JSON body (both templates with {!merge} fields). SalesOS adds the credential's authentication. responses map dot paths into the JSON reply (data.status), $status or $body (non-JSON replies) into variables. A non-2xx reply fails the step (CALLOUT_HTTP_ERROR), so its fault path can handle it. |
PUBLISH_EVENT | Publishes a custom event (eventApiName, payload mappings); the payload is checked against the event's schema. storeAs receives the event id. The flow's own event subscription never re-runs it for this event. |
SEND_EMAIL | Up to 20 recipients (addresses or user ids). Fails with EMAIL_NOT_CONFIGURED when email is off. |
SUBMIT_APPROVAL | Submits a record to its approval process. |
SUBFLOW | Runs an active autolaunched flow inline (at most 3 levels). |
END | Finishes, optionally with a message. |
Record, action, callout, event, email, approval and subflow elements can have a fault path
(fault): when the step fails, the flow continues there and
{!$Flow.FaultMessage} holds the reason. Without one, the interview stops and
shows the error.
Variables, values and formulas
Variables are text, number, currency, boolean, date, datetime,
record (with an object) or collection. Mark them isInput (filled from
the launch or a parent flow) or isOutput (returned when the flow completes).
A screen field stores its answer in a variable of the same name.
A value is one of:
- text, with merge fields —
"Follow up {!$Record.firstName}". A string that is exactly one merge keeps the value's type; - a number or boolean;
{ "ref": "deal.amount" };{ "formula": "deal__amount * 0.9" }— the custom-field formula language (no code runs). In formulas, record fields are<variable>__<field>, the launching record's areRecord__<field>, a collection's size is<variable>__count.
Built-in references: $Record (the record the flow was launched on, read as the
runner), $User (id, name, email, role), $Label.<name> (a
custom label in the runner's language),
$Flow.interviewId, $Flow.FaultMessage, $Event (event-launched flows), and
recordIds (list launches).
Run mode
By default record steps run as the person running the flow, with their record access and field security. An org admin can switch a flow to system mode: record steps then run as that admin, whoever runs the flow. The builder and the flow list label it, and it stops working if that person loses org admin access.
Runtime guarantees
- Version pinning: definitions are versioned (
DRAFT→ACTIVE→ARCHIVED); an interview keeps the version it started on. - Server validation: every submitted screen is checked again (types, options,
dependent values, lookups the runner can see, required, validation formulas).
A rejected screen answers
400 FLOW_SCREEN_INVALIDwithfieldErrorsand the interview stays on that screen. - One submission per step: each request carries the interview's
revision; a stale one (double click, second tab) is409 FLOW_STALE_SCREENand changes nothing. - Idempotent writes: a retried or resumed request never repeats a completed
step, and a step whose outcome is unknown (the request was interrupted
mid-call) fails to its fault path with
FLOW_STEP_INTERRUPTEDinstead of running twice. - Back returns to the previous screen with its answers, only when nothing was written since it.
- Loop guard: 500 elements per request, 200 runs of one element per request,
5,000 per interview (
FLOW_STEP_LIMIT,FLOW_LOOP_LIMIT). - Interviews are kept 30 days.
Launching a flow
| From | How |
|---|---|
| Record pages | Launch context record:<OBJECT> (record:LEAD, record:custom:project…) adds the flow to the page's Flows button. |
| List pages | list:<OBJECT> adds a bulk-bar action; the selected ids arrive in recordIds. |
| Page builder | page, rendered by the flow component. |
| A link | standalone: /dashboard/flows/run/<apiName> (?recordId=&objectType= to launch on a record, ?interview= to resume). |
| Workflows | A Run flow step (RUN_FLOW, { "flowApiName": "…", "inputs": { "var": "field" } }) runs an autolaunched flow with the trigger record as $Record, as the workflow's creator. |
| Events | An autolaunched flow's eventTypes (any catalog event or custom event; a custom event's payload is {!$Event.<field>}). It runs as the flow's creator, never for writes the same flow made. |
API
Running flows is public (tag Flows). API keys and OAuth tokens can run flows;
their record steps need the matching READ_/WRITE_ scope, and custom action,
function, email and approval steps need full access.
Each answer is the interview: status (PAUSED on a screen, COMPLETED,
FAILED, CANCELLED), revision, screen (fields with their labels, options,
current value and the visibleWhen conditions the client evaluates as the user
types), message, error and outputs.
Building flows (/v1/flows/definitions…, /v1/flows/validate, /v1/flows/meta)
needs an org owner or admin and is not part of the public contract.
Metadata
Flows move between orgs as FLOW components (flows/<apiName>.flow.json) with
the definition of their active version; interviews never move. See
metadata.
Serverless functions
Write TypeScript or JavaScript that SalesOS runs for you in an isolated sandbox — from workflows, custom actions, events, schedules, HTTP and the save path.
Approvals
Multi-step approval processes on any record — entry criteria, approvers by user, profile or manager, record locking while pending, and status write-back.