Reference

FlowDSL Examples

Real-world FlowDSL flow definitions — from minimal single-event services to multi-module pipelines.

These examples show progressively more complete FlowDSL documents. Each can be fed directly into the Redelay compile pipeline:

shell
# Export to AsyncAPI
redelayctl export --format asyncapi my.flowdsl.yaml

# Export to OpenAPI
redelayctl export --format openapi my.flowdsl.yaml

# Validate only
redelayctl validate my.flowdsl.yaml

1. Minimal — single event + action

The smallest useful FlowDSL: one event, one HTTP action that emits it, one Kafka channel.

yaml
# minimal.flowdsl.yaml
version: "1.0"
title: Minimal Service

events:
  user_created:
    name: user_created
    entity_type: user
    action: created
    topic: users.created
    payload:
      user_id:
        type: uuid
        required: true
      email:
        type: string
        required: true

actions:
  create_user:
    name: create_user
    kind: command
    method: POST
    path: /users
    input:
      email:
        type: string
        required: true
      name:
        type: string
    output:
      user_id:
        type: uuid
    emits: [user_created]

channels:
  users:
    name: users
    protocol: kafka

What Redelay generates from this:

OutputContent
AsyncAPIOne channel users.created, one message user_created with payload schema
OpenAPI 3.0One path POST /users with input/output schemas

2. E-Commerce — multi-module order lifecycle

A realistic order service with modules, entities, five events, three actions, a workflow, channels, and a cron schedule.

yaml
# ecommerce.flowdsl.yaml
version: "1.0"
title: E-Commerce Order Service
description: >
  Handles the full order lifecycle — placement, payment, fulfilment,
  and shipping notifications.

modules:
  orders:
    name: orders
    description: Core order processing
    version: "1.0"
    provides: [order_placed, order_shipped, order_cancelled]
  payments:
    name: payments
    description: Payment processing and refunds
    version: "1.0"
    depends_on: [orders]
    provides: [payment_captured, payment_failed]
  notifications:
    name: notifications
    description: Email and push notifications
    version: "1.0"
    depends_on: [orders, payments]

entities:
  order:
    name: Order
    fields:
      id:
        type: uuid
        required: true
      customer_id:
        type: uuid
        required: true
      total:
        type: float
        required: true
      status:
        type: enum
        enum: [pending, confirmed, shipped, cancelled]
      created_at:
        type: datetime

events:
  order_placed:
    name: order_placed
    entity_type: order
    action: placed
    topic: orders.placed
    payload:
      order_id: { type: uuid, required: true }
      customer_id: { type: uuid, required: true }
      amount: { type: float, required: true }
      currency: { type: string }

  order_shipped:
    name: order_shipped
    entity_type: order
    action: shipped
    topic: orders.shipped
    payload:
      order_id: { type: uuid, required: true }
      tracking_number: { type: string }
      carrier: { type: string }
      shipped_at: { type: datetime }

  order_cancelled:
    name: order_cancelled
    entity_type: order
    action: cancelled
    topic: orders.cancelled
    payload:
      order_id: { type: uuid, required: true }
      reason: { type: string }

  payment_captured:
    name: payment_captured
    entity_type: order
    action: payment_captured
    topic: payments.captured
    payload:
      order_id: { type: uuid, required: true }
      payment_id: { type: uuid, required: true }
      amount: { type: float }

  payment_failed:
    name: payment_failed
    entity_type: order
    action: payment_failed
    topic: payments.failed
    payload:
      order_id: { type: uuid, required: true }
      error_code: { type: string }
      error_message: { type: string }

actions:
  place_order:
    name: place_order
    description: Create a new order and initiate payment
    kind: command
    method: POST
    path: /orders
    input:
      customer_id: { type: uuid, required: true }
      items: { type: array }
      total: { type: float, required: true }
      currency: { type: string }
    output:
      order_id: { type: uuid }
      status: { type: string }
    emits: [order_placed]

  cancel_order:
    name: cancel_order
    kind: command
    method: POST
    path: /orders/{id}/cancel
    input:
      order_id: { type: uuid, required: true }
      reason: { type: string }
    emits: [order_cancelled]

  get_order:
    name: get_order
    kind: query
    method: GET
    path: /orders/{id}
    output:
      order_id: { type: uuid }
      status: { type: string }
      total: { type: float }

workflows:
  order_fulfilment:
    name: order_fulfilment
    description: End-to-end processing from placement to shipment
    triggers: [order_placed]
    steps:
      - id: capture_payment
        kind: action
        action: place_order
        next: notify_customer
        on_error: handle_payment_failure
      - id: notify_customer
        kind: emit
        event: order_shipped
        next: done
      - id: handle_payment_failure
        kind: emit
        event: payment_failed
      - id: done
        kind: end

channels:
  orders_topic:
    name: orders
    description: Main orders Kafka topic
    protocol: kafka
  payments_topic:
    name: payments
    description: Payment events Kafka topic
    protocol: kafka

schedules:
  daily_reconciliation:
    name: daily_reconciliation
    description: Reconcile orders with payment provider at 02:00 UTC
    cron: "0 2 * * *"
    action: get_order

Module dependency graph:

text
notifications
  ├── orders
  └── payments
        └── orders

Redelay's compile step resolves this topological order and validates all depends_on references before wiring.


3. IoT Sensor Pipeline — mixed transports, anomaly detection

Shows mixed transport protocols (Kafka for high-throughput ingestion, NATS for low-latency alerting), transform actions, and a cron schedule for ClickHouse archival.

yaml
# iot-pipeline.flowdsl.yaml
version: "1.0"
title: IoT Sensor Pipeline
description: >
  Ingests telemetry from IoT sensors, detects anomalies via threshold
  comparison, dispatches alerts, and archives readings to ClickHouse.

modules:
  ingest:
    name: ingest
    description: Sensor data ingestion
    provides: [reading_received]
  analytics:
    name: analytics
    description: Real-time anomaly detection
    depends_on: [ingest]
    provides: [anomaly_detected]
  alerting:
    name: alerting
    description: Alert dispatch and acknowledgement
    depends_on: [analytics]

entities:
  sensor:
    name: Sensor
    fields:
      id: { type: uuid, required: true }
      device_id: { type: string, required: true }
      location: { type: string }

  reading:
    name: Reading
    fields:
      id: { type: uuid, required: true }
      sensor_id: { type: uuid, required: true }
      value: { type: float, required: true }
      unit: { type: string }
      recorded_at: { type: datetime, required: true }

events:
  reading_received:
    name: reading_received
    entity_type: sensor
    action: reading_received
    topic: iot.readings
    payload:
      sensor_id: { type: uuid, required: true }
      device_id: { type: string, required: true }
      value: { type: float, required: true }
      unit: { type: string }
      recorded_at: { type: datetime }

  anomaly_detected:
    name: anomaly_detected
    entity_type: sensor
    action: anomaly_detected
    topic: iot.anomalies
    payload:
      sensor_id: { type: uuid, required: true }
      value: { type: float, required: true }
      threshold: { type: float, required: true }
      severity:
        type: enum
        enum: [low, medium, high, critical]

  alert_dispatched:
    name: alert_dispatched
    entity_type: sensor
    action: alert_dispatched
    topic: iot.alerts
    payload:
      sensor_id: { type: uuid, required: true }
      channel: { type: string }
      dispatched_at: { type: datetime }

actions:
  ingest_reading:
    name: ingest_reading
    kind: command
    method: POST
    path: /readings
    input:
      sensor_id: { type: uuid, required: true }
      value: { type: float, required: true }
      unit: { type: string }
    emits: [reading_received]

  detect_anomaly:
    name: detect_anomaly
    kind: transform
    input:
      sensor_id: { type: uuid }
      value: { type: float }
    emits: [anomaly_detected]

  dispatch_alert:
    name: dispatch_alert
    kind: command
    input:
      sensor_id: { type: uuid }
      severity: { type: string }
      message: { type: string }
    emits: [alert_dispatched]

workflows:
  anomaly_response:
    name: anomaly_response
    triggers: [reading_received]
    steps:
      - id: check
        kind: action
        action: detect_anomaly
        next: alert
        on_error: log_error
      - id: alert
        kind: action
        action: dispatch_alert
        next: done
      - id: log_error
        kind: end
      - id: done
        kind: end

channels:
  iot_readings:
    name: iot.readings
    description: Sensor readings — Kafka (high-throughput, durable)
    protocol: kafka
  iot_anomalies:
    name: iot.anomalies
    description: Anomaly events — NATS JetStream (low-latency alerting)
    protocol: nats

schedules:
  archive_readings:
    name: archive_readings
    description: Archive raw readings from Kafka to ClickHouse every 5 minutes
    cron: "*/5 * * * *"
    action: ingest_reading

4. Domain drop pipeline

The canonical example from nameniac.com — detects dropping domains via ICANN zone files, enriches with WHOIS, scores with LLM.

The full file (spec/examples/domain-pipeline.flowdsl.yaml):

yamldomain-pipeline.flowdsl.yaml
# Domain Drop Pipeline — Redelay example
# This flow describes the nameniac.com domain drop detection system.
# Each node operationId maps to a Redelay event handler.

flowdsl: "1.0"
info:
  title: Domain Drop Pipeline
  version: "1.0.0"
  description: >
    Detects expiring/dropping domains from ICANN zone files,
    enriches them with WHOIS data, detects the actual drop moment via DNS SOA,
    then scores domains with an LLM.

servers:
  production:
    url: kafka.nameniac.com:9092
    protocol: kafka
    description: Production Kafka cluster

nodes:
  ZoneImport:
    operationId: import_zone_file
    kind: source
    summary: Reads ICANN zone files and emits domains removed from the zone

  WhoisLookup:
    operationId: perform_whois
    kind: action
    summary: Queries WHOIS/RDAP for domain registration status

  DropMonitor:
    operationId: monitor_drop
    kind: action
    summary: Polls DNS SOA records and detects when a domain actually drops

  LLMScorer:
    operationId: score_domain
    kind: llm
    summary: Scores domain memorability and commercial value using LLM

edges:
  - from: ZoneImport
    to: WhoisLookup
    delivery:
      mode: durable
      packet: DomainRemovedPayload

  - from: WhoisLookup
    to: DropMonitor
    delivery:
      mode: durable
      packet: WhoisCompletedPayload

  - from: DropMonitor
    to: LLMScorer
    delivery:
      mode: durable
      packet: DomainDropPayload

components:
  packets:
    DomainRemovedPayload:
      type: object
      properties:
        domain:
          type: string
          description: The domain name removed from the zone file
        tld:
          type: string
          description: Top-level domain (com, net, org, ...)
        removed_at:
          type: string
          format: date-time
      required: [domain, tld, removed_at]

    WhoisCompletedPayload:
      type: object
      properties:
        domain:
          type: string
        status:
          type: string
          enum: [pending_check, redemption_period, pending_delete, active, dropped]
        expiry_date:
          type: string
          format: date-time
          nullable: true
        registrar:
          type: string
          nullable: true
      required: [domain, status]

    DomainDropPayload:
      type: object
      properties:
        domain:
          type: string
        tld:
          type: string
        dropped_at:
          type: string
          format: date-time
      required: [domain, tld, dropped_at]

All field types

FlowDSL typeJSON Schema / AsyncAPIOpenAPI
stringstringstring
integer / intintegerinteger
float / numbernumbernumber
boolean / boolbooleanboolean
arrayarrayarray
objectobjectobject
datestring + format: datestring + format: date
datetimestring + format: date-timestring + format: date-time
uuidstring + format: uuidstring + format: uuid
binarystring + format: binarystring + format: binary
enumstring + enum: [...]string + enum: [...]
ref$ref$ref
any{} (any schema){}

All channel protocols

ProtocolTransport
kafkaApache Kafka (at-least-once, durable)
natsNATS core or JetStream
redisRedis Streams
httpHTTP webhook / SSE
grpcgRPC streaming
ws / websocketWebSocket
amqpRabbitMQ / AMQP

Validation error codes

CodeSeverityRule
FDL001errorDocument is nil
FDL002warningVersion is empty
FDL010errorEvent missing entity_type
FDL011errorEvent missing action
FDL012warningEvent payload field has unrecognised type
FDL020errorWorkflow step missing id
FDL021warningStep next references unknown step
FDL022warningStep on_error references unknown step
FDL023warningStep action references unknown action
FDL024warningStep event references unknown event
FDL030warningModule depends_on references unknown module
FDL040warningSchedule action references unknown action
FDL050warningChannel protocol is not recognised
FDL060warningAction input/output field has unrecognised type