{
  "name": "Webhook - Export assets",
  "nodes": [
    {
      "parameters": {
        "path": "add-ID",
        "responseMode": "responseNode",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -16,
        64
      ],
      "id": "add-ID",
      "name": "Webhook",
      "webhookId": "add-ID"
    },
    {
      "parameters": {
        "url": "=https//:yourHuduURL/api/v1/asset_layouts/{{ $json.query.layout_id }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "accept",
              "value": "application/json"
            },
            {
              "name": "x-api-key",
              "value": "api key"
            }
          ]
        },
        "options": {
          "allowUnauthorizedCerts": true
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        208,
        64
      ],
      "id": "7f244124-5ef8-46ac-9528-4e8e045490e0",
      "name": "Get Asset Layout"
    },
    {
      "parameters": {
        "jsCode": "// Extract list IDs from asset layout fields\nconst layout = $input.first().json;\nconst listIds = new Set();\n\n// The layout might be wrapped in an \"asset_layout\" property\nconst actualLayout = layout.asset_layout || layout;\n\nif (actualLayout. fields) {\n  actualLayout.fields.forEach(field => {\n    // Check if field has a list_id property (meaning it's a list-type field)\n    if (field. list_id) {\n      listIds.add(field.list_id);\n    }\n  });\n}\n\n// Pass through webhook params too\nconst webhookParams = $('Webhook'). first().json. query;\n\n// Return unique list IDs to fetch\nconst result = Array.from(listIds).map(id => ({ \n  json: { \n    list_id: id,\n    layout_id: webhookParams.layout_id,\n    company_id: webhookParams.company_id\n  } \n}));\n\nconsole.log('Found list IDs:', Array.from(listIds));  // Debug\n\n// If no lists found, still pass params forward\nif (result.length === 0) {\n  return [{ json: { layout_id: webhookParams.layout_id, company_id: webhookParams. company_id, no_lists: true } }];\n}\n\nreturn result;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        432,
        64
      ],
      "id": "c5ba7d53-5471-4b73-b6d1-d1355db61403",
      "name": "Extract List IDs from Layout"
    },
    {
      "parameters": {
        "url": "=https:/yourHuduURL/api/v1/lists/{{ $json.list_id }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "accept",
              "value": "application/json"
            },
            {
              "name": "x-api-key",
              "value": "api key"
            }
          ]
        },
        "options": {
          "allowUnauthorizedCerts": true,
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        656,
        64
      ],
      "id": "479c1762-cc46-4fbd-9fc8-a244a8abb0bd",
      "name": "Get Lists",
      "continueOnFail": true
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "company_id",
              "name": "company_id",
              "value": "={{ $('Extract List IDs from Layout').first().json.company_id }}",
              "type": "number"
            },
            {
              "id": "layout_id",
              "name": "layout_id",
              "value": "={{ $('Extract List IDs from Layout').first().json.layout_id }}",
              "type": "number"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        880,
        64
      ],
      "id": "8778e733-91b0-46b8-b508-ad73bdaf34de",
      "name": "Pass Parameters",
      "executeOnce": true
    },
    {
      "parameters": {
        "jsCode": "// Extract assets from response\nconst response = $input.first().json;\nconst assets = response.assets || [];\n\nreturn assets.map(asset => ({ json: asset }));"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1328,
        64
      ],
      "id": "c31d4384-61cf-4f50-8fa6-7a64a11c2713",
      "name": "Extract Assets"
    },
    {
      "parameters": {
        "jsCode": "// Get all assets and list data\nconst assets = $('Extract Assets').all();\nconst listResponses = $('Get Lists'). all();\n\n// Build lookup map: list_item_id → name\nconst listItemMap = {};\n\nlistResponses.forEach(item => {\n  const listData = item. json;\n  \n  if (listData.list_items && Array.isArray(listData.list_items)) {\n    listData.list_items.forEach(listItem => {\n      listItemMap[listItem.id] = listItem.name;\n    });\n  }\n});\n\n// Helper function to format date as YYYY-MM-DD\nfunction formatDateOnly(dateString) {\n  const date = new Date(dateString);\n  const year = date.getFullYear();\n  const month = String(date.getMonth() + 1). padStart(2, '0');\n  const day = String(date.getDate()).padStart(2, '0');\n  return `${year}-${month}-${day}`;\n}\n\n// Transform assets\nconst transformed = [];\n\nassets. forEach(item => {\n  const asset = item.json;\n  const row = {\n    'Asset Name': asset.name,\n    'Company': asset.company_name,\n    'Created': formatDateOnly(asset.created_at),\n    'Updated': formatDateOnly(asset.updated_at),\n    'Archived': asset.archived ? 'Yes' : 'No'\n  };\n  \n  // Process fields\n  if (asset.fields) {\n    asset.fields.forEach(field => {\n      const label = field.label;\n      let value = field. value;\n      \n      if (! value || value === 'null') {\n        row[label] = '';\n        return;\n      }\n      \n      try {\n        const parsed = JSON.parse(value);\n        \n        // Handle asset links (user references)\n        if (Array.isArray(parsed)) {\n          row[label] = parsed.map(v => v. name || v).join(', ');\n        }\n        // Handle list references\n        else if (parsed.list_ids && Array.isArray(parsed.list_ids)) {\n          row[label] = parsed.list_ids\n            . map(listItemId => listItemMap[listItemId] || `[Unknown: ${listItemId}]`)\n            .join(', ');\n        }\n        else {\n          row[label] = value;\n        }\n      } catch (e) {\n        // Not JSON, check if it's a date or boolean\n        if (typeof value === 'boolean') {\n          row[label] = value ?  'Yes' : 'No';\n        } else if (value.includes && value.includes('T') && value.includes('Z')) {\n          // Format as date only (YYYY-MM-DD)\n          row[label] = formatDateOnly(value);\n        } else {\n          row[label] = value;\n        }\n      }\n    });\n  }\n  \n  transformed.push({ json: row });\n});\n\nreturn transformed;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1552,
        64
      ],
      "id": "1ea9f3b9-6612-4807-8c30-6ef1072f61d0",
      "name": "Transform Data"
    },
    {
      "parameters": {
        "operation": "toFile",
        "fileFormat": "xlsx",
        "options": {
          "fileName": "={{ $('Extract Assets').item.json.asset_type }}_{{ $now.toFormat('yyyy-MM-dd_HHmm') }}.xlsx",
          "sheetName": "={{ $('Extract Assets').item.json.asset_type }}_{{ $('Extract Assets').item.json.object_type }}"
        }
      },
      "type": "n8n-nodes-base.spreadsheetFile",
      "typeVersion": 1,
      "position": [
        1776,
        64
      ],
      "id": "9c33ed13-24e9-4703-b414-9126c94282f9",
      "name": "Convert to Excel"
    },
    {
      "parameters": {
        "respondWith": "binary",
        "options": {
          "responseHeaders": {
            "entries": [
              {
                "name": "Content-Type",
                "value": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
              },
              {
                "name": "Content-Disposition",
                "value": "=attachment; filename=\"{{ $('Get Asset Layout').first(). json.asset_layout.name. toLowerCase().replace(/ /g, '_') }}_export_{{ $now. toFormat('yyyy-MM-dd_HHmm') }}.xlsx\""
              }
            ]
          }
        }
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        2000,
        64
      ],
      "id": "d1f4e03f-4c4a-4345-8aec-28f068f98eba",
      "name": "Respond to Webhook"
    },
    {
      "parameters": {
        "url": "=https://yourHuduURL/api/v1/assets? company_id={{ $json.company_id }}&asset_layout_id={{ $json. layout_id }}&page_size=1000",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "accept",
              "value": "application/json"
            },
            {
              "name": "x-api-key",
              "value": "api key"
            }
          ]
        },
        "options": {
          "allowUnauthorizedCerts": true
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        1104,
        64
      ],
      "id": "b60c66ff-a910-4b37-bbf9-6f8629630ddb",
      "name": "Get All Assets"
    },
    {
      "parameters": {
        "content": "## Webhook\nProduction webhook. Use link from node in Hudu button.",
        "height": 320,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -80,
        -80
      ],
      "id": "7acd212d-f533-4000-a09e-16a64c152c41",
      "name": "Sticky Note"
    },
    {
      "parameters": {
        "content": "## Retrieve data from Hudu\nGet asset layout and assets based on ID given from webhook call, and list item data for data transformation.",
        "height": 320,
        "width": 1072,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        176,
        -80
      ],
      "id": "7d78c665-6ebf-49db-b33b-b371f791389b",
      "name": "Sticky Note1"
    },
    {
      "parameters": {
        "content": "## Data processing and preparation\nMaps the data into one excel file.",
        "height": 320,
        "width": 640,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1280,
        -80
      ],
      "id": "31c90fbc-023f-4079-a250-0eb7588ad488",
      "name": "Sticky Note2"
    },
    {
      "parameters": {
        "content": "## Forward excel file to user\nSends the excel file to the user. Auto downloads in the browser.",
        "height": 320,
        "width": 256,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1936,
        -80
      ],
      "id": "d55e7811-ba00-4ab7-a77f-a58661ce7b33",
      "name": "Sticky Note3"
    },
    {
      "parameters": {
        "content": "## Proper asset export to excel\nUsing a webhook, this automation pulls all asset data based on asset layout ID and transforms it into excel document that auto downloads for the user.",
        "height": 192,
        "width": 432
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -80,
        -304
      ],
      "id": "f5e40c69-3e07-4c33-826d-eed3696f4191",
      "name": "Sticky Note4"
    }
  ],
  "pinData": {},
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "Get Asset Layout",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Asset Layout": {
      "main": [
        [
          {
            "node": "Extract List IDs from Layout",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract List IDs from Layout": {
      "main": [
        [
          {
            "node": "Get Lists",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Lists": {
      "main": [
        [
          {
            "node": "Pass Parameters",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pass Parameters": {
      "main": [
        [
          {
            "node": "Get All Assets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Assets": {
      "main": [
        [
          {
            "node": "Transform Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Transform Data": {
      "main": [
        [
          {
            "node": "Convert to Excel",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Convert to Excel": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get All Assets": {
      "main": [
        [
          {
            "node": "Extract Assets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": true,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "37f2f87e-6822-4e52-a902-df62af7feb8e",
  "meta": {
    "instanceId": "87f71bd5cac6e2ad059da8f22935450a5ddb0f09c07c6e8b70c2456b412db937"
  },
  "id": "Bv3TqIym0IPIXk8t",
  "tags": [
    {
      "updatedAt": "2025-12-01T13:36:00.604Z",
      "createdAt": "2025-12-01T13:36:00.604Z",
      "id": "dVz3anOgm1zwDL26",
      "name": "Working"
    }
  ]
}