Workflow Node Reference
Complete reference of every Kasimir workflow node type with config fields, inputs/outputs, ports, and limits.
This reference documents every node type executed by the Kasimir workflow engine (server/utils/workflowExecutor.ts). The engine is a linear walker along the edges (workflow_edges): starting from the trigger node it follows an edge queue; multiple edges out of one port run sequentially in enqueue order (Make.com semantics), and each node runs at most once per run (diamond-join: first branch wins). Values flow through an in-memory InterpolationState map and are resolved in downstream nodes via the template reference {{slug.field[.path]}}. All *_template fields pass through interpolate(); the node-type literal (type) inside config is the discriminator of the NodeConfig union.
Core concepts
Reference syntax (interpolation)
Templates reference prior nodes via {{slug.field[.path]}}. The regex allows slug = [a-z][a-z0-9_]{0,39}, field = input or output, plus an optional dot-separated path.
Reference | Meaning |
|---|---|
| Value of trigger/form input |
| Full output of node |
| Path traversal into a JSON output object |
| Current item / index inside a |
| An answer from an |
Missing references do NOT error: unresolvable refs yield the placeholder string [MISSING:slug] or [MISSING:{{…}}]. null/undefined render as ''. Reserved slugs: trigger, now, output, error, headers, status, body.
Cross-cutting node fields
These WorkflowNode fields apply to EVERY node type:
Field | Type | Effect |
|---|---|---|
|
| Display name (display = |
|
| Editor note |
|
| Node is skipped (status |
|
|
|
|
| On failure the node is re-run up to n times before |
|
| Run pauses with status |
|
| AI-filled config fields; |
|
| Model used for AI-field resolution |
A pause (ask_user, delay, requires_approval) behind a router or parallel branches is NOT supported — the run fails with a corresponding message if the edge queue is non-empty at pause time.
Node overview
| Category | Output ports |
|---|---|---|
| Entry |
|
| AI |
|
| AI / guardrail |
|
| AI |
|
| Logic |
|
| Branching |
|
| Branching | one port id per route (+ fallback) |
| Loop |
|
| Action |
|
| Pause |
|
| Pause |
|
| Data |
|
| Data / RAG |
|
| Data |
|
| Output |
|
| Output |
|
| Output |
|
| Action |
|
| Action |
|
| Action |
|
| Integration (Microsoft) |
|
| Integration (Microsoft) |
|
| Integration (Microsoft) |
|
| Integration (Google) |
|
Entry
trigger
Every workflow has exactly one trigger; if missing, the run fails immediately (Kein Trigger-Knoten). The trigger defines the input variables available as {{trigger.input.<key>}}. It runs no logic itself — the run inputs are written to state, the node is marked completed, and the output edge is followed.
Config field | Type | Description |
|---|---|---|
|
| Trigger kind |
|
| Input variables (shared across modes) |
|
| Only for |
|
| Trigger key from |
|
| Provider-specific fields |
WorkflowInputDef: key ([a-z][a-z0-9_]{0,39}), label, type (text/longtext/select/boolean), placeholder?, required, options? (select only), default_value?. Required fields are validated at run start; optional empty fields stay as '' / false.
The Outlook-mail trigger exposes a FIXED variable set: from, subject, to, cc, body, received_at, message_id, attachments.
AI nodes
llm
The central text-generation node. Free text is streamed (partial flush to output_text_partial every 300 ms, stream deadline 120 s); structured output and the agentic path run non-streaming.
Config field | Type | Description |
|---|---|---|
|
|
|
|
| System prompt (interpolated); the current date is always prepended |
|
| User prompt (interpolated) |
|
| Subset of the |
|
| Legacy; without |
|
| Agentic tools; non-empty ⇒ reasoning loop (structured_output then inactive) |
|
| Interpolated; non-empty ⇒ conversation memory active (except with structured_output) |
|
| Number of prior interactions loaded (default 5) |
|
| Forces a JSON-schema response |
|
| JSON schema when |
|
|
|
|
| Max iterations of the tool loop (clamp 1..25, default 6) |
|
| Reuse an existing assistant (supplies persona/model/web tools; node values take precedence) |
Output: free-text string, JSON object (structured), or agent answer. tokens_in/tokens_out are captured. With structured_output and unparseable JSON a retry runs with "Reply exclusively as valid JSON."; if that also fails → error. Available agentic tools: web_search, fetch_page, doc_search. The agentic loop has a 150 s wall-clock deadline and drops the tools on the last allowed iteration to force a final answer.
check (guardrail / "Review")
LLM-based content classification returning a verdict. GDPR-relevant.
Config field | Type | Description |
|---|---|---|
|
| Text to evaluate (interpolated) |
|
|
|
|
|
|
Output (JSON): { passed, flags[], reason (German), checks[] }. passed is false as soon as any enabled check fires. Unparseable model reply ⇒ passed=false, flags=['parse_error'].
image_gen ("Image generation")
Text prompt → OpenAI-compatible /images/generations via the LiteLLM proxy; stores a PNG.
Config field | Type | Description |
|---|---|---|
|
| Image prompt (interpolated) |
|
|
|
|
| Image size |
|
| Number of images |
Output: { path } (path in workflow-outputs, output_file_path). The response is requested as b64_json; missing image data → error. Runtime requires an image model configured in LiteLLM (ops dependency).
Logic
code
Runs LLM/user-written Python in the kasimir-codebox sidecar (timeout 30 s). If the sandbox is not configured (CODEBOX_BASE_URL/API_KEY), the node fails.
Config field | Type | Description |
|---|---|---|
|
| Python source |
Predecessor outputs are provided as an inputs dict (key = node slug, each {input} or {output}) — base64-embedded and decoded in Python. A helper result(x) prints JSON via print().
Output: trimmed stdout; valid JSON is parsed (structured output), otherwise plain text. Code error/timeout ⇒ node error (stderr truncated to 500 chars).
Branching
if_else
Evaluates a condition and follows port true or false.
Config field | Type | Description |
|---|---|---|
|
| Legacy single condition |
|
| Multi-condition; non-empty ⇒ replaces |
|
|
|
Operators (op): ==, !=, contains, matches (RegExp), is_empty (empty or [MISSING:…]), is_truthy, is_falsy, >, <, >=, <=. Both sides are interpolated; numeric comparisons cast via Number(). Output port = true/false; output itself is null.
router (Make.com semantics)
ALL routes whose conditions match are activated and run sequentially. A route with no conditions always runs; the "else" route (is_fallback) runs only if NO other matched.
Config field | Type | Description |
|---|---|---|
|
| List of routes |
RouterRoute: id (= port id / edge.from_port), label, conditions[], match (all/any), is_fallback?. Output: { matched_routes: string[] }; output_port = comma-separated list of matched route ids; the walker enqueues the edges of ALL matched ports.
Loop
for_each + aggregate
for_each evaluates over_expr to an array and runs the LINEAR body chain from the output port up to the next aggregate node once per item.
| Type | Description |
|---|---|---|
|
| e.g. |
|
| Optional iteration cap; |
Per iteration: {{<loopSlug>.output.item}} and .index. If over_expr is a string, JSON.parse is attempted; if it isn't an array ⇒ error.
| Type | Description |
|---|---|---|
|
| Evaluated per iteration (typically |
|
|
|
|
|
|
|
| Remove duplicate (value-equal) entries before output |
Output at the aggregate output port: the collected array or joined string.
v1 limitations: the body MUST be linear — if_else, router, http_out, ask_user, delay, and nested for_each are forbidden in the body. Exactly one output port; the body must end in an aggregate. Body nodes persist only the LAST iteration as a run-node row. An aggregate reached without a preceding for_each fails the run.
Actions
http_out
External HTTP request with SSRF protection (httpsOnly, blocks private/loopback/metadata hosts; every redirect is re-validated, max 5 hops).
Config field | Type | Description |
|---|---|---|
|
| HTTP method |
|
| Target URL (interpolated) |
|
| Appended query params (interpolated) |
|
| Headers (values interpolated) |
|
| Body mode (sets |
|
| Request body (interpolated) |
|
|
|
|
| Auth scheme |
|
| Env-declared slot ( |
|
| Timeout (default 30) |
Output: { status, body, headers }; port = success (status < 400) or error. Timeout/exception ⇒ status:0, port error. Redirects: 301/302/303 → GET + body dropped; 307/308 → method/body preserved.
send_email
Email via SendGrid; body_markdown becomes HTML via marked.
Config field | Type | Description |
|---|---|---|
|
| Must resolve to a valid address (else error) |
|
| Optional; comma/semicolon-separated, valid-only |
|
| Subject |
|
| Markdown body |
|
| Optional reply-to |
|
| Optional sender display name (address stays the verified sender) |
Output: { to, cc, bcc, subject, body, sent }. If SendGrid rejects (missing key/invalid address) ⇒ node error.
post_message
Posts to a chat/webhook endpoint; payload shaped per target.
Config field | Type | Description |
|---|---|---|
|
| Target type |
|
| Webhook URL (interpolated; must be |
|
| Message text |
|
| Slack: optional name override |
|
| Slack: optional channel override |
Payload: Slack {text, username?, channel?}; Teams MessageCard-light; webhook {text, source, workflow_id, node_slug}. Output: { target, status, ok }; non-OK HTTP ⇒ error.
notify ("Send notification")
Writes an in-app notification to the run initiator's inbox. No initiator (scheduled/webhook run) ⇒ no-op.
Config field | Type | Description |
|---|---|---|
|
| Message text (interpolated) |
Output: the rendered text.
Pause
ask_user
Pauses the run and collects a form's worth of answers; behaves like a second trigger mid-flow. Run status → waiting_input. /resume re-invokes the executor with {resume}; answers are available as {{<slug>.input.<key>}}.
Config field | Type | Description |
|---|---|---|
|
| Shown above the form |
|
| Form fields (same shape as trigger) |
delay
Time-based pause; run status → waiting_timer, resume_at is set.
Config field | Type | Description |
|---|---|---|
|
| Fixed value |
|
| Unit |
|
| Interpolated to a number > 0; overrides |
Data
load_table
Loads a workflow-linked table (workflow_documents) from the documents bucket and parses it.
Config field | Type | Description |
|---|---|---|
|
| Linked document ( |
|
|
|
|
| Row 1 = column names |
|
| Cap (default 1000) |
Supports .xlsx and .csv; the document must be linked to THIS workflow (defense-in-depth). Output: { rows: Record<string,string>[], count, columns }. A downstream for_each iterates over {{slug.output.rows}}, fields via {{loop.output.item.<column>}}.
doc_search (RAG)
Semantic search over company-wide document chunks (embedding model → searchChunks).
Config field | Type | Description |
|---|---|---|
|
| Query (interpolated) |
|
| see note |
|
| Default 5, cap 20 |
Output: { chunks: [{document_name, snippet, score}], text }. Similarity threshold 0.55.
v1 gap: hits are hard-restricted to scope_type='company' documents (GDPR guard). scope='project' falls back to company-wide search — project-scoped RAG needs an RPC change.
web_search
Web search via the configured provider (timeout 20 s).
Config field | Type | Description |
|---|---|---|
|
| Query (interpolated) |
|
| Default 5, cap 10 |
Output: { results: [{title, url, snippet}], text }. No provider configured ⇒ node error Keine Web-Suche konfiguriert.
Output
doc_render
Generates a real file (DOCX/XLSX/PDF/PPTX) and stores it in the workflow-outputs bucket.
Config field | Type | Description |
|---|---|---|
|
| Target format |
|
| Render path |
|
| Template (only |
|
| Markdown source (interpolated) |
|
| Filename (interpolated) |
With with_template, all trigger inputs + node outputs are provided as variables. Output: { path } (output_file_path). Path: {company_id}/{workflow_id}/{run_id}/{slug}.{ext}.
save_to_storage
Persists output as a file (survives the run).
Config field | Type | Description |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
| Target filename (interpolated) |
|
| MIME for |
Output: { path }. Path: {company_id}/{workflow_id}/outputs/{filename}. Missing source path / upload error ⇒ node error.
output
Writes a named value into the run-level outputs map (read-modify-write).
Config field | Type | Description |
|---|---|---|
|
| Output key |
|
| Value (interpolated) |
Output: the rendered string; also merged into workflow_runs.outputs.
Integrations
All integration actions resolve the run actor: for manual runs the initiator (user_id); for webhook/scheduled runs (user_id NULL) the workflow creator (Zapier/Make semantics). If a valid connection or permission is missing (403 from the API), the node fails with a message pointing to /integrations.
Microsoft (Graph)
| Endpoint | Permission | Key config fields | Output |
|---|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| Adaptive Card to a Teams incoming webhook | — (webhook URL is the credential) |
|
|
teams_card resolves the managed, company-scoped destination and decrypts its webhook URL; a button is added only for a valid http(s) URL.
| Endpoint | Permission | Key config fields | Output |
|---|---|---|---|---|
| Drive v3 multipart upload |
|
|
|
| Calendar v3 |
|
|
|
| Sheets v4 |
|
|
|
forward mode (OneDrive/Google Drive/save_to_storage) fetches the bytes via source_node_slug from a doc_render node's output — the typical chain is llm → doc_render → *_upload.
Run status values
Status | Meaning |
|---|---|
| Queued / executing |
| Finished / failed / cancelled |
| Paused in |
| Paused in |
| Paused before a |
Run-node status: pending, running, completed, failed, skipped. Unreached pending nodes are set to skipped at run end.