{
  "id": "yZVlVAsth8DT105o",
  "name": "Route measurement data from Webhook to Google Sheets, Email or JS & 700+ apps",
  "tags": [],
  "nodes": [
    {
      "id": "7e946a2d-1c05-4b22-8b30-27f97dc0d031",
      "name": "Note - Webhook Entry Point",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -5584,
        -1184
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 520,
        "content": "## Webhook Entry Point\n\nReceives POST requests with measurement data at:\n`/measurement-data`\n\n**Expected payload:**\n- measurementId (required)\n- timestamp (required)\n- values (required)\n- deviceId, operator, partNumber (optional)"
      },
      "typeVersion": 1
    },
    {
      "id": "b8839aba-b527-4537-8198-ff6f49383c0d",
      "name": "Note - Validation & Error Handling",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4304,
        -784
      ],
      "parameters": {
        "color": 7,
        "width": 368,
        "height": 680,
        "content": "##  Validation & Error Handling\n\n**Checks for required fields:**\n- measurementId\n- timestamp\n- values\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n**If validation fails:**\n- Returns 400 error response\n- Stops workflow execution"
      },
      "typeVersion": 1
    },
    {
      "id": "1fea9530-e0fa-48f7-b827-0297ff0171bf",
      "name": "Note - Data Processing",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4304,
        -1440
      ],
      "parameters": {
        "color": 7,
        "width": 560,
        "height": 456,
        "content": "## Data Processing\n\n**Parse and Normalize:**\nExtracts and structures measurement data including dimensions (x, y, z), tolerance, status, and metadata.\n\n**Success Response:**\nReturns 200 OK with confirmation message and measurementId."
      },
      "typeVersion": 1
    },
    {
      "id": "004df3a4-4635-4530-90fa-f5b1ee424a2d",
      "name": "Note - Output Routing",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3600,
        -1440
      ],
      "parameters": {
        "color": 7,
        "width": 692,
        "height": 528,
        "content": "## Output Routing\n\nRoutes data based on `outputMode` configuration:\n\n- **sheets** \u2192 Google Sheets\n- **html** \u2192 HTML Page\n- **csv** \u2192 CSV Email\n- **custom** \u2192 Custom JavaScript\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n## Set outputMode in Workflow Configuration node."
      },
      "typeVersion": 1
    },
    {
      "id": "073288bb-6bf7-40b3-8b5a-c991b5c7c7b3",
      "name": "Note - Google Sheets Output",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2544,
        -2128
      ],
      "parameters": {
        "color": 7,
        "width": 488,
        "height": 432,
        "content": "## Google Sheets Output\n\nAppends/updates measurement data to Google Sheets.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\u26a0\ufe0f **Setup Required:**\n- Configure Google Sheets credentials\n- Set googleSheetId in Workflow Configuration\n- Sheet name: \"Measurements\""
      },
      "typeVersion": 1
    },
    {
      "id": "5ef56d94-a716-4231-b184-f370ce1f014c",
      "name": "Note - HTML Output",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2544,
        -1616
      ],
      "parameters": {
        "color": 7,
        "width": 568,
        "height": 376,
        "content": "## \ud83d\udcc4 HTML Report Output\n\nGenerates formatted HTML table with measurement data and returns as web page.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n## Useful for viewing data directly in browser."
      },
      "typeVersion": 1
    },
    {
      "id": "2775c854-bd24-4bf7-b89f-82a27c7aeb78",
      "name": "Note - CSV Email Output",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2544,
        -1168
      ],
      "parameters": {
        "color": 7,
        "width": 536,
        "height": 448,
        "content": "## CSV Email Output\n\nConverts data to CSV file and sends via email.\n\n\u26a0\ufe0f **Setup Required:**\n- Configure SMTP credentials\n- Set emailRecipient in Workflow Configuration\n- Customize emailSubject if needed"
      },
      "typeVersion": 1
    },
    {
      "id": "8ebdc446-9fc6-47ef-ad49-4f4242953872",
      "name": "Note - Custom JavaScript",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2544,
        -672
      ],
      "parameters": {
        "color": 7,
        "width": 440,
        "height": 500,
        "content": "## Custom JavaScript Logic\n\nExtensible code node with examples for:\n- External API calls\n- S3/cloud storage uploads\n- Triggering other workflows\n- Custom calculations\n- Data transformations\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n## Modify the code to fit your needs!"
      },
      "typeVersion": 1
    },
    {
      "id": "7784bbae-1604-4945-a230-0a15e4175aa1",
      "name": "Note - Configuration",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -5088,
        -1184
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 520,
        "content": "## Workflow Configuration\n\n**Key Settings:**\n- outputMode: 'sheets' | 'html' | 'csv' | 'custom'\n- googleSheetId: Your Google Sheet ID\n- emailRecipient: Email address for CSV\n- emailSubject: Email subject line\n\nModify these values to customize behavior."
      },
      "typeVersion": 1
    },
    {
      "id": "3334eff6-e5a6-4915-b95a-02258e507822",
      "name": "Validate Required Fields1",
      "type": "n8n-nodes-base.if",
      "position": [
        -4560,
        -864
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "id-1",
              "operator": {
                "type": "string",
                "operation": "exists"
              },
              "leftValue": "={{ $('Webhook Trigger - Receive Measurement Data').item.json.body.measurementId }}"
            },
            {
              "id": "id-2",
              "operator": {
                "type": "string",
                "operation": "exists"
              },
              "leftValue": "={{ $('Webhook Trigger - Receive Measurement Data').item.json.body.timestamp }}"
            },
            {
              "id": "id-3",
              "operator": {
                "type": "string",
                "operation": "exists"
              },
              "leftValue": "={{ $('Webhook Trigger - Receive Measurement Data').item.json.body.values }}"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "39ecbe50-5067-4092-a6ed-023ab45ca147",
      "name": "Parse and Normalize Data1",
      "type": "n8n-nodes-base.set",
      "position": [
        -4240,
        -1184
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "measurementId",
              "type": "string",
              "value": "={{ $('Webhook Trigger - Receive Measurement Data').item.json.body.measurementId }}"
            },
            {
              "id": "id-2",
              "name": "timestamp",
              "type": "string",
              "value": "={{ $('Webhook Trigger - Receive Measurement Data').item.json.body.timestamp }}"
            },
            {
              "id": "id-3",
              "name": "deviceId",
              "type": "string",
              "value": "={{ $('Webhook Trigger - Receive Measurement Data').item.json.body.deviceId || 'unknown' }}"
            },
            {
              "id": "id-4",
              "name": "operator",
              "type": "string",
              "value": "={{ $('Webhook Trigger - Receive Measurement Data').item.json.body.operator || 'N/A' }}"
            },
            {
              "id": "id-5",
              "name": "partNumber",
              "type": "string",
              "value": "={{ $('Webhook Trigger - Receive Measurement Data').item.json.body.partNumber || 'N/A' }}"
            },
            {
              "id": "id-6",
              "name": "dimension_x",
              "type": "number",
              "value": "={{ $('Webhook Trigger - Receive Measurement Data').item.json.body.values.x || 0 }}"
            },
            {
              "id": "id-7",
              "name": "dimension_y",
              "type": "number",
              "value": "={{ $('Webhook Trigger - Receive Measurement Data').item.json.body.values.y || 0 }}"
            },
            {
              "id": "id-8",
              "name": "dimension_z",
              "type": "number",
              "value": "={{ $('Webhook Trigger - Receive Measurement Data').item.json.body.values.z || 0 }}"
            },
            {
              "id": "id-9",
              "name": "tolerance",
              "type": "number",
              "value": "={{ $('Webhook Trigger - Receive Measurement Data').item.json.body.values.tolerance || 0 }}"
            },
            {
              "id": "id-10",
              "name": "status",
              "type": "string",
              "value": "={{ $('Webhook Trigger - Receive Measurement Data').item.json.body.status || 'received' }}"
            },
            {
              "id": "id-11",
              "name": "notes",
              "type": "string",
              "value": "={{ $('Webhook Trigger - Receive Measurement Data').item.json.body.notes || '' }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "435a73e2-f90a-429d-984c-9db449e664c0",
      "name": "Send Success Response1",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        -3968,
        -1184
      ],
      "parameters": {
        "options": {
          "responseCode": 200
        },
        "respondWith": "json",
        "responseBody": "={\n  \"success\": true,\n  \"message\": \"Measurement data received successfully\",\n  \"measurementId\": \"{{ $json.measurementId }}\"\n}",
        "enableResponseOutput": true
      },
      "typeVersion": 1.5
    },
    {
      "id": "77cd0f46-66e8-4175-b2e1-10814a2d48cc",
      "name": "Route to Output Options1",
      "type": "n8n-nodes-base.switch",
      "position": [
        -3168,
        -1232
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "Google Sheets",
              "conditions": {
                "options": {
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $('Workflow Configuration').item.json.outputMode }}",
                    "rightValue": "sheets"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "HTML Page",
              "conditions": {
                "options": {
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $('Workflow Configuration').item.json.outputMode }}",
                    "rightValue": "html"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "CSV Email",
              "conditions": {
                "options": {
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $('Workflow Configuration').item.json.outputMode }}",
                    "rightValue": "csv"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Custom Code",
              "conditions": {
                "options": {
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $('Workflow Configuration').item.json.outputMode }}",
                    "rightValue": "custom"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.4
    },
    {
      "id": "31c7fa4e-fd95-467c-b6c6-b0280052be2c",
      "name": "Save to Google Sheets1",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -2480,
        -2000
      ],
      "parameters": {
        "columns": {
          "value": {
            "notes": "={{ $json.notes }}",
            "status": "={{ $json.status }}",
            "deviceId": "={{ $json.deviceId }}",
            "operator": "={{ $json.operator }}",
            "timestamp": "={{ $json.timestamp }}",
            "tolerance": "={{ $json.tolerance }}",
            "partNumber": "={{ $json.partNumber }}",
            "dimension_x": "={{ $json.dimension_x }}",
            "dimension_y": "={{ $json.dimension_y }}",
            "dimension_z": "={{ $json.dimension_z }}",
            "measurementId": "={{ $json.measurementId }}"
          },
          "schema": [
            {
              "id": "measurementId",
              "required": false,
              "displayName": "measurementId",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "timestamp",
              "required": false,
              "displayName": "timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "deviceId",
              "required": false,
              "displayName": "deviceId",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "operator",
              "required": false,
              "displayName": "operator",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "partNumber",
              "required": false,
              "displayName": "partNumber",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "dimension_x",
              "required": false,
              "displayName": "dimension_x",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "dimension_y",
              "required": false,
              "displayName": "dimension_y",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "dimension_z",
              "required": false,
              "displayName": "dimension_z",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "tolerance",
              "required": false,
              "displayName": "tolerance",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "status",
              "required": false,
              "displayName": "status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "notes",
              "required": false,
              "displayName": "notes",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "measurementId"
          ]
        },
        "options": {
          "useAppend": true
        },
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Measurements"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Workflow Configuration').item.json.googleSheetId }}"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "40847a27-4468-43c1-b603-670ba2590cc9",
      "name": "Generate HTML Table1",
      "type": "n8n-nodes-base.html",
      "position": [
        -2464,
        -1488
      ],
      "parameters": {
        "html": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"UTF-8\" />\n  <title>Measurement Data Report</title>\n</head>\n<body>\n  <div class=\"container\">\n    <h1>Measurement Data Report</h1>\n    <table>\n      <thead>\n        <tr>\n          <th>Field</th>\n          <th>Value</th>\n        </tr>\n      </thead>\n      <tbody>\n        <tr>\n          <td>Measurement ID</td>\n          <td>{{ $json.measurementId }}</td>\n        </tr>\n        <tr>\n          <td>Timestamp</td>\n          <td>{{ $json.timestamp }}</td>\n        </tr>\n        <tr>\n          <td>Device ID</td>\n          <td>{{ $json.deviceId }}</td>\n        </tr>\n        <tr>\n          <td>Operator</td>\n          <td>{{ $json.operator }}</td>\n        </tr>\n        <tr>\n          <td>Part Number</td>\n          <td>{{ $json.partNumber }}</td>\n        </tr>\n        <tr>\n          <td>Dimension X</td>\n          <td>{{ $json.dimension_x }}</td>\n        </tr>\n        <tr>\n          <td>Dimension Y</td>\n          <td>{{ $json.dimension_y }}</td>\n        </tr>\n        <tr>\n          <td>Dimension Z</td>\n          <td>{{ $json.dimension_z }}</td>\n        </tr>\n        <tr>\n          <td>Tolerance</td>\n          <td>{{ $json.tolerance }}</td>\n        </tr>\n        <tr>\n          <td>Status</td>\n          <td>{{ $json.status }}</td>\n        </tr>\n        <tr>\n          <td>Notes</td>\n          <td>{{ $json.notes }}</td>\n        </tr>\n      </tbody>\n    </table>\n  </div>\n</body>\n</html>\n\n<style>\n.container {\n  background-color: #f5f5f5;\n  padding: 20px;\n  font-family: Arial, sans-serif;\n}\n\nh1 {\n  color: #333;\n  text-align: center;\n  margin-bottom: 20px;\n}\n\ntable {\n  width: 100%;\n  border-collapse: collapse;\n  background-color: #ffffff;\n  box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n}\n\nth {\n  background-color: #4CAF50;\n  color: white;\n  padding: 12px;\n  text-align: left;\n  font-weight: bold;\n}\n\ntd {\n  padding: 10px;\n  border-bottom: 1px solid #ddd;\n}\n\ntr:hover {\n  background-color: #f1f1f1;\n}\n</style>"
      },
      "typeVersion": 1.2
    },
    {
      "id": "6ebcade9-e215-4656-9fcd-360e1b5515c5",
      "name": "Return HTML Page1",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        -2176,
        -1488
      ],
      "parameters": {
        "options": {
          "responseCode": 200,
          "responseHeaders": {
            "entries": [
              {
                "name": "Content-Type",
                "value": "text/html"
              }
            ]
          }
        },
        "respondWith": "text",
        "responseBody": "={{ $json.html }}"
      },
      "typeVersion": 1.5
    },
    {
      "id": "feaa7db6-7908-4fb3-8628-30db47511c5e",
      "name": "Convert to CSV File1",
      "type": "n8n-nodes-base.convertToFile",
      "position": [
        -2464,
        -896
      ],
      "parameters": {
        "options": {
          "fileName": "={{ 'measurement-data-' + $now.format('yyyy-MM-dd-HHmmss') + '.csv' }}"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "a0608b0e-34fd-4da6-a8b5-1dbc04421679",
      "name": "Send Email with CSV1",
      "type": "n8n-nodes-base.emailSend",
      "position": [
        -2240,
        -896
      ],
      "parameters": {
        "text": "={{ 'Please find attached the measurement data export from ' + $('Parse and Normalize Data1').item.json.timestamp }}",
        "options": {
          "attachments": "data"
        },
        "subject": "={{ $('Workflow Configuration').item.json.emailSubject }}",
        "toEmail": "={{ $('Workflow Configuration').item.json.emailRecipient }}",
        "fromEmail": "user@example.com",
        "emailFormat": "text"
      },
      "typeVersion": 2.1
    },
    {
      "id": "44d39600-25f1-4359-914b-a3cf358da708",
      "name": "Custom JavaScript Logic1",
      "type": "n8n-nodes-base.code",
      "position": [
        -2448,
        -416
      ],
      "parameters": {
        "jsCode": "// ============================================\n// CUSTOM JAVASCRIPT LOGIC FOR MEASUREMENT DATA\n// ============================================\n// This code demonstrates various ways to process measurement data:\n// 1. Access and transform the incoming data\n// 2. Send data to external APIs\n// 3. Store files in S3 (or other cloud storage)\n// 4. Trigger other n8n workflows\n// 5. Perform custom calculations and filtering\n\n// ============================================\n// 1. ACCESS INCOMING MEASUREMENT DATA\n// ============================================\n// Get all items from the previous node\nconst items = $input.all();\n\n// Access the first item's data\nconst firstItem = items[0].json;\nconsole.log('First measurement:', firstItem);\n\n// Example: Access specific fields from the measurement data\n// Assuming structure: { timestamp, sensorId, temperature, humidity, pressure }\nconst measurements = items.map(item => ({\n  timestamp: item.json.timestamp,\n  sensorId: item.json.sensorId,\n  temperature: item.json.temperature,\n  humidity: item.json.humidity,\n  pressure: item.json.pressure\n}));\n\nconsole.log('Processed measurements:', measurements);\n\n// ============================================\n// 2. SEND DATA TO EXTERNAL API\n// ============================================\n// Example: Send measurement data to a custom API endpoint\nconst apiResults = [];\n\nfor (const measurement of measurements) {\n  try {\n    // Make HTTP request to external API\n    const response = await $http.request({\n      method: 'POST',\n      url: 'https://api.example.com/measurements',\n      headers: {\n        'Content-Type': 'application/json',\n        'Authorization': 'Bearer YOUR_TOKEN_HERE'\n      },\n      body: {\n        sensor_id: measurement.sensorId,\n        timestamp: measurement.timestamp,\n        readings: {\n          temperature: measurement.temperature,\n          humidity: measurement.humidity,\n          pressure: measurement.pressure\n        }\n      }\n    });\n    \n    apiResults.push({\n      success: true,\n      sensorId: measurement.sensorId,\n      apiResponse: response\n    });\n  } catch (error) {\n    console.error('API Error:', error);\n    apiResults.push({\n      success: false,\n      sensorId: measurement.sensorId,\n      error: error.message\n    });\n  }\n}\n\n// ============================================\n// 3. STORE DATA IN AWS S3 (OR OTHER STORAGE)\n// ============================================\n// Example: Prepare data for S3 upload\nconst csvData = measurements.map(m => \n  `${m.timestamp},${m.sensorId},${m.temperature},${m.humidity},${m.pressure}`\n).join('\\n');\n\nconst csvWithHeader = 'Timestamp,Sensor ID,Temperature,Humidity,Pressure\\n' + csvData;\n\n// Create a filename with current date\nconst filename = `measurements_${new Date().toISOString().split('T')[0]}.csv`;\n\n// Note: To actually upload to S3, you would use the AWS S3 node or make an API call\n// This example shows how to prepare the data\nconst s3Payload = {\n  bucket: 'your-bucket-name',\n  key: `measurements/${filename}`,\n  content: csvWithHeader,\n  contentType: 'text/csv'\n};\n\nconsole.log('S3 upload payload prepared:', s3Payload);\n\n// ============================================\n// 4. TRIGGER ANOTHER N8N WORKFLOW\n// ============================================\n// Example: Trigger another workflow via webhook\nconst workflowTriggerResults = [];\n\n// Only trigger if temperature exceeds threshold\nconst highTempMeasurements = measurements.filter(m => m.temperature > 30);\n\nif (highTempMeasurements.length > 0) {\n  try {\n    const triggerResponse = await $http.request({\n      method: 'POST',\n      url: 'https://avikadam.app.n8n.cloud/webhook/alert-workflow',\n      headers: {\n        'Content-Type': 'application/json'\n      },\n      body: {\n        alertType: 'HIGH_TEMPERATURE',\n        measurements: highTempMeasurements,\n        triggeredAt: new Date().toISOString()\n      }\n    });\n    \n    workflowTriggerResults.push({\n      triggered: true,\n      count: highTempMeasurements.length,\n      response: triggerResponse\n    });\n  } catch (error) {\n    console.error('Workflow trigger error:', error);\n    workflowTriggerResults.push({\n      triggered: false,\n      error: error.message\n    });\n  }\n}\n\n// ============================================\n// 5. CUSTOM CALCULATIONS AND FILTERING\n// ============================================\n// Calculate statistics\nconst temperatures = measurements.map(m => m.temperature);\nconst avgTemp = temperatures.reduce((a, b) => a + b, 0) / temperatures.length;\nconst maxTemp = Math.max(...temperatures);\nconst minTemp = Math.min(...temperatures);\n\nconst statistics = {\n  totalMeasurements: measurements.length,\n  averageTemperature: avgTemp.toFixed(2),\n  maxTemperature: maxTemp,\n  minTemperature: minTemp,\n  highTempAlerts: highTempMeasurements.length,\n  processedAt: new Date().toISOString()\n};\n\nconsole.log('Statistics:', statistics);\n\n// ============================================\n// 6. RETURN PROCESSED DATA\n// ============================================\n// Return all processed data as output\nreturn [\n  {\n    json: {\n      summary: statistics,\n      measurements: measurements,\n      apiResults: apiResults,\n      s3Upload: s3Payload,\n      workflowTriggers: workflowTriggerResults,\n      processedCount: measurements.length\n    }\n  }\n];\n\n// ============================================\n// ADDITIONAL EXAMPLES\n// ============================================\n\n// Example: Group measurements by sensor ID\n// const groupedBySensor = measurements.reduce((acc, m) => {\n//   if (!acc[m.sensorId]) acc[m.sensorId] = [];\n//   acc[m.sensorId].push(m);\n//   return acc;\n// }, {});\n\n// Example: Filter measurements by date range\n// const today = new Date().toISOString().split('T')[0];\n// const todayMeasurements = measurements.filter(m => \n//   m.timestamp.startsWith(today)\n// );\n\n// Example: Transform data for specific database format\n// const dbRecords = measurements.map(m => ({\n//   id: `${m.sensorId}_${m.timestamp}`,\n//   sensor_id: m.sensorId,\n//   measured_at: new Date(m.timestamp),\n//   temp_celsius: m.temperature,\n//   humidity_percent: m.humidity,\n//   pressure_hpa: m.pressure,\n//   created_at: new Date()\n// }));"
      },
      "typeVersion": 2
    },
    {
      "id": "18cbe32b-9067-4114-b059-f87d4f08dc40",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -6576,
        -1392
      ],
      "parameters": {
        "color": "#66651A",
        "width": 896,
        "height": 928,
        "content": "# Measurement Data Routing Workflow\n\nThis workflow receives measurement data via webhook and routes it to multiple output destinations based on configuration.\n\n## How it works:\n1. **Webhook receives** POST requests with measurement data (ID, timestamp, dimensions, tolerance, metadata)\n2. **Validation** ensures required fields are present; returns error if missing\n3. **Data normalization** extracts and structures all measurement fields with defaults for optional values\n4. **Success response** confirms receipt immediately (200 OK)\n5. **Routing** directs data to one of four outputs based on the outputMode setting:\n   - **sheets**: Appends to Google Sheets for tracking and analysis\n   - **html**: Returns formatted HTML table for browser viewing\n   - **csv**: Emails CSV file attachment to specified recipient\n   - **custom**: Runs JavaScript code for API calls, cloud storage, or custom logic\n\n## Setup steps:\n1. **Configure output mode**: Edit \"Workflow Configuration\" node and set outputMode to your preferred destination\n2. **Google Sheets** (if using sheets mode): Add Google Sheets credentials and update googleSheetId with your sheet ID\n3. **Email** (if using csv mode): Add SMTP credentials and set emailRecipient to the destination address\n4. **Custom code** (if using custom mode): Modify the JavaScript node with your integration logic\n5. **Test**: Send a POST request to the webhook URL with sample measurement data\n\n\n\n## How to get the Webhook URL:\n- **Double click** on the 1st node **Webhook Trigger - Receive Measurement Data**\n- You can see Test / Production URL section at the top in drop down \u2304 Webhook URLs\n**Ex.** Webhook URL format: https://{your-cloud-name}.app.n8n.cloud/webhook/measurement-data\n\n\n## How to take measurements:\n- Import and activate the workflow in n8n.  \n- Copy the **Webhook URL** from the Webhook Trigger node.  \n- Open the **3D Measure Up Web Application**:   https://3dmeasureup.ai/  \n- Log in and navigate to **Settings \u2192 Webhooks**.  \n- Paste the webhook URL into the **Webhook URL** field and click **Save**. \n\n### Now, whenever you take a measurement with 3D Measure Up, it will be available in your n8n workflow"
      },
      "typeVersion": 1
    },
    {
      "id": "acafb3fd-947c-4782-966e-ee185bffd5e9",
      "name": "Webhook Trigger - Receive Measurement Data",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -5488,
        -864
      ],
      "parameters": {
        "path": "measurement-data",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "lastNode"
      },
      "typeVersion": 2.1,
      "alwaysOutputData": false
    },
    {
      "id": "fe53a33d-c1ad-4bd3-a4b9-a90a7111882e",
      "name": "Send Error Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        -4192,
        -368
      ],
      "parameters": {
        "options": {
          "responseCode": 400
        },
        "respondWith": "json",
        "responseBody": "{\n  \"success\": false,\n  \"error\": \"Missing required fields: measurementId, timestamp, values\"\n}"
      },
      "typeVersion": 1.5
    },
    {
      "id": "4aa05768-a3a3-460f-b5ea-d203bcd8af01",
      "name": "Error - Invalid Payload",
      "type": "n8n-nodes-base.stopAndError",
      "position": [
        -4192,
        -576
      ],
      "parameters": {
        "errorMessage": "Invalid payload: Missing required fields (measurementId, timestamp, values)"
      },
      "typeVersion": 1
    },
    {
      "id": "1930f36c-1b48-4938-8f5e-fe4547023825",
      "name": "Workflow Configuration",
      "type": "n8n-nodes-base.set",
      "position": [
        -4976,
        -864
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "outputMode",
              "type": "string",
              "value": "sheets"
            },
            {
              "id": "id-2",
              "name": "googleSheetId",
              "type": "string",
              "value": "YOUR_SHEET_ID_HERE - Please replace with your actual Google Sheet ID"
            },
            {
              "id": "id-3",
              "name": "emailRecipient",
              "type": "string",
              "value": "user@example.com"
            },
            {
              "id": "id-4",
              "name": "emailSubject",
              "type": "string",
              "value": "Measurement Data Export"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "69b60df7-a90e-44a3-b804-908bc6ec5e1c",
  "connections": {
    "Convert to CSV File1": {
      "main": [
        [
          {
            "node": "Send Email with CSV1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate HTML Table1": {
      "main": [
        [
          {
            "node": "Return HTML Page1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Success Response1": {
      "main": [
        [
          {
            "node": "Route to Output Options1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Workflow Configuration": {
      "main": [
        [
          {
            "node": "Validate Required Fields1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route to Output Options1": {
      "main": [
        [
          {
            "node": "Save to Google Sheets1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Generate HTML Table1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Convert to CSV File1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Custom JavaScript Logic1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse and Normalize Data1": {
      "main": [
        [
          {
            "node": "Send Success Response1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Required Fields1": {
      "main": [
        [
          {
            "node": "Parse and Normalize Data1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Error - Invalid Payload",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send Error Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook Trigger - Receive Measurement Data": {
      "main": [
        [
          {
            "node": "Workflow Configuration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}