Skip to content

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.

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.

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.

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.subject plus trigger.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 as trigger.recipient (.id, .ref, .email, its fields), trigger.link (.url, .label, .clicks, .firstClick) and trigger.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.

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 templatesHello {{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.

Action steps do the work, through the same engine everything else uses (so validation, permissions, audit and notifications all apply):

GroupActions
RecordsCreate / 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
CommunicationSend 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)
UsersEnsure 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
IntegrationsCall 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
AIGenerate / summarize / extract / classify with the AI Assistant’s model (it’s one action — the prompt decides)
UtilitiesTransform data into variables, run a sandboxed script, log a message
BridgesRun a v1 Automation, mint a Portal action link
  • A Condition node evaluates its branches in order and follows the matching edge (else catches 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 (with steps.KEY.item / .index available), the body returns via a loop back edge, and the loop done edge continues afterwards.

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 assigneewait until the record reaches a decisionbranch 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.

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.

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.

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.

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.