{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "1305d4ee-3227-4adf-99a1-307da4fdef53",
      "name": "Execute Workflow Trigger",
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "position": [
        1340,
        320
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "300a89e6-ff90-4076-8d93-6189c7669829",
      "name": "Inputs",
      "type": "n8n-nodes-base.set",
      "position": [
        1560,
        320
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "e202e9a6-e01a-4f3b-b48c-ca8227f05176",
              "name": "base_id",
              "type": "string",
              "value": "={{ $json.base_id }}"
            },
            {
              "id": "0426f50e-8eb6-482b-88fa-0b10c6d31cc0",
              "name": "table_id",
              "type": "string",
              "value": "={{ $json.table_id }}"
            },
            {
              "id": "a6cffb5c-a73d-49e2-87b3-87e4df3bd9b0",
              "name": "record_id",
              "type": "string",
              "value": "={{ $json.record_id }}"
            },
            {
              "id": "6fce3472-be79-4cf7-a5ee-0d7b548ca7e5",
              "name": "level_3",
              "type": "array",
              "value": "={{ $json.level_3 }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "f1cdb90e-2cc2-4c0a-a9ee-c1bc5feb240f",
      "name": "Split Out Fields",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        2440,
        220
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "fields"
      },
      "typeVersion": 1
    },
    {
      "id": "894b24e6-3090-46fe-8902-9939595849cd",
      "name": "Add Inverse LinkTo Field name",
      "type": "n8n-nodes-base.set",
      "position": [
        2880,
        220
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "792af6bc-4537-4959-ab47-7ed38ef369b7",
              "name": "inverseFieldName",
              "type": "string",
              "value": "={{ $('Airtable Schema').all()\n     .filter(x =>  x.json.id == $json.options.linkedTableId)[0].json.fields\n     .filter(x => x.id == $json.options.inverseLinkFieldId)[0].name  }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "44e1d4f9-cd50-4e68-be4c-dd62d5e5eeaa",
      "name": "Add origin LinkTo",
      "type": "n8n-nodes-base.set",
      "position": [
        4500,
        680
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "f3cb3396-cf4f-42fb-8f62-abc008176ec3",
              "name": "originLinkTo",
              "type": "string",
              "value": "={{ $('Add Inverse LinkTo Field name').item.json.name }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "08f499f3-c675-4e4e-865c-397f705a35dd",
      "name": "Filter Link To fields",
      "type": "n8n-nodes-base.filter",
      "position": [
        2660,
        220
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cd174792-7f7f-40cb-862e-7ac24ef780ad",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.type }}",
              "rightValue": "multipleRecordLinks"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "32cbf163-3114-4fb5-983d-de25f53598af",
      "name": "Airtable Schema",
      "type": "n8n-nodes-base.airtable",
      "position": [
        2000,
        220
      ],
      "parameters": {
        "base": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Inputs').item.json.base_id }}"
        },
        "resource": "base",
        "operation": "getSchema"
      },
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "6d1186d2-96c6-4303-9614-38fa8200a540",
      "name": "Airtable - Get Target Record",
      "type": "n8n-nodes-base.airtable",
      "position": [
        1780,
        220
      ],
      "parameters": {
        "id": "={{ $json.record_id }}",
        "base": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.base_id }}"
        },
        "table": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.table_id }}"
        },
        "options": {}
      },
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "9211e559-f6b3-4086-8a64-37e2218fa221",
      "name": "Insert linked records to originRecord & Clean Field Name",
      "type": "n8n-nodes-base.code",
      "position": [
        4740,
        680
      ],
      "parameters": {
        "jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\n\nlet originRecord = $('Airtable - Get Target Record').first().json\nfor (const item of $input.all()) {\n  originRecord[item.json.originLinkTo] = item.json.records.map(x => x.fields);\n  //item.json.myNewField = 1;\n}\n\nreturn originRecord;"
      },
      "typeVersion": 2
    },
    {
      "id": "d928dee8-d050-4ad3-8e54-0dd058900f83",
      "name": "Filter Main Table",
      "type": "n8n-nodes-base.filter",
      "position": [
        2220,
        220
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "97862298-8455-4516-915e-7983109b48d6",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.id }}",
              "rightValue": "={{ $('Inputs').item.json.table_id }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "75eb8440-d179-444b-beb1-0e4c41a05293",
      "name": "Get Linked records without Link To fields Except for Level 3",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3100,
        220
      ],
      "parameters": {
        "url": "=https://api.airtable.com/v0/{{ $('Inputs').item.json.base_id }}/{{ $json.options.linkedTableId }}/listRecords",
        "method": "POST",
        "options": {
          "batching": {
            "batch": {
              "batchSize": 4
            }
          }
        },
        "sendBody": true,
        "sendQuery": true,
        "authentication": "predefinedCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "filterByFormula",
              "value": "=FIND(RECORD_ID(),\"{{ $('Airtable - Get Target Record').item.json[$json.name].join() }}\")"
            },
            {
              "name": "fields",
              "value": "={{ $('Airtable Schema').all().filter(x => x.json.id == $json.options.linkedTableId)[0].json.fields.filter(x => x.type != 'multipleRecordLinks' || $('Inputs').first().json.level_3.includes(x.id)).map(x => x.id)\n}}"
            }
          ]
        },
        "queryParameters": {
          "parameters": [
            {}
          ]
        },
        "nodeCredentialType": "airtableTokenApi"
      },
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "6d908b82-5c3d-4e1c-9c9f-547828c7f462",
      "name": "If Has Level 3",
      "type": "n8n-nodes-base.if",
      "position": [
        3540,
        -20
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "33590a18-0724-4b8c-923f-05a186ba7a78",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ \n$('Inputs').item.json.level_3\n   .some(\n      x => $('Airtable Schema').all().filter(x => x.json.id == $('Add Inverse LinkTo Field name').item.json.options.linkedTableId)[0].json.fields.map(x => x.id).includes(x)\n      ) \n}}",
              "rightValue": "={{ $('Add Inverse LinkTo Field name').item.json.options.linkedTableId }}"
            },
            {
              "id": "4bd93161-a9a9-4bac-a465-8b376bbf728c",
              "operator": {
                "type": "array",
                "operation": "lengthGt",
                "rightType": "number"
              },
              "leftValue": "={{ $json.records }}",
              "rightValue": 0
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "238b89f0-b68d-4e81-a837-0175348281cc",
      "name": "Get L3 Linked records without reverse Link To fields",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        5140,
        -120
      ],
      "parameters": {
        "url": "=https://api.airtable.com/v0/{{ $('Inputs').item.json.base_id }}/{{ $json.level_3_table_id }}/listRecords",
        "method": "POST",
        "options": {
          "batching": {
            "batch": {
              "batchSize": 4
            }
          }
        },
        "sendBody": true,
        "sendQuery": true,
        "authentication": "predefinedCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "filterByFormula",
              "value": "=FIND(RECORD_ID(),\"{{ $('Loop Over L2 Records for a given L1 field').item.json.fields[$json.level_2_field_name].join() }}}\")"
            },
            {
              "name": "fields",
              "value": "={{ $('Airtable Schema').all().filter(x => x.json.id == $json.level_3_table_id)[0].json.fields.filter(x => x.type != 'multipleRecordLinks' ).map(x => x.id) }}"
            }
          ]
        },
        "queryParameters": {
          "parameters": [
            {}
          ]
        },
        "nodeCredentialType": "airtableTokenApi"
      },
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "b71ff770-0253-4b93-b57b-9ed5a7eb6462",
      "name": "Level 2 records",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        4040,
        60
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "records"
      },
      "typeVersion": 1
    },
    {
      "id": "4d3ad059-ea71-4e76-8ce1-89928b824613",
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "position": [
        4260,
        -40
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineAll"
      },
      "typeVersion": 3
    },
    {
      "id": "5e8becd7-9056-424c-bc5c-3cb553b88e78",
      "name": "Merge1",
      "type": "n8n-nodes-base.merge",
      "position": [
        4280,
        680
      ],
      "parameters": {},
      "typeVersion": 3
    },
    {
      "id": "eee4b3a4-dd73-485c-adbc-db64bb22157f",
      "name": "Repeat L3 links of current L2 table",
      "type": "n8n-nodes-base.set",
      "position": [
        4040,
        -140
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "8f6ae31e-9641-4453-8d28-1a2d1f10a4c7",
              "name": "level_3_links",
              "type": "array",
              "value": "={{ $('Inputs').item.json.level_3.filter(x => $('Airtable Schema').all().filter(x => x.json.id == $('Add Inverse LinkTo Field name').item.json.options.linkedTableId)[0].json.fields.map(x => x.id).includes(x)) }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "8fc621bc-5f9d-4d9c-8c88-12cd7aafca68",
      "name": "Aggregate1",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        5580,
        -120
      ],
      "parameters": {
        "options": {
          "mergeLists": false
        },
        "fieldsToAggregate": {
          "fieldToAggregate": [
            {
              "fieldToAggregate": "level_2_field_name"
            },
            {
              "fieldToAggregate": "level_3_records"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "c9d8fabd-b9c1-409a-a899-0dc6260a8199",
      "name": "Split Out L3 Potential Links",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        4700,
        -120
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "level_3_links"
      },
      "typeVersion": 1
    },
    {
      "id": "eb8e8c3b-972a-4d6f-878d-cb5d435e0250",
      "name": "Prepare HTTP Call",
      "type": "n8n-nodes-base.set",
      "position": [
        4920,
        -120
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "e3399e28-b76b-4d7b-ba54-830d4f526f87",
              "name": "level_2_table_id",
              "type": "string",
              "value": "={{ $('Add Inverse LinkTo Field name').item.json.options.linkedTableId }}"
            },
            {
              "id": "42dbca2b-6714-425b-94c2-2dd4f76b3783",
              "name": "level_2_field_id",
              "type": "string",
              "value": "={{ $json.level_3_links }}"
            },
            {
              "id": "915e891e-2e8e-4540-8642-38f44e36008a",
              "name": "level_3_table_id",
              "type": "string",
              "value": "={{ $('Airtable Schema').all().filter(x => x.json.id == $('Add Inverse LinkTo Field name').item.json.options.linkedTableId)[0].json.fields.filter(x => x.id == $json.level_3_links)[0].options.linkedTableId }}"
            },
            {
              "id": "92042131-dc3f-40d1-a38e-3aede5dec35a",
              "name": "level_3_field_id",
              "type": "string",
              "value": "={{ $('Airtable Schema').all().filter(x => x.json.id == $('Add Inverse LinkTo Field name').item.json.options.linkedTableId)[0].json.fields.filter(x => x.id == $json.level_3_links)[0].options.inverseLinkFieldId }}"
            },
            {
              "id": "e4c1a3b3-592c-477d-82f8-32306cee478d",
              "name": "level_2_field_name",
              "type": "string",
              "value": "={{ $('Airtable Schema').all().filter(x => x.json.id == $('Add Inverse LinkTo Field name').item.json.options.linkedTableId)[0].json.fields.filter(x => x.id == $json.level_3_links)[0].name }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "25d3c6b3-9f3f-4703-9def-ca7435608d10",
      "name": "Preparing aggregation",
      "type": "n8n-nodes-base.set",
      "position": [
        5360,
        -120
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "f07ee481-b1fe-409a-b620-9cb4d151de31",
              "name": "level_2_record",
              "type": "object",
              "value": "={{ $('Level 2 records').item.json.fields }}"
            },
            {
              "id": "4e49b6a0-8b3b-4c6e-bce6-178d720415dc",
              "name": "level_3_records",
              "type": "array",
              "value": "={{ $json.records.map(x => x.fields) }}"
            },
            {
              "id": "f397d1cb-f5fa-4d9f-956b-ec665ab22992",
              "name": "level_2_field_name",
              "type": "string",
              "value": "={{ $('Prepare HTTP Call').item.json.level_2_field_name }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "47ac8540-6a58-46b5-b690-6beadcbbd52f",
      "name": "Code to single field property",
      "type": "n8n-nodes-base.code",
      "position": [
        5800,
        -120
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Add a new field called 'myNewField' to the JSON of the item\n\njson = {\"fields\": {}}\ni=0;\nfor (let k of $input.item.json.level_2_field_name) {\n  json.fields[k] = $input.item.json.level_3_records[i];\n  i++;\n}\n\n\n$input.item.json = json;\n\nreturn $input.item;"
      },
      "typeVersion": 2
    },
    {
      "id": "1e4f7e9d-923e-4b91-a5a4-3c928f5004a5",
      "name": "Merge with original field property",
      "type": "n8n-nodes-base.set",
      "position": [
        6020,
        -40
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "ba4874e7-a116-4a8e-9c67-2731e7ebdf06",
              "name": "fields",
              "type": "object",
              "value": "={{ {fields: {...$('Loop Over L2 Records for a given L1 field').item.json.fields, ...$json.fields} } }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "6e45ff3e-0e31-4658-a718-51a1b580b8ef",
      "name": "Aggregate to record list",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        4640,
        -400
      ],
      "parameters": {
        "options": {},
        "fieldsToAggregate": {
          "fieldToAggregate": [
            {
              "renameField": true,
              "outputFieldName": "records",
              "fieldToAggregate": "fields"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "c791981c-d73f-4af5-b64e-27fec10994bc",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3480,
        -720
      ],
      "parameters": {
        "width": 2730,
        "height": 1040,
        "content": "## Level 3 children\n\nThis is an iteration on each L2 records array having level 3 children to be fetched"
      },
      "typeVersion": 1
    },
    {
      "id": "ec5ebe98-56f9-4568-afc7-2b5202d205b4",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        800,
        180
      ],
      "parameters": {
        "width": 440,
        "height": 380,
        "content": "## Input\n\n```\n[\n  {\n    \"base_id\": \"appN8nPMGoLNuzUbY\",\n    \"table_id\": \"tblLVOwpYIe0fGQ52\",\n    \"record_id\": \"reczMh1Pp5l94HdYf\",\n    \"level_3\": [\n      \"fldRaFra1rLta66cD\",\n      \"fld3FxCaYk8AVaEHt\"\n    ],\n    \"to_html\": true\n  }\n]\n```"
      },
      "typeVersion": 1
    },
    {
      "id": "4d689138-a84e-45e2-9202-757057f6a326",
      "name": "Rich Text Fields list",
      "type": "n8n-nodes-base.set",
      "position": [
        5200,
        480
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "3137cbad-90c5-4718-a3f4-cdc1fb04cd33",
              "name": "richTextFields",
              "type": "array",
              "value": "={{ $('Airtable Schema').all().map(\n\nx => x.json.fields.filter(x => x.type == \"richText\").map(x => x.name)\n\n).flat() }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "32bfa99d-8675-4bfa-9644-4f0eb10d66a2",
      "name": "Markdown to Html",
      "type": "n8n-nodes-base.code",
      "position": [
        5460,
        480
      ],
      "parameters": {
        "jsCode": "const marked = require('marked');\n\nfunction convertMarkdownToHtml(obj, propertiesToConvert) {\n  // Check if the object is an array\n  if (Array.isArray(obj)) {\n    // If it's an array, loop through each item and recursively call the function\n    return obj.map(item => convertMarkdownToHtml(item, propertiesToConvert));\n  } else if (typeof obj === 'object') {\n    // If it's an object, loop through each property\n    for (const key in obj) {\n      if (Object.prototype.hasOwnProperty.call(obj, key)) {\n        const value = obj[key];\n        // Check if the property name is in the array of properties to convert\n        if (propertiesToConvert.includes(key)) {\n          // Check if the value is a string (potential markdown)\n          if (typeof value === 'string') {\n            // Convert markdown to HTML using marked library\n            obj[key] = marked.parse(value);\n          }\n        } else {\n          // If the property name is not in the array, recursively check if it's an object or array\n          if (typeof value === 'object') {\n            obj[key] = convertMarkdownToHtml(value, propertiesToConvert);\n          }\n        }\n      }\n    }\n    return obj;\n  } else {\n    // If it's not an object or array, return as is (since we can't convert it)\n    return obj;\n  }\n}\n\nreturn convertMarkdownToHtml($('Insert linked records to originRecord & Clean Field Name').first().json,$('Rich Text Fields list').first().json.richTextFields)\n\n\n\n"
      },
      "typeVersion": 2
    },
    {
      "id": "737044e7-1263-4ef0-a6b3-2746f4532171",
      "name": "If markdown to html",
      "type": "n8n-nodes-base.if",
      "position": [
        4960,
        680
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "5ff55a7e-ebb2-4cd7-af12-680b87fd2d53",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $('Execute Workflow Trigger').first().json.to_html }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "82d7b6f5-a1be-45f1-97b3-0221c5b6263d",
      "name": "Merge2",
      "type": "n8n-nodes-base.merge",
      "position": [
        5860,
        800
      ],
      "parameters": {
        "mode": "combine",
        "options": {
          "includeUnpaired": true
        },
        "combineBy": "combineByPosition",
        "numberInputs": 3
      },
      "typeVersion": 3
    },
    {
      "id": "57f90bea-9a0b-4093-83ba-1bd18200b84c",
      "name": "Get Airtable Comments",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1880,
        820
      ],
      "parameters": {
        "url": "=https://api.airtable.com/v0/{{ $json.base_id }}/{{ $json.table_id }}/{{ $json.record_id }}/comments",
        "options": {},
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "airtableTokenApi"
      },
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "ae6b84ef-940e-497a-8081-a1da8ed7f960",
      "name": "Rename comments",
      "type": "n8n-nodes-base.set",
      "position": [
        2100,
        820
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "a58e06ab-906f-4cb4-a108-d9839264912e",
              "name": "AirtableComments",
              "type": "array",
              "value": "={{ $json.comments }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "d4d73be8-4d60-47b1-be08-057dc4ae0e98",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        800,
        -1340
      ],
      "parameters": {
        "color": 4,
        "width": 920,
        "height": 1500,
        "content": "# Airtable Hierarchical Record Fetcher\n\n## Description\n\nThis n8n workflow retrieves an Airtable record along with its related child records in a hierarchical structure. It can fetch up to 3 levels of linked records and assembles them into a comprehensive JSON object, making it ideal for complex data relationships and nested record structures.\n\n## Features\n\n- **Multi-level Record Fetching**: Retrieves parent record, linked child records (level 2), and optionally grandchild records (level 3)\n- **Selective Level 3 Fetching**: Only fetches level 3 records for specified linked fields to optimize performance\n- **Rich Text Processing**: Converts Airtable's pseudo-markdown rich text fields to HTML format\n- **Hierarchical JSON Output**: Organizes all data in a structured, nested JSON format\n- **Flexible Configuration**: Customizable depth and field selection per execution\n\n## Input Parameters\n\nThe workflow accepts a JSON array with the following structure:\n\n```json\n[\n  {\n    \"base_id\": \"appN8nPMGoLNuzUbY\",\n    \"table_id\": \"tblLVOwpYIe0fGQ52\", \n    \"record_id\": \"reczMh1Pp5l94HdYf\",\n    \"level_3\": [\n      \"fldRaFra1rLta66cD\",\n      \"fld3FxCaYk8AVaEHt\"\n    ],\n    \"to_html\": true\n  }\n]\n```\n\n### Parameter Details\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `base_id` | string | Yes | Airtable base identifier |\n| `table_id` | string | Yes | Airtable table identifier for the main record |\n| `record_id` | string | Yes | Airtable record identifier to fetch |\n| `level_3` | array | No | Array of field IDs from level 2 records for which to fetch level 3 children |\n| `to_html` | boolean | No | Convert rich text fields from pseudo-markdown to HTML (default: false). This requires `marked` npm package. |\n\n## Output Structure\n\nThe workflow returns a hierarchical JSON object with the following structure:\n\n```json\n{\n   \"id\": \"recXXXXXXX\",\n   \"field_1\": ...,\n   \"field_2\": ...,\n   \"level2_child\": [\n      {\n         \"id\": \"recXXXXXXX\",\n         \"field_a\": ...,\n         \"field_b\": ...,\n         \"level3_child\": [\n            {\n               \"id\": \"recXXXXXXX\",\n               \"field_y\": ...,\n               \"field_z\": ...,\n            },\n            ...\n         ]\n      },\n      ...\n   ]\n}\n```"
      },
      "typeVersion": 1
    },
    {
      "id": "64dd6a5e-20ec-44a3-87d0-aac91b5cd42a",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5060,
        360
      ],
      "parameters": {
        "color": 3,
        "width": 700,
        "height": 280,
        "content": "## Converts Airtable Rich Text (pseudo-markdown) to HTML\n \n**This requires `marked` npm package.** \n\nYou can delete these nodes if you don't need the feature"
      },
      "typeVersion": 1
    },
    {
      "id": "632211fc-312c-4023-b96f-8b2e1eff561e",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2420,
        -120
      ],
      "parameters": {
        "width": 860,
        "height": 520,
        "content": "## Level 2 children\n\nFor each `Link to another record` fields:\n\n- We fetch the linked records array (up to 100 children) thanks to a `filterbyformula` API trick\n- In those level 2 children records, we exclude `link to another record` fields except for fields given in `level_3` workflow input\n"
      },
      "typeVersion": 1
    },
    {
      "id": "fa4bcdc8-c977-4f4a-89e9-545a97855a38",
      "name": "Loop Over L2 records arrays having L3 children to fetch",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        3820,
        -40
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "f9589f05-4ec5-4060-a5d6-8a4263be8c0b",
      "name": "Loop Over L2 Records for a given L1 field",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        4480,
        -40
      ],
      "parameters": {
        "options": {
          "reset": "={{ $node[\"Loop Over L2 Records for a given L1 field\"].context[\"done\"] }}"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "d62be593-40bf-4ba2-83ec-e32121a35d18",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4380,
        -540
      ],
      "parameters": {
        "color": 2,
        "width": 1820,
        "height": 740,
        "content": "## Level 3 children\n\nThis is an iteration on each record within a record array to fetch its level 3 children"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Merge": {
      "main": [
        [
          {
            "node": "Loop Over L2 Records for a given L1 field",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Inputs": {
      "main": [
        [
          {
            "node": "Airtable - Get Target Record",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Airtable Comments",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge1": {
      "main": [
        [
          {
            "node": "Add origin LinkTo",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate1": {
      "main": [
        [
          {
            "node": "Code to single field property",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Has Level 3": {
      "main": [
        [
          {
            "node": "Loop Over L2 records arrays having L3 children to fetch",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Merge1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Airtable Schema": {
      "main": [
        [
          {
            "node": "Filter Main Table",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Level 2 records": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Rename comments": {
      "main": [
        [
          {
            "node": "Merge2",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "Markdown to Html": {
      "main": [
        [
          {
            "node": "Merge2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Out Fields": {
      "main": [
        [
          {
            "node": "Filter Link To fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add origin LinkTo": {
      "main": [
        [
          {
            "node": "Insert linked records to originRecord & Clean Field Name",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Main Table": {
      "main": [
        [
          {
            "node": "Split Out Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare HTTP Call": {
      "main": [
        [
          {
            "node": "Get L3 Linked records without reverse Link To fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If markdown to html": {
      "main": [
        [
          {
            "node": "Rich Text Fields list",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Merge2",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Filter Link To fields": {
      "main": [
        [
          {
            "node": "Add Inverse LinkTo Field name",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Airtable Comments": {
      "main": [
        [
          {
            "node": "Rename comments",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Preparing aggregation": {
      "main": [
        [
          {
            "node": "Aggregate1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Rich Text Fields list": {
      "main": [
        [
          {
            "node": "Markdown to Html",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate to record list": {
      "main": [
        [
          {
            "node": "Loop Over L2 records arrays having L3 children to fetch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Execute Workflow Trigger": {
      "main": [
        [
          {
            "node": "Inputs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Airtable - Get Target Record": {
      "main": [
        [
          {
            "node": "Airtable Schema",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Out L3 Potential Links": {
      "main": [
        [
          {
            "node": "Prepare HTTP Call",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add Inverse LinkTo Field name": {
      "main": [
        [
          {
            "node": "Get Linked records without Link To fields Except for Level 3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code to single field property": {
      "main": [
        [
          {
            "node": "Merge with original field property",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge with original field property": {
      "main": [
        [
          {
            "node": "Loop Over L2 Records for a given L1 field",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Repeat L3 links of current L2 table": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over L2 Records for a given L1 field": {
      "main": [
        [
          {
            "node": "Aggregate to record list",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Split Out L3 Potential Links",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get L3 Linked records without reverse Link To fields": {
      "main": [
        [
          {
            "node": "Preparing aggregation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over L2 records arrays having L3 children to fetch": {
      "main": [
        [
          {
            "node": "Merge1",
            "type": "main",
            "index": 1
          }
        ],
        [
          {
            "node": "Repeat L3 links of current L2 table",
            "type": "main",
            "index": 0
          },
          {
            "node": "Level 2 records",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Insert linked records to originRecord & Clean Field Name": {
      "main": [
        [
          {
            "node": "If markdown to html",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Linked records without Link To fields Except for Level 3": {
      "main": [
        [
          {
            "node": "If Has Level 3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}