Skip to main content
This guide covers how to design workflows in Flowker — from understanding node types and edges to applying common patterns for real-world fintech flows. It also covers status transitions, technical limits, and best practices to help you build reliable, maintainable orchestrations.

Node types


Every workflow is built from nodes. Each node has a type that determines how Flowker processes it at runtime.

trigger

The entry point of a workflow. Every workflow must begin with a trigger node. When a workflow execution starts, the engine enters through this node and begins routing from it.
{
  "id": "node-trigger",
  "type": "trigger",
  "name": "Payment Received"
}

executor

Calls an external executor — an HTTP service, API, or provider registered in Flowker. This is the primary building block for integrating with fraud engines, payment providers, notification services, and other external systems.
{
  "id": "node-fraud-check",
  "type": "executor",
  "name": "Check Fraud Score"
}

conditional

Evaluates an expression against the execution context and routes to different branches based on the result. Conditional nodes enable branching logic — for example, routing to an approval path when a risk score is high, or continuing directly when it is low.
{
  "id": "node-risk-decision",
  "type": "conditional",
  "name": "Evaluate Risk Score"
}

action

A generic action node for steps that do not involve external calls but represent meaningful operations within the workflow — such as transforming data, emitting an event, or recording a state change.
{
  "id": "node-record-approval",
  "type": "action",
  "name": "Record Approval Decision"
}

Edges


Edges connect nodes and define the execution path. Each edge has the following fields:
FieldDescription
idUnique identifier for the edge
sourceID of the origin node
targetID of the destination node
sourceHandleWhich handle (output port) of the source node this edge originates from
conditionAn expression that must evaluate to true for this edge to be followed
labelHuman-readable label for the edge (used in visualization and debugging)

Example edge

{
  "id": "edge-approved",
  "source": "node-risk-decision",
  "target": "node-process-payment",
  "sourceHandle": "approved",
  "condition": "data.riskScore < 70",
  "label": "Approved"
}
The condition field accepts expressions evaluated against the execution context. When omitted, the edge is always followed if the source node completes successfully.

Status transitions


Workflows move through a well-defined lifecycle. Understanding status transitions is critical for safely deploying and iterating on workflows. Workflow status transition diagram showing three states: draft, active, and inactive. An arrow labeled 'activate' points from draft to active. An arrow labeled 'deactivate' points from active to inactive. There are no transitions back to draft or from inactive to any other state.
  • draft — The initial state. All modifications (adding nodes, editing edges, changing configuration) are only allowed in draft status.
  • active — A workflow that has been activated. It can be executed. No modifications are permitted while active.
  • inactive — A workflow that has been deactivated. It can no longer be executed.

Rules

  • Only a draft workflow can be activated (transition: draft → active).
  • Only an active workflow can be deactivated (transition: active → inactive).
  • Attempting an invalid transition returns error FLK-0102.
  • Attempting to modify a workflow that is not in draft returns error FLK-0103.

Iterating safely with clone

To modify an active workflow, clone it first. Cloning creates a new draft from any status, copying all nodes and edges. You can then edit the draft, test it, and activate it when ready — without affecting the currently running version. This is the recommended approach for versioning workflows in production.

Technical limits


LimitValueError code
Maximum nodes per workflow100FLK-0113
Maximum edges per workflow200FLK-0114
Maximum execution input payload1 MBFLK-0506
Keep these limits in mind when designing complex flows. Workflows with more than ~50 nodes are typically a sign that the flow should be broken into smaller, composable workflows.

Common patterns


Sequential

The simplest pattern. Nodes execute one after another in a linear chain. Use this when each step depends on the result of the previous one and there is no branching. Sequential workflow pattern: a trigger node connects to a first executor node, which connects to a second executor node, which connects to a third executor node. All connections are single directed arrows forming a straight line. Example: Payment orchestration
{
  "nodes": [
    { "id": "n1", "type": "trigger",  "name": "Payment Initiated" },
    { "id": "n2", "type": "executor", "name": "Validate Payment Data" },
    { "id": "n3", "type": "executor", "name": "Route to Provider" },
    { "id": "n4", "type": "executor", "name": "Send Confirmation Notification" }
  ],
  "edges": [
    { "id": "e1", "source": "n1", "target": "n2", "label": "Start" },
    { "id": "e2", "source": "n2", "target": "n3", "label": "Valid" },
    { "id": "e3", "source": "n3", "target": "n4", "label": "Routed" }
  ]
}

Parallel (fan-out / fan-in)

Multiple executor nodes are started from a common entry point and their results are collected at a join node. Use this when independent checks or calls can happen concurrently to reduce total execution time. Parallel workflow pattern (fan-out / fan-in): a trigger node splits into two executor nodes running concurrently. Both executor nodes then converge into a single join action node. This pattern is useful when you need to call multiple fraud or compliance providers simultaneously and aggregate their responses before making a routing decision.

Conditional branching

A conditional node evaluates an expression and routes execution to different nodes depending on the result. Each outgoing edge from the conditional node carries a condition expression. Conditional branching workflow pattern: a trigger node connects to an executor node, which connects to a conditional node. The conditional node has two outgoing arrows: one labeled 'Path A' pointing to a first executor node, and one labeled 'Path B' pointing to a second executor node. Example: Anti-fraud check
{
  "nodes": [
    { "id": "n1", "type": "trigger",     "name": "Transaction Received" },
    { "id": "n2", "type": "executor",    "name": "Get Fraud Score" },
    { "id": "n3", "type": "conditional", "name": "Evaluate Score" },
    { "id": "n4", "type": "executor",    "name": "Approve Transaction" },
    { "id": "n5", "type": "executor",    "name": "Reject Transaction" }
  ],
  "edges": [
    { "id": "e1", "source": "n1", "target": "n2", "label": "Start" },
    { "id": "e2", "source": "n2", "target": "n3", "label": "Score received" },
    {
      "id": "e3",
      "source": "n3",
      "target": "n4",
      "sourceHandle": "approved",
      "condition": "data.fraudScore < 70",
      "label": "Approved"
    },
    {
      "id": "e4",
      "source": "n3",
      "target": "n5",
      "sourceHandle": "rejected",
      "condition": "data.fraudScore >= 70",
      "label": "Rejected"
    }
  ]
}

Real-world examples


Anti-fraud check

A transaction arrives, a fraud score is retrieved from an external engine, and the result routes execution to either an approval or rejection path.
{
  "nodes": [
    { "id": "n1", "type": "trigger",     "name": "Transaction Received" },
    { "id": "n2", "type": "executor",    "name": "Get Fraud Score" },
    { "id": "n3", "type": "conditional", "name": "Evaluate Fraud Score" },
    { "id": "n4", "type": "executor",    "name": "Approve Transaction" },
    { "id": "n5", "type": "executor",    "name": "Reject and Notify" }
  ],
  "edges": [
    { "id": "e1", "source": "n1", "target": "n2", "label": "Start" },
    { "id": "e2", "source": "n2", "target": "n3", "label": "Score received" },
    {
      "id": "e3",
      "source": "n3",
      "target": "n4",
      "sourceHandle": "approved",
      "condition": "data.fraudScore < 70",
      "label": "Low risk"
    },
    {
      "id": "e4",
      "source": "n3",
      "target": "n5",
      "sourceHandle": "rejected",
      "condition": "data.fraudScore >= 70",
      "label": "High risk"
    }
  ]
}

Payment orchestration

A linear flow that validates incoming payment data, routes it to the appropriate provider, and sends a confirmation.
{
  "nodes": [
    { "id": "n1", "type": "trigger",  "name": "Payment Initiated" },
    { "id": "n2", "type": "executor", "name": "Validate Payment Data" },
    { "id": "n3", "type": "executor", "name": "Route to Payment Provider" },
    { "id": "n4", "type": "executor", "name": "Send Confirmation" }
  ],
  "edges": [
    { "id": "e1", "source": "n1", "target": "n2", "label": "Start" },
    { "id": "e2", "source": "n2", "target": "n3", "label": "Valid" },
    { "id": "e3", "source": "n3", "target": "n4", "label": "Payment routed" }
  ]
}

KYC onboarding

A customer’s documents are checked by an external service. A conditional node evaluates whether manual review is required. If so, a manual approval action is triggered before activating the account.
{
  "nodes": [
    { "id": "n1", "type": "trigger",     "name": "Onboarding Started" },
    { "id": "n2", "type": "executor",    "name": "Run Document Check" },
    { "id": "n3", "type": "conditional", "name": "Manual Review Required?" },
    { "id": "n4", "type": "action",      "name": "Request Manual Approval" },
    { "id": "n5", "type": "executor",    "name": "Activate Account" }
  ],
  "edges": [
    { "id": "e1", "source": "n1", "target": "n2", "label": "Start" },
    { "id": "e2", "source": "n2", "target": "n3", "label": "Check complete" },
    {
      "id": "e3",
      "source": "n3",
      "target": "n4",
      "sourceHandle": "review",
      "condition": "data.reviewRequired == true",
      "label": "Needs review"
    },
    {
      "id": "e4",
      "source": "n3",
      "target": "n5",
      "sourceHandle": "clear",
      "condition": "data.reviewRequired == false",
      "label": "Auto-approved"
    },
    { "id": "e5", "source": "n4", "target": "n5", "label": "Approved" }
  ]
}

Manual approval flow

A submission is sent for review. An approval action waits for a decision. A conditional node then routes to either the approved or rejected path.
{
  "nodes": [
    { "id": "n1", "type": "trigger",     "name": "Request Submitted" },
    { "id": "n2", "type": "executor",    "name": "Submit for Review" },
    { "id": "n3", "type": "action",      "name": "Await Approval Decision" },
    { "id": "n4", "type": "conditional", "name": "Decision Received" },
    { "id": "n5", "type": "executor",    "name": "Process Approved Request" },
    { "id": "n6", "type": "executor",    "name": "Notify Rejection" }
  ],
  "edges": [
    { "id": "e1", "source": "n1", "target": "n2", "label": "Start" },
    { "id": "e2", "source": "n2", "target": "n3", "label": "Submitted" },
    { "id": "e3", "source": "n3", "target": "n4", "label": "Decision received" },
    {
      "id": "e4",
      "source": "n4",
      "target": "n5",
      "sourceHandle": "approved",
      "condition": "data.decision == 'approved'",
      "label": "Approved"
    },
    {
      "id": "e5",
      "source": "n4",
      "target": "n6",
      "sourceHandle": "rejected",
      "condition": "data.decision == 'rejected'",
      "label": "Rejected"
    }
  ]
}

Best practices


Node naming conventions

Use descriptive, action-oriented names that communicate what the node does — not what type it is.
  • Validate Payment Data, Get Fraud Score, Notify Customer, Await Approval
  • executor1, conditional node, node3
Good names make workflows readable without opening the node configuration. They also appear in audit logs and execution traces, making debugging significantly faster.

Edge condition expressions

Conditions on edges are evaluated against the execution context. Keep them simple and explicit:
  • Use direct field comparisons: data.status == 'approved'
  • Use numeric comparisons: data.riskScore < 70
  • Use boolean fields: data.reviewRequired == true
Avoid complex expressions that are hard to read or debug. If logic is non-trivial, consider routing through a conditional node that encapsulates the decision with a clear name. An invalid expression returns FLK-0105. Always validate expressions before activating a workflow.

Error handling strategies

Design workflows to handle failure explicitly:
  • Add rejection paths from conditional nodes for every decision point that can fail.
  • Use separate executor nodes for retry logic or fallback providers.
  • Name error paths clearly (e.g., Reject and Notify, Fallback to Manual Review) so audit logs are self-explanatory.

Avoiding cycles

Flowker uses a DFS-based cycle guard at runtime. If a cycle is detected during execution, the workflow fails with FLK-0508. Cycles are not caught at design time, so validate your edge structure before activating. Rules to prevent cycles:
  • Edges must always point forward in the flow — never back to a previously executed node.
  • Review the graph visually before activating any workflow with branching or merging paths.
  • If a retry or loop is needed, model it as a separate workflow invocation, not a back-edge in the current graph.

Versioning via clone

Never edit an active workflow directly. Instead:
  1. Clone the workflow (creates a new draft with all nodes and edges copied).
  2. Make your changes in the draft.
  3. Test the draft with execution runs.
  4. Activate the new version.
  5. Deactivate the old version if it is no longer needed.
This preserves the execution history of the active version and gives you a clean rollback path if the new version has issues.

Error reference


The following error codes are relevant to workflow design and execution:
CodeDescription
FLK-0102Invalid status transition
FLK-0103Workflow cannot be modified — not in draft status
FLK-0105Invalid conditional expression
FLK-0113Too many nodes — maximum is 100
FLK-0114Too many edges — maximum is 200
FLK-0506Execution input payload too large — maximum is 1 MB
FLK-0508Cycle detected during workflow execution