Overview
The Flow Editor is a visual automation builder powered by React Flow that allows users to create Instagram/Facebook DM automation workflows. It uses a node-based architecture where different node types handle specific automation tasks.
Architecture
Technology Stack
- React Flow: Visual node-based editor (
@xyflow/react) - Firebase Firestore: Draft storage (
automations/{userId}/draft_automations/{automationId}) - REST API: Production publishing (
/flow_editor) - Ant Design: UI components
- react-beautiful-dnd: Drag-and-drop within nodes
Key Files
| File | Purpose |
|---|---|
src/pages/flow-automte/main.js | Main flow editor component |
src/pages/flow-automte/comps/trigger/main.js | Trigger configuration node |
src/pages/flow-automte/comps/message_reply/main.js | Message reply node |
src/pages/flow-automte/comps/question_node/main.js | Lead capture questions |
src/pages/flow-automte/comps/followers_only/main.js | Followers-only gate |
src/pages/automationList/templates.json | Pre-built automation templates |
Complete Flow Payload Structure
Top-Level State Object
{
"name": "My Automation Flow",
"description": "Flow description",
"live": false,
"unsaved_changes": true,
"published_on": "2025-01-26T12:00:00Z",
"gsheet_id": "google_sheet_id",
"gsheetName": "My Leads Sheet",
"nodes": [],
"edges": []
}
Node Types
1. TriggerComp (triggerComp)
Purpose: Entry point for automation - defines when the automation triggers
{
"id": "node-1",
"type": "triggerComp",
"position": { "x": 40, "y": 40 },
"data": {
"auto_reply": {
"dm_reply": {
"enabled": true,
"automate_all": "false",
"triger_words": ["info", "details", "price"],
"one_time_only": true
},
"story_reply": {
"enabled": true,
"automate_all": "false",
"triger_words": ["interested"],
"all_stories": "true",
"selected_posts": ["post_id_1", "post_id_2"]
},
"comment_reply": {
"enabled": true,
"automate_all": "false",
"triger_words": ["link", "interested"],
"all_posts": "true",
"selected_posts": ["post_id_1"],
"comment_response": ["Thanks for commenting!"]
},
"welcome_message": {
"title": "Welcome Title",
"description": "Welcome description",
"button": "Continue"
}
}
}
}
Trigger Types:
| Type | Purpose | Options |
|---|---|---|
dm_reply | DM keyword triggers | automate_all, one_time_only |
story_reply | Story reply triggers | all_stories, selected_posts |
comment_reply | Comment triggers | all_posts, selected_posts, comment_response |
2. MessageReply (messageReply)
Purpose: Send automated DM responses
{
"id": "node-2",
"type": "messageReply",
"position": { "x": 500, "y": 40 },
"data": {
"toogleDrawer": false,
"messages": []
}
}
Message Types:
Text Message:
{
"id": "msg-uuid",
"type": "text",
"text": "Hello! Thanks for reaching out 👋",
"smartReplies": ["Tell me more", "Get Started", "No thanks"],
"follow_up_duration": 30,
"follow_up_duration_type": "m"
}
Button Template Message:
{
"id": "msg-uuid",
"type": "button",
"templates": [
{
"id": "template-uuid",
"title": "Choose an Option",
"description": "What would you like to know?",
"thumb_url": "https://..../image.jpg",
"buttons": [
{ "title": "Pricing", "type": "postback" },
{ "title": "Visit Website", "type": "web_url", "url": "https://..." }
]
}
],
"follow_up_duration": 60,
"follow_up_duration_type": "m"
}
Image Message:
{
"id": "msg-uuid",
"type": "image",
"image_url": "https://public-assets.instantdm.com/..."
}
Audio Message:
{
"id": "msg-uuid",
"type": "audio",
"audio_url": "https://public-assets.instantdm.com/..."
}
Delay Message:
{
"id": "msg-uuid",
"type": "delay",
"delayDuration": 30,
"durationType": "s"
}
3. QuestionNode (questionNode)
Purpose: Capture lead information with validation
{
"id": "node-3",
"type": "questionNode",
"position": { "x": 800, "y": 40 },
"data": {
"messages": []
}
}
Question Types:
| Type | Purpose | Validation |
|---|---|---|
name | Full name | Valid name format |
email | Email address | Valid email format |
phone | Phone number | Valid phone format |
address | Full address | Street + postal code |
text | Free text input | No validation |
number | Numeric input | Integer validation |
date | Date input | Date format validation |
bool | Yes/No choice | Choice selection |
rating | Star rating (1-5) | Rating selection |
Question Message Structure:
{
"id": "q-uuid",
"question_type": "email",
"question": "What's your email address?",
"smartReplies": [],
"skip_option": {
"enable": true,
"skip_text": "Skip this question"
},
"retry_attempt": {
"count": 3
},
"wrong_input": {
"message": "Please enter a valid email (e.g., name@example.com)"
},
"custom_variable_name": "user_email",
"follow_up_duration": 30,
"follow_up_duration_type": "m"
}
Bool/Rating with Choices:
{
"id": "q-uuid",
"question_type": "rating",
"question": "How would you rate our service?",
"smartReplies": ["⭐⭐⭐⭐⭐", "⭐⭐⭐⭐", "⭐⭐⭐", "⭐⭐", "⭐"]
}
4. FollowersOnly (followersOnly)
Purpose: Gate content for followers only
{
"id": "node-4",
"type": "followersOnly",
"position": { "x": 300, "y": 200 },
"data": {
"followers_only": true,
"message_reply": "true",
"message": {
"title": "Follow for Exclusive Content",
"description": "Looks like you haven't followed me yet!",
"button": "I started following you"
}
}
}
5. Notes (notes)
Purpose: Internal notes (not sent to users)
{
"id": "node-5",
"type": "notes",
"position": { "x": 100, "y": 300 },
"data": {
"notes": "Internal note: Follow up with this user after 24 hours"
}
}
Edge Structure
Edges define connections between nodes.
{
"id": "e-1-3",
"source": "node-1",
"target": "node-3",
"sourceHandle": "node-1-right",
"targetHandle": "node-3-top",
"type": "custom-edge",
"markerEnd": {
"type": "arrowclosed"
},
"data": {}
}
Handle Types:
- Source handles (output):
{nodeId},{messageId}-{buttonIndex},{messageId}-followup - Target handles (input):
{nodeId}-top
Smart Reply Connection:
{
"source": "node-2",
"sourceHandle": "msg-uuid-0",
"target": "node-3"
}
Button Template Connection:
{
"source": "node-2",
"sourceHandle": "template-uuid-0",
"target": "node-3"
}
Follow-up Connection:
{
"source": "node-2",
"sourceHandle": "msg-uuid-followup",
"target": "node-4"
}
API Endpoints
1. Get Flow Data
GET /flow_editor?flow_id={automation_id}
2. Publish Flow (Set Live)
POST /flow_editor?flow_id={automation_id}
Body: { ...complete_flow_payload }
3. Delete Flow
DELETE /flow_editor?flow_id={automation_id}
4. Get Flow Responses
GET /flow_editor_response?flow_id={flow_id}
Firebase Storage
Draft Automations: automations/{userId}/draft_automations/{automationId}
Auto-saves on every state change with debounced updates.
Complete Example Payload
{
"name": "Lead Collection Flow",
"live": true,
"published_on": "2025-01-26T12:00:00Z",
"nodes": [
{
"id": "trigger-1",
"type": "triggerComp",
"position": { "x": 50, "y": 50 },
"data": {
"auto_reply": {
"comment_reply": {
"enabled": true,
"triger_words": ["interested"],
"all_posts": "true"
},
"welcome_message": {
"title": "Thanks!",
"description": "Let me get your details",
"button": "Continue"
}
}
}
},
{
"id": "welcome-1",
"type": "messageReply",
"position": { "x": 50, "y": 200 },
"data": {
"messages": [
{
"id": "msg-1",
"type": "text",
"text": "Thanks for your interest! 🎉"
}
]
}
},
{
"id": "question-email",
"type": "questionNode",
"position": { "x": 50, "y": 350 },
"data": {
"messages": [
{
"id": "q-email",
"question_type": "email",
"question": "What's your email?",
"retry_attempt": { "count": 3 },
"wrong_input": { "message": "Please enter valid email" }
}
]
}
}
],
"edges": [
{
"id": "e1",
"source": "trigger-1",
"target": "welcome-1",
"type": "custom-edge"
},
{
"id": "e2",
"source": "welcome-1",
"target": "question-email",
"type": "custom-edge"
}
]
}
Quick DM Feature Design
Concept
A simplified "click and use" interface that generates the full flow payload in the background.
Proposed Approach
- Pre-select Trigger Type → Comment/DM/Story reply
- Quick Message Templates → Select from pre-built responses
- Lead Collection Fields → Toggle on/off: Name, Email, Phone
- Confirmation Message → Set closing message
The UI will auto-generate:
- Trigger node with selected configuration
- Message nodes for each response
- Question nodes for selected lead fields
- Proper edges connecting all nodes
Quick DM → Flow Mapping
| Quick DM Field | Flow Payload Location |
|---|---|
| Trigger Type | nodes[0].data.auto_reply.{type}.enabled |
| Trigger Words | nodes[0].data.auto_reply.{type}.triger_words |
| Welcome Message | nodes[1].data.messages[0].text |
| Collect Name | Add questionNode with question_type: "name" |
| Collect Email | Add questionNode with question_type: "email" |
| Collect Phone | Add questionNode with question_type: "phone" |
| Thank You | Final messageReply node |
Pre-built Templates
Available in src/pages/automationList/templates.json:
| Template | Description |
|---|---|
lead-collection | Name, email, phone collection |
product-inquiry | Product selection + order capture |
feedback-collection | Rating + feedback |
appointment-booking | Date/time scheduling |
quiz-engagement | Interactive quiz |
Key TypeScript/Interface Definitions
interface FlowState {
name: string;
live: boolean;
published_on?: string;
gsheet_id?: string;
nodes: FlowNode[];
edges: FlowEdge[];
}
interface FlowNode {
id: string;
type: 'triggerComp' | 'messageReply' | 'questionNode' | 'followersOnly' | 'notes';
position: { x: number; y: number };
data: TriggerData | MessageData | QuestionData | FollowersOnlyData | NotesData;
}
interface FlowEdge {
id: string;
source: string;
target: string;
sourceHandle?: string;
targetHandle?: string;
type: 'custom-edge';
markerEnd?: { type: 'arrowclosed' };
}
interface Message {
id: string;
type: 'text' | 'button' | 'image' | 'audio' | 'delay';
text?: string;
smartReplies?: string[];
templates?: Template[];
image_url?: string;
audio_url?: string;
delayDuration?: number;
durationType?: 's' | 'm';
follow_up_duration?: number;
follow_up_duration_type?: 's' | 'm' | 'h';
}
interface Question extends Message {
question_type: 'name' | 'email' | 'phone' | 'address' | 'text' | 'number' | 'date' | 'bool' | 'rating';
question: string;
skip_option?: { enable: boolean; skip_text: string };
retry_attempt?: { count: number };
wrong_input?: { message: string };
custom_variable_name?: string;
}