Ch. 17 · ServiceNow

ServiceNow Flow Designer: Triggers, Run Options, Duplicates

Pick the right Flow Designer trigger and run option, avoid duplicate flows on one table, and build flows that a team can own and debug.

~7 min readintermediateupdated Oct 12, 2026

Flow Designer lets you build automation from a trigger, a sequence of actions and some flow logic, without writing script. In current releases it lives inside Workflow Studio, and ServiceNow’s documentation now steers new process automation there rather than to business rules. That ease is also the risk: a flow built in an afternoon can fire three times per update, collide with another team’s flow on the same table, and fail quietly in the background. “Ownership” in an interview answer means you can say exactly when the flow starts, as whom it runs, and who notices when it breaks.

The parts of a flow

A flow has one trigger and a body of actions (Create Record, Update Record, Ask for Approval, Send Email, spoke actions for external systems) arranged with flow logic such as If, For Each, Do the following in parallel, Wait for a condition and Go back to. Values move between steps as data pills: the trigger record, its changed fields, outputs of earlier actions. Reusable pieces go into subflows (which take inputs, unlike flows) and custom actions.

Each run of a flow is a context, and every context has an execution details page showing which steps ran, with what inputs and outputs, and where it failed. That page is the first place to look when someone says “the flow didn’t work”.

The trigger is the contract

Record triggers come in three kinds: Created, Updated, and Created or Updated, on a specific table, with an optional condition. Updated triggers also need a run option, and in current releases the choices are:

  • Once: run once for the life of the record.
  • For each unique change: run for every unique update to a non-system field, even while an earlier context is still running.
  • Only if not currently running: run on a matching update only if no context of this flow is running on the record. Older releases called this Always.
  • For every update: run every time the record is updated and matches, regardless of running contexts.

The advanced options matter just as much: whether interactive sessions, non-interactive sessions or both can trigger the flow; whether particular users are included or excluded (integration accounts are a common exclusion); whether the flow runs on the table only or on its extended tables too; and whether it runs in the background (the default) or in the foreground, blocking the user’s session until it finishes. Record triggers also ignore records written by applying update sets or importing XML.

What each run option actually does

The run options are easiest to compare on one record’s history. This Node.js model (plain JavaScript, not the flow engine) replays an incident that’s created as P3, escalated to P1, worked, downgraded and escalated again, with a flow that stays running after its first start because it waits for a bridge task to close. The “unique change” option is approximated as “this exact field change hasn’t been seen on this record before”, which is how the documentation’s example behaves.

const updates = [
  { step: 2, set: { priority: 1 } },                    // escalated to P1
  { step: 3, set: { work_notes: 'bridge opened' } },
  { step: 4, set: { assignment_group: 'Network' } },
  { step: 5, set: { priority: 2 } },                    // downgraded
  { step: 6, set: { priority: 1 } },                    // escalated again
  { step: 7, set: { work_notes: 'vendor engaged' } },
];

const isP1 = (rec) => rec.priority === 1;
const changesToP1 = (rec, set) => set.priority === 1;
const STILL_RUNNING_UNTIL = 99;  // the flow waits on a bridge task, so its first context stays open

function simulate(option, condition) {
  let rec = { priority: 3 };                            // step 1: created as P3, no match
  const seen = new Set(), runs = [];
  let runningUntil = -1;
  for (const u of updates) {
    rec = { ...rec, ...u.set };
    if (!condition(rec, u.set)) continue;
    const key = JSON.stringify(u.set);
    let start = false;
    if (option === 'Once') start = runs.length === 0;
    if (option === 'For every update') start = true;
    if (option === 'Only if not currently running') start = u.step > runningUntil;
    if (option === 'For each unique change') start = !seen.has(key);
    seen.add(key);
    if (start) { runs.push(u.step); runningUntil = STILL_RUNNING_UNTIL; }
  }
  return runs;
}

for (const opt of ['Once', 'For every update', 'Only if not currently running', 'For each unique change'])
  console.log(`${opt.padEnd(30)} priority is 1         -> runs at steps [${simulate(opt, isP1)}]`);
console.log(`${'For every update'.padEnd(30)} priority changes to 1 -> runs at steps [${simulate('For every update', changesToP1)}]`);
JavaScript
Once                           priority is 1         -> runs at steps [2]
For every update               priority is 1         -> runs at steps [2,3,4,6,7]
Only if not currently running  priority is 1         -> runs at steps [2]
For each unique change         priority is 1         -> runs at steps [2,3,4,7]
For every update               priority changes to 1 -> runs at steps [2,6]
Text

Suppose the requirement is “page the major incident manager every time an incident becomes P1”. Once misses the second escalation at step 6. For every update with “Priority is 1” pages on every work note while the incident is P1: five pages instead of two. Only if not currently running is suppressed by the still-running first context. For each unique change fires on the work notes and the reassignment (each is a new change) but not on the second escalation, because priority changing to 1 has been seen before.

Only the last line matches the requirement, and the fix wasn’t the run option at all: the condition now describes a transition (“Priority changes to 1”) instead of a state (“Priority is 1”). A useful rule of thumb: conditions that describe a state belong with Once; repeatable events need a condition that describes the change.

The documentation adds two warnings worth repeating in an interview. A flow on For each unique change that updates its own trigger record from a non-interactive session can trigger itself recursively. And flows that contain approvals should trigger Once, using Go back to logic when an approval has to be asked again.

Two flows, one table

Problems multiply when nobody owns the table as a whole. If two flows on incident have the same trigger conditions, there’s no way to know which runs first, and if both set the assignment group, the last writer wins. Add a business rule that also sets assignment group and an assignment rule from years ago, and the field’s final value depends on timing.

ServiceNow’s guidance is to give each flow on a table unique conditions. In practice that means someone keeps an inventory: for each table, which flows, business rules and assignment rules write which fields, under which conditions. Before adding a flow, check whether an existing one should be extended instead. “One field, one writer” is a good default.

Running as whom

A flow runs as the system user or as the user who initiates the session, and you can grant extra roles for the run. Running as the system user is convenient and dangerous in the same way as a server script: ACLs don’t stop it, so a flow triggered by a low-privilege user can read or change data that user couldn’t. Running as the initiating user keeps actions inside that user’s access, which is the safer choice for anything triggered by end users or inbound email (an inbound email flow from an unknown sender runs as Guest). When a step genuinely needs more access, move it into a subflow that runs with the specific roles it needs, rather than elevating the whole flow.

Trigger conditions have an access angle too: if the triggering user can’t read a field used in the condition, the flow may not evaluate the way you expect.

Making a flow someone can own

The flows that survive are the boring ones:

  • A name and description that state the contract, for example “Incident: page MIM when priority changes to 1”, including the owning team.
  • Built in the owning team’s application scope, so its tables, flows and access are packaged together and changes move as a unit.
  • Subflows for anything reused, with explicit inputs, instead of copying the same ten actions into four flows.
  • An error path. Add error handling for steps that call external systems, and route failures to a queue a person watches. A background flow that fails without telling anyone is worse than no automation, because everyone assumes it ran.
  • Tested with representative records, including the ones that shouldn’t trigger it, using the flow’s test option and then real updates as a non-admin user.

Interview practice

Users report that every time they add a comment to a P1 incident, the major incident manager receives another page. The flow was built with an Updated trigger, condition “Priority is 1”, and run option For every update. A colleague suggests switching to Once. What do you recommend?

Answer

Switching to Once stops the duplicate pages but introduces a new defect: an incident that’s downgraded and escalated again won’t page anyone the second time. The real problem is that the condition describes a state, so every update to a P1 incident matches. I’d keep a run option that allows repeat runs and change the condition to describe the transition, Priority changes to 1, so the flow starts exactly when an incident becomes P1. If the trigger should also catch incidents created as P1, use a Created or Updated trigger and make sure the created case is covered.

Before changing it, I’d check the execution details for a handful of recent contexts to confirm which updates started them, and check whether any other flow or business rule on incident also pages on priority, since duplicate notifications sometimes come from two automations rather than one. After the change, I’d test with a sequence like the one above (escalate, comment, downgrade, escalate) as a normal agent and confirm exactly two contexts.

More in ServiceNow

read ✓ServiceNow · hard

ServiceNow ACL Scripting and Access

When an ACL really needs a script, how to write one that is cheap and null-safe on every row, and how to prove it works by testing as the user.

~7 min readread →
read ✓ServiceNow · hard

ServiceNow Caching and Invalidation

What a ServiceNow instance caches on each node, why a system property change triggers a costly flush, and where changing settings belong.

~7 min readread →
esc