Google-sheets, update-row, node-issue

:exclamation:Seeking Help: Google Sheets ID Not Recognized in n8n Automation (Full Workflow Orchestration)

Hello n8n & Perplexity community! :raised_hands:

We’re building a no-code AI content production pipeline using Google Sheets + Google Apps Script + n8n + Perplexity + OpenAI + Tavus, and we’ve hit a blocker we can’t resolve on our own.


:white_check_mark: The Goal

We want to create a fully automated, scalable system for AI content generation and video creation using avatars. Here’s how it’s supposed to work:

1. User adds a topic to a Google Sheet in column B (“Topic”)

2. Google Apps Script:

  • Automatically generates a unique ID in column A
  • Adds a timestamp in column G
  • Then triggers an n8n webhook via UrlFetchApp.fetch() using id and topic as GET query params

3. n8n Flow (Stage 1 expected sequence):

  • Webhook Trigger (GET) receives the ID & Topic
  • SanitizeParams Code node standardizes input
  • SaveToSheet node (Google Sheets – Update Row) updates the corresponding row with status = "pending" and created_at
  • Flow continues through logic branches depending on approval status

4. If Status = “approved”:

  • Perplexity is called (sonar-pro-online)
  • The response is processed by OpenAI (caption + hashtags + full post)
  • Tavus API is triggered to render an avatar-based video with ElevenLabs voice
  • Final result is stored in the sheet (Media_URL column) and prepared for publishing

:warning: The Problem

Everything was working… until we accidentally deleted and recreated the Google Sheet tab (same name: "Social_Media_Plan_v2").

Since then:

  • Webhook still receives the ID and topic :white_check_mark:
  • SanitizeParams node works correctly :white_check_mark:
  • BUT SaveToSheet fails silently – no update happens, and id becomes {} in input
  • Even after rebuilding the Google Sheets node, setting Sheet by ID, reauthenticating OAuth2 credentials, and mapping all columns manually – it still doesn’t match the id column.

:mag: What We’ve Tried

  • Confirmed gid (sheetId) and manually configured node Sheet → Select by ID
  • Ensured “ID” column is formatted as Plain Text
  • Manually hardcoded an ID for testing – still no update
  • Verified webhook is sending: https://example.n8n.cloud/webhook/studio-trigger?id=ID-test123&topic=AutomationTest
  • Used Debug node to inspect input – JSON shows:

json

КопіюватиРедагувати

{
  "query": {
    "id": "ID-test123",
    "topic": "AutomationTest"
  }
}
  • Updated Code Node to:

javascript

КопіюватиРедагувати

const query = $node["Webhook Trigger"].json.query || {};
return [{
  json: {
    id: String(query.id).trim(),
    topic: query.topic.trim(),
    status: "pending",
    createdAt: new Date().toISOString(),
    updatedAt: new Date().toISOString()
  }
}];
  • Headers match exactly: “ID” (not “Id” or “id”)
  • Tried Map Each Column Manually, Match on Column: ID, header row = 1, first data row = 2
  • Tried reauthenticating Google credentials completely

:wrench: What We Need Help With

  1. Why is n8n’s Google Sheets node not recognizing the ID, even when everything is correctly passed and mapped?
  2. Could Google Apps Script triggers or sheet recreation (gid change) cause a hidden issue even after rebuilding the node?
  3. Can you help us validate if our webhook setup → param parsing → Google Sheets Update pattern is best practice?

:telescope: Our Goal After Fixing This

Once the SaveToSheet works again:

  • If status == approved, we call Perplexity (sonar-pro-online) to generate content
  • Process that content through OpenAI (gpt-4), generate 1 caption, hashtags, and post
  • Trigger Tavus API + D-ID for video rendering
  • Store Media_URL and final content back to the Google Sheet
  • Later: auto-post to Buffer, Telegram, LinkedIn

:robot: Tools Used

  • Google Sheets – structured with columns: ID, Topic, Status, Caption, Hashtags, Post, Media_URL, Created_At, Updated_At
  • n8n Cloud – main automation flow
  • Perplexity API – via HTTP node
  • OpenAI – for content rewriting
  • Tavus + ElevenLabs – video + audio
  • Apps Script – to trigger webhook and insert metadata

We would truly appreciate any advice or help from the community or Perplexity team.
If needed, I can share screenshots or sanitized JSON examples privately.

Thanks in advance,

This sounds like an interesting problem.

What I think you are doing is something like this:

Apps Script:

/**

  • returns a unique ID
  • @customfunction
    */

function SETUUID(){

// This creates a UUID, packages “topic” and UUID into data object, triggers N8N webhook (including data), then returns UUID in data.id

// Make a POST request with a JSON payload.
const data = { topic: ‘Topic Name’, id: Utilities.getUuid() };
const options = {
method: ‘post’,
contentType: ‘application/json’,

// Convert the JavaScript object to a JSON string.
payload: JSON.stringify(data),
};

UrlFetchApp.fetch(‘https://{your url}’, options);

return data.id;

}

Note that I’m doing a POST, not a GET.

My steps:

  • Enter ‘=SETUUID()’ into the ID column of the spreadsheet.
    • This puts a UUID into the spreadsheet and triggers the webhook sending the topic and id in ‘data’.
  • Follow with an Edit Field node (in place of a Code node)
  • Use (Update Row) in Google Sheet node by matching the id from the webhook with the ID in the spreadsheet.

This worked until you deleted the sheet, then recreated it.
Do I have the basics down?

I was unable to recreate the problem and have a couple ideas but given how thorough you seem to be, you’ve probably covered them.

  1. Did you refresh the Sheet in the Google Sheets node after recreating the deleted sheet?
  2. Is it possible that there is hidden text in the ID column? I’ve seen others encountering this which can result in updating the wrong row (like row 1500!).
  3. My results are in the “body” param, not the 'query" param
  4. Create a minimal workflow that only tries to update a row

Are you self-hosting?
Would you be comfortable sharing your workflow? If so, copy your entire workflow, then use </> and paste.

Sorry I’m not much help here. Perhaps I’m missing something?

2 Likes

Hey! 👋

Thanks again for being open to help — I really appreciate it. We’re building something quite deep and exciting here, and I’d love to walk you through what we’re doing and where things are acting weird.


🧠 Quick context

I’m building a full studio-grade automation flow (we call it BAS Studio), where Google Sheets acts as both the control panel and the trigger surface for n8n.

Here’s the current flow:

  1. A custom =SETUUID() Apps Script function in the sheet generates a UUID.
  2. It POSTs that UUID (plus a topic) to our n8n webhook.
  3. n8n processes the data → routes it through Perplexity (topic → caption) → then tries to update the original row, matching via id.

This setup used to work flawlessly — until recently.


🐛 The problem

  • The webhook triggers perfectly
  • The data arrives — but often the id field is mysteriously undefined
  • Sometimes, the row doesn’t get updated at all (even when the id looks correct)
  • Logs show things like:
{ "id": "NO_ID", "topic": "NO_TOPIC" }
I suspect one of a few things might be going on:

Timing — maybe the UUID isn’t fully written when the webhook fires

Formatting — maybe there’s hidden white space or encoding in the ID column

Google sync delay — especially since we recently deleted and recreated the Sheet

![✅](https://fonts.gstatic.com/s/e/notoemoji/16.0/2705/72.png) What I’ve tried
Cleaned up the ID column (cleared all hidden text/formatting)

Ensured clean POST payload structure

Matched id field properly in the “Update Row” node

Added delays and debug nodes between steps

Tried both “By ID” and “By URL” references in the Sheet node

Re-authenticated Google credentials

![🎯](https://fonts.gstatic.com/s/e/notoemoji/16.0/1f3af/72.png) What I’m aiming for
A stable closed loop:

Sheet generates row + UUID

Webhook POST triggers n8n

Perplexity/ChatGPT generate caption/post

Final result is written back to the same row, matched by UUID

Bonus:

Full logging into a second sheet (already working)

Error handling + re-validation logic

100% webhook-driven, zero manual steps

![🙏](https://fonts.gstatic.com/s/e/notoemoji/16.0/1f64f/72.png) Where I’d love your input
Have you ever run into this type of “undefined” behavior in a webhook → update flow?

Could this be a race condition with Google Sheets?

Would a Wait node help to give Sheets time to “lock in” the ID before writing back?

Is there a trick with flush() in Apps Script before sending the POST?

Any known quirks with Apps Script + n8n POSTs you've run into?
Thanks again — really appreciate your help.
We’re super close to making this thing bulletproof ![🙌] 

Here’s the current version of the workflow (clean JSON, no API keys):
🔗 https://gist.github.com/AndBlo8/82baca6c36fa47b0bd16283eb4e90bda

Let me know if you spot anything strange — I'm happy to tweak things based on your feedback 🙌

Hi @andrii_blonskyi ,

I wanna add something to your “What We’ve Tried” list.

Since you say that everything was working until:

Are you sure your Google Apps Script is linked to the new Google Sheet you rebuilt?!

2 Likes

Request: Webhook not triggering in n8n from Google Apps Script — All configs verified, still 404

Hi team,
I’m building an automation between Google Sheets → Apps Script → n8n webhook. Everything is connected and verified:

  • :white_check_mark: Google Apps Script is correctly linked to the current Google Sheet
  • :white_check_mark: Webhook URL (n8n cloud) is correct and responds manually
  • :white_check_mark: sendToWebhook function sends a valid POST with payload: { id, topic, status, regenerate }
  • :white_check_mark: Lock, retry logic, logging – all implemented and working
  • :white_check_mark: Logs show the row is detected, ID and topic are generated and sent
  • :stop_sign: BUT: n8n always returns HTTP 404 from the script

Screenshots confirm:

  • Webhook triggers fine from Postman or browser
  • Script reaches .fetch(WEBHOOK_URL) with correct data
  • n8n workflow is active and listening on webhook/studio-trigger

We’ve tried:

  • Changing between test URL and production URL
  • Debugging body and headers
  • Verifying that onEditTrigger() fires correctly
  • Ensuring Content-Type: application/json
  • Checking that payload is not empty

:question:What are we missing?

Could this be a timing issue, webhook registration bug, or something specific to UrlFetchApp behavior with n8n?

Thanks for any insight — this is blocking a production automation.

Hi @andrii_blonskyi

Before anything else, In Google Sheets (Apps Script Triggers): Are you sure you added the trigger with event type On edit and selected the correct function to run?

Please note that because you’re using UrlFetchApp, you must add an installable trigger (event type On edit) and select the correct function to run—otherwise the webhook call won’t execute.

Also, please help us help you: you haven’t shared any Google Apps Script code, screenshots, or your workflow.

The text you provided is helpful, but sharing more details will benefit everyone.

By the way, here’s a simple Google Apps Script code that’s working on my end:

/**
 * Simple on‑edit trigger that grabs ID/topic from columns A/B
 * and calls your triggerN8n function.
 *
 * NOTE: Because UrlFetchApp requires authorization, you must
 * install this as an “On edit” trigger (not rely on the simple trigger).
 */
function onEdit(e) {
  const sheet = e.source.getActiveSheet();
  const range = e.range;
  
  // Only fire on the sheet you care about
  if (sheet.getName() !== 'Sheet1') return;
  
  // Only fire when column B (topic) is edited
  if (range.getColumn() !== 2) return;
  
  const row = range.getRow();
  const id = sheet.getRange(row, 1).getValue();      // Col A
  const topic = sheet.getRange(row, 2).getValue();   // Col B
  
  // If you want to only fire on non-empty topic:
  if (!topic) return;
  
  // Call your webhook helper
  triggerN8n(id, topic);
}

/**
 * Your existing helper — sends the POST to n8n
 */
function triggerN8n(id, topic) {
  const url = 'https://your-n8n-instance.com/webhook/myWebhook';
  const options = {
    method: 'post',
    contentType: 'application/json',
    payload: JSON.stringify({ id, topic }),
    muteHttpExceptions: true
  };
  const response = UrlFetchApp.fetch(url, options);
  Logger.log(response.getResponseCode(), response.getContentText());
}

Hey! Thanks again for staying with us on this — we really appreciate your help!

We’re building an AI-driven automation pipeline under Blonskyi Studio, where Google Sheets acts as the control center for triggering content flows (caption generation, video creation, publishing). We’re almost ready to launch but stuck on this final critical webhook issue.


:wrench: What we’re doing

  • User edits a “Topic” cell (Column B) in our Social_Media_Plan_v2 Sheet
  • If there’s no ID yet, we:
    • Generate a UUID (ID-xxxx)
    • Log it
    • Trigger our sendToWebhook() function
  • The webhook calls an n8n cloud endpoint with this payload:
{
  "id": "ID-xxxx",
  "topic": "Edited topic",
  "status": "approved",
  "regenerate": "no"
}
n8n then routes through Perplexity/OpenAI, generates captions, and pushes results back into the same row

❗The issue
When the script runs inside onEditTrigger() (installed trigger), the UrlFetchApp.fetch() returns a 404

BUT:

The exact same webhook works fine via Postman

It even works from within the script editor (manually run)

Data is logged correctly, and everything looks clean

📄 Here’s the full Apps Script (clean & commented):
🔗 https://gist.github.com/AndBlo8/b5cb3e3a0cd5f1004984e0b801639b1c

Let me know if you see anything off or if you'd suggest a more robust approach.

🧠 Open question for you:
Is it possible we’re overengineering this a bit?

Do you think the logic would be better structured differently — e.g., triggering n8n from a button, or using Sheets → Web App → n8n instead of Apps Script triggers?

We’re aiming for:

zero manual steps

webhook-only control

full row mapping by ID

Again — we’re super close, and just trying to make this bulletproof. Thanks for taking the time 🙏

Okay, thank you!

Did you try the things I explained?

Yes — thank you again!

We’ve carefully followed every step you shared earlier:

:white_check_mark: Installed the trigger manually via the UI
:white_check_mark: Confirmed it points to onEditTrigger()
:white_check_mark: Shared the full Google Apps Script code here: gist:b5cb3e3a0cd5f1004984e0b801639b1c · GitHub

The webhook still returns a 404 when triggered from the installed script, but works via Postman and direct execution. So strange.

Let me know if you think we’re missing something subtle — or if there’s a better architecture altogether that you’d recommend :pray:

Thank you,

I just tested your code here:

Shared the full Google Apps Script code here: gist:b5cb3e3a0cd5f1004984e0b801639b1c · GitHub

and it’s working on my end

image

image

Hey again! :blush:
Thanks a ton for your help earlier — it really cleared things up. We’ve been making solid progress, and the whole flow is almost working beautifully. But there’s one last piece giving us trouble, and I’d love to get your expert thoughts on it :pray:


:jigsaw: Quick Recap of What We’re Building

We’re automating our AI content creation workflow using:

  • Google Sheets (as our content tracker)
  • Google Apps Script (triggering a webhook on topic entry)
  • n8n (to orchestrate everything)
  • Perplexity & OpenAI (for content generation)
  • Tavus (for AI video creation)

The flow begins when a new topic is added in Google Sheets. The Apps Script sends a POST request to an n8n webhook with a payload like:

json

КопіюватиРедагувати

{
  "id": "ID-abc123",
  "topic": "The Future of AI Agents",
  "status": "approved",
  "regenerate": "no"
}

That flows through our scenario:

  1. SanitizeParams → adds timestamp + formats
  2. OpenAI Request → generates caption/post
  3. ValidateOpenAIContent → handles GPT output
  4. FlattenForSheets → flattens payload
  5. Google Sheets (Update Row) → saves results back to the original row by ID

:sweat_smile: The Problem We’re Hitting

Even though data is definitely flowing, and all values (ID, topic, caption, status) are clearly visible in the input before the Sheets node…
:arrow_right: the Google Sheets node still outputs {}
…no row is updated.

We’ve followed your earlier advice and did:

:white_check_mark: Flatten the JSON using a Code node:

js

КопіюватиРедагувати

return items.map(item => {
  const payload = item.json.payload || {};
  return {
    json: {
      id: payload.id || "",
      topic: payload.topic || "",
      caption: payload.content || "",
      status: payload.status || "",
      timestamp: payload.timestamp || ""
    }
  };
});

:white_check_mark: In the Google Sheets node:

  • Matching column: ID
  • Operation: Update Row
  • Mapping fields like {{$json.id}}, {{$json.caption}}, etc.
  • Sheet and column names 100% match and are case-exact

Still, we just get [{}] in output :weary:


:mag: What We Suspect

  • Could it be that if the ID value doesn’t exactly match in the sheet, Google Sheets silently returns {} without any warning?
  • Or does it require the row to be visible/not filtered somehow?
  • Also wondering: is there a more robust way you’d suggest handling structured payloads? (like splitting meta, content, etc.?)

:bulb: Our Goal

We just want to write the final content (caption, status, etc.) back to the original Google Sheets row by ID after OpenAI generates it. Super simple, but this last step keeps failing silently.

Would love to hear how you would architect this — even if we need to change some things. You’ve already helped a ton, and this is the last piece before we go live :rocket:

Massive thanks again! :raised_hands:

If this is the new issue, please take a screenshot of the Google Sheet node showing the inputs, the mapping fields, and the output with the unexpected result.

This will help me and others understand the issue and assist you more effectively…

1 Like

Hi Muhammad :wave:

We’re building a fully automated AI content pipeline for our Studio Flow — tightly integrating:

  • Google Sheets
  • OpenAI / Perplexity APIs
  • Caption + Post Generation
  • Data validation
  • Google Apps Script trigger via Webhook
  • And full update back to Google Sheets.

:brain: Context and Flow Architecture:

We trigger the flow via a Google Apps Script Webhook (after a Perplexity API call).
The data is passed into n8n as query params: ?id=123&topic=xyz.

The flow runs smoothly through:

  1. Trigger Webhook (Listen Mode ON)
  2. Set Nodes / Parse Caption / Compose Final Output
  3. Validate & Format
  4. Prepare for Google Sheets Update
  5. Update Row by ID
  6. Respond to Script
  7. Final Debug & Log Output

:warning: Current Issue:

Everything executes without error, but the id is consistently undefined across multiple steps, starting from Combine All.
As a result:

:white_check_mark: Flow completes
:no_entry_sign: Google Sheet does not update, even though:

  • Column to match on is set to ID
  • JSON payload seems correct
  • status and caption are passed and logged correctly
  • All debug nodes show working chain — except id

We’ve triple-checked:

  • Incoming query param parsing
  • Webhook setup (production & test modes)
  • Data propagation through Set, Combine, and Prepare Row
  • Variable references like payload.id, item.json.id, $json.id

Yet the id stays "undefined" throughout.


:mag_right: Reference Screenshots:

We’ve documented the full execution path, debug outputs, and config nodes here:

:paperclip: Google Doc with Flow Screenshots & Output Logs
:point_right: n8n Debug Flow — Studio Flow 22 Apr 2025 - Google Docs


:speech_balloon: What we need help with:

  1. Where is the likely break in the id propagation? Is it in Set, Combine, or input referencing?
  2. What’s the most reliable and scalable method to pass Webhook query params through a full update cycle?
  3. Are there any best practices for using Update Row with ID matching from external triggers?
  4. Do you see any redundant or overly complex logic we should simplify?

We’re pushing this to production soon, and would deeply value community or core team insights on how to fix this final blocker.

With appreciation :pray: