PrintFactory Workflow · Building a flow
Connectors are the arrows that join one component to the next. They decide where a job goes after each step, and which jobs are allowed to go there.
A flow is a set of components joined by connectors: an input, a few preparation steps, and a printer or an export. The components do the work; the connectors decide the order and carry the job from one to the next, along with its files, its job data and its history. A connector never changes a job. Only components do that.
Each connector has four parts:
| Part | What it means |
|---|---|
| Where it starts | A result socket on the component the job has just finished, such as OK or Fail. Connectors always start at a socket, never at the component as a whole. |
| Where it ends | The component that runs next. You drop the arrow onto the component itself, because components have several results but only one way in. |
| Name | A label for the route, such as Retail or Oversize. Names appear in the job history and can be found with the canvas search box. |
| Condition | An optional test that decides whether a job may take this route. No condition means every job takes it. |
Every component shows its result sockets as small labelled tabs along the bottom of its panel on the canvas. Which sockets a component offers is fixed by what the component does, and you cannot add or rename them.
| Socket | You will see it on | It means |
|---|---|---|
| OK | Every component | The step finished successfully. |
| Fail | Most components, including the inputs, Preflight, Printer, Cutter, Layout, Nester, Database and JavaScript | The step could not process the job. A job leaving through Fail is in an error state. See What happens when a job reaches a branch. |
| Error | Webhook | The same idea as Fail, named differently on that component. |
| Fixed | Preflight | The file passed, but only after Preflight corrected it, so corrected work can be routed differently from clean work. |
| Handled, Filled | Jig | A part-filled jig and a completed jig, so each can take its own route. |
Releasing anywhere else creates nothing. If that socket already connects to that component, Workflow selects the existing connector instead of adding a second one.
Drag the start of the arrow onto a different socket, or drag the arrow head onto a different component. Both ends can be moved without losing the connector's name or condition, so you can re-route a branch without rebuilding it.
Double-click the arrow to open its Condition window.
Select it and press Delete. If it holds a condition, Workflow asks you to confirm and offers Copy Script, which puts the condition on the clipboard before it is removed. Deleting a component deletes all of its connectors too, with the same warning.
The canvas search box matches connector names as well as component names, and highlights what it finds. It can only find connectors that have been named, which is one more reason to name every route that carries a condition.
Double-click a connector to open the Condition window. Give the route a name, then choose how the test is described:
| Option | Use it when |
|---|---|
| Match Rules | You are comparing job data or file properties such as customer, size, file type or approval status. Rules are built from drop-down lists, with no scripting, and they are checked as you build them. The right choice for almost every flow. |
| Run Script | The test needs calculation, text handling, dates, or several pieces of data combined in a way rules cannot express. Requires basic JavaScript. |
A name is required before you can save, and in Run Script mode OK stays greyed out until the script is not empty. Both versions are stored, so you can switch between the two without losing your work. Only the option selected when you press OK is used when jobs run.
Each rule reads left to right: what to look at, which value, how to compare it, what to compare it with. The buttons at the end of the row add a rule (+), remove it (-) or nest it inside a new group ([...]).
A group header reads All of the following rules or Any of the following rules, and groups can be nested inside other groups, which is enough for tests like "approved, and either a PDF or larger than 100 MB". The full list of what you can look at and how you can compare it is in the Rule reference below.
Orders arrive with a Destination of either Retail or Wholesale, and each is resized differently. Draw two connectors from the same OK socket:
| Connector name | Rule | Goes to |
|---|---|---|
| Retail | Keyword · Destination · Is · Retail | Resize 600 × 400 |
| Wholesale | Keyword · Destination · Is · Wholesale | Resize 300 × 200 |
The two rules can never both be true, so each job takes exactly one route. An order with any other value, such as retail in lower case, a blank or a typo, matches neither and is reported as a problem job, which is usually the right outcome for an order that was not filled in correctly.
A script condition is a short JavaScript function that answers one question: should this job take this route? Return true to accept it, false to reject it.
function () {
return Key.Destination === 'Retail' && Documents.length === 1;
}The function runs once per job, per connector, with the job's data available to it. The Script reference below lists everything you can read; console.log() writes to the Workflow log and is the simplest way to see what your script actually received.
When a component finishes, Workflow takes the socket that matches the outcome and checks each connector leaving it.
| Situation | What happens to the job |
|---|---|
| Nothing is connected to that socket | The route ends here. The job is finished, or reported as a problem job if it came out of a Fail socket. |
| One connector accepts it | It continues to that component. |
| Several connectors accept it | The job takes the first route, and a copy of the job is created for each of the others. Each copy then runs independently, with its own history. |
| No connector accepts it | The job stops and is reported as a problem job, with the message no further valid connections. |
Jobs that have already gone wrong follow two extra rules:
Routes are checked in the order the connectors were drawn, which is not shown on the canvas. Never rely on that order to give one branch priority. Put the priority in the conditions instead.
Several connectors from one OK socket, each testing the same value for a different result such as customer, product or material. Keep the conditions mutually exclusive so each job takes one route.
There is no automatic "everything else" route. If unusual jobs should be looked at rather than reported as problems, add one more connector whose condition covers what the other routes do not, and send it to a hot folder, a Webhook or a manual step. Letting the job fail instead is a perfectly good design when an unknown value means the order data is wrong.
Two connectors that both accept the same job give you two jobs on purpose: one prints, the other is exported or reported to your MIS. Each copy is a complete job in its own right.
Any number of connectors can end on the same component, so branches prepared differently can rejoin a shared nester, printer or export.
Connect the Fail socket of the components that matter to a Webhook or a hot folder, so a failure raises a notification instead of waiting quietly in the problem list.
| What you see | What to check |
|---|---|
| A job stops with no further valid connections | No route accepted it. Compare the job's data with your rule, including capitals and stray spaces, and check the value is actually set by that point in the flow. |
| The same job appears twice | Two routes both accepted it. Make the conditions mutually exclusive, or remove the route you did not intend. |
| A branch never runs | Its condition is never true, or the previous step leaves by a different socket than you expect. A corrected file leaves Preflight through Fixed, not OK. |
| A job finishes early with no error | The socket it used has nothing connected to it, so the route simply ended. |
| A rule fails on data you know is there | The value is added later in the flow than this connector, the name is spelled differently, or the value has spaces around it. |
| A document rule fails on a multi-file job | Document rules must be true for every file in the job. Test a single property, or split the job earlier. |
| A script condition reports an error | Open the log: it names the line number. Guard data the script assumed would be there, and add console.log() lines to see what it received. |
| You cannot edit the condition | The flow is running. Stop it, edit, then start it again. |
| The connector will not draw | You are dropping onto an input component, onto the component you started from, or that socket already connects to that component. |
Reference
| Type | Value | Notes |
|---|---|---|
| Keyword | Any keyword carried by the job, such as Destination, Approved or Deadline | The list offers the keywords the flow already knows about, and you can type a name that an earlier component adds at runtime. Names and values are case-sensitive. |
| Document | Name, Type or Size | Type is the file type, such as pdf. Size is in megabytes and takes numeric comparisons only. A document rule must be true for every file in the job. |
| Comparison | Applies to | True when |
|---|---|---|
| Is / Is Not | Text, numbers | The values are equal, or are not. Text comparison is exact, including case and spaces. |
| Is Less Than / Is Greater Than | Numbers | Numeric comparison, rounded to two decimal places. |
| Is Before / Is After | Dates | Chronological comparison. Use a full date and time with an explicit offset, such as 2026-09-30T17:00:00+01:00. |
| Contains / Does Not Contain | Text | The text appears anywhere in the value. |
| Begins With / Ends With | Text | The value starts or ends with the given text. |
| Is True / Is False | Yes/no values | The value is set that way. No comparison value needed. |
| Exists / Does Not Exist | Keyword, Document | The job carries that value at all, whatever it is. No comparison value needed. |
Comparisons that do not suit the data are refused when you save. Contains on Document Size is rejected, for example, because a size is a number.
A script condition is a single function returning true or false. These objects are available while it runs:
| Object | What it holds |
|---|---|
Key | The job's keywords as properties, for example Key.Destination. Use Key.hasOwnProperty('Deadline') to test for presence. Always available, even when the job has no keywords. |
Documents | An array of the job's files. Each has Name, Type, Size (megabytes), Location, ID, PageCount and Pages; each page has Width, Height and HasShape. |
Job | The complete job data as an object, for reading order details that were never turned into keywords. |
$ | The job state written by earlier JavaScript components. An empty object when none has been set. |
console | console.log() writes a line to the Workflow log. |
Values assigned to Key properties are written back to the job when the script finishes, and changing Key.Copies duplicates job parts, which is why a connector script should test rather than change. An uncaught error, a syntax error or a function that never returns does not simply reject the job: it is reported against the job, with the line number in the message.
Conditions built before rules and scripts existed use placeholders in curly brackets. They are still evaluated exactly as before, so a flow that runs well needs no conversion. Rebuild them with Match Rules when you next edit one.
| Form | Meaning |
|---|---|
{Key:Destination} == 'Retail' | A keyword substituted into a JavaScript comparison. |
{Job:/Job/Order/Priority} == '1' | A value read from the job data by path. |
{Document:Type} == 'pdf' | A document property. |
exists {Key:Deadline} | True when the keyword is present. !exists inverts it. |
{Key?:Deadline} | The question mark marks the lookup optional. Without it, a missing keyword makes the job fail rather than quietly rejecting the route. |
Connectors are saved inside the flow, so copying, duplicating or exporting a flow keeps every route, name and condition with it. In the flow file, one connector looks like this:
<Connector ID="…" Name="Retail">
<From ID="…" Connection="OK"/>
<To ID="…"/>
<Condition>…what is actually evaluated…</Condition>
<Rules>…the rules you built…</Rules>
<Script>…a hand-written script…</Script>
<MatchRules>true</MatchRules>
</Connector>
From carries the socket name, MatchRules records which option is selected, and Condition holds what is evaluated at runtime: either the script generated from your rules, or your own script. A connector whose components are missing, or that repeats a connection that already exists, is dropped when the flow is opened.
Flows can also be built and inspected by other systems. Connector conditions are typed there, in four modes: all (accept every job), rules (the rule definition, turned into a script by the server), script and expression (the older placeholder form).
| Operation | Endpoint |
|---|---|
| List, read, create, update or delete a connector | /flow/{flowId}/connector |
| Read or replace a condition | /flow/{flowId}/connector/{connectorId}/condition |
| Read or replace the stored XML | /flow/{flowId}/connector/{connectorId}/configuration |
| Read the condition schema | /connector/condition/schema |
| Check a flow before applying it | /flow/{flowId}/validate |
The same operations are available to AI assistants through the Workflow MCP server. As on the canvas, the flow must be stopped before anything can be changed.
Related: Components and result sockets · Keywords and job data · The JavaScript component · Testing a flow with sample jobs · Job history and problem jobs