Orchestrator
The Orchestrator is PeoplePit’s workflow orchestration engine. Where an Automation is a sentence — when this happens, do these steps — an orchestration is a process: a diagram of connected steps that can branch, run in parallel, pause for days waiting on a person or an external system, loop over collections, recover from errors, and call other orchestrations. You build it visually in Admin → Orchestrator.
Your existing Automations keep working exactly as before — the Orchestrator is a second, independent engine for the processes that outgrow a single trigger-and-action list. A good rule of thumb: data chores (rollups, duplicate sweeps, field syncs) stay Automations; multi-step business processes (onboarding, approvals, provisioning) are orchestrations.
Building a flow
Section titled “Building a flow”Create one with + New orchestration (name first, like everywhere else) and you land on the canvas. A flow is a graph:
- Nodes are the steps — one Trigger, then Actions, Conditions, Parallel/Join, Loops, Waits, Subflows, Ends.
- Edges are the transitions between them. Use Connect, click a source node, then a target.
Select any node or edge to configure it in the right-hand panel. Validate checks the whole graph (unreachable steps, missing branches, unpaired joins…) at any time.
Draft, publish & version history
Section titled “Draft, publish & version history”Your edits are a draft that autosaves continuously — even while incomplete or invalid. Refreshing the page never loses work, and a “Draft saved ✓” note confirms each save. The draft does not run: the currently published version keeps triggering and executing untouched while you work.
When the flow is ready, click Publish. That validates the graph, promotes the draft to the live version, bumps the version number, and records the published graph in version history. If validation fails, the errors are listed and nothing goes live. Discard changes throws the draft away and reverts to the live version.
History lists every published version (who published it and when). Restore to draft loads any past version back into your draft so you can review — and optionally tweak — before Publishing it as a new version. History is append-only, so rolling back is just publishing an older graph again; nothing is ever overwritten. Runs already in flight always keep the exact graph they started with, so publishing never disturbs them.
The trigger
Section titled “The trigger”Every flow starts at its single Trigger node, which can fire on any combination of:
- A record event — created / updated / deleted, optionally only when specific fields change, with an optional guard condition on the record.
- A workflow transition — e.g. when “Approve” fires on a record.
- A form submit — any generic form, or one specific form.
- A comment is added — on any record of a type, optionally scoped to one specific comment stream (a COMMENTS field), with an optional guard on the record. The run gets the commented record as
trigger.subjectplustrigger.comment.text(plain),.body(HTML),.author(display name),.authorEmail(the stable value to match in conditions),.date(the comment’s datetime) and.fieldCode. - A link in a sent message is clicked — when a notification rule or broadcast with Track link clicks is used. Type-agnostic: the run gets the record the message was about as
trigger.subject(any type), the record it was sent to astrigger.recipient(.id,.ref,.email, its fields),trigger.link(.url,.label,.clicks,.firstClick) andtrigger.message(.id,.subject,.ruleId). Narrow it to a subject type, one rule, links whose URL contains some text, or the first click only; link scanners are ignored unless you opt in. The classic follow-up is Create relationship from{{trigger.recipient.id}}to{{trigger.subject.id}}— the engine’s unique-edge rule makes repeated clicks harmless. - A schedule — every N minutes or daily at a time.
- A webhook / API call — generate a secret URL; POSTed JSON becomes
trigger.body. Email-received and message-queue triggers integrate here too: point a mailbox job or a queue bridge at the hook URL. - Manually — a Run button (you can hand it a subject record for testing).
- Another orchestration completing — chain flows together.
The context — how data moves
Section titled “The context — how data moves”Every run carries a context, addressable by dot paths everywhere:
trigger.subject.email,trigger.subjectId,trigger.body,trigger.inputs— what started the run.steps.create_ticket.id,steps.find_people.rows— every step’s outputs, under the step’s key.vars.total— variables you set with Transform steps.
Text inputs are templates — Hello {{trigger.subject.firstName}} — and a value that is exactly one placeholder keeps its raw type (arrays and objects survive). Conditions, loop items and transforms use expressions — sandboxed JavaScript over the same paths: trigger.subject.salary > 100000, steps.orders.rows.reduce((s,o) => s + o.amount, 0).
You don’t have to remember any of these paths. Click any field, then ⌗ Insert reference in the node panel: it lists what’s addressable right now — the trigger record’s fields, each earlier step’s outputs, and your variables — and drops in the token formatted correctly for that field. That matters because the two evaluators differ: template fields use {{ }} around a path and a dot for an array index ({{rows.0.from}}), while expression fields (Condition branches, Transform “set”, Loop items, Wait “until”) are plain JavaScript — no {{ }}, and a bracket for an index (rows[0].from), with == (not =) to compare. A hint by each field tells you which mode it is, and expression fields validate as you type — flagging a {{ }} used in an expression, a .0 index that should be [0], a stray =, or a syntax error before you ever run the flow.
The context has a size limit so a runaway query or loop can’t grow a run without bound — when a run exceeds it, it fails explicitly with “Run context exceeded …” rather than silently dropping data. The limit is configurable per workspace in Admin → Notifications → Settings → “Max orchestration run context” (default 1024 KB): raise it for flows that legitimately carry large content — for example email records with full HTML bodies and inline images — or reduce what your query steps select.
Actions
Section titled “Actions”Action steps do the work, through the same engine everything else uses (so validation, permissions, audit and notifications all apply):
| Group | Actions |
|---|---|
| Records | Create / update / read / delete a record, query records (filters + joins, $subject correlation; filter values take {{templates}} too, so a Loop can query per item or walk a chain via {{vars.cursor}}), add a comment to a record’s comment stream (any COMMENTS field — with optional author attribution, a backdated date, file attachments carried from a FILE field, a linked record, and a threading mode: independent, or reply to the latest comment so successive comments chain reply→reply→reply), create a relationship, run a workflow transition |
| Communication | Send email — To/Cc/Bcc recipients (emails or templates), with an optional From override (address + display name, e.g. the acting user via trigger.initiator.name), real file attachments from FILE-field values, In-Reply-To/References threading headers, and a generated Message-ID returned as step output (store it on your email record so a mailbox loopback reconciles by key instead of duplicating; headers/attachments need the SMTP transport). Screenshots pasted into the body are converted to mail-native inline images (CID) automatically, so they display in recipients’ mail clients. For replies, the optional Reply + Quoted history inputs compose the body around an invisible boundary — a mailbox loopback then reads replyHtml back exactly as sent instead of guessing where the reply ends — and every send gets an auto-generated plain-text alternative (better deliverability than HTML-only) — and notify users (bell + push) |
| Users | Ensure a user account — find-or-create a workspace account by email (e.g. the sender of an inbound email), optionally linked to a person record and added to a group. Created accounts have no password (sign-in only via password reset), existing accounts only get missing details filled in, and ADMIN groups are refused — a flow reacting to external input can never mint privileged access |
| Integrations | Call a saved integration endpoint, run an integration job (with its pipeline; the step fails if the job fails, and exposes created/updated/skipped/recordCount to later steps), send a raw outbound webhook |
| AI | Generate / summarize / extract / classify with the AI Assistant’s model (it’s one action — the prompt decides) |
| Utilities | Transform data into variables, run a sandboxed script, log a message |
| Bridges | Run a v1 Automation, mint a Portal action link |
Branching, parallel work and loops
Section titled “Branching, parallel work and loops”- A Condition node evaluates its branches in order and follows the matching edge (
elsecatches the rest). Branches can be context expressions or live record conditions built with the standard filter editor. - A Parallel node splits the run down every outgoing edge at once; the paired Join node waits until all branches arrive before continuing. (A node with several incoming ordinary edges is just a merge — first arrival passes through.)
- A Loop node iterates over any array (
steps.find_people.rows,trigger.body.items…): the loop body edge runs once per item (withsteps.KEY.item/.indexavailable), the body returns via a loop back edge, and the loop done edge continues afterwards.
Waits and human tasks
Section titled “Waits and human tasks”A Wait node suspends the run — state is persisted, nothing is held in memory, and the flow resumes when:
- Time — a duration (seconds / minutes / hours / days; second-level waits are approximate — the wait scheduler ticks every ~30 seconds) or an expression that yields a date (“until the contract date”).
- A record changes — watch a specific record (or any record of a type) until it matches a condition. This is the engine behind approvals.
- An external callback — the step exposes a single-use URL in
steps.KEY.callbackPath; send it to any system, and its POST resumes the flow with the posted body.
Human tasks are a pattern, not a special step — nothing about approvals is hardcoded. The Human task button on the canvas scaffolds it for you: create a task record (any type you design, with its own form and workflow) → notify the assignee → wait until the record reaches a decision → branch on the outcome. People act on the task like on any record — in the app or through a Portal action link — and because it’s an ordinary record, everything applies: permissions, forms, workflows, reports.
Error handling
Section titled “Error handling”Per action step you can configure:
- Retries — count, backoff and multiplier; failures wait and try again.
- An error path — an edge of kind error routes the failure to an alternate branch (notify an admin, try a fallback).
- Compensation — an undo action registered on the step. If the run later fails, compensations execute in reverse order (delete the account, cancel the laptop order, revoke the badge) before the run is marked failed.
Subflows
Section titled “Subflows”A Subflow node runs another orchestration and waits for it: inputs you map become the child’s trigger.inputs, and the child’s End outputs come back as the step’s outputs. Package “Employee onboarding” once, call it from three flows. Cycles are rejected at save time.
By default a subflow acts on the same record as the flow that called it — the parent’s trigger.subject is inherited, so record actions inside the child (update a field, run a transition, add a comment) just work without threading the id through inputs. Need it to act on a different record? Set the node’s Subject record to an expression that resolves to a record id (for example {{trigger.subject.id}}, or {{steps.find_person.id}} to hand the child a related record). Leave it empty to inherit.
Runs — tracking every execution
Section titled “Runs — tracking every execution”Admin → Orchestrator → Runs lists every execution; each run’s page shows the diagram with the path taken lit up (green done, blue running/waiting, red failed), every step with its inputs, outputs, timing and attempts, what the run is currently waiting on, and the full context. You can pause, resume, cancel, or — after a failure — retry a run from its failing step.
Runs are durable: thousands can be in flight, waiting runs survive restarts, and a crashed step is picked up automatically within a minute.
Export & the AI assistant
Section titled “Export & the AI assistant”Orchestrations travel with configuration packages (identified by name, like automations — re-select instance-specific references such as transitions or endpoints after import), and the AI assistant can list, inspect and draft them for you.