I need to connect my google sheet workflow through n8n

Hey Buddies , i have created a workflow in n8n , in that i have a google sheets node , that node i need to control with REST api so, 80% done , The issue i am facing is that i am unable to control document _mode through my restapi how can i do that

Share more screenshot or the workflow might be helpful.

Can’t guess the real issue here.

This below is the screenshot of configuration i need to get in my sheets through a REST api

Api Reference: router.post(“/workflows/:id/add-google-sheets”, async (req, res) => {

try {

const id = req.params.id;

const {

nodeName = “Append to Google Sheets”,

documentUrl, // preferred: full URL from UI

documentId: documentUrlOrId, // legacy: URL or bare ID

range = “Sheet1!A:Z”,

googleCredentialId,

// we’ll force modern (v6) for Option A

headerMapping = { Date: “Date”, Name: “Name”, Mobile: “Mobile” }, // header → payload key

mapping = { A: “Date”, B: “Name”, C: “Mobile” }, // v4 fallback (A/B/C)

} = req.body || {};

// — helpers —

const isUrl = (s) => /^https?:\/\//i.test(String(s || “”));

const extractSheetId = (str) => {

if (!str) return “”;

const m = String(str).match(/\/spreadsheets\/d\/([a-zA-Z0-9-_]+)/);

return m ? m[1] : String(str).trim();

};

const parseSheetNameFromRange = (rng) => {

const m = String(rng || “”).match(/^([^!]+)!/);

return m ? m[1] : “Sheet1”;

};

const rawDoc = documentUrl ?? documentUrlOrId;

if (!rawDoc) {

return res.status(400).json({ error: “documentUrl (or documentId) is required” });

}

const canonicalUrl = isUrl(rawDoc)

? String(rawDoc)

: `https://docs.google.com/spreadsheets/d/${extractSheetId(rawDoc)}/edit\`;

const sheetName = parseSheetNameFromRange(range);

// — load workflow & webhook —

const wf = await getWorkflow(id);

const webhookNode =

wf.nodes.find((n) => n.type === “n8n-nodes-base.webhook”) ||

wf.nodes.find((n) => n.name === “Webhook”);

if (!webhookNode) {

return res.status(400).json({ error: “No Webhook node found in this workflow” });

}

// — Option A: Modern UI, no list lookups (sheet by name) —

// Variant 1: nested objects (many builds accept this)

const paramsModernV1 = {

resource: “sheetWithinDocument”,

operation: “appendRow”,

document: { mode: “url”, value: canonicalUrl }, // Document → By URL

sheet: { mode: “name”, value: sheetName }, // Sheet → By name (e.g., “Sheet1”)

columnsMode: “mapEachColumnManually”,

columnsUi: {

column: Object.entries(headerMapping).map(([header, key]) => ({

column: header,

value: `={{$json[“${key}”]}}`,

    })),

  },

options: {},

};

// Variant 2: flattened keys (other builds prefer this)

const paramsModernV2 = {

resource: “sheetWithinDocument”,

operation: “appendRow”,

documentMode: “url”,

documentUrl: canonicalUrl,

sheetMode: “name”,

sheetName,

columnsMode: “mapEachColumnManually”,

columnsUi: {

column: Object.entries(headerMapping).map(([header, key]) => ({

column: header,

value: `={{$json[“${key}”]}}`,

    })),

  },

options: {},

};

// — v4 fallback (kept for resilience; targets Sheet1 via range) —

const paramsV4 = {

operation: “append”,

documentId: canonicalUrl, // URL works in many n8n builds; bare ID works as well

range, // e.g., “Sheet1!A:Z”

valueInputMode: “RAW”,

fieldsUi: {

field: Object.entries(mapping).map(([col, key]) => ({

column: col,

value: `={{$json[“${key}”]}}`,

    })),

  },

};

// — add/update node helper —

const isSheets = (n) => n.type === “n8n-nodes-base.googleSheets”;

const existingIdx = wf.nodes.findIndex(isSheets);

const willUpdate = existingIdx >= 0;

const baseNode = {

id: willUpdate ? wf.nodes[existingIdx].id : `${nodeName.replace(/\s+/g, “_”)}_${Date.now()}`,

name: willUpdate ? wf.nodes[existingIdx].name : nodeName,

type: “n8n-nodes-base.googleSheets”,

typeVersion: 6, // force modern for Option A

position: willUpdate

? wf.nodes[existingIdx].position

: [(webhookNode.position?.[0] ?? 260) + 300, webhookNode.position?.[1] ?? 300],

…(googleCredentialId

? { credentials: { googleSheetsOAuth2Api: { id: Number(googleCredentialId) } } }

: {}),

};

const putWithParams = async (parameters, forceTypeVersion) => {

const node = {

…baseNode,

…(forceTypeVersion ? { typeVersion: forceTypeVersion } : {}),

parameters,

  };

let nodes = wf.nodes.slice();

if (willUpdate) nodes[existingIdx] = { …wf.nodes[existingIdx], …node };

else nodes = […nodes, node];

const connections = { …(wf.connections || {}) };

const fromKey = webhookNode.name;

if (!connections[fromKey]) connections[fromKey] = { main: [[]] };

if (!connections[fromKey].main) connections[fromKey].main = [[]];

const targetName = willUpdate ? nodes[existingIdx].name : node.name;

const already = connections[fromKey].main[0].some((e) => e.node === targetName);

if (!already) connections[fromKey].main[0].push({ node: targetName, type: “main”, index: 0 });

const updated = await updateWorkflow(id, {

name: wf.name,

nodes,

connections,

settings: wf.settings || {},

staticData: wf.staticData || {},

  });

return { updated, targetName };

};

// Try modern (V1), then modern (V2); finally v4 as safety

let out, mode = “modern”;

try {

out = await putWithParams(paramsModernV1, 6);

} catch {

try {

out = await putWithParams(paramsModernV2, 6);

  } catch {

mode = “v4”;

out = await putWithParams(paramsV4, 4);

  }

}

return res.json({

workflowId: out.updated.id,

nodeName: out.targetName,

modeUsed: mode, // “modern” or “v4”

sheetName,

usedDocumentUrl: canonicalUrl,

});

} catch (e) {

console.error(e);

res.status(500).json({ error: String(e.message || e) });

}

});

HERE. , all configurations are getting except that document mode needs to select as By URL