Design a Process
Goal
Draw a process: the lanes it crosses, the steps in each, and the connections between them.
Opening the designer
From Processes, select a process and choose Design — or click the row. The canvas opens inside the app, with the breadcrumb and the title bar still above it.
A process you have never drawn opens on a starter diagram rather than an empty canvas: one lane, a start and a finish. That is the shape every process has, so it is the shape you begin from.
The four regions
- The palette, on the left edge — the things you can add: a lane, and the node kinds.
- The outline, beside it — the process as a tree: the root, its lanes, and the nodes in each. Selecting in the outline selects on the canvas, and the other way round. Drag its right edge to widen it.
- The canvas, in the middle — the diagram itself.
- The properties panel, on the right — everything about whatever is selected.
Lanes: who does the work
Add a lane from the palette, or drag one onto the canvas. Name it for the role, not the person — "Manager", not a name — because a process outlives whoever is doing the job this month.
- Rename a lane by double-clicking its header.
- Resize one by dragging the boundary below it. The lanes underneath move down with it, and so do their nodes.
- A node belongs to the lane it sits in. Drag it into another lane and it changes hands.
Set the lane's Role, or its steps go to nobody
Naming a lane "Manager" tells a reader who does the work. It does not tell the engine. Select the lane and choose its Role in the properties panel — a lookup over the security roles this environment has, with a search box: start typing and the list narrows, by the role's name or by its logical name.
It used to be a box you typed the name into, which meant a typo produced a lane whose work went to a role that does not exist — and nothing said so until a run stopped. Leave it empty and each step decides for itself.
Every user task in that lane then goes to whoever holds that role — which is the whole reason a swimlane is worth drawing, and it is set once for the lane instead of on each step. A step with an Assigned to expression of its own overrides it.
A lane with no Role and steps with no Assigned to is a process that runs and then stops with "… could not be assigned to anybody". The task is still created, so an administrator can see it — but it is in nobody's list.
Two more things have to be true before a role reaches a person: the role has to exist (Roles, in the Admin Control Center) and somebody has to hold it in a unit (Process Roles, in the Organisation app). See Set up your organisation.
Nodes: the steps
| Kind | What it means |
|---|---|
| Start | Where the process begins. Draw exactly one. |
| Finish | Where it ends. There may be several — approved and rejected are both endings. |
| Task | A step. The one you will use most. |
| Branch | A decision: one way in, several ways out. |
| Fork | Split into paths that all run. |
| Join | Wait for those paths to come back together. |
| Timer | Wait for a length of time, or until a moment. Nobody is asked to do anything. |
A task also has a kind of task, chosen in the properties panel — user, service, script, manual, send, receive, business rule. It draws a small badge on the node, and it is how a reader tells "somebody fills this in" from "the platform does this by itself" at a glance.
Which steps wait
Only a User task makes the run wait for a person. Every other kind of task runs its On enter and On exit and carries straight on; its kind is there for whoever reads the drawing.
When a run has several routes
After a Fork, a run is on several routes at once. Two choices decide what happens as they end.
- A Join's Carries on when: every route has arrived (the default), the first route arrives, or a number of routes have arrived, with How many routes beside it. It counts the routes its Fork started, not the connections drawn into it. A route still going when the Join carries on is stopped, and its open task is withdrawn.
- A Finish's When a route reaches it: end this route (the default: the run ends when no other route is still going), or end the whole run, which stops every other route. That second one is the ending for "rejected, stop everything".
A step's Title and its Name
Every step has both, and they do different jobs — the same pair as a table's display name and its logical name.
- Title is what the box reads on the canvas. Write it for a reader, in whatever language you work in, and reword it whenever you like.
- Name is what code, the run history and another process call the step by. It is lower-case letters, digits and underscores, and it is required.
You do not normally type the name. It fills in from the title as you type — "Approve Leave"
becomes approveleave — and it stops following the moment you edit it yourself, so a name you
chose is never overwritten. Clear the box to get the suggestion back.
Two steps cannot share a name. If a title derives one that is taken, the newcomer gets _2; the
step that had it keeps it, because renaming that one would change what every script already
pointing at it refers to.
A title in Persian or Arabic derives no name, so the step is named after its kind — task,
task_2. Rename it to something you will recognise in a run's history.
A name used to be optional, so a process drawn earlier may have steps with none — or with a name in the old free-form style. Those documents are no longer accepted.
Open each one in the designer and save it. The names fill in from the titles as it opens, and saving is the whole of the fix. Nothing converts them for you, and a process you have not reopened will refuse to save the next time you edit it.
Connections
Choose Connect in the palette, then drag from one node to another. Connections are routed automatically — you do not draw the corners.
- Give a connection a label where it needs one. On a branch's outgoing connections it is what a reader takes for the decision: Approved, Over limit, Rejected.
- Connector style — curved or right-angled — is on the process root and applies to the whole diagram, because a diagram mixing the two reads as a mistake rather than a choice.
Variables: what the process carries
Select the process root in the outline and add Variables — the values this process carries from one step to the next. Each has a name and a type (text, number, yes/no, date).
The name is what a script writes: getVariable("total"). Renaming a variable does not rewrite
the scripts that name it, so treat the name as the thing you are naming, not a label you can change
freely. A variable you have not named yet is simply not offered anywhere.
Coforms: what a person fills in to finish a step
A coform is the short form somebody fills in to complete a user task, together with the outcomes — the buttons that end it. It is not a model-driven form over a table: it belongs to the process, travels inside the process document, and asks only what the decision needs.
Select the process root in the outline — which is where the designer opens — and you get Coforms below Variables, because a coform belongs to the process rather than to any one step: several steps can show the same one.
Each coform has:
- A coform name, which is how a task points at it, and a heading, which is what the person reading it sees.
- Fields — a field name, a label and a data type. They are the platform's own field types, so a coform gets the same date pickers, calendars and choice lists a record form has.
- Outcomes — one row per button. The button text is what is on it, the style is how it looks (ordinary, primary, destructive), and Requires a comment makes that one button insist on an answer in the coform's first multiline field. Set it on a rejection and leave it off an approval: that is the difference the setting exists for.
Then select a user task and pick its Coform from the list.
A task with no coform still completes, but the person gets no way to say anything about it. A process whose human steps ask nothing is usually a process that has not finished being designed.
What they look at while they decide
Below the coform, What they look at chooses what fills the main area while the person is deciding: nothing, a record's form, a list, a report or a screen. Choose one and the rest of the section appears.
| Field | What it is for |
|---|---|
| App | Which app it opens inside. Leave it blank and it opens in an app that has what the step is about: the one the person is in, if it does, otherwise one that takes part in business processes. That is what lets a process work after being imported somewhere else. |
| Table / Report / Screen | What the step is about. Which of the three you get depends on what you chose above. |
| Which form / Which view | A named form or view instead of the table's default. Blank uses the default. |
| Opened | Read-only, or for editing. Read-only is the usual answer — somebody is reading in order to decide — and editing runs under the record's own permissions: a task grants no access of its own. |
Every one of these is a dropdown of what this environment actually has. They were boxes you typed a logical name into, and a name typed by hand can only be spelled wrong — which shows up as "the step opens nothing", weeks later, in front of whoever the process was built for.
Which record it opens on is not in this list: it is an expression, so it lives under Code
with the rest of them. Usually getRecordRef("request") — the record this run is about.
Parameters: what else the step is told
The record answers which one, and on a form that is usually the whole question. It is not the whole question anywhere else: a report wants the period it covers, a screen wants the amount being approved and who raised it. Those values are in this run rather than in the artefact.
Select Add parameter, give it the name the report or screen expects, and its value appears
as a row under Code — because a value is an expression over the process's variables and the
state of this run: getVariable("month"), instanceStartedBy().
Names are how the artefact finds a value, so two parameters cannot share one and a parameter with no name is dropped when the process is saved.
Outcomes: which decision takes a connection
A user task ends when somebody presses one of its coform's outcomes, and each outcome sends the process a different way. Select a connection leaving a user task and set Taken when — a dropdown of that task's coform's outcomes.
That is what makes Approve or reject one step rather than a task followed by a branch that asks again what the person already decided.
- It is offered only where the task shows a coform with outcomes. Elsewhere there is nothing to choose from.
- A coform with one outcome needs none of this: one button, one way out.
- With several, each one wants its own connection. The Problems panel says so — it reports an outcome no connection takes, two connections claiming the same one, and a connection naming an outcome the coform no longer offers, which is what happens when you edit a coform that another task also shows.
Calling another process
Set a task's Type to Call process and pick the Process to run. The idea is that this step waits for that process to finish, then carries on — but the engine does not run it yet. The choice is kept and the Problems panel checks one is picked; at run time the step runs its On enter and On exit and carries straight on without starting the other process, like every step that is not a user task.
Use it when several processes need the same piece of work — an approval, a credit check. One process called three times stays one thing to change; three copies of it disagree within a year.
The picker lists the other processes in this environment. A process cannot call itself.
Conditions: which way a branch goes
Select a connection leaving a branch and open its Condition under Code — one expression
that decides whether the flow takes that connection. getVariable("approved") on one side,
!getVariable("approved") on the other.
It is offered on a branch's connections and nowhere else. A task has one way out, so a condition there would be a question with one answer.
A condition may only read. It decides a route, and the engine may ask it more than once, so a condition that changed something would make the process depend on how many times it happened to be asked — the editor does not offer you the words to do it.
Who a task goes to
A user task has an Assigned to row under Code: one expression answering who should do it.
| You want | Write |
|---|---|
| Whoever holds a role | role("Manager") |
| A particular person | user("a.khan") |
| A team | team("Finance") |
| Whoever started the process | instanceStartedBy() |
It is an expression rather than a picker on purpose: the assignments worth writing are the ones a picker cannot express — the approver for the amount in a variable, the manager of whoever raised it.
Only a user task has one. A service task is done by the platform, so there is nobody to give it to.
A step with no expression falls back to its lane's Role, which is the ordinary case: write the expression only where one step differs from the rest of the lane.
Waiting, and running late
A timer: waiting for the clock
Draw a Timer where the process should simply wait. Nobody is asked to do anything: the run stops there and carries on by itself.
Set Fires to After a length of time and fill in Wait for: a number and a unit picked beside it — 3 Days, 4 Hours, 90 Minutes.
A timer with no length does not wait — it stops the run. It is the one setting on a process with no sensible default behind it: firing straight away would silently turn a wait into no wait, so instead the route stops and the run's history says "… does not say when it fires". If a timer you drew never fires, that is the first thing to check.
Months and years are not offered, on purpose. A month is 28 days in February and 31 in March, so a wait written that way would mean a different number of hours depending on when it started. Ask for 30 days.
The length is stored as an ISO-8601 duration — P3D, PT4H, PT90M — which is what the engine
reads and what a process written by hand or generated by an AI has to say. You never type it; the
number and the unit are the whole of the field. A length written by hand in two units (PT4H30M)
opens as the same length in one (270 Minutes).
Choose At a moment the code answers instead when the moment depends on the data — the date on the record, a variable somebody filled in. Then write the expression under Code; it may answer either a moment or a length.
A deadline: when a step should be done by
Select a user task and fill in Due within — the same number-and-unit pair the timer takes. The clock starts the moment the task appears in somebody's list — not when they open it — so a task raised on Friday afternoon does not quietly gain the weekend.
The person sees Due … under the task in their list, and once the moment has passed it turns red and reads Was due …. Beside it, Waiting says how long the task has been sitting there.
When it is late says what the process does about it:
| Choice | What happens |
|---|---|
| Mark it late | Nothing moves. The task is visibly late in the list, and the run's history records it. |
| Offer it upwards as well | Whoever the lane's escalation names is offered it too. The person who already had it keeps it — one more person can act, and nothing is taken away from somebody who might be about to finish. |
| Decide it automatically | The process presses one of the task's own buttons for them. Choose which under Decide it as. |
Deciding it automatically uses the step's own outcomes, so the diagram already shows where the process goes next — there is no second, invisible way out of the step. "If nobody approves within three days, treat it as rejected" is written by choosing Decide it automatically and then Reject.
Leave Due within empty for a step with no deadline, which is most of them — clearing the number is what takes a deadline back off.
While you are building, set the lengths to a minute or two, publish, and start a run: the escalation,
the wait and the automatic decision all happen while you are looking at the run's history. That is
what bpms-deadlines-and-timers ships set to, and the only unrealistic thing about
that sample.
Code: what runs, and what is worked out
Select a task and you get its Code list — everything about that step that is written in BS Lang. Selecting any row opens the code page.
The first four are the moments it can react to:
| Slot | When it runs |
|---|---|
| On enter | The process reaches this step. |
| On assign | The task has somebody — user tasks only. |
| On complete | That person marks it done — user tasks only. |
| On exit | The process leaves this step, whichever way it goes. |
The process root has three of its own, in the same shape: On start, On complete and On cancel. On start is where you give a variable its first value.
Below them, on a user task, are the things it works out rather than runs: Assigned to, Record, and one row per parameter, named after the parameter. They are in the same list because they are the same kind of thing — a piece of BS Lang belonging to this step — and one list is one place to look for "what does this step run".
The list says which rows already have something written on them, so you can see at a glance where the work is.
A branch has On enter and On exit, and no slot that chooses a route.
- On enter runs the moment the process arrives, before any condition is asked. It is where a branch does its arithmetic: work the answer out once, put it in a variable, and let every condition read it. Six bands of an amount then means six short conditions rather than six copies of the sum.
- On exit runs once a route has been chosen. It does not run when no condition was true — nothing left, so there was no exit.
- The decision stays on the connections. There is deliberately no code that picks a way out: a diagram whose branch is decided somewhere you cannot see is a diagram that lies about what the process does.
The code page
It fills the editor and carries the slot list down its side, so switching between a task's moments
is one click. The eye in its header lists everything you can call from that slot — which is
not the same everywhere: a condition and an Assigned to may only read, a handler may change
things, and only Assigned to is offered user(), role() and team().
There is no Save on the code page. Save in the title bar is the only one, and it saves the whole process.
Saving a script does not run it. The variables, conditions, assignments and scripts are stored with the design and checked as you write them; they run once the process is published and a run starts.
Problems: what is still missing
In the corner of the canvas is a bar saying how many problems the process has. Open it for the list; select a line and the designer takes you to the thing it is about — the node, the connection, or the process root.
It checks the things the engine will require:
- exactly one Start, and at least one Finish;
- nothing connects into a Start or out of a Finish, since a run begins at one and ends at the other, so such a connection is never taken;
- no step the flow can never reach, and none it can never leave;
- a condition on every connection leaving a branch, and at least two ways out of one;
- at least two ways out of a fork and two into a join, and the same number of each;
- every variable a script names is declared, and every variable row has a name;
- every script parses.
Nothing here stops you. A half-drawn process reports a page of problems and saves perfectly well — that is the point of drawing one. The list is how you find out what is left, and it is the same list the platform will apply when a process is published to be run.
A note on two of them. The panel says whether a script parses; whether it may call a particular name is the code editor's business, and it underlines that as you type against what that slot can see. And forks and joins are counted, not matched up one to one — a process that splits more often than it merges has a path still running when it should be over, which is nearly always the mistake.
Auto arrange
View → Auto arrange tidies the diagram left to right, in flow order, keeping every node in its own lane. It moves nodes horizontally and re-routes the connections; it never moves work from one person to another.
Generate with AI
Generate with AI describes a process in words and draws it. Whatever comes back is validated before it reaches the canvas, exactly as an imported file is — so a generated diagram that does not hold together is refused rather than drawn.
It corrects itself before it gives up. When what comes back does not hold together, the platform sends it straight back to the model with the problem named, rather than asking you to fix a schema error you never wrote. It tries up to three times, and the button reads Correcting… attempt 2 of 3 while it does. You are shown a failure only once correcting it has been tried, and what you are shown then is the last thing that was wrong.
It replaces what is on the canvas, and it does not save. Look at it before you press Save.
You describe the process, and nothing else. Which AI model answers is set up once for the whole platform by an administrator — there is no key to paste and no provider to pick, and nothing you type here goes anywhere except to that one model.
If no AI is connected, Generate says so. A tenant administrator connects one once, in Settings → AI, for everybody in the organisation. It is not something a maker turns on.
Saving
Save, in the title bar, writes the process. That is the only Save: the design is part of the component, so there is no file to keep in step with it and nothing to remember to export.
Leaving with unsaved changes asks first — from Close, from the navigation rail, and from the browser's own back button.
Publishing: saving is not shipping
Save keeps your work. Publish changes what runs. They are two buttons because they are two decisions, and the split exists for one reason: a process that is already running must not change under the people running it. Without it, correcting a typo at 4pm would rewrite the process every in-flight instance was following — including steps they had already passed.
So the engine reads only what you have published, and a publish is a numbered, frozen copy of the drawing as it was at that moment.
The line under the title says which of the three states you are in:
| It says | It means |
|---|---|
| Not published yet | Nothing is running this process. |
| Version 3 is running, and matches what you see | You are looking at what is live. |
| Version 3 is running. You have unpublished changes | Your saved edits are not live yet. |
Select Publish and the draft becomes the next version. If you have unsaved edits it saves them first, because publishing what is on screen is what the word means.
A few things worth knowing:
- Publishing an unchanged process is refused. If nothing has changed since the last publish there is nothing to publish, and a history full of identical versions is one nobody can read.
- There is no unpublish. A version somebody may be running is not something to delete. If you want to go back, put the previous drawing on the canvas and publish that — the history stays intact, which is what makes it worth having.
- Instances keep the version they started on. Something that began under version 2 finishes under version 2, whatever you publish while it is running.
Publishing does not start anything. It decides what runs the next time the process starts, and runs already going keep the version they began with.
Publish waits for the problems list to be empty. While the corner of the canvas lists anything, the button is unavailable, and holding the pointer over it says why. A process with problems would not run, and publishing it used to save the drawing first and then be refused.
Import and export
Export JSON downloads the design as a .bpms.json file — what is on the canvas now,
unsaved edits included, because the file is for carrying a design somewhere else and exporting
last week's version while you are looking at this week's is the sort of surprise you only find on
the far side.
Import JSON loads one back. It replaces what is drawn and does not save, so an import that turns out to be the wrong file is undone by leaving the screen without saving.
The file is checked before it is loaded: the version, the lanes, the nodes, and that every connection actually joins two nodes that exist. A file that fails says which id was wrong.
Once a process travels in its solution, import and export are for moving a design between environments by hand — a convenience, not the way work is promoted. That is the solution.
Exporting a picture
Export → Image (PNG) and Image (SVG) draw the diagram as it looks — for a document, a slide, or a wall. Neither is the process: they cannot be imported back.
Troubleshooting
-
My variable does not appear in the editor's suggestions. — an unnamed variable is not offered anywhere. Give the row a name, and leave the field so the name is committed.
-
I cannot find
setVariablein a condition. — a condition may only read; see above. Put the change in a task's script instead. -
A script I wrote on a task has disappeared. — changing a task's kind changes which moments it has. Change a user task to a service task and its On assign and On complete are no longer offered, and a design imported with them has them dropped.
-
"That file is not valid JSON, so it could not be imported." — the file is truncated or is not a design at all.
-
"That file is not a valid process design: …" — the detail names the id that is wrong: a node in a lane that is not in the file, a connection to a node that is not there.
-
Save says the design is not valid. — the same check runs before saving. Fix the node or connection it names; the diagram on screen is not lost.
-
A node jumped when I opened the process. — a node that was outside its lane is pulled back into it on open. A node's lane is who does the work, so the two are never allowed to disagree.
-
Auto arrange did nothing. — it arranges by flow, so it needs connections. A diagram of unconnected nodes has no order to arrange into.
-
The problems panel says a step "leads nowhere". — no Finish can be reached from it. Connect it onward, or connect something that already reaches one.
-
It says my script does not parse, and the code editor looked fine. — the panel checks every script in the process, including ones you are not looking at. Select the line to be taken to the node, then open the slot it names.
-
"No AI provider is set up. Open Settings → AI to connect one." — the organisation has no AI connection yet. A tenant administrator connects one in Settings → AI, once, for everybody.
-
"The AI answered without a design in it." — the model replied in prose instead of a process. Describe the lanes and the steps more plainly and generate again; nothing on the canvas is lost.