Google Sheets update only writes one contact per row after Hunter/merge – want 3 contacts mapped per sponsor lead

Hi n8n community,

I’m running into an issue with my outreach workflow that involves deduping sponsor leads and enriching them with multiple contacts (via Hunter), then writing them back to Google Sheets.

Flow overview:

  1. Trigger: Manual or Google Sheets trigger.
  2. Read: Pull all sponsor leads from a Google Sheet.
  3. Deduplicate: Remove duplicate sponsor leads (by “Sponsor Name”).
  4. Filter: Only keep leads marked as Qualified (“YES” or “MAYBE”).
  5. Contact enrichment:
  • For each deduped/filtered sponsor lead, I use the Hunter integration to find up to 3 relevant contacts (name, surname, title, email, LinkedIn) for each sponsor domain.
  1. Merge/Summarise:
  • I merge the Hunter contacts back with each sponsor lead.
  • Output format: For each sponsor, I now have an array of up to 3 contacts under a field called contacts.
  1. Google Sheets update:
  • My Google Sheet is structured with columns like:
    • Contact 1 Name, Contact 1 Surname, Contact 1 Raw Title, Contact 1 Email, Contact 1 LinkedIn, …and the same for Contact 2 and Contact 3.
  • I want to map each contact from the array to these columns, so every sponsor lead row has up to 3 contact details side by side (not stacked as separate rows).

The issue:

When I try to update Google Sheets with the results, only one contact’s details (usually the first) are written into the relevant columns. The other contacts are ignored or left blank. I’ve tried using the Merge node and setting up mappings, but I can’t get all 3 contacts into the separate fields in the same row.

I believe I need a way (probably via the Code node) to take the array of contacts and map each contact to their respective columns for each sponsor row before updating the sheet. I tried some JS snippets, but I keep hitting either errors (about top-level keys) or nothing is written except for the first contact.


Current data structure:

Each item from the merge node before the update looks like:

{
  "row_number": 11,
  "Sponsor Name": "Lambda",
  "contacts": [
    {
      "firstName": "Nikki",
      "lastName": "Phillips",
      "rawTitle": "Head of Marketing",
      "email": "[email protected]",
      "linkedin": "https://www.linkedin.com/in/nikki-phillips-5133384b"
    },
    {
      "firstName": "Andrew",
      "lastName": "Butterworth",
      "rawTitle": "CMO",
      "email": "[email protected]",
      "linkedin": "https://www.linkedin.com/in/andrew-butterworth-93b71b14"
    },
    {
      "firstName": "Chris",
      "lastName": "Oglesby",
      "rawTitle": "Head of Partnerships",
      "email": "[email protected]",
      "linkedin": "https://www.linkedin.com/in/chris-oglesby-90522070"
    }
  ],
  // ... other fields
}

Desired Google Sheets output:

Each sponsor row in Sheets should look like:

Sponsor Name Contact 1 Name Contact 1 Surname Contact 1 Raw Title Contact 1 Email Contact 1 LinkedIn Contact 2 Name …etc
Lambda Nikki Phillips Head of Marketing nikki@… https://… Andrew

What I’ve tried:

  • Using the Merge node to combine sponsor and contact data.
  • Mapping fields directly in the Sheets node (doesn’t work with arrays).
  • Code node to flatten the array to separate fields, but I keep getting errors about top-level keys, or the update just fails.
  • Documentation and forum posts, but can’t find a working example for this specific “array to fields” Sheets update pattern.

What I need:

  • The best-practice way to flatten up to 3 contacts from the array into fields for each sponsor row before updating Google Sheets, using n8n’s native nodes and a Code node if needed.
  • Any tips on how to avoid overwriting the wrong row (I’m using row_number as a key for update).

Any step-by-step advice, working code snippets, or best-practice approaches would be hugely appreciated!

Thanks a ton!

Daniel

P.S.: My current flow attached.

P.P.S: I am a newbie and been doing this all with AI assistance and very limited code.

Hi @Daniel_Ionescu

It looks liek the merge step is only sending one item to the google sheet. you probably need to parse the array into multiple rows. Can you upload your workflow here?

Here’s a gif on how to do that.

pasteworkflow

Hi Robert, thanks for your assistance…

My GSheet has the following columns (with one sponsor per row as extracted and qualified by two other agents). The idea is that after this I will have email outreach and follow-up agents to also add to this sheet their actions.

Sponsor Name Sponsor Link Sponsor Message Qualified? Reason Sponsor Web Domain Contact 1 Name Contact 1 Surname Contact 1 Title Contact 1 Email Contact 1 LinkedIn Contact 1 Status Contact 2 Name Contact 2 Surname Contact 2 Title Contact 2 Email Contact 2 LinkedIn Contact 2 Status Contact 3 Name Contact 3 Surname Contact 3 Title Contact 3 Email Contact 3 LinkedIn Contact 3 Status


Flow code below:

{
  "nodes": [
    {
      "parameters": {},
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        0,
        75
      ],
      "id": "c9e5109a-06dd-495f-8b4c-d7a13db71008",
      "name": "When clicking ‘Execute workflow’"
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "1SGTtcF8LnnyBer8sSZNHPLEOHRfsytGE8NqDy_m8LUA",
          "mode": "list",
          "cachedResultName": "MM N8N Sponsors",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1SGTtcF8LnnyBer8sSZNHPLEOHRfsytGE8NqDy_m8LUA/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": "gid=0",
          "mode": "list",
          "cachedResultName": "Sponsors",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1SGTtcF8LnnyBer8sSZNHPLEOHRfsytGE8NqDy_m8LUA/edit#gid=0"
        },
        "filtersUI": {
          "values": [
            {
              "lookupColumn": "Qualified?",
              "lookupValue": "YES"
            },
            {
              "lookupColumn": "Qualified?",
              "lookupValue": "MAYBE"
            }
          ]
        },
        "combineFilters": "OR",
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.6,
      "position": [
        220,
        75
      ],
      "id": "1551d21f-3bb2-4c70-aeef-4ecf74997604",
      "name": "Google Sheets",
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "W6QJ8EvWDSmufVgv",
          "name": "Google Sheets account"
        }
      }
    },
    {
      "parameters": {
        "compare": "selectedFields",
        "fieldsToCompare": "['Sponsor Name']",
        "options": {}
      },
      "type": "n8n-nodes-base.removeDuplicates",
      "typeVersion": 2,
      "position": [
        440,
        75
      ],
      "id": "d57864ee-46e0-458a-bfd5-887519c19ec0",
      "name": "Remove Duplicates"
    },
    {
      "parameters": {
        "domain": "={{ $json['Sponsor Web Domain'] }}",
        "limit": 3,
        "filters": {
          "type": "personal",
          "department": [
            "marketing",
            "communication",
            "executive"
          ]
        }
      },
      "type": "n8n-nodes-base.hunter",
      "typeVersion": 1,
      "position": [
        1100,
        0
      ],
      "id": "3f8f1117-f080-48f9-a60e-44fd6cf4f8ac",
      "name": "Hunter",
      "alwaysOutputData": true,
      "credentials": {
        "hunterApi": {
          "id": "zS2gZI6VG4oaQFVK",
          "name": "Hunter account"
        }
      }
    },
    {
      "parameters": {
        "operation": "update",
        "documentId": {
          "__rl": true,
          "value": "1SGTtcF8LnnyBer8sSZNHPLEOHRfsytGE8NqDy_m8LUA",
          "mode": "list",
          "cachedResultName": "MM N8N Sponsors",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1SGTtcF8LnnyBer8sSZNHPLEOHRfsytGE8NqDy_m8LUA/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": "gid=0",
          "mode": "list",
          "cachedResultName": "Sponsors",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1SGTtcF8LnnyBer8sSZNHPLEOHRfsytGE8NqDy_m8LUA/edit#gid=0"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "Contact 1 Name": "={{ $json.appended_first_name }}",
            "Contact 1 LinkedIn": "={{ $json.appended_linkedin }}",
            "Contact 1 Email": "={{ $json.appended_value }}",
            "Contact 2 Name": "={{ $json.appended_first_name }}",
            "Contact 2 Surname": "={{ $json.appended_last_name }}",
            "Contact 2 Title": "={{ $json.appended_position_raw }}",
            "Contact 2 Email": "={{ $json.appended_value }}",
            "Contact 2 LinkedIn": "={{ $json.appended_linkedin }}",
            "Contact 3 Name": "={{ $json.appended_first_name }}",
            "Contact 3 Surname": "={{ $json.appended_last_name }}",
            "Contact 3 Title": "={{ $json.appended_position_raw }}",
            "Contact 3 Email": "={{ $json.appended_value }}",
            "Contact 3 LinkedIn": "={{ $json.appended_linkedin }}",
            "Contact 1 Surname": "={{ $json.appended_last_name }}",
            "Contact 1 Title": "={{ $json.appended_position_raw }}",
            "Sponsor Web Domain": "={{ $('Google Sheets').item.json['Sponsor Web Domain'] }}"
          },
          "matchingColumns": [
            "Sponsor Web Domain"
          ],
          "schema": [
            {
              "id": "Date",
              "displayName": "Date",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Newsletter",
              "displayName": "Newsletter",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Sponsor Name",
              "displayName": "Sponsor Name",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Sponsor Link",
              "displayName": "Sponsor Link",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Sponsor Message",
              "displayName": "Sponsor Message",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Qualified?",
              "displayName": "Qualified?",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Reason",
              "displayName": "Reason",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Sponsor Web Domain",
              "displayName": "Sponsor Web Domain",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "Contact 1 Name",
              "displayName": "Contact 1 Name",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Contact 1 Surname",
              "displayName": "Contact 1 Surname",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "Contact 1 Title",
              "displayName": "Contact 1 Title",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "Contact 1 Email",
              "displayName": "Contact 1 Email",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Contact 1 LinkedIn",
              "displayName": "Contact 1 LinkedIn",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Contact 1 Status",
              "displayName": "Contact 1 Status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Contact 2 Name",
              "displayName": "Contact 2 Name",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Contact 2 Surname",
              "displayName": "Contact 2 Surname",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "Contact 2 Title",
              "displayName": "Contact 2 Title",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Contact 2 Email",
              "displayName": "Contact 2 Email",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Contact 2 LinkedIn",
              "displayName": "Contact 2 LinkedIn",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "Contact 2 Status",
              "displayName": "Contact 2 Status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Contact 3 Name",
              "displayName": "Contact 3 Name",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Contact 3 Surname",
              "displayName": "Contact 3 Surname",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "Contact 3 Title",
              "displayName": "Contact 3 Title",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Contact 3 Email",
              "displayName": "Contact 3 Email",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Contact 3 LinkedIn",
              "displayName": "Contact 3 LinkedIn",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "Contact 3 Status",
              "displayName": "Contact 3 Status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "row_number",
              "displayName": "row_number",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "readOnly": true,
              "removed": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.6,
      "position": [
        1760,
        75
      ],
      "id": "b288f2e7-9bef-41c1-ae3f-71b65d4000a2",
      "name": "Google Sheets1",
      "alwaysOutputData": true,
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "W6QJ8EvWDSmufVgv",
          "name": "Google Sheets account"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "67eb21b8-8499-4de5-acd2-24a524598b2c",
              "leftValue": "={{ $json['Qualified?'] }}",
              "rightValue": "YES",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            },
            {
              "id": "a3a7504d-4271-46f8-8fba-a54226480369",
              "leftValue": "={{ $json['Qualified?'] }}",
              "rightValue": "MAYBE",
              "operator": {
                "type": "string",
                "operation": "equals",
                "name": "filter.operator.equals"
              }
            }
          ],
          "combinator": "or"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.filter",
      "typeVersion": 2.2,
      "position": [
        660,
        75
      ],
      "id": "55b995d0-bfc4-4226-b7da-931afd47ea7a",
      "name": "Filter Yes+Maybe",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "95714acb-cb3c-4d2f-8fb3-693c6944d80e",
              "leftValue": "={{ $json['Contact 1 Name'] }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.filter",
      "typeVersion": 2.2,
      "position": [
        880,
        75
      ],
      "id": "1384699b-1bf0-4db5-9222-fb3c4241e5f7",
      "name": "Filter No Contact"
    },
    {
      "parameters": {
        "fieldsToSummarize": {
          "values": [
            {
              "aggregation": "append",
              "field": "value"
            },
            {
              "aggregation": "append",
              "field": "first_name"
            },
            {
              "aggregation": "append",
              "field": "last_name"
            },
            {
              "aggregation": "append",
              "field": "position_raw"
            },
            {
              "aggregation": "append",
              "field": "linkedin"
            }
          ]
        },
        "fieldsToSplitBy": "['Sponsor Web Domain']",
        "options": {}
      },
      "type": "n8n-nodes-base.summarize",
      "typeVersion": 1.1,
      "position": [
        1320,
        0
      ],
      "id": "b56ff738-93a4-4d47-a58f-9ce2c9d5b945",
      "name": "Summarize"
    },
    {
      "parameters": {
        "mode": "combine",
        "combineBy": "combineByPosition",
        "options": {}
      },
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3.2,
      "position": [
        1540,
        75
      ],
      "id": "44a1eafe-1f81-4db7-ba43-d9ba0951382b",
      "name": "Merge"
    }
  ],
  "connections": {
    "When clicking ‘Execute workflow’": {
      "main": [
        [
          {
            "node": "Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Sheets": {
      "main": [
        [
          {
            "node": "Remove Duplicates",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Remove Duplicates": {
      "main": [
        [
          {
            "node": "Filter Yes+Maybe",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Hunter": {
      "main": [
        [
          {
            "node": "Summarize",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Yes+Maybe": {
      "main": [
        [
          {
            "node": "Filter No Contact",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter No Contact": {
      "main": [
        [
          {
            "node": "Hunter",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Summarize": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge": {
      "main": [
        [
          {
            "node": "Google Sheets1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "meta": {
    "templateCredsSetupCompleted": true,
    "instanceId": "43c47acba8a79c1f5fe3ae5bf1b9daf63ed852323c6804df317cd05dce90ada6"
  }
}```

Hi @Daniel_Ionescu

Your code did not render in the chat. If you put the code in a code block, it will show an interactive version of your workflow.

Your workflow should render like the example below. Can you upload it here?

@Daniel_Ionescu

I was able to recreate your workflow with sample data. THe hunter step outputs multiple rows. You need to aggregate the rows into one, and input into the google sheet. Here’s a sample.

Google sheet: sponsors - Google Sheets

:heart:If this response helped you, please click the heart to show that it is useful
:white_check_mark:If this response solved your issue, mark it as the solution to help the community

Hi Rob, thank you very much for the suggestion.

I’ve amended the flow as suggested and I got all the items in one row (code below)

I tried a version without the summarize/merge nodes, but then it doesn’t know which row to write on in the sheet.

1 Like

Hi @Daniel_Ionescu

On your google sheets node at the end, you need to tell it which one coresponds with each value. The workflow that I put above shows name[0], name[1], etc going into different columns.

:heart:If this response helped you, please click the heart to show that it is useful

1 Like

Thanks again for your help Robert, much appreciated.