Eviworx
Docs

Workflows API

Die Workflows API ermöglicht die Automatisierung von Geschäftsprozessen mit visuellen Workflows, Multi-Step-Genehmigungen, Timer-Events, Parallel-Branches, Wiederholung fehlgeschlagener Schritte, Pausieren und 7 integrierten Actions (E-Mail, Webhooks, Tickets, etc.). Templates unterstützen Versioning mit Rollback und Publishing.

🚀
Funktionen
✓ 8 Node-Typen (Approval, Timer, Gateway …)
✓ 7 Actions (E-Mail, Webhook, Ticket …)
✓ Visueller Drag-&-Drop-Designer
✓ Auto-Approval (Feldwert, Recht, Rolle)
✓ Bedingtes Routing (18 Operatoren)
✓ Wiederholung fehlgeschlagener Steps
✓ Pausieren (keine weiteren Schritte)
✓ Versionierung mit Rollback
✓ Activity Log mit Audit-Trail (zweisprachig)
✓ Start durch externe Systeme (API-Key)

Endpoints Übersicht

Workflow Instances

Method Endpoint Beschreibung
GET/api/workflow/instancesAlle Instanzen abrufen (mit Filtering)
GET/api/workflow/instances/:idEinzelne Instanz abrufen (mit Steps & Activities)
GET/api/workflow/instances/statsZähler im eigenen Sichtbarkeits-Scope + offene eigene Aufgaben
POST/api/workflow/instances/:id/steps/:stepId/completeStep abschließen (Genehmigungsschritte laufen über /approve)
POST/api/workflow/instances/:id/steps/:stepId/approveApproval-Entscheidung treffen
POST/api/workflow/instances/:id/cancelWorkflow abbrechen (202, ohne Body)
POST/api/workflow/instances/:id/pauseWorkflow pausieren; bis zum Fortsetzen werden keine Schritte ausgeführt (202, ohne Body)
POST/api/workflow/instances/:id/resumeWorkflow fortsetzen (202, ohne Body)
POST/api/workflow/instances/:id/steps/:stepId/retryFehlgeschlagenen Step wiederholen
POST/api/workflow/instances/:id/steps/:stepId/reassignStep einem anderen Benutzer zuweisen

Gestartet werden Workflows über einen der beiden Start-Pfade — Benutzer über POST /api/workflow/catalog/:templateId/start, externe Systeme per API-Key über POST /api/workflow/api/trigger/:templateId. Beide antworten 202 Accepted; die Ausführung übernimmt die workflow-engine asynchron. Ebenso cancel/pause/resume: 202 ohne Body.

Templates (CRUD & Publishing)

Method Endpoint Beschreibung
GET/api/workflow/templatesAlle Templates (Filter + Pagination; ?includeDeleted=true zeigt den Papierkorb)
GET/api/workflow/templates/statisticsZähler über alle Vorlagen: total, published, draft, archived, inactive (?includeDeleted)
GET/api/workflow/templates/categoriesDistinkte Kategorien aller Vorlagen ({data: string[]}) — Quelle der Kategorie-Filter
GET/api/workflow/templates/:idEinzelnes Template mit Steps
POST/api/workflow/templatesTemplate erstellen (Drag-&-Drop-Editor)
PUT/api/workflow/templates/:idTemplate aktualisieren
DELETE/api/workflow/templates/:idTemplate löschen (Soft-Delete)
POST/api/workflow/templates/:id/restoreTemplate wiederherstellen (verlangt restoreTemplates UND viewDeletedTemplates)
POST/api/workflow/templates/:id/publishTemplate veröffentlichen
POST/api/workflow/templates/:id/unpublishTemplate zurückziehen
PUT/api/workflow/templates/:id/permissionsTemplate-Berechtigungen setzen

Versioning

Method Endpoint Beschreibung
POST/api/workflow/templates/:id/versionsNeue Version erstellen (Semver, Changelog)
POST/api/workflow/templates/:name/rollbackAuf frühere Version zurücksetzen
POST/api/workflow/templates/:id/publish · /unpublishVeröffentlichen / zurückziehen

Meine Tasks

Die offenen Workflow-Schritte eines Benutzers liefert GET /api/my-tasks?types=WORKFLOW_STEP. Instanz-Statistiken liefert GET /api/workflow/instances/stats.

Activity & Audit

Method Endpoint Beschreibung
GET/api/workflow/activity/historyGlobale Workflow-History
GET/api/workflow/activity/statisticsWorkflow-Statistiken
Activities einer einzelnen Instanz kommen eingebettet aus GET /api/workflow/instances/:id.

Workflow Catalog

Method Endpoint Beschreibung
GET/api/workflow/catalog/listStartbare Workflows: flache Liste ({data, pagination}, Search & Kategorie-Filter)
POST/api/workflow/catalog/:templateId/startWorkflow aus Catalog starten (202)

API-Trigger (Externe Systeme)

Method Endpoint Beschreibung
POST/api/workflow/api/trigger/:templateIdWorkflow via API-Key triggern

Workflow-Konzepte

Templates vs. Instances

WorkflowTemplate:
├─ Blueprint/Definition (wiederverwendbar)
├─ Enthält Step-Definitionen (Array von Nodes)
├─ Versioniert (v1, v2, v3...)
└─ Kann published/unpublished sein

WorkflowInstance:
├─ Konkrete Ausführung eines Templates
├─ Hat eigene Runtime-Daten (data, variables)
├─ Erzeugt StepExecutions (eine pro Step)
├─ Status: DRAFT → RUNNING → COMPLETED/FAILED/CANCELLED
└─ Hat Activities (Audit-Trail)

Workflow-Status

Status Beschreibung
DRAFTErstellt, aber noch nicht gestartet
RUNNINGAktiv laufend, Steps werden ausgeführt
PAUSEDTemporär pausiert, kann fortgesetzt werden
COMPLETEDErfolgreich abgeschlossen
CANCELLEDVom Benutzer abgebrochen
FAILEDFehlgeschlagen (z.B. Approval ohne Eskalation rejected)

Step-Status

Status Beschreibung
PENDINGNoch nicht erreicht
ASSIGNEDZugewiesen, wartet auf Benutzer-Aktion
IN_PROGRESSWird gerade bearbeitet
COMPLETEDAbgeschlossen
REJECTEDAbgelehnt (nur bei Approval)
FAILEDFehlgeschlagen (z.B. Webhook-Error)
SKIPPEDÜbersprungen (z.B. nicht gewählter Conditional Branch)

8 Node-Typen (Step-Typen)

1. MANUAL_TASK

Manuelle Aufgabe, die einem Benutzer oder einer Gruppe zugewiesen wird. Der Benutzer muss die Aufgabe manuell abschließen.

Eigenschaften:
• Assignment: User, Group oder Role-basiert
• Deadlines: Optional (z.B. "in 24 Stunden")
• Notifications: Automatisch via Unified System
• Instructions: Anweisungen für den Benutzer

Ablauf:
1. Step is set to ASSIGNED
2. User receives notification
3. User completes task (POST .../complete)
4. Step changes to COMPLETED
5. Next step is triggered

2. APPROVAL

Genehmigungsschritt mit Auto-Approve/Reject-Logik, Eskalations-Support und manueller Entscheidung.

Features:Auto-Approve: Field-basiert (z.B. "wenn Betrag < 5000")
• Auto-Reject: Hat höhere Priorität als Auto-Approve
• Eskalation: Bei jeder Ablehnung zum konfigurierten Step (escalationStepId)
• Manual Approval: Wenn keine Auto-Regel greift
• failOnReject: Workflow FAILED bei Ablehnung — Eskalation geht vor

Ablauf (Auto-Reject):
1. Check auto-reject condition (FIRST priority)
2. If TRUE: Reject + escalation (if configured)
3. If escalation missing: Workflow FAILED

Ablauf (Auto-Approve):
1. Check auto-reject condition (FALSE)
2. Check auto-approve condition (SECOND priority)
3. If TRUE: Immediately APPROVED + next step

Ablauf (Manual):
1. No auto-rule applies
2. Step is set to ASSIGNED
3. Approver receives notification
4. Approver decides (POST .../approve)
5. On APPROVED: Next step
6. On REJECTED: Escalation or Workflow FAILED

3. AUTOMATED_ACTION

Automatisierte Aktion - führt eine von 7 integrierten Actions aus.

7 Action-Typen:

1. send_email      = E-Mail senden (via Notification-Worker)
2. webhook         = HTTP-Request (POST/GET/PUT), SSRF-Protected
3. create_ticket   = Neues Ticket erstellen
4. update_ticket   = Ticket aktualisieren (Status, Priority, Assignee)
5. add_ticket_comment = Kommentar zu Ticket hinzufügen
6. update_field    = Workflow-Feld aktualisieren (data)
7. assign_ticket   = Ticket automatisch zuweisen (via Assignment-Engine)

Sicherheit:SSRF-Protection: Webhooks blockieren private IPs (127.0.0.1, 10.x, 192.168.x)
• Timeout: Default 10s, konfigurierbar bis 60s
• Redirects: Deaktiviert (redirect: "error")
• Variable Replacement: {{"{{"}}field{{"}}"}} wird durch workflow.data.field ersetzt

Ablauf:
1. Step changes to IN_PROGRESS
2. Action is executed (with timeout protection)
3. On success: COMPLETED + outputData saved
4. On error: FAILED + errorMessage
5. Next step is triggered (only on success)

4. NOTIFICATION

Sendet Benachrichtigungen an Benutzer oder Gruppen (via Unified Notification System).

Empfänger-Typen:userIds: Array von User-IDs
• groupId: Alle Mitglieder einer Gruppe
• roleName: Alle User mit dieser Rolle
• notifyInitiator: Workflow-Initiator benachrichtigen
• recipientField: Dynamisch aus workflow.data lesen

Notification-Typen:task: Allgemeine Task-Notification (Standard)
• reminder: Reminder-Notification (z.B. "Deadline in 2h")
• escalation: Eskalations-Notification

Ablauf:
1. Recipients are resolved (deduplicated)
2. For each recipient: Notification via unified system
3. Step completes immediately (no waiting)
4. outputData: { notificationsSent: X, totalRecipients: Y }

5. PARALLEL_GATEWAY

Startet mehrere Workflow-Branches parallel. Alle Branches werden gleichzeitig ausgeführt.

Eigenschaften:nextSteps: Array von Step-IDs (Parallel-Branches)
• Keine Synchronisation am Ende (kein "Join")
• Workflow ist COMPLETED wenn ALLE Branches completed sind

Ablauf:
1. Gateway step changes to COMPLETED
2. WorkflowEngine starts ALL nextSteps simultaneously
3. Each branch runs independently
4. Workflow status stays RUNNING until all branches done

Beispiel:
Gateway → [Branch A: Legal Approval, Branch B: Finance Approval]
Both approvals run in parallel, independently of each other

6. CONDITIONAL_BRANCH

Bedingte Verzweigung - wählt einen von mehreren Pfaden basierend auf Conditions.

18 unterstützte Operatoren:

Gleichheit: equals, notEquals
Numerisch: greaterThan, lessThan, greaterOrEqual, lessOrEqual, between
Text: contains, notContains, startsWith, endsWith, matches (regex)
Liste: in, notIn
Leer/Null: isNull, isNotNull, isEmpty, isNotEmpty

in und notIn erwarten eine nicht-leere Werte-Liste, between genau zwei Grenzwerte;die vier Leer/Null-Operatoren lesen value nicht. Mit valueType (string,number, boolean) werden beide Seiten vor dem Vergleich umgewandelt — ohnediese Angabe vergleichen equals/notEquals streng ("5" ist nicht 5).
Komplexe Conditions (Nested):
{
  "type": "AND",
  "conditions": [
    { "type": "field", "field": "trigger.priority", "operator": "equals", "value": "HIGH" },
    { "type": "field", "field": "trigger.amount", "operator": "greaterThan", "value": 10000 }
  ]
}

Ablauf:
1. Conditions are evaluated in order
2. First match = Selected branch
3. No match = Default branch (if configured)
4. No match + no default = FAILED
5. Selected branch is triggered (overrideNextSteps)

Sicherheit:
• Max Recursion Depth: 10 (prevents stack overflow)
• Max Field Depth: 10 (prevents DoS via "a.b.c.d.e...")
• Dangerous keys blocked: __proto__, constructor, prototype
• Regex limit: 500 characters

7. DATA_COLLECTION

Formular-Schritt - präsentiert ein Formular und sammelt Benutzer-Input.

Eigenschaften:formSchema: JSON-Schema für Formular-Felder
• prefillFields: Felder vorausfüllen aus workflow.data
• Validierung: serverseitig beim Abschließen, gegen das formSchema

Ablauf:
1. Step is set to ASSIGNED
2. User receives notification
3. User fills out form
4. POST .../complete with data
5. Backend validates data against formSchema
6. On success: the validated values land in data.stepOutputs[stepName]
7. Step COMPLETED + next step

Beispiel formSchema:
{
  "requestReason": {
    "type": "textarea",
    "label": "Reason",
    "required": true,
    "minLength": 50
  },
  "urgency": {
    "type": "select",
    "label": "Urgency",
    "options": ["LOW", "MEDIUM", "HIGH"]
  }
}

8. TIMER_EVENT

Timer-Schritt - verzögert den Workflow um eine feste Zeit oder wartet bis zu einem bestimmten Zeitpunkt.

3 Timer-Typen (timerType):

1. duration = Relative Verzögerung ab Start des Schritts (z.B. "3 Stunden")
   • days: 0
   • hours: 3
   • minutes: 30
   • seconds: 0

2. datetime = Absoluter Zeitpunkt, ISO-8601 mit Zeitzone (Z oder ±HH:MM)
   • datetime: "2026-02-01T09:00:00Z"

3. expression = Zeitpunkt aus den Workflow-Daten; der aufgelöste Wert muss eine Zeitzone tragen
   • expression: "{{trigger.dueDate}}"

Ablauf:
1. Step changes to IN_PROGRESS
2. TimerJob is created (persistent in DB)
3. Step waits (shouldComplete = false)
4. TimerChecker service checks every 30s for due timers
5. When executeAt reached: Step is set to COMPLETED
6. Next step is triggered

Besonderheit:
• Delay = 0 or timestamp already past → Immediate completion
• No CRON support (use the CronJob system for that)

API-Beispiele

Workflow starten

POST /api/workflow/catalog/:templateId/start
{
  "data": {
    "requestType": "Hardware",
    "amount": 12500,
    "justification": "New laptops for development team"
  }
}

data wird gegen das triggerSchema der Vorlage validiert (Pflichtfelder → 400 FORM_VALIDATION_FAILED) und landet auf der Instanz unter data.trigger. Geprüft werden außerdem die Start-Berechtigung des Templates, das Limit gleichzeitiger Instanzen pro Benutzer und die Workflow-Daten-Limits.

Response (202 Accepted)

{
  "message": "Workflow started successfully",
  "templateId": "clx...",
  "templateName": "Hardware Purchase Request"
}

Der Start läuft asynchron über die workflow-engine — die Antwort trägt daher noch keine Instanz-ID. Die entstandene Instanz erscheint in GET /api/workflow/instances.

Manual Task abschließen

POST /api/workflow/instances/:instanceId/steps/:stepId/complete
{
  "outcome": "COMPLETED",
  "data": {
    "budgetLineItem": "IT-Equipment-2026",
    "approvalCode": "FIN-2026-045"
  }
}

outcome ∈ APPROVED | REJECTED | COMPLETED | FAILED (optional). data trägt die Eingaben des Schritts — bei DATA_COLLECTION die Formularfelder, die gegen das formSchema validiert werden. Antwort: die abgeschlossene StepExecution ({id, status, completedAt}).

Ein APPROVAL-Schritt wird über /approve entschieden, nicht über diesen Endpoint (400 APPROVAL_STEP_REQUIRES_DECISION). Damit nimmt jede Genehmigungs-Entscheidung denselben Weg — mit derselben Rechteprüfung, demselben Kommentar-Zwang und demselben Verhalten bei Ablehnung.

Approval-Entscheidung

POST /api/workflow/instances/:instanceId/steps/:stepId/approve
{
  "decision": "APPROVED",
  "comment": "Approved based on business justification and available budget"
}

// OR

{
  "decision": "REJECTED",
  "comment": "Budget exceeded for Q1, resubmit in Q2"
}

decision ist genau APPROVED oder REJECTED (400 INVALID_APPROVAL_DECISION). Der Entscheider braucht zusätzlich zur Zuweisung das Recht workflows.completeSteps. Ist am Schritt requireRejectionComment gesetzt, verlangt eine Ablehnung einen Kommentar (400 REJECTION_COMMENT_REQUIRED). Antwort: {decision, complete} — complete=false heißt, dass weitere Zustimmungen ausstehen. Der Kommentar — bei Zustimmung wie bei Ablehnung — landet im Vorgang unter data.stepOutputs[Schritt-Name].comments und ist dort in der Übersicht und im Aktivitäts-Verlauf sichtbar.

Workflow via API-Key triggern (Externe Systeme)

POST /api/workflow/api/trigger/:templateId
X-API-Key: your-api-key-here
{
  "data": {
    "externalSystemId": "SAP-12345",
    "purchaseOrderNumber": "PO-2026-1234",
    "amount": 25000,
    "vendor": "Dell Technologies"
  },
  "metadata": {
    "source": "SAP",
    "correlationId": "550e8400-e29b-41d4-a716-446655440000",
    "timestamp": "2026-01-27T15:30:00Z"
  }
}
  • Auth: X-API-Key Header. Die Rolle des API-Keys braucht das Recht workflows.startWorkflow UND muss — sofern das Template Start-Berechtigungen führt — in dessen allowedRoles stehen (allowedUsers greift nur für Benutzer-Starts, nicht für API-Keys; sind beide Listen leer, genügt das globale Recht).
  • Body: data (optional, gegen das triggerSchema des Templates validiert, Größen-Limit; landet in der Instanz unter data.trigger) · metadata (optional: source, correlationId (UUID), timestamp)
  • Response: 202 Accepted — die Instanz wird erstellt und asynchron von der workflow-engine abgearbeitet.
  • Die Vorlage muss triggerType API tragen — bei jedem anderen Wert antwortet die Route 400 INVALID_TRIGGER_TYPE. Das Feld kennt genau zwei Werte: MANUAL (Start über den Katalog) und API (Start durch ein externes System); ein anderer Wert wird beim Speichern der Vorlage mit 400 abgelehnt.

Workflow Catalog abrufen

GET /api/workflow/catalog/list?category=Procurement&search=hardware&page=1&limit=20

Response

{
  "data": [
    {
      "id": "clx...",
      "name": "Hardware Purchase Request",
      "description": "Multi-step approval for hardware purchases > €5,000",
      "category": "Procurement",
      "version": 3,
      "isActive": true,
      "isPublished": true,
      "triggerType": "MANUAL",
      "triggerSchema": { "fields": [ "..." ] }
    }
  ],
  "pagination": { "page": 1, "limit": 20, "total": 7, "totalPages": 1, "hasMore": false }
}

Der Katalog ist die Nutzer-Fläche: Er verlangt workflows.startWorkflow und liefert ausschließlich veröffentlichte, aktive Vorlagen, die der Aufrufer auch tatsächlich starten darf (allowedRoles/allowedUsers). Die Vorlagen-VERWALTUNG (GET /api/workflow/templates mit den vollständigen Step-Definitionen) erfordert dagegen workflows.viewTemplates.

Instanz mit Details abrufen

GET /api/workflow/instances/:id

Response (Full Instance)

{
  "id": "clx...",
  "templateId": "clx...",
  "status": "RUNNING",
  "priority": "HIGH",
  "currentSteps": ["step-3"],
  "completedSteps": {
    "step-1": { "status": "COMPLETED", "outcome": "COMPLETED", "startedAt": "2026-01-27T15:30:00Z", "completedAt": "2026-01-27T15:35:00Z", "errorMessage": null },
    "step-2": { "status": "COMPLETED", "outcome": "APPROVED",  "startedAt": "2026-01-27T15:35:00Z", "completedAt": "2026-01-27T16:20:00Z", "errorMessage": null }
  },
  "data": {
    "trigger": { "requestType": "Hardware", "amount": 12500 },
    "variables": { "budgetLineItem": "IT-Equipment-2026" },
    "stepOutputs": {
      "Submit Request":   { "stepType": "DATA_COLLECTION", "budgetLineItem": "IT-Equipment-2026" },
      "Finance Approval": { "stepType": "APPROVAL", "outcome": "APPROVED", "autoApproved": true }
    }
  },
  "stepDefinitions": [ "... snapshot of the steps at start ..." ],
  "templateVersion": 3,
  "template": {
    "id": "clx...",
    "name": "Hardware Purchase Request",
    "category": "Procurement"
  },
  "stepExecutions": [
    {
      "id": "clx...",
      "stepId": "step-1",
      "stepName": "Submit Request",
      "stepType": "DATA_COLLECTION",
      "status": "COMPLETED",
      "assignedToId": "clx...",
      "completedAt": "2026-01-27T15:35:00Z",
      "outputData": {
        "budgetLineItem": "IT-Equipment-2026"
      }
    },
    {
      "id": "clx...",
      "stepId": "step-2",
      "stepName": "Finance Approval",
      "stepType": "APPROVAL",
      "status": "COMPLETED",
      "outcome": "APPROVED",
      "assignedToId": "clx...",
      "completedAt": "2026-01-27T16:20:00Z",
      "outputData": {
        "autoApproved": true,
        "reason": "Amount below auto-approval threshold"
      }
    },
    {
      "id": "clx...",
      "stepId": "step-3",
      "stepName": "Manager Approval",
      "stepType": "APPROVAL",
      "status": "ASSIGNED",
      "assignedToId": "clx...",
      "assignedAt": "2026-01-27T16:20:00Z"
    }
  ],
  "activities": [
    {
      "id": "clx...",
      "action": "WORKFLOW_STARTED",
      "timestamp": "2026-01-27T15:30:00Z",
      "user": {
        "id": "clx...",
        "name": "John Doe",
        "email": "john@example.com"
      }
    },
    {
      "id": "clx...",
      "action": "STEP_COMPLETED",
      "timestamp": "2026-01-27T15:35:00Z",
      "details": {
        "stepName": "Submit Request",
        "outcome": "COMPLETED"
      }
    },
    {
      "id": "clx...",
      "action": "STEP_AUTO_APPROVED",
      "timestamp": "2026-01-27T16:20:00Z",
      "details": {
        "stepName": "Finance Approval",
        "reason": "Amount below auto-approval threshold"
      }
    }
  ],
  "initiator": {
    "id": "clx...",
    "name": "John Doe",
    "email": "john@example.com"
  },
  "startedAt": "2026-01-27T15:30:00Z",
  "createdAt": "2026-01-27T15:30:00Z",
  "updatedAt": "2026-01-27T16:20:00Z"
}

Datenmodell eines laufenden Workflows

WorkflowInstance.data hat genau drei Bereiche: trigger, variables und stepOutputs. Die Schlüssel von stepOutputs sind die Step-Namen, deshalb müssen Step-Namen pro Vorlage eindeutig sein. Jeder Eintrag trägt stepType plus die im Step gesammelten Ausgaben (z.B. comments bei MANUAL_TASK und bei der Entscheidung eines APPROVAL-Schritts, Formularfelder bei DATA_COLLECTION, ein checklist-Objekt bei Checklisten).

// WorkflowInstance.data
{
  "trigger":   { /* Trigger/start data from triggerSchema */ },
  "variables": { /* Workflow variables */ },
  "stepOutputs": {
    "Step 1": { "stepType": "MANUAL_TASK", "comments": "Ok cool" },
    "Step 2": { "stepType": "DATA_COLLECTION", "roomNumber": "option4" },
    "Finance Approval": { "stepType": "APPROVAL", "comments": "Budget confirmed" },
    "Step 3": {
      "stepType": "MANUAL_TASK",
      "comments": "Rejected",
      "checklist": { "items": ["..."], "checked": ["..."], "totalCount": 5, "completedCount": 5 }
    }
  }
}

Zusätzlich auf der Instanz: currentSteps[] (aktive Step-IDs), completedSteps (Map Step-ID → {status, outcome, startedAt, completedAt, errorMessage}), sowie stepDefinitions (Snapshot der Step-Definitionen beim Start) + templateVersion — so brechen Template-Änderungen laufende Instanzen nicht. Die eingebettete template-Relation enthält nur id, name und category; die Schritt-Struktur des Vorgangs steht in stepDefinitions.

Templating/Pfade greifen auf dieses Modell zu: {{ trigger.x }}, {{ variables.y }}, {{ stepOutputs["Step Name"].field }} (Punkt- oder Bracket-Notation).

StepExecution

Pro Step (eindeutig je Instanz, [workflowInstanceId, stepId]) existiert eine StepExecution:

  • status: PENDING, ASSIGNED, IN_PROGRESS, COMPLETED, REJECTED, FAILED, SKIPPED · outcome: APPROVED, REJECTED, COMPLETED, FAILED
  • assignmentType: user, group, system, initiator, initiator_manager, previous_step_user, dynamic (+ assignedToId / assignedRole / assignedGroup)
  • inputData, outputData, formData, formSchema (für DATA_COLLECTION), validationErrors, retryCount, comments/rejectionReason
  • Beim Abschluss wird outputData zusätzlich nach data.stepOutputs[stepName] gespiegelt (Audit-Trail).

Workflow-Engine (Container)

Die Ausführung übernimmt ein eigener Container (workflow-engine). Nach außen bietet er nur /health; alle Daten liest und schreibt er über die interne Backend-API: Er liest laufende und überfällige Instanzen und Steps, legt die Folge-Schritte an, schließt Steps ab oder markiert sie als fehlgeschlagen, führt Variablen zusammen und verarbeitet Timer und Approvals. Jeder Schritt-Typ hat einen eigenen Ausführungsbaustein (automated, approval, manual, notification, gateway, dataCollection, timer).

  • Timer – prüft alle 30 Sekunden, welche TIMER_EVENT-Steps ihren Zeitpunkt (executeAt) erreicht haben
  • SLA-Überwachung – überfällige Steps/Instanzen, Eskalation
  • Zuweisung – dynamische Zuweisung (user/role/group, Templating gegen trigger/variables/stepOutputs)
  • Auto-Approval – Auto-Approve/Reject per Bedingung

Advanced Features

Auto-Approval Logic

Approval-Steps können automatisch approved/rejected werden basierend auf Conditions:

{
  "type": "APPROVAL",
  "name": "Finance Approval",
  "config": {
    "autoApproveConditions": [
      {
        "type": "field",
        "field": "trigger.amount",
        "operator": "lessThan",
        "value": 5000
      }
    ],
    "autoRejectConditions": [
      {
        "type": "field",
        "field": "trigger.priority",
        "operator": "in",
        "value": ["URGENT", "HIGH"]
      }
    ],
    "autoRejectLogic": "OR",
    "failOnReject": true,
    "escalationStepId": "step-escalation"
  }
}

Neben type: "field" kennen Auto-Entscheidungen auch type: "permission" (Recht) und type: "role" (Rolle); checkPermissionsFor wählt dabei, wer geprüft wird — der Initiator (Standard) oder der Bearbeiter des Schritts. Mehrere Bedingungen verknüpft autoApproveLogic mit AND (Standard) oder OR, autoRejectLogic mit OR (Standard) oder AND — eine Ablehnung genügt also im Zweifel schon aus einem Grund.

Bedingungen müssen auswertbar sein: Beim Speichern einer Vorlage prüft die API jede Bedingung der Auto-Entscheidungen und jede Pfad-Bedingung eines CONDITIONAL_BRANCH. Unvollständige Bedingungen lehnt sie mit 400 INVALID_STEP_CONDITIONS ab; details.conditionErrors nennt je Zeile den Schritt, die Fläche (autoApprove, autoReject, branchPath), die Zeilennummer und den Grund — fehlendes Feld, fehlender oder unbekannter Operator, fehlendes Recht bzw. fehlende Rolle, fehlende Werte-Liste bei in/notIn, unvollständige Grenzen bei between, leere AND/OR-Gruppe oder eine Verschachtelung über zehn Ebenen.

Der Grund für die Strenge: Eine unvollständige Bedingung liefert zur Laufzeit immer dasselbe Ergebnis. Sie gilt deshalb als nicht erfüllt — eine Auto-Genehmigung greift dann nicht, ein Verzweigungs-Pfad wird nicht gewählt. Was nichts bewirken kann, soll gar nicht erst gespeichert werden.

Webhook mit SSRF-Protection

{
  "type": "AUTOMATED_ACTION",
  "name": "Notify External System",
  "config": {
    "actionType": "webhook",
    "actionConfig": {
      "url": "https://external-system.com/api/webhook",
      "method": "POST",
      "headers": {
        "Authorization": "Bearer {{"{{"}}apiToken{{"}}"}}",
        "X-Event-Type": "workflow.completed"
      },
      "body": {
        "workflowId": "{{"{{"}}workflowId{{"}}"}}",
        "status": "{{"{{"}}status{{"}}"}}",
        "completedAt": "{{"{{"}}completedAt{{"}}"}}"
      },
      "timeout": 15000
    }
  }
}
Hinweis: Webhook-URLs werden validiert:
  • Private IPs blockiert (127.0.0.1, 10.x, 192.168.x)
  • DNS-Resolution-Check gegen SSRF
  • Redirects deaktiviert
  • Timeout-Protection (Default: 10s, Max: 60s)

Parallel Approval

{
  "type": "PARALLEL_GATEWAY",
  "name": "Multi-Department Approval",
  "nextSteps": [
    "step-legal-approval",
    "step-finance-approval",
    "step-it-approval"
  ]
}

// All 3 approvals run in parallel
// Workflow is COMPLETED when ALL 3 are approved

Conditional Routing

{
  "type": "CONDITIONAL_BRANCH",
  "name": "Route by Amount",
  "config": {
    "paths": [
      {
        "id": "high-value",
        "name": "CFO",
        "condition": {
          "type": "field",
          "field": "trigger.amount",
          "operator": "greaterThan",
          "value": 50000
        },
        "nextSteps": ["step-cfo-approval"]
      },
      {
        "id": "medium-value",
        "name": "Manager",
        "condition": {
          "type": "field",
          "field": "trigger.amount",
          "operator": "between",
          "value": [10000, 50000]
        },
        "nextSteps": ["step-manager-approval"]
      },
      {
        "id": "rest",
        "name": "Default",
        "isDefault": true,
        "nextSteps": ["step-auto-approve"]
      }
    ]
  }
}

Die Pfade werden der Reihe nach geprüft, der erste Treffer gewinnt; Pfade mit isDefault greifen erst, wenn keine Bedingung passt. Ein Default-Pfad braucht selbst keine Bedingung — er ist der einzige Pfad, der ohne auskommt. Passt nichts und gibt es keinen Default-Pfad, schlägt der Schritt fehl.

Timer mit Notification

{
  "steps": [
    {
      "id": "step-1",
      "type": "APPROVAL",
      "name": "Approval Required",
      "nextSteps": ["step-2"]
    },
    {
      "id": "step-2",
      "type": "TIMER_EVENT",
      "name": "Wait 24 Hours",
      "config": {
        "timerType": "duration",
        "hours": 24
      },
      "nextSteps": ["step-3"]
    },
    {
      "id": "step-3",
      "type": "NOTIFICATION",
      "name": "Reminder",
      "config": {
        "notifyInitiator": true,
        "message": "Your request has been pending for 24 hours"
      },
      "nextSteps": ["step-4"]
    }
  ]
}

Filtering & Pagination

Instanzen filtern

GET /api/workflow/instances?status=RUNNING&priority=HIGH&templateId=clx...&search=purchase&page=1&limit=20&sortBy=startedAt&sortOrder=desc
Parameter Beschreibung
statusDRAFT, RUNNING, PAUSED, COMPLETED, CANCELLED, FAILED
priorityLOW, MEDIUM, HIGH, URGENT
templateIdFilter nach Template
initiatorIdFilter nach Initiator
categoryFilter nach Vorlagen-Kategorie
searchSuche in Template-Namen
pageSeite (Default: 1)
limitItems pro Seite (Default: 50, Max: 100)
sortBystartedAt, status oder templateName (Default: startedAt)
sortOrderasc oder desc (Default: desc)

Permissions

Permission Beschreibung
workflows.startWorkflowKatalog sehen und Workflows starten (Nutzer-Fläche; auch für API-Key-Trigger)
workflows.viewOwnInstancesEigene Workflow-Instanzen sehen
workflows.viewAllInstancesAlle Workflow-Instanzen sehen
workflows.cancelInstancesInstanzen abbrechen/pausieren/fortsetzen und fehlgeschlagene Steps wiederholen
workflows.completeStepsZugewiesene Schritte abschließen und Approval-Entscheidungen buchen
workflows.reassignStepsSchritte einem anderen Benutzer zuweisen
workflows.assignableKann Workflow-Schritte zugewiesen bekommen
workflows.viewTemplatesVorlagen-VERWALTUNG: Vorlagen-Liste, Statistik und Definitionen sehen
workflows.createTemplatesVorlagen erstellen
workflows.editTemplatesVorlagen bearbeiten, versionieren und Start-Berechtigungen setzen
workflows.publishTemplatesVorlagen veröffentlichen/zurückziehen
workflows.deleteTemplatesVorlagen löschen (Soft-Delete)
workflows.viewDeletedTemplatesPapierkorb sehen (?includeDeleted auf Liste und Statistik)
workflows.restoreTemplatesGelöschte Vorlagen wiederherstellen — zusammen mit viewDeletedTemplates

Nutzung und Verwaltung sind getrennt berechtigt: startWorkflow deckt Katalog, Start und die eigenen Aufgaben ab, viewOwn/viewAllInstances die laufenden Vorgänge — die vollständigen Vorlagen-Definitionen (inklusive Webhook-Konfiguration der AUTOMATED_ACTION-Schritte) sieht nur, wer viewTemplates trägt.

Zwei Rechte gelten zusätzlich nur für sichtbare Instanzen: Abbrechen, Pausieren, Fortsetzen und Retry wirken nur auf Instanzen, die der Aufrufer sehen darf (als Initiator oder mit viewAllInstances). Dasselbe prüft Reassign zusätzlich zum Recht.

Error-Handling

Häufige Fehler

Error Code HTTP Status Beschreibung
WORKFLOW_TEMPLATE_NOT_FOUND404Template-ID existiert nicht
TEMPLATE_INACTIVE403Template ist nicht aktiv
TEMPLATE_NOT_PUBLISHED403Template nicht published
TEMPLATE_ARCHIVED403Template ist archiviert
CANNOT_EDIT_PUBLISHED_TEMPLATE400Veröffentlichte Vorlagen sind gesperrt — neue Version erstellen (Ausnahme: isActive und die Start-Berechtigungen)
INVALID_PERMISSIONS_CONFIG400Start-Berechtigungen ungültig; details.validationErrors nennt je Zeile Code + Parameter
INVALID_STEP_CONDITIONS400Unvollständige Bedingung in einem Schritt; details.conditionErrors nennt je Zeile Schritt, Fläche und Grund
ESCALATION_STEP_NOT_FOUND400escalationStepId verweist auf einen Schritt, den die Vorlage nicht enthält
ESCALATION_CYCLE_DETECTED400Die Eskalations-Kette führt im Kreis; details.path zeigt den Ring
WORKFLOW_INSTANCE_NOT_FOUND404Instanz-ID existiert nicht
STEP_NOT_ASSIGNED403Step ist nicht dir zugewiesen
STEP_ALREADY_COMPLETED409Step bereits abgeschlossen
STEP_INVALID_STATUS400Step ist nicht ASSIGNED/IN_PROGRESS
WORKFLOW_NOT_RUNNING400Workflow ist nicht RUNNING
FORM_VALIDATION_FAILED400Form-Daten ungültig
APPROVAL_STEP_REQUIRES_DECISION400Genehmigungsschritt über /complete statt über /approve angesprochen
INVALID_APPROVAL_DECISION400decision ist nicht APPROVED/REJECTED
REJECTION_COMMENT_REQUIRED400Ablehnung ohne Kommentar, obwohl der Schritt ihn verlangt
WORKFLOW_DATA_LIMIT_EXCEEDED400Workflow-Daten > 1MB
CONCURRENT_MODIFICATION409Gleichzeitige Änderung erkannt (Optimistic Locking)

Fehler-Beispiel

{
  "error": "Step not assigned to you",
  "errorCode": "STEP_NOT_ASSIGNED",
  "message": "You cannot complete this step as it is assigned to user clx...",
  "statusCode": 403
}

Best Practices

💡 Tipps

1. Workflow-Design

  • • Verwende PARALLEL_GATEWAY für unabhängige Approvals (schneller)
  • • Setze Auto-Approve für Low-Risk Approvals (< €5k)
  • • Verwende CONDITIONAL_BRANCH statt mehrerer Workflows
  • • Eskalation immer konfigurieren (verhindert FAILED-Status)

2. Performance

  • • Halte workflow.data < 500KB (Limit: 1MB)
  • • Verwende Webhook-Timeouts (Default: 10s, Max: 60s)
  • • Vermeide tief verschachtelte Conditions (Max: 10 Levels)

3. Sicherheit

  • • Keine Secrets in Workflow-Daten (trigger, variables, stepOutputs) speichern; Zugangsdaten für Webhooks gehören in die Step-Konfiguration der Vorlage, die nur mit workflows.viewTemplates sichtbar ist
  • • Webhook-URLs validieren (SSRF-Protection aktiv)
  • • API-Keys für externe Trigger rotieren (alle 90 Tage)
  • • Permissions prüfen vor Template-Publish

4. Monitoring

  • • Activity Log für Audit-Trail nutzen
  • • Monitoring auf FAILED/PAUSED Workflows
  • • SLA-Monitoring für zeitkritische Workflows

Technical Details

Architecture

┌─────────────────────────────────────────────────────────────┐
│                      FRONTEND (React)                        │
│  • Visual Workflow Designer (React Flow)                   │
│  • Instance Monitoring Dashboard                           │
│  • Step Action UI (Complete, Approve, Form)                │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                    BACKEND API (Node.js)                     │
│  • /api/workflow/instances (CRUD)                          │
│  • /api/workflow/catalog (Browse & Start)                  │
│  • /api/workflow/api/trigger (External Systems)            │
│  • Validation (Zod), RBAC, Activity Logging                │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│              WORKFLOW-ENGINE (Separate Container)            │
│  • WorkflowEngine: State Machine, Step Orchestration       │
│  • StepExecutorFactory: Routes to correct Executor         │
│  • 8 Step Executors (Manual, Approval, Automated, etc.)    │
│  • ConditionEvaluator: 18 Operators                        │
│  • AutoApprovalService: Field/Permission/Role Logic        │
│  • TimerChecker: Polls Timer Jobs every 30s                │
│  • Redis PubSub: workflow:step:complete Events             │
└─────────────────────────────────────────────────────────────┘
                              │
                   ┌──────────┴──────────┐
                   ▼                     ▼
         ┌─────────────────┐   ┌─────────────────┐
         │  NOTIFICATION   │   │  JOB-WORKER    │
         │  WORKER         │   │  (CronJobs)     │
         │                 │   │                 │
         │  • Email        │   │  • Timer Jobs   │
         │  • In-App       │   │  • Scheduled    │
         └─────────────────┘   └─────────────────┘

Versioning

Workflows unterstützen Versioning auf Template-Ebene.

Hinweis: Laufende Instanzen verwenden immer die Template-Version, mit der sie gestartet wurden. Template-Änderungen beeinflussen nur neue Instanzen.

Attachments

Workflows nutzen das Unified Attachment System für Approval-Documents, Supporting-Documents, etc.:

# Upload file to workflow
POST /api/attachments/WORKFLOW/:workflowId

# All attachments of a workflow
GET /api/attachments/WORKFLOW/:workflowId
Details: Siehe Attachments & File Settings API für Zero-Trust Virus-Scan, File-Settings und Retention-Policies.