FlowDSL Examples
These examples show progressively more complete FlowDSL documents. Each can be fed directly into the Redelay compile pipeline:
# 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.
# 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:
| Output | Content |
|---|---|
| AsyncAPI | One channel users.created, one message user_created with payload schema |
| OpenAPI 3.0 | One 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.
# 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:
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.
# 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):
# 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 type | JSON Schema / AsyncAPI | OpenAPI |
|---|---|---|
string | string | string |
integer / int | integer | integer |
float / number | number | number |
boolean / bool | boolean | boolean |
array | array | array |
object | object | object |
date | string + format: date | string + format: date |
datetime | string + format: date-time | string + format: date-time |
uuid | string + format: uuid | string + format: uuid |
binary | string + format: binary | string + format: binary |
enum | string + enum: [...] | string + enum: [...] |
ref | $ref | $ref |
any | {} (any schema) | {} |
All channel protocols
| Protocol | Transport |
|---|---|
kafka | Apache Kafka (at-least-once, durable) |
nats | NATS core or JetStream |
redis | Redis Streams |
http | HTTP webhook / SSE |
grpc | gRPC streaming |
ws / websocket | WebSocket |
amqp | RabbitMQ / AMQP |
Validation error codes
| Code | Severity | Rule |
|---|---|---|
FDL001 | error | Document is nil |
FDL002 | warning | Version is empty |
FDL010 | error | Event missing entity_type |
FDL011 | error | Event missing action |
FDL012 | warning | Event payload field has unrecognised type |
FDL020 | error | Workflow step missing id |
FDL021 | warning | Step next references unknown step |
FDL022 | warning | Step on_error references unknown step |
FDL023 | warning | Step action references unknown action |
FDL024 | warning | Step event references unknown event |
FDL030 | warning | Module depends_on references unknown module |
FDL040 | warning | Schedule action references unknown action |
FDL050 | warning | Channel protocol is not recognised |
FDL060 | warning | Action input/output field has unrecognised type |