Skip to content

Flow-Builder

Der Flow-Builder steuert den strukturierten Gesprächsablauf eines Bots. Du definierst eine Abfolge von Steps, die der Bot durchläuft — von einfachen Textnachrichten über interaktive Buttons bis hin zu KI-gesteuerten Antworten und zeitverzögerten Nachrichten.

6 Block-Typen

Jeder Flow-Step hat einen step_type und eine step_config (JSONB). Die erlaubten Typen und ihre Pflichtfelder:

step_typeBeschreibungstep_config Pflichtfelder
textEinfache Textnachrichtmessage (String, max 4096 Zeichen). Optional: typing_delay (0, 1, 2 oder 3)
dsgvoDatenschutz-Blockdsgvo_mode (consent, notice oder disabled)
interactiveButtons oder Liste (WhatsApp)Benötigt block_id (Referenz auf bot_interactive_blocks)
n8nn8n Webhook-Aufrufn8n_mode (fire_and_forget oder wait_for_response). Optional: timeout_seconds (1-60)
delayZeitverzögerungdelay_mode (hours, days oder fixed_date). Bei hours/days: delay_value (min 0.01/1, max 365). Optional: message_after_delay (max 4096 Zeichen)
aiKI-gesteuerte AntwortOptional: max_answers (0, 1, 3 oder 5), ai_prompt (max 4096 Zeichen)

Universelle step_config Felder (alle Typen)

Zusätzlich zu den typspezifischen Feldern kann jeder Step diese Felder in step_config haben:

FeldTypBeschreibung
conditionsArrayBedingungen, wann der Step ausgeführt wird. Siehe FlowConditionEngine.
condition_logicStringOR (default) oder AND — Verknüpfung der Conditions
exitsObjectDefinierte Ausgänge (GOTO). Keys sind Exit-Namen, Values sind step_order (Zahl) oder 'end'.

Flow-API-Endpoints

Alle Endpoints erfordern Auth + Tenant-Check. Basis-Pfad: /api/bots/:botId/flow

MethodePfadBeschreibung
GET/Alle Flow-Steps eines Bots laden (sortiert nach step_order, mit Checkpoint-Info)
POST/Neuen Step hinzufügen. Body: step_type, step_config, optional block_id, trigger_text, option_actions, step_order
PUT/reorderReihenfolge ändern. Body: order (Array von Step-IDs)
GET/generate-promptFlow-Prompt für den Bot generieren
PUT/:stepIdEinzelnen Step aktualisieren
DELETE/:stepIdStep entfernen

Route-Reihenfolge

/reorder und /generate-prompt sind VOR /:stepId definiert (spezifisch vor generisch).

FlowConditionEngine

Die FlowConditionEngine steuert die Bedingungslogik und Navigation zwischen Steps. Sie verhindert Doppel-Senden und ermöglicht Verzweigungen.

Methoden

MethodeParameterBeschreibung
shouldExecuteStep(step, progress)Step-Objekt, Progress-RecordPrüft ob ein Step ausgeführt werden soll. false wenn bereits besucht oder Conditions nicht erfüllt.
getNextStep(step, exitKey, tenantId, botId, progress)Aktueller Step, Exit-Key (z.B. 'default'), IDs, ProgressBestimmt den nächsten Step. Prüft exits, dann linearen Nachfolger.
getNextLinearStep(currentStep, tenantId, botId, progress)Aktueller Step, IDs, ProgressFindet den nächsten ausführbaren Step nach step_order. Überspringt Steps deren Conditions nicht erfüllt sind.
recordChoice(contactId, botId, stepOrder, optionId)IDs, Step-Nummer, gewählte OptionSpeichert eine User-Auswahl (Button/Liste) im Progress.
markStepVisited(contactId, botId, stepOrder)IDs, Step-NummerMarkiert einen Step als besucht (verhindert Doppel-Senden).
resetProgress(contactId, botId)IDsSetzt den Flow-Progress zurück (z.B. bei neuem Gespräch).
getProgress(contactId, botId)IDsLädt den aktuellen Progress für einen Kontakt.

Conditions (AND/OR Logik)

Conditions prüfen, welche Optionen der User in vorherigen Steps gewählt hat:

json
{
  "conditions": [
    { "source_step_order": 2, "option_id": "ja" },
    { "source_step_order": 3, "option_id": "premium" }
  ],
  "condition_logic": "AND"
}
  • OR (default): Mindestens eine Condition muss erfüllt sein
  • AND: Alle Conditions müssen erfüllt sein

Die Choices werden im choices-Feld des Progress-Records als { "step_order": "option_id" } gespeichert.

Exits-System

Exits definieren wohin der Flow nach einem Step springt:

json
{
  "exits": {
    "default": 5,
    "success": 3,
    "error": "end"
  }
}
  • Ziel kann eine step_order (positive Zahl) oder 'end' (Flow beenden) sein
  • Der Exit-Key wird beim Aufruf von getNextStep() übergeben
  • Wenn kein passender Exit-Key gefunden wird, fällt die Engine auf 'default' zurück
  • Wenn weder passender Key noch 'default' vorhanden: Flow-Stopp (NICHT linear weiter), sofern sowohl Conditions als auch Exits definiert sind
  • Ohne Exits: linearer Fallback zum nächsten Step nach step_order

Datenbank-Schemas

bot_flow_steps

SpalteTypNullableDefaultBeschreibung
iduuidNOT NULLgen_random_uuid()Primary Key
tenant_iduuidNOT NULLFK auf tenants
bot_iduuidNOT NULLFK auf bots
block_iduuidNULLFK auf bot_interactive_blocks (nur bei step_type='interactive')
step_orderintegerNOT NULL0Position im Flow
trigger_textvarchar(255)NULLAuslöse-Text
option_actionsjsonbNULL'{}'Aktionen pro Option (altes System)
is_activebooleanNULLtrueAktiv/Inaktiv
created_attimestamptzNOT NULLCURRENT_TIMESTAMPErstelldatum
updated_attimestamptzNOT NULLCURRENT_TIMESTAMPÄnderungsdatum
step_typevarchar(20)NOT NULL'interactive'Block-Typ (interactive, text, dsgvo, consent, n8n, delay, ai)
step_configjsonbNOT NULL'{}'Typ-spezifische Konfiguration

Indizes:

  • bot_flow_steps_bot_id_block_id_unique — UNIQUE auf (bot_id, block_id)
  • bot_flow_steps_bot_id_step_order_index — btree auf (bot_id, step_order)
  • bot_flow_steps_tenant_id_index — btree auf (tenant_id)
  • idx_flow_steps_step_type — btree auf (bot_id, step_type)

contact_flow_progress

SpalteTypNullableDefaultBeschreibung
iduuidNOT NULLgen_random_uuid()Primary Key
contact_iduuidNOT NULLFK auf contacts
bot_iduuidNOT NULLFK auf bots
tenant_iduuidNOT NULLFK auf tenants
current_step_orderintegerNULL1Aktueller Step
flow_completedbooleanNULLfalseFlow abgeschlossen?
flow_started_attimestamptzNULLCURRENT_TIMESTAMPStart-Zeitpunkt
flow_completed_attimestamptzNULLAbschluss-Zeitpunkt
updated_attimestamptzNULLCURRENT_TIMESTAMPLetzte Änderung
choicesjsonbNOT NULL'{}'Gewählte Optionen pro Step: {"step_order": "option_id"}
visited_stepsjsonbNOT NULL'[]'Liste besuchter step_orders
last_sent_block_iduuidNULLFK auf bot_interactive_blocks
statusvarchar(20)NULL'active'Status des Progress-Records
step_datajsonbNOT NULL'{}'Zusätzliche Step-Daten

Constraint: UNIQUE auf (contact_id, bot_id) — ein Progress-Record pro Kontakt/Bot-Paar.

Status-Werte: active, waiting_delay, waiting_ai

scheduled_flow_steps

SpalteTypNullableDefaultBeschreibung
iduuidNOT NULLgen_random_uuid()Primary Key
tenant_iduuidNOT NULLFK auf tenants
bot_iduuidNOT NULLFK auf bots
contact_iduuidNOT NULLFK auf contacts
step_iduuidNOT NULLFK auf bot_flow_steps
scheduled_attimestampNOT NULLGeplanter Ausführungszeitpunkt
cancel_on_replybooleanNULLtrueBei User-Antwort abbrechen?
statusvarchar(20)NULL'pending'Status (pending, executed, cancelled)
cancelled_reasonvarchar(50)NULLGrund der Stornierung
created_attimestampNULLnow()Erstelldatum
executed_attimestampNULLAusführungszeitpunkt

Indizes:

  • idx_scheduled_pending — Partial Index auf (status, scheduled_at) WHERE status='pending'
  • idx_scheduled_contact — btree auf (contact_id, status)
  • uq_scheduled_step_pending — UNIQUE auf (contact_id, step_id) WHERE status='pending'

Partial Unique Index

Der Unique-Constraint uq_scheduled_step_pending gilt NUR für status='pending'. Dadurch kann ein Kontakt denselben Step mehrfach durchlaufen — aber nie zwei gleichzeitig wartende Delays für denselben Step haben.

DelayExecutorJob

Der DelayExecutorJob ist ein Background-Job, der fällige Delay-Steps ausführt.

Konfiguration

ParameterWertBeschreibung
Polling-Intervall60 SekundenPrüft alle 60s auf fällige Delays
Batch-Größe10Maximal 10 Delays pro Durchlauf

Ablauf

  1. Fällige Delays laden: status='pending' AND scheduled_at <= NOW()
  2. Atomisch auf executed setzen (verhindert Doppel-Ausführung)
  3. message_after_delay an den Kontakt senden (falls konfiguriert)
  4. Flow fortsetzen via FlowConditionEngine.getNextStep()

delay_mode

ModusBeschreibungdelay_value
hoursVerzögerung in Stunden0.01 – 365
daysVerzögerung in Tagen1 – 365
fixed_dateFestes Datum/UhrzeitNicht verwendet (scheduled_at wird direkt gesetzt)

Fehlerbehandlung

Bei Ausführungsfehlern wird der Status auf cancelled gesetzt mit cancelled_reason: 'execution_error', damit der Delay nicht endlos wiederholt wird.