Appearance
Workflow node reference - every ready-made step
What it is for
A node is a ready-made step you drop into a stage instead of writing code. Each runs as an ordinary service task. It calls the platform's own endpoints as the configured user, so permissions, tenancy and audit apply exactly as for a person.
Every node returns its answer under the task key: ${context.<taskKey>.<field>}. Values from the workflow are written ${context.name}. The designer checks required fields and shows a problem on the node before you can publish.
Actions
Set variables - variable.set
Names values for later steps.
json
{"taskKey": "set-1", "stage": "start", "taskType": "variable.set", "kind": "service",
"payload": {"values": {"limit": 500, "owner": "${context.managerEmail}"}}}Later steps read ${context.limit}.
Create, update, delete, get record - record.create, record.update, record.delete, record.get
One record through the entity endpoints.
json
{"taskKey": "create-1", "stage": "approved", "taskType": "record.create", "kind": "service",
"payload": {"entity": "loan_payment", "values": {"loan": "${context.recordId}", "status": "OPEN"}}}Fields: entity, id (update, delete, get), values (create, update; update changes only the listed fields). The new record comes back as ${context.create-1.id}.
Export to CSV - data.exportCsv
Turns a list of records into CSV text: ${context.<key>.csv} and rowCount. Fields: rows, columns (comma separated, optional; empty means every field of the first row). Cells that begin with =, +, - or @ get a leading apostrophe so a spreadsheet cannot run them as a formula.
Generate document - document.generate
Makes a PDF, HTML or text file.
- From a print template (
printTemplate): a designed, branded PDF filled with real data. GiverecordIdto print a record and its linked child rows, or givedataandchildRowsto print values the workflow holds. Not both. - From text (
template): a text with placeholders, shown below. A missing value is an error. HTML output escapes values. A text template saved as PDF is a plain-text document.
text
Dear (name), (name | fallback)
Repeat a block for every item in a list: (#each list) (@index). (item.x) (/each)
Keep a block only when a value exists: (#if x) ... (/if)In the real template each placeholder is written with double curly braces instead of the round brackets used here.
- Where it goes: with
cabinetId(and optionalfolderId) the file is stored in document management and its id returned;attachEntityTypeandattachEntityIdalso attach it to a record. Without a cabinet the content comes back inside the workflow ascontentBase64(up to 256 KB).
json
{"taskKey": "letter", "stage": "approved", "taskType": "document.generate", "kind": "service",
"payload": {"printTemplate": "offer-letter", "recordId": "${context.recordId}", "cabinetId": 3, "filename": "offer-letter",
"attachEntityType": "candidate", "attachEntityId": "${context.recordId}"}}Designed PDFs need PDF export switched on and a browser engine available to the backend. Rendering takes a few seconds, so give the worker a longer task timeout.
Run integration - integration.run
Runs a connector or a flow from the integration platform. Fields: kind (connector or flow), key, input (values), timeoutSeconds. The integration platform owns addresses and credentials, and the call is made idempotent per task.
HTTP - http
Calls an address you give. Prefer Run integration, which keeps addresses and secrets out of the workflow.
Logic
Calculate - calculate
Arithmetic without scripting. + - * / % ( ) and round abs min max ceil floor, with exact decimals. Later formulas may use earlier results.
json
"payload": {"variables": {"qty": "${context.quantity}", "price": "${context.unitPrice}"},
"expressions": {"total": "qty * price", "tax": "round(total * 0.18, 2)"}}Script - script
A few lines of logic, safely. name = expression per line over the node's variables. Numbers, text, true/false, lists and records; + - * / %, comparisons, && || !, cond ? a : b, a.b, a[0]; and len upper lower trim substring contains startsWith endsWith replace split join coalesce isEmpty toNumber toString round abs min max ceil floor sum count. It is parsed and evaluated, never executed as code: no loops, no files, no network, no clock, at most 5000 characters.
Code (JavaScript) - code.js
Real JavaScript for logic that needs more. Write a function body that gets input and console and returns a value. A returned object's fields join the workflow's data; anything else comes back as result. console.log lines appear in the run history. Limits: 20000 characters, a time limit of 5 seconds by default (at most 20), 256 KB in, 512 KB out.
It runs in a separate, memory-limited process with an empty environment and no access to Java classes, files, network or the database, and the process is killed at the deadline. This is strong isolation but not an operating-system jail, so for untrusted multi-tenant use run workers in containers without network access.
For each - loop.foreach
Starts a workflow once per item of a list.
| Field | Meaning |
|---|---|
items | The list, e.g. ${context.lines} |
workflow | The workflow to start for each item |
itemName | Name of the item inside the child (default item; index and count are also provided) |
wait | When on, finish only when every started run has finished (checked every pollSeconds, default 15) |
failOnChildFailure | When waiting, fail if any run fails (default on). The result also carries completed, failed, cancelled |
maxWaitMinutes | Give up waiting after this long (default 1440) |
Starting is idempotent: a retry never starts the same child twice.
Notifications
All of these send through a named integration connector, so no address or secret is stored in the workflow.
| Node | taskType | Fields |
|---|---|---|
| Send email | notify.email | connector, to, subject, message |
| Send SMS | notify.sms | connector, to, message |
| Slack message | notify.slack | connector, channel (optional), message |
| Teams message | notify.teams | connector, channel (optional), message |
| In-app notification | notify.inapp | to (user id, comma list or list), subject (optional), message, category |
In-app notifications go to the person's notification inbox and are idempotent per task.
Timers
| Node | taskType | Fields |
|---|---|---|
| Delay | timer.delay | days, hours, minutes, seconds (at least one) |
| Wait until | timer.until | at: an ISO date-time, a date, or a value from the workflow |
A timer does not hold a thread. The task is queued to become due later, so waiting survives restarts and scaling.
AI nodes
Each does one bounded thing and returns typed fields; ordinary conditions and approvals decide what happens next. They call the AI platform's public endpoints as the configured user.
| Node | taskType | What it does | Key fields |
|---|---|---|---|
| Run a prompt | ai.prompt | Runs a library prompt | code, values, version or label, profileCode |
| Classify | ai.classify | Runs a prompt that answers in JSON and returns its fields | same as Run a prompt |
| Retrieve | ai.retrieve | Searches a knowledge base | knowledgeBase, query, mode, topK, threshold |
| Answer from knowledge | ai.rag | Runs a RAG pipeline | pipelineCode, question, version or label |
| Run an agent | ai.agent | Runs an agent | agentCode, intent (what it should do), timeoutSeconds |
| Guardrail | ai.guardrail | Local checks for personal data and prompt injection (the defaults), blocked terms and length. No model call | text, checks, blockedTerms, maxLength |
See Put AI inside a business workflow.
Engagement nodes
engage.message, engage.voice, engage.wait-reply and engage.mark are the steps Engagement journeys compile to. See Reach customers with journeys.
