This workflow corresponds to n8n.io template #12734 — we link there as the canonical source.
This workflow follows the Agent → Emailsend recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"id": "PE9kXdJpHe48fwiD",
"name": "Intelligent Airline Operations Control and Disruption Management System",
"tags": [],
"nodes": [
{
"id": "a7f521b0-b179-477f-937b-998217bd1b2f",
"name": "Schedule: Poll APIs Every 2 Minutes",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-3872,
80
],
"parameters": {
"rule": {
"interval": [
{
"field": "minutes",
"minutesInterval": 2
}
]
}
},
"typeVersion": 1.3
},
{
"id": "489b1835-c0bc-40e9-a64a-7ff3afa28631",
"name": "Webhook: Real-Time Event Ingestion",
"type": "n8n-nodes-base.webhook",
"position": [
-3200,
272
],
"parameters": {
"path": "airline-events",
"options": {},
"httpMethod": "POST",
"responseMode": "lastNode",
"authentication": "headerAuth"
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "8540bc8a-4bef-43c1-b078-cf17f931559a",
"name": "Workflow Configuration",
"type": "n8n-nodes-base.set",
"position": [
-3648,
80
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "id-1",
"name": "flightApiUrl",
"type": "string",
"value": "<__PLACEHOLDER_VALUE__Flight Schedule API Endpoint URL__>"
},
{
"id": "id-2",
"name": "weatherApiUrl",
"type": "string",
"value": "<__PLACEHOLDER_VALUE__Weather API Endpoint URL__>"
},
{
"id": "id-3",
"name": "telemetryApiUrl",
"type": "string",
"value": "<__PLACEHOLDER_VALUE__Telemetry API Endpoint URL__>"
},
{
"id": "id-4",
"name": "passengerApiUrl",
"type": "string",
"value": "<__PLACEHOLDER_VALUE__Passenger Service API Endpoint URL__>"
},
{
"id": "id-5",
"name": "dbConnectionString",
"type": "string",
"value": "<__PLACEHOLDER_VALUE__PostgreSQL Connection String__>"
},
{
"id": "id-6",
"name": "slackOccChannel",
"type": "string",
"value": "<__PLACEHOLDER_VALUE__OCC Slack Channel ID__>"
},
{
"id": "id-7",
"name": "groundStaffEmail",
"type": "string",
"value": "<__PLACEHOLDER_VALUE__Ground Staff Email Address__>"
},
{
"id": "id-8",
"name": "customerServicePhone",
"type": "string",
"value": "<__PLACEHOLDER_VALUE__Customer Service SMS Number__>"
},
{
"id": "id-9",
"name": "delayThresholdMinutes",
"type": "number",
"value": 30
},
{
"id": "id-10",
"name": "criticalSeverityThreshold",
"type": "number",
"value": 90
}
]
},
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "6e313efb-9b26-4d6b-aa85-b1352f0a51a0",
"name": "Fetch Flight & Weather Data",
"type": "n8n-nodes-base.httpRequest",
"position": [
-3424,
80
],
"parameters": {
"url": "={{ $('Workflow Configuration').first().json.flightApiUrl }}",
"options": {
"batching": {
"batch": {}
},
"response": {
"response": {
"responseFormat": "json"
}
}
},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "limit",
"value": "100"
}
]
}
},
"typeVersion": 4.3
},
{
"id": "9759fbf3-d8a3-4d88-bb1c-caed4fe8163b",
"name": "Validate & Normalize Data",
"type": "n8n-nodes-base.code",
"position": [
-2976,
176
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// Validate & Normalize Data\n// This code validates incoming data schema, normalizes field names,\n// converts timestamps to ISO format, validates flight numbers,\n// checks for required fields, sanitizes input, and adds validation timestamp\n\nconst item = $input.item.json;\n\n// Helper function to validate IATA flight number format (e.g., AA1234, BA456)\nfunction validateFlightNumber(flightNumber) {\n if (!flightNumber) return false;\n // IATA format: 2-3 letter airline code + 1-4 digit flight number\n const iataPattern = /^[A-Z]{2,3}\\d{1,4}$/i;\n return iataPattern.test(flightNumber.toString().replace(/\\s+/g, ''));\n}\n\n// Helper function to convert various timestamp formats to ISO format\nfunction normalizeTimestamp(timestamp) {\n if (!timestamp) return null;\n try {\n const date = new Date(timestamp);\n if (isNaN(date.getTime())) return null;\n return date.toISOString();\n } catch (error) {\n return null;\n }\n}\n\n// Helper function to sanitize string input\nfunction sanitizeString(str) {\n if (typeof str !== 'string') return str;\n return str.trim().replace(/[<>\"']/g, '');\n}\n\n// Normalize field names (handle various naming conventions)\nconst normalizedData = {\n flightNumber: item.flightNumber || item.flight_number || item.FlightNumber || item.flight || null,\n departureTime: item.departureTime || item.departure_time || item.DepartureTime || item.scheduled_departure || null,\n arrivalTime: item.arrivalTime || item.arrival_time || item.ArrivalTime || item.scheduled_arrival || null,\n status: item.status || item.Status || item.flight_status || null,\n origin: item.origin || item.departure_airport || item.from || null,\n destination: item.destination || item.arrival_airport || item.to || null,\n aircraft: item.aircraft || item.aircraft_type || null,\n gate: item.gate || item.departure_gate || null,\n terminal: item.terminal || item.departure_terminal || null,\n weather: item.weather || item.weather_data || null,\n delay: item.delay || item.delay_minutes || null,\n rawData: item\n};\n\n// Validation results\nconst validationErrors = [];\nconst validationWarnings = [];\n\n// Check required fields\nconst requiredFields = ['flightNumber', 'departureTime', 'arrivalTime', 'status'];\nfor (const field of requiredFields) {\n if (!normalizedData[field]) {\n validationErrors.push(`Missing required field: ${field}`);\n }\n}\n\n// Validate flight number format\nif (normalizedData.flightNumber) {\n normalizedData.flightNumber = sanitizeString(normalizedData.flightNumber.toString());\n if (!validateFlightNumber(normalizedData.flightNumber)) {\n validationErrors.push(`Invalid flight number format: ${normalizedData.flightNumber}`);\n }\n} else {\n validationErrors.push('Flight number is missing');\n}\n\n// Normalize timestamps to ISO format\nif (normalizedData.departureTime) {\n const isoTime = normalizeTimestamp(normalizedData.departureTime);\n if (isoTime) {\n normalizedData.departureTime = isoTime;\n } else {\n validationErrors.push('Invalid departure time format');\n }\n}\n\nif (normalizedData.arrivalTime) {\n const isoTime = normalizeTimestamp(normalizedData.arrivalTime);\n if (isoTime) {\n normalizedData.arrivalTime = isoTime;\n } else {\n validationErrors.push('Invalid arrival time format');\n }\n}\n\n// Sanitize string fields\nif (normalizedData.status) {\n normalizedData.status = sanitizeString(normalizedData.status.toString()).toUpperCase();\n}\nif (normalizedData.origin) {\n normalizedData.origin = sanitizeString(normalizedData.origin.toString()).toUpperCase();\n}\nif (normalizedData.destination) {\n normalizedData.destination = sanitizeString(normalizedData.destination.toString()).toUpperCase();\n}\n\n// Add validation metadata\nconst validationTimestamp = new Date().toISOString();\nconst isValid = validationErrors.length === 0;\n\n// Return normalized data with validation status\nreturn {\n json: {\n ...normalizedData,\n validation: {\n isValid: isValid,\n timestamp: validationTimestamp,\n errors: validationErrors,\n warnings: validationWarnings\n }\n }\n};"
},
"typeVersion": 2
},
{
"id": "efa8778d-fbf1-49a7-b8af-d88cc217c51c",
"name": "Check Idempotency & Deduplication",
"type": "n8n-nodes-base.code",
"position": [
-2752,
176
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// Idempotency & Deduplication Logic\n// Generate unique event ID using SHA-256 hash and check for duplicates\n\nconst crypto = require('crypto');\n\n// Extract event data from input\nconst item = $input.item.json;\nconst flightNumber = item.flightNumber || item.flight_number || '';\nconst departureTime = item.departureTime || item.departure_time || item.scheduledDeparture || '';\nconst eventType = item.eventType || item.event_type || item.type || '';\n\n// Generate unique event ID using SHA-256 hash\nconst eventString = `${flightNumber}|${departureTime}|${eventType}`;\nconst eventId = crypto.createHash('sha256').update(eventString).digest('hex');\n\n// Get current timestamp\nconst now = new Date();\nconst twentyFourHoursAgo = new Date(now.getTime() - 24 * 60 * 60 * 1000);\n\n// Initialize in-memory cache if not exists (using workflow static data)\nconst cache = $workflow.staticData.eventCache || {};\nconst cacheTimestamps = $workflow.staticData.eventCacheTimestamps || {};\n\n// Clean up old entries (sliding window - remove events older than 24 hours)\nfor (const [key, timestamp] of Object.entries(cacheTimestamps)) {\n if (new Date(timestamp) < twentyFourHoursAgo) {\n delete cache[key];\n delete cacheTimestamps[key];\n }\n}\n\n// Check if event is duplicate\nconst isDuplicate = cache.hasOwnProperty(eventId);\n\n// If not duplicate, add to cache\nif (!isDuplicate) {\n cache[eventId] = true;\n cacheTimestamps[eventId] = now.toISOString();\n}\n\n// Save updated cache back to workflow static data\n$workflow.staticData.eventCache = cache;\n$workflow.staticData.eventCacheTimestamps = cacheTimestamps;\n\n// Return enriched item with idempotency data\nreturn {\n json: {\n ...item,\n idempotencyKey: eventId,\n isDuplicate: isDuplicate,\n eventStatus: isDuplicate ? 'duplicate' : 'new',\n deduplicationTimestamp: now.toISOString(),\n eventHash: eventId,\n cacheSize: Object.keys(cache).length\n }\n};"
},
"typeVersion": 2
},
{
"id": "55f7f73d-13d9-469e-8d58-3a67b8fb5b8c",
"name": "Is Duplicate Event?",
"type": "n8n-nodes-base.if",
"position": [
-2528,
176
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": false,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "id-1",
"operator": {
"type": "boolean",
"operation": "true"
},
"leftValue": "={{ $('Check Idempotency & Deduplication').item.json.isDuplicate }}"
}
]
}
},
"typeVersion": 2.3
},
{
"id": "d43f9352-2d1e-489b-964e-e95266a5e1ac",
"name": "AI Agent: Disruption Prediction & Analysis",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
-2272,
160
],
"parameters": {
"text": "=Flight Data: {{ $json.flightNumber }} departing {{ $json.departureTime }} to {{ $json.destination }}. Current Status: {{ $json.status }}. Weather: {{ $json.weatherConditions }}. Telemetry: {{ $json.telemetryData }}",
"options": {
"systemMessage": "You are an expert airline operations analyst specializing in disruption prediction and delay impact analysis.\n\nYour task is to:\n1. Analyze flight schedules, real-time telemetry, weather conditions, and passenger service events\n2. Predict disruption probability (0-100%) based on historical patterns and current conditions\n3. Estimate delay impact in minutes considering cascading effects\n4. Assess safety risks (low/medium/high/critical) based on weather, mechanical issues, and crew fatigue\n5. Identify crew constraint violations (duty time limits, rest requirements)\n6. Evaluate aircraft rotation impact on downstream flights\n7. Check slot constraint violations at congested airports\n8. Recommend specific operational actions\n9. Assign severity level (low/medium/high/critical)\n10. Flag if OCC review is required (severity >= high OR safety risk >= high)\n\nConsider:\n- Weather severity and trends\n- Aircraft maintenance status\n- Crew availability and duty limits\n- Airport slot constraints\n- Passenger connection impacts\n- Historical delay patterns for this route\n- Cascading effects on aircraft rotation\n\nReturn structured JSON output with all required fields."
},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 3.1
},
{
"id": "c14dbdc4-a623-4a8e-8286-4bcf95609cd4",
"name": "OpenAI GPT-4",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
-2272,
480
],
"parameters": {
"model": {
"__rl": true,
"mode": "id",
"value": "gpt-4o"
},
"options": {
"maxTokens": 2000,
"temperature": 0.3
},
"builtInTools": {}
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.3
},
{
"id": "dcc8392e-dfac-46b4-a1c5-8656c0623d66",
"name": "Structured Output: Disruption Analysis",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
-2112,
336
],
"parameters": {
"autoFix": true
},
"typeVersion": 1.3
},
{
"id": "3e49d10c-a9b3-4491-8fb9-092bed93d9a5",
"name": "Apply Operational Rules Engine",
"type": "n8n-nodes-base.code",
"position": [
-1824,
160
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// Apply Operational Rules Engine\n// Process each disruption event and apply airline operational rules\n\nconst item = $input.item.json;\n\n// Initialize rule violations array\nconst ruleViolations = [];\nlet operationalFeasibilityScore = 100;\n\n// Extract relevant data from the AI analysis\nconst disruptionType = item.disruptionType || item.type || 'unknown';\nconst severity = item.severity || 'medium';\nconst affectedFlights = item.affectedFlights || [];\nconst proposedActions = item.proposedActions || [];\n\n// Rule 1: Crew Duty Time Limits (max 14 hours)\nconst crewDutyTime = item.crewDutyTime || item.estimatedCrewDutyTime || 0;\nif (crewDutyTime > 14) {\n ruleViolations.push({\n rule: 'CREW_DUTY_TIME_EXCEEDED',\n description: `Crew duty time of ${crewDutyTime} hours exceeds maximum of 14 hours`,\n severity: 'critical',\n impact: 'Flight cannot proceed without crew change'\n });\n operationalFeasibilityScore -= 30;\n}\n\n// Rule 2: Aircraft Turnaround Time (min 45 minutes)\nconst turnaroundTime = item.turnaroundTime || item.estimatedTurnaroundTime || 60;\nif (turnaroundTime < 45) {\n ruleViolations.push({\n rule: 'INSUFFICIENT_TURNAROUND_TIME',\n description: `Turnaround time of ${turnaroundTime} minutes is below minimum of 45 minutes`,\n severity: 'high',\n impact: 'Risk of delayed departure and cascading delays'\n });\n operationalFeasibilityScore -= 20;\n}\n\n// Rule 3: Slot Constraints at Congested Airports\nconst airport = item.airport || item.departureAirport || '';\nconst congestedAirports = ['LHR', 'JFK', 'LAX', 'ORD', 'ATL', 'DXB', 'CDG', 'FRA', 'AMS', 'HKG'];\nconst hasSlotConstraint = congestedAirports.includes(airport);\n\nif (hasSlotConstraint) {\n const slotAvailable = item.slotAvailable !== false; // Default to true if not specified\n if (!slotAvailable) {\n ruleViolations.push({\n rule: 'SLOT_CONSTRAINT_VIOLATION',\n description: `No available slot at congested airport ${airport}`,\n severity: 'critical',\n impact: 'Cannot depart without allocated slot'\n });\n operationalFeasibilityScore -= 40;\n }\n}\n\n// Rule 4: Maintenance Requirements\nconst maintenanceStatus = item.maintenanceStatus || 'ok';\nconst hoursSinceLastMaintenance = item.hoursSinceLastMaintenance || 0;\nconst maintenanceRequired = maintenanceStatus === 'required' || hoursSinceLastMaintenance > 100;\n\nif (maintenanceRequired) {\n ruleViolations.push({\n rule: 'MAINTENANCE_REQUIRED',\n description: `Aircraft requires maintenance (status: ${maintenanceStatus}, hours since last: ${hoursSinceLastMaintenance})`,\n severity: 'critical',\n impact: 'Aircraft cannot operate until maintenance completed'\n });\n operationalFeasibilityScore -= 50;\n}\n\n// Rule 5: Passenger Connection Times (min 60 minutes international)\nconst connectingPassengers = item.connectingPassengers || [];\nconnectingPassengers.forEach((pax, index) => {\n const connectionTime = pax.connectionTime || 0;\n const isInternational = pax.isInternational || false;\n const minConnectionTime = isInternational ? 60 : 45;\n \n if (connectionTime < minConnectionTime) {\n ruleViolations.push({\n rule: 'INSUFFICIENT_CONNECTION_TIME',\n description: `Passenger connection time of ${connectionTime} minutes is below minimum of ${minConnectionTime} minutes (${isInternational ? 'international' : 'domestic'})`,\n severity: 'high',\n impact: 'Passengers will miss connections, rebooking required'\n });\n operationalFeasibilityScore -= 10;\n }\n});\n\n// Rule 6: IATA Regulations Compliance\nconst iataCompliance = {\n delayCompensation: false,\n passengerRights: false,\n baggageHandling: true\n};\n\n// Check if delay exceeds thresholds for compensation (EU261 style)\nconst delayMinutes = item.delayMinutes || item.estimatedDelay || 0;\nif (delayMinutes > 180) {\n iataCompliance.delayCompensation = true;\n ruleViolations.push({\n rule: 'IATA_DELAY_COMPENSATION_REQUIRED',\n description: `Delay of ${delayMinutes} minutes triggers passenger compensation requirements`,\n severity: 'medium',\n impact: 'Compensation and care obligations apply'\n });\n}\n\nif (delayMinutes > 120) {\n iataCompliance.passengerRights = true;\n ruleViolations.push({\n rule: 'IATA_PASSENGER_CARE_REQUIRED',\n description: `Delay of ${delayMinutes} minutes triggers passenger care requirements (meals, accommodation)`,\n severity: 'medium',\n impact: 'Must provide passenger care and assistance'\n });\n}\n\n// Calculate final operational feasibility score (0-100)\noperationalFeasibilityScore = Math.max(0, Math.min(100, operationalFeasibilityScore));\n\n// Determine operational status\nlet operationalStatus = 'FEASIBLE';\nif (operationalFeasibilityScore < 30) {\n operationalStatus = 'NOT_FEASIBLE';\n} else if (operationalFeasibilityScore < 60) {\n operationalStatus = 'REQUIRES_INTERVENTION';\n} else if (operationalFeasibilityScore < 80) {\n operationalStatus = 'FEASIBLE_WITH_CONSTRAINTS';\n}\n\n// Enrich data with operational context\nconst enrichedData = {\n ...item,\n operationalRules: {\n ruleViolations: ruleViolations,\n violationCount: ruleViolations.length,\n criticalViolations: ruleViolations.filter(v => v.severity === 'critical').length,\n highViolations: ruleViolations.filter(v => v.severity === 'high').length,\n mediumViolations: ruleViolations.filter(v => v.severity === 'medium').length\n },\n operationalFeasibility: {\n score: operationalFeasibilityScore,\n status: operationalStatus,\n canProceed: operationalFeasibilityScore >= 60,\n requiresApproval: operationalFeasibilityScore < 80 && operationalFeasibilityScore >= 60\n },\n iataCompliance: iataCompliance,\n operationalContext: {\n congestedAirport: hasSlotConstraint,\n maintenanceRequired: maintenanceRequired,\n crewConstraints: crewDutyTime > 12,\n connectionImpact: connectingPassengers.length,\n compensationLiability: iataCompliance.delayCompensation,\n processedAt: new Date().toISOString(),\n rulesEngineVersion: '1.0'\n }\n};\n\nreturn enrichedData;"
},
"typeVersion": 2
},
{
"id": "a2f02d04-1b7e-4d81-b17c-a1965e26506d",
"name": "Route by Severity & Type",
"type": "n8n-nodes-base.switch",
"position": [
-1600,
128
],
"parameters": {
"rules": {
"values": [
{
"outputKey": "Critical",
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": false,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.severity }}",
"rightValue": "critical"
}
]
},
"renameOutput": true
},
{
"outputKey": "High",
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": false,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.severity }}",
"rightValue": "high"
}
]
},
"renameOutput": true
},
{
"outputKey": "Medium",
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": false,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.severity }}",
"rightValue": "medium"
}
]
},
"renameOutput": true
},
{
"outputKey": "Low",
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": false,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.severity }}",
"rightValue": "low"
}
]
},
"renameOutput": true
}
]
},
"options": {
"fallbackOutput": 3
}
},
"typeVersion": 3.4
},
{
"id": "1956a504-9912-4a47-bf6f-9b8c47d2b854",
"name": "Store: Critical Events DB",
"type": "n8n-nodes-base.postgres",
"position": [
-1396,
72
],
"parameters": {
"table": {
"__rl": true,
"mode": "name",
"value": "critical_events"
},
"schema": {
"__rl": true,
"mode": "list",
"value": "public"
},
"columns": {
"value": {
"event_id": "={{ $json.eventId }}",
"raw_data": "={{ $json }}",
"severity": "={{ $json.severity }}",
"created_at": "={{ $now }}",
"safety_risk": "={{ $json.safetyRisk }}",
"flight_number": "={{ $json.flightNumber }}",
"affected_passengers": "={{ $json.affectedPassengers }}",
"recommended_actions": "={{ $json.recommendedActions }}",
"delay_impact_minutes": "={{ $json.delayImpactMinutes }}",
"disruption_probability": "={{ $json.disruptionProbability }}"
},
"schema": [],
"mappingMode": "defineBelow",
"matchingColumns": []
},
"options": {}
},
"typeVersion": 2.6
},
{
"id": "412a9bec-8467-463b-ad4d-3bf26c74222d",
"name": "Store: High Priority Events DB",
"type": "n8n-nodes-base.postgres",
"position": [
-52,
288
],
"parameters": {
"table": {
"__rl": true,
"mode": "name",
"value": "high_priority_events"
},
"schema": {
"__rl": true,
"mode": "list",
"value": "public"
},
"columns": {
"value": {
"event_id": "={{ $json.event_id }}",
"raw_data": "={{ $json.raw_data }}",
"severity": "={{ $json.severity }}",
"created_at": "={{ $json.created_at }}",
"flight_number": "={{ $json.flight_number }}",
"affected_passengers": "={{ $json.affected_passengers }}",
"delay_impact_minutes": "={{ $json.delay_impact_minutes }}",
"disruption_probability": "={{ $json.disruption_probability }}"
},
"schema": [
{
"id": "event_id",
"required": false,
"displayName": "event_id",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "flight_number",
"required": false,
"displayName": "flight_number",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "severity",
"required": false,
"displayName": "severity",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "disruption_probability",
"required": false,
"displayName": "disruption_probability",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "delay_impact_minutes",
"required": false,
"displayName": "delay_impact_minutes",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "affected_passengers",
"required": false,
"displayName": "affected_passengers",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "created_at",
"required": false,
"displayName": "created_at",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "raw_data",
"required": false,
"displayName": "raw_data",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": []
},
"options": {}
},
"typeVersion": 2.6
},
{
"id": "01dcf0fc-39a5-45a8-85c9-fe1a38bf9a28",
"name": "Store: Medium Priority Events DB",
"type": "n8n-nodes-base.postgres",
"position": [
-52,
480
],
"parameters": {
"table": {
"__rl": true,
"mode": "name",
"value": "medium_priority_events"
},
"schema": {
"__rl": true,
"mode": "list",
"value": "public"
},
"columns": {
"value": {
"event_id": "={{ $json.event_id }}",
"raw_data": "={{ $json.raw_data }}",
"severity": "={{ $json.severity }}",
"created_at": "={{ $json.created_at }}",
"flight_number": "={{ $json.flight_number }}"
},
"mappingMode": "defineBelow"
},
"options": {}
},
"typeVersion": 2.6
},
{
"id": "94ff6f62-9d6b-4e62-b4d1-66c5ba1224f9",
"name": "Store: Low Priority Events DB",
"type": "n8n-nodes-base.postgres",
"position": [
-1184,
544
],
"parameters": {
"table": {
"__rl": true,
"mode": "name",
"value": "low_priority_events"
},
"schema": {
"__rl": true,
"mode": "list",
"value": "public"
},
"columns": {
"value": {
"event_id": "={{ $json.event_id }}",
"severity": "={{ $json.severity }}",
"created_at": "={{ $json.created_at }}",
"flight_number": "={{ $json.flight_number }}"
},
"mappingMode": "defineBelow"
},
"options": {}
},
"typeVersion": 2.6
},
{
"id": "79a49437-4a46-4729-9ad8-437b3d31ceaa",
"name": "Alert: OCC Slack Channel",
"type": "n8n-nodes-base.slack",
"position": [
-1172,
72
],
"parameters": {
"text": "=CRITICAL ALERT: Flight {{ $json.flightNumber }} - Disruption Probability: {{ $json.disruptionProbability }}% - Delay Impact: {{ $json.delayImpactMinutes }} min - Safety Risk: {{ $json.safetyRisk }} - Affected Passengers: {{ $json.affectedPassengers }} - Actions: {{ $json.recommendedActions.join(\", \") }}",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Workflow Configuration').first().json.slackOccChannel }}"
},
"otherOptions": {},
"authentication": "oAuth2"
},
"credentials": {
"slackOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 2.4
},
{
"id": "80b91ab1-4a73-47b4-b935-96ce4e075a28",
"name": "Email: Ground Staff Notification",
"type": "n8n-nodes-base.emailSend",
"position": [
-948,
72
],
"parameters": {
"html": "=<h2>Critical Flight Disruption</h2><p><strong>Flight:</strong> {{ $json.flightNumber }}</p><p><strong>Disruption Probability:</strong> {{ $json.disruptionProbability }}%</p><p><strong>Delay Impact:</strong> {{ $json.delayImpactMinutes }} minutes</p><p><strong>Safety Risk:</strong> {{ $json.safetyRisk }}</p><p><strong>Affected Passengers:</strong> {{ $json.affectedPassengers }}</p><p><strong>Recommended Actions:</strong></p><ul>{{ $json.recommendedActions.map(a => `<li>${a}</li>`).join(\"\") }}</ul>",
"options": {},
"subject": "=Flight {{ $json.flightNumber }} - Critical Disruption Alert",
"toEmail": "={{ $('Workflow Configuration').first().json.groundStaffEmail }}",
"fromEmail": "user@example.com"
},
"typeVersion": 2.1
},
{
"id": "2c3dabe7-32db-4cbb-8dbf-31436cf21a44",
"name": "SMS: Customer Service Alert",
"type": "n8n-nodes-base.twilio",
"position": [
-724,
72
],
"parameters": {
"to": "={{ $('Workflow Configuration').first().json.customerServicePhone }}",
"from": "<__PLACEHOLDER_VALUE__Twilio From Phone Number__>",
"message": "=ALERT: Flight {{ $json.flightNumber }} critical disruption. Probability: {{ $json.disruptionProbability }}%. Delay: {{ $json.delayImpactMinutes }}min. Passengers: {{ $json.affectedPassengers }}. Review required.",
"options": {}
},
"typeVersion": 1
},
{
"id": "db4e26fc-9ec7-4a58-9bf2-d0964a6ff2b6",
"name": "Prepare Passenger Notification Data",
"type": "n8n-nodes-base.set",
"position": [
-500,
72
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "id-1",
"name": "notificationType",
"type": "string",
"value": "passenger_alert"
},
{
"id": "id-2",
"name": "notificationChannel",
"type": "string",
"value": "email_sms"
},
{
"id": "id-3",
"name": "passengerList",
"type": "string",
"value": "={{ $json.affectedPassengerIds }}"
},
{
"id": "id-4",
"name": "messageTemplate",
"type": "string",
"value": "flight_disruption"
},
{
"id": "id-5",
"name": "urgency",
"type": "string",
"value": "={{ $json.severity }}"
}
]
},
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "6fdd3c91-e1a1-4656-8a90-24b700a18bc7",
"name": "Requires Rebooking?",
"type": "n8n-nodes-base.if",
"position": [
-276,
72
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": false,
"typeValidation": "loose"
},
"combinator": "or",
"conditions": [
{
"id": "id-1",
"operator": {
"type": "number",
"operation": "gt"
},
"leftValue": "={{ $json.delayImpactMinutes }}",
"rightValue": "={{ $('Workflow Configuration').first().json.delayThresholdMinutes }}"
},
{
"id": "id-2",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.severity }}",
"rightValue": "critical"
}
]
}
},
"typeVersion": 2.3
},
{
"id": "db0f8677-c4af-4c14-ba9e-76f9812de2b6",
"name": "Trigger Rebooking Workflow",
"type": "n8n-nodes-base.code",
"position": [
0,
48
],
"parameters": {
"jsCode": "// Trigger Rebooking Workflow\n// This code initiates the rebooking process by calling internal API endpoint\n\nconst items = $input.all();\nconst rebookingRequests = [];\n\nfor (const item of items) {\n const passengerList = item.json.passengers || [];\n const flightDetails = item.json.flightDetails || {};\n const alternativeFlights = item.json.alternativeFlights || [];\n const priorityLevel = item.json.priorityLevel || 'medium';\n \n // Generate unique rebooking request ID\n const rebookingRequestId = `RBK-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n \n // Create rebooking request record\n const rebookingRequest = {\n requestId: rebookingRequestId,\n timestamp: new Date().toISOString(),\n originalFlight: {\n flightNumber: flightDetails.flightNumber,\n departure: flightDetails.departure,\n arrival: flightDetails.arrival,\n scheduledDeparture: flightDetails.scheduledDeparture\n },\n passengers: passengerList.map(p => ({\n passengerId: p.id,\n name: p.name,\n pnr: p.pnr,\n seatClass: p.seatClass,\n specialRequirements: p.specialRequirements || []\n })),\n alternativeOptions: alternativeFlights.map(f => ({\n flightNumber: f.flightNumber,\n departure: f.departure,\n arrival: f.arrival,\n availableSeats: f.availableSeats,\n seatClass: f.seatClass\n })),\n priority: priorityLevel,\n status: 'initiated',\n initiatedBy: 'disruption-management-system',\n initiatedAt: new Date().toISOString()\n };\n \n // Log rebooking initiation\n console.log(`Rebooking workflow initiated for request ID: ${rebookingRequestId}`);\n console.log(`Priority Level: ${priorityLevel}`);\n console.log(`Passengers affected: ${passengerList.length}`);\n console.log(`Alternative flights available: ${alternativeFlights.length}`);\n \n // Prepare API call payload (simulated - replace with actual API endpoint)\n const apiPayload = {\n endpoint: '/api/rebooking/initiate',\n method: 'POST',\n data: rebookingRequest\n };\n \n // Add to results\n rebookingRequests.push({\n json: {\n success: true,\n rebookingRequestId: rebookingRequestId,\n status: 'initiated',\n passengersCount: passengerList.length,\n alternativeFlightsCount: alternativeFlights.length,\n priorityLevel: priorityLevel,\n apiPayload: apiPayload,\n rebookingRequest: rebookingRequest,\n message: `Rebooking workflow successfully initiated with request ID: ${rebookingRequestId}`\n }\n });\n}\n\nreturn rebookingRequests;"
},
"typeVersion": 2
},
{
"id": "cfb3eb29-bb33-4233-81b2-307b77341e7a",
"name": "Merge All Notification Paths",
"type": "n8n-nodes-base.merge",
"position": [
172,
184
],
"parameters": {
"numberInputs": 4
},
"typeVersion": 3.2
},
{
"id": "7e9e8503-1ca5-4050-b2df-4c530d7438b9",
"name": "Generate Dashboard Metrics",
"type": "n8n-nodes-base.aggregate",
"position": [
396,
216
],
"parameters": {
"options": {},
"aggregate": "aggregateAllItemData"
},
"typeVersion": 1
},
{
"id": "eb2ad27e-cc54-4ca9-8d93-65d5aa72896a",
"name": "Store: Dashboard Metrics DB",
"type": "n8n-nodes-base.postgres",
"position": [
620,
216
],
"parameters": {
"table": {
"__rl": true,
"mode": "name",
"value": "dashboard_metrics"
},
"schema": {
"__rl": true,
"mode": "list",
"value": "public"
},
"columns": {
"value": {
"low_count": "={{ $json.lowCount }}",
"high_count": "={{ $json.highCount }}",
"medium_count": "={{ $json.mediumCount }}",
"total_events": "={{ $json.totalEvents }}",
"critical_count": "={{ $json.criticalCount }}",
"avg_delay_impact": "={{ $json.avgDelayImpact }}",
"metric_timestamp": "={{ $now }}",
"total_affected_passengers": "={{ $json.totalAffectedPassengers }}",
"avg_disruption_probability": "={{ $json.avgDisruptionProbability }}"
},
"mappingMode": "defineBelow"
},
"options": {}
},
"typeVersion": 2.6
},
{
"id": "db8fd5fe-ff22-41d9-b705-0cc7ec523530",
"name": "Rate Limit Handler",
"type": "n8n-nodes-base.wait",
"position": [
-3200,
80
],
"parameters": {
"amount": 0.5
},
"typeVersion": 1.1
},
{
"id": "aab486b8-341f-420f-9b47-a5ede5ebc16b",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2160,
-480
],
"parameters": {
"color": 6,
"width": 496,
"height": 336,
"content": "## Prerequisites\nActive EHR system with FHIR API access or HL7 integration capability. \n## Use Cases\nAutomated appointment reminder campaigns reducing no-shows.\n## Customization\nModify risk scoring models for specialty-specific patient populations. \n## Benefits\nReduces patient no-show rates by 40% through timely, personalized reminders."
},
"typeVersion": 1
},
{
"id": "604a0531-ec9e-46c7-a84a-171e100237dc",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2800,
-416
],
"parameters": {
"width": 592,
"height": 224,
"content": "## Setup Steps\n1. Configure EHR/FHIR API credentialsfor patient data access\n2. Set up webhook endpoints for real-time clinical event notifications\n3. Add OpenAI API key for patient risk stratification and communication personalization\n4. Configure Twilio credentials for SMS and voice call delivery\n5. Set Gmail OAuth or SMTP credentials for email appointment reminders\n6. Connect Slack workspace and define care coordination alert channels\n\n"
},
"typeVersion": 1
},
{
"id": "9c9e53e5-6602-4fbe-8697-f033b07cad80",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3872,
-416
],
"parameters": {
"width": 1024,
"height": 272,
"content": "## How It Works\nThis workflow automates end-to-end patient care coordination by monitoring appointment schedules, clinical events, and care milestones while orchestrating personalized communications across multiple channels. Designed for healthcare operations teams, care coordinators, and patient engagement specialists, it solves the challenge of manual patient follow-up, missed appointments, and fragmented communication across care teams. The system triggers on scheduled intervals and real-time clinical events, ingesting data from EHR systems, appointment schedulers, and lab result feeds. Patient records flow through validation and risk stratification layers using AI models that identify high-risk patients, predict no-show probability, and recommend intervention timing. The workflow applies clinical protocols for appointment reminders, medication adherence checks, and post-discharge follow-ups. Critical cases automatically route to care coordinators via Slack alerts, while routine communications deploy via SMS, email, and patient portal notifications. All interactions log to secure databases for compliance documentation. This eliminates manual outreach coordination, reduces no-shows by 40%, and ensures HIPAA-compliant patient engagement at scale."
},
"typeVersion": 1
},
{
"id": "c994005b-caf5-4e5d-92a7-17589515ab96",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-544,
-64
],
"parameters": {
"color": 7,
"width": 1328,
"height": 928,
"content": "## Care Team Alerting \nRoutes high-risk patient cases to care coordinators via Slack with clinical context and recommended interventions.\n**Why** - Ensures timely human oversight for complex cases while automating routine coordination tasks."
},
"typeVersion": 1
},
{
"id": "a27fa62d-330a-4755-a408-3b5a47513729",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1408,
-64
],
"parameters": {
"color": 7,
"width": 848,
"height": 944,
"content": "## Clinical Protocol Application \nApplies evidence-based care pathways for appointment reminders, medication adherence, post-discharge follow-ups based on patient conditions.\n**Why** - Ensures consistent, guideline-compliant patient communications while reducing manual protocol interpretation by care teams."
},
"typeVersion": 1
},
{
"id": "dbdcb684-21e4-4589-a147-c156f507f5a2",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2320,
-64
],
"parameters": {
"color": 7,
"width": 896,
"height": 928,
"content": "## Risk Stratification\nRoutes patient records through AI models calculating no-show probability, readmission risk, and care urgency scores.\n**Why** - Identifies patients requiring proactive intervention before adverse events occur, shifting from reactive to predictive care management."
},
"typeVersion": 1
},
{
"id": "afb98920-0fee-4e42-bfe8-a1f3eb186dcd",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3936,
-64
],
"parameters": {
"color": 7,
"width": 1600,
"height": 720,
"content": "## Patient Data Ingestion \nFetches appointment schedules, clinical events, and patient demographics from EHR/FHIR APIs using scheduled triggers and webhooks.\n**Why** - Centralizes patient data from fragmented healthcare systems, enabling unified care coordination without manual data compilation."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"availableInMCP": false,
"executionOrder": "v1"
},
"versionId": "f5f06521-4b90-4564-b846-e47ffd4d0bbd",
"connections": {
"OpenAI GPT-4": {
"ai_languageModel": [
[
{
"node": "AI Agent: Disruption Prediction & Analysis",
"type": "ai_languageModel",
"index": 0
},
{
"node": "Structured Output: Disruption Analysis",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Rate Limit Handler": {
"main": [
[
{
"node": "Validate & Normalize Data",
"type": "main",
"index": 0
}
]
]
},
"Is Duplicate Event?": {
"main": [
[],
[
{
"node": "AI Agent: Disruption Prediction & Analysis",
"type": "main",
"index": 0
}
]
]
},
"Requires Rebooking?": {
"main": [
[
{
"node": "Trigger Rebooking Workflow",
"type": "main",
"index": 0
}
],
[
{
"node": "Merge All Notification Paths",
"type": "main",
"index": 1
}
]
]
},
"Workflow Configuration": {
"main": [
[
{
"node": "Fetch Flight & Weather Data",
"type": "main",
"index": 0
}
]
]
},
"Alert: OCC Slack Channel": {
"main": [
[
{
"node": "Email: Ground Staff Notification",
"type": "main",
"index": 0
}
]
]
},
"Route by Severity & Type": {
"main": [
[
{
"node": "Store: Critical Events DB",
"type": "main",
"index": 0
}
],
[
{
"node": "Store: High Priority Events DB",
"type": "main",
"index": 0
}
],
[
{
"node": "Store: Medium Priority Events DB",
"type": "main",
"index": 0
}
],
[
{
"node": "Store: Low Priority Events DB",
"type": "main",
"index": 0
}
]
]
},
"Store: Critical Events DB": {
"main": [
[
{
"node": "Alert: OCC Slack Channel",
"type": "main",
"index": 0
}
]
]
},
"Validate & Normalize Data": {
"main": [
[
{
"node": "Check Idempotency & Deduplication",
"type": "main",
"index": 0
}
]
]
},
"Generate Dashboard Metrics": {
"main": [
[
{
"node": "Store: Dashboard Metrics DB",
"type": "main",
"index": 0
}
]
]
},
"Trigger Rebooking Workflow": {
"main": [
[
{
"node": "Merge All Notification Paths",
"type": "main",
"index": 0
}
]
]
},
"Fetch Flight & Weather Data": {
"main": [
[
{
"node": "Rate Limit Handler",
"type": "main",
"index": 0
}
]
]
},
"SMS: Customer Service Alert": {
"main": [
[
{
"node": "Prepare Passenger Notification Data",
"type": "main",
"index": 0
}
]
]
},
"Merge All Notification Paths": {
"main": [
[
{
"node": "Generate Dashboard Metrics",
"type": "main",
"index": 0
}
]
]
},
"Apply Operational Rules Engine": {
"main": [
[
{
"node": "Route by Severity & Type",
"type": "main",
"index": 0
}
]
]
},
"Store: High Priority Events DB": {
"main": [
[
{
"node": "Merge All Notification Paths",
"type": "main",
"index": 2
}
]
]
},
"Email: Ground Staff Notification": {
"main": [
[
{
"node": "SMS: Customer Service Alert",
"type": "main",
"index": 0
}
]
]
},
"Store: Medium Priority Events DB": {
"main": [
[
{
"node": "Merge All Notification Paths",
"type": "main",
"index": 3
}
]
]
},
"Check Idempotency & Deduplication": {
"main": [
[
{
"node": "Is Duplicate Event?",
"type": "main",
"index": 0
}
]
]
},
"Webhook: Real-Time Event Ingestion": {
"main": [
[
{
"node": "Validate & Normalize Data",
"type": "main",
"index": 0
}
]
]
},
"Prepare Passenger Notification Data": {
"main": [
[
{
"node": "Requires Rebooking?",
"type": "main",
"index": 0
}
]
]
},
"Schedule: Poll APIs Every 2 Minutes": {
"main": [
[
{
"node": "Workflow Configuration",
"type": "main",
"index": 0
}
]
]
},
"Structured Output: Disruption Analysis": {
"ai_outputParser": [
[
{
"node": "AI Agent: Disruption Prediction & Analysis",
"type": "ai_outputParser",
"index": 0
}
]
]
},
"AI Agent: Disruption Prediction & Analysis": {
"main": [
[
{
"node": "Apply Operational Rules Engine",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
httpHeaderAuthopenAiApislackOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow automates end-to-end patient care coordination by monitoring appointment schedules, clinical events, and care milestones while orchestrating personalized communications across multiple channels. Designed for healthcare operations teams, care coordinators, and…
Source: https://n8n.io/workflows/12734/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This workflow automates end-to-end carbon emissions monitoring, strategy optimisation, and ESG reporting using a multi-agent AI supervisor architecture in n8n. Designed for sustainability managers, ES
This workflow automates end-to-end carbon emissions monitoring, strategy optimisation, and ESG reporting using a multi-agent AI supervisor architecture in n8n. Designed for sustainability managers, ES
This workflow automates end-to-end carbon emissions monitoring, strategy optimisation, and ESG reporting using a multi-agent AI supervisor architecture in n8n. Designed for sustainability managers, ES
Automates sales data analysis and strategic insight generation for sales managers and strategists needing actionable intelligence. Fetches multi-source data from sales, marketing, and financial system
This workflow automates comprehensive risk signal detection and regulatory compliance management across financial and claims data sources. Designed for risk management teams, compliance officers, and