This workflow corresponds to n8n.io template #13712 — we link there as the canonical source.
This workflow follows the Gmail → Google Sheets recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"id": "NEImGuMG5P2Ug9KMWyn0R",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "LINE OA \u2013 Stateless Email Approval for Orders",
"tags": [],
"nodes": [
{
"id": "6eab5afa-3308-47c6-8914-d24128b8d30c",
"name": "Overview",
"type": "n8n-nodes-base.stickyNote",
"position": [
25664,
8608
],
"parameters": {
"width": 540,
"height": 776,
"content": "## How it works\n1. **Google Sheets Trigger** fires when a new row is added to your Orders sheet\n2. **New order?** skips rows that already have a status \u2014 preventing duplicate processing\n3. **Config** node sets your reviewer email, LINE User ID, approval threshold, and n8n webhook base URL\n4. A unique approval token is generated and written back to the sheet row\n5. If the order amount is **below** the threshold \u2192 auto-approved immediately with a LINE push notification\n6. If the amount **meets** the threshold \u2192 an HTML email is sent with independent Approve / Reject links (one email per order, no blocking)\n7. **Webhook B** handles each link click: validates the token, checks the order has not already been reviewed, then updates Google Sheets and sends a LINE push notification\n\n## Setup steps\n1. Create a **Google Sheets OAuth2** credential \u2192 assign to the *Google Sheets Trigger* node\n2. Create a **Google Sheets Service Account** credential \u2192 assign to all other Google Sheets nodes\n3. Update `YOUR_GOOGLE_SHEET_URL` in every Google Sheets node with your spreadsheet URL\n4. Create a **Gmail OAuth2** credential \u2192 assign to the *Send Approval Request* node\n5. Create an **HTTP Bearer Auth** credential with your LINE Channel Access Token \u2192 assign to both LINE Push nodes\n6. In the **Config** node: set `approverEmail`, `lineUserId`, `approvalThreshold`, and `n8nWebhookBaseUrl` (e.g. `https://your-domain.zeabur.app/webhook` or `https://your-instance.app.n8n.cloud/webhook`)\n7. Required sheet columns: `orderId` \u00b7 `customerName` \u00b7 `amount` \u00b7 `approvalToken` \u00b7 `status`\n8. Activate the workflow \u2014 it will automatically process new rows and send LINE notifications"
},
"typeVersion": 1
},
{
"id": "4dd06ec8-aa34-49c6-94bb-907d2b139063",
"name": "Section A",
"type": "n8n-nodes-base.stickyNote",
"position": [
26272,
8832
],
"parameters": {
"color": 7,
"width": 1160,
"height": 168,
"content": "## \u26a1 Section A \u2013 Order Intake\n\nPolls Google Sheets every minute. Fires for new rows only. Skips rows that already have a `status` value to prevent duplicate processing. Generates a unique approval token per order."
},
"typeVersion": 1
},
{
"id": "85d8890d-d80b-48d3-9340-8111af285b71",
"name": "Email Path",
"type": "n8n-nodes-base.stickyNote",
"position": [
27568,
8640
],
"parameters": {
"color": 7,
"width": 700,
"height": 380,
"content": "## \ud83d\udce7 Email Review Path\n\nAmount meets the threshold. Generates independent Approve / Reject links (orderId + token), marks the row as Pending, and emails the reviewer. Each email is stateless \u2014 multiple orders can wait for review simultaneously."
},
"typeVersion": 1
},
{
"id": "ff49d8f6-4977-4974-970d-cc2c86b787bb",
"name": "Auto Path",
"type": "n8n-nodes-base.stickyNote",
"position": [
27584,
9088
],
"parameters": {
"color": 7,
"width": 620,
"height": 312,
"content": "## \u2705 Auto-Approve Path\n\nAmount is below threshold. Marks the order as Auto-Approved immediately and sends a LINE push notification. No email required."
},
"typeVersion": 1
},
{
"id": "73737ef4-9a99-41bd-8fef-31c14f783db4",
"name": "Section B",
"type": "n8n-nodes-base.stickyNote",
"position": [
26272,
9408
],
"parameters": {
"color": 7,
"width": 780,
"height": 168,
"content": "## \ud83d\udd17 Section B \u2013 Approval Handler\n\nTriggered when a reviewer clicks Approve or Reject in the email. Validates the one-time token, checks the order has not already been processed, then routes to approve or reject. Each click runs as a fully independent execution."
},
"typeVersion": 1
},
{
"id": "f99fc61f-88e4-49ab-8915-43e1513b6e3d",
"name": "Approval Actions",
"type": "n8n-nodes-base.stickyNote",
"position": [
27600,
9424
],
"parameters": {
"color": 7,
"width": 876,
"height": 568,
"content": "## \ud83c\udfc1 Approval Actions\n\nUpdates Google Sheets with the final status (Approved / Rejected), records a `reviewedAt` timestamp, then sends a LINE push notification. Returns an HTML confirmation page to the reviewer's browser."
},
"typeVersion": 1
},
{
"id": "ac6e3843-0468-45d1-8061-e5ae5b7d25f8",
"name": "Sticky Note \u2013 More Templates",
"type": "n8n-nodes-base.stickyNote",
"position": [
28512,
9600
],
"parameters": {
"color": 7,
"width": 412,
"height": 240,
"content": "## Want more automation templates?\n\nCheck out the **Content Automation Bundle** \u2014 6 n8n workflows for AI content creation, social media publishing, and Threads analytics.\n\n\ud83d\udc49 https://jasonchuang0818.gumroad.com/l/n8n-content-automation-bundle"
},
"typeVersion": 1
},
{
"id": "b4cead6b-f732-4dc6-97f3-5c90abd6c152",
"name": "Google Sheets Trigger",
"type": "n8n-nodes-base.googleSheetsTrigger",
"position": [
26288,
9040
],
"parameters": {
"event": "rowAdded",
"options": {},
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "YOUR_GOOGLE_SHEET_URL",
"cachedResultName": "Orders"
},
"documentId": {
"__rl": true,
"mode": "url",
"value": "YOUR_GOOGLE_SHEET_URL"
}
},
"typeVersion": 1
},
{
"id": "39f1f5af-62b1-4efb-b607-e3a5a69e4cc3",
"name": "New order?",
"type": "n8n-nodes-base.if",
"position": [
26512,
9040
],
"parameters": {
"conditions": {
"string": [
{
"value1": "={{ $json.status }}",
"operation": "isEmpty"
}
]
}
},
"typeVersion": 1
},
{
"id": "46fd5076-d227-4411-a505-b8a9c2f80c1d",
"name": "Config",
"type": "n8n-nodes-base.set",
"position": [
26736,
9040
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "cfg-01",
"name": "approverEmail",
"type": "string",
"value": "user@example.com"
},
{
"id": "cfg-02",
"name": "lineUserId",
"type": "string",
"value": "YOUR_LINE_USER_ID"
},
{
"id": "cfg-03",
"name": "approvalThreshold",
"type": "number",
"value": 500
},
{
"id": "cfg-04",
"name": "n8nWebhookBaseUrl",
"type": "string",
"value": "https://YOUR_N8N_DOMAIN/webhook"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "53714208-292c-4f6f-80fc-207f317ee83f",
"name": "Prepare Order Data",
"type": "n8n-nodes-base.set",
"position": [
26944,
9040
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "prep-01",
"name": "orderId",
"type": "string",
"value": "={{ $('Google Sheets Trigger').item.json.orderId }}"
},
{
"id": "prep-02",
"name": "customerName",
"type": "string",
"value": "={{ $('Google Sheets Trigger').item.json.customerName }}"
},
{
"id": "prep-03",
"name": "amount",
"type": "number",
"value": "={{ $('Google Sheets Trigger').item.json.amount }}"
},
{
"id": "prep-04",
"name": "approverEmail",
"type": "string",
"value": "={{ $json.approverEmail }}"
},
{
"id": "prep-05",
"name": "lineUserId",
"type": "string",
"value": "={{ $json.lineUserId }}"
},
{
"id": "prep-06",
"name": "approvalThreshold",
"type": "number",
"value": "={{ $json.approvalThreshold }}"
},
{
"id": "prep-07",
"name": "n8nWebhookBaseUrl",
"type": "string",
"value": "={{ $json.n8nWebhookBaseUrl }}"
},
{
"id": "prep-08",
"name": "approvalToken",
"type": "string",
"value": "={{ $('Google Sheets Trigger').item.json.orderId + '-' + $now.toMillis() }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "c85d872c-7c8d-4756-b619-956ab72fd700",
"name": "Write Approval Token",
"type": "n8n-nodes-base.googleSheets",
"position": [
27168,
9040
],
"parameters": {
"columns": {
"value": {
"orderId": "={{ $json.orderId }}",
"approvalToken": "={{ $json.approvalToken }}"
},
"schema": [
{
"id": "orderId",
"type": "string",
"display": true,
"required": false,
"displayName": "orderId",
"defaultMatch": true,
"canBeUsedToMatch": true
},
{
"id": "approvalToken",
"type": "string",
"display": true,
"required": false,
"displayName": "approvalToken",
"defaultMatch": false,
"canBeUsedToMatch": false
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"orderId"
]
},
"options": {},
"operation": "update",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "Orders"
},
"documentId": {
"__rl": true,
"mode": "url",
"value": "YOUR_GOOGLE_SHEET_URL"
},
"authentication": "serviceAccount"
},
"typeVersion": 4.4
},
{
"id": "df92024d-4e3b-4dde-94f3-883599efa579",
"name": "Needs Approval?",
"type": "n8n-nodes-base.if",
"position": [
27392,
9040
],
"parameters": {
"conditions": {
"number": [
{
"value1": "={{ $('Prepare Order Data').item.json.amount }}",
"value2": "={{ $('Prepare Order Data').item.json.approvalThreshold }}",
"operation": "largerEqual"
}
]
}
},
"typeVersion": 1
},
{
"id": "9525d87e-1d7c-48d8-a6c3-f278635b5286",
"name": "Build Approval Links",
"type": "n8n-nodes-base.set",
"position": [
27616,
8832
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "link-01",
"name": "approveUrl",
"type": "string",
"value": "={{ $('Prepare Order Data').item.json.n8nWebhookBaseUrl + '/order-approval?orderId=' + $('Prepare Order Data').item.json.orderId + '&action=approve&token=' + $('Prepare Order Data').item.json.approvalToken }}"
},
{
"id": "link-02",
"name": "rejectUrl",
"type": "string",
"value": "={{ $('Prepare Order Data').item.json.n8nWebhookBaseUrl + '/order-approval?orderId=' + $('Prepare Order Data').item.json.orderId + '&action=reject&token=' + $('Prepare Order Data').item.json.approvalToken }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "a3b43dda-bc7f-43ae-9b20-ff6914f975cc",
"name": "Mark as Pending Approval",
"type": "n8n-nodes-base.googleSheets",
"position": [
27824,
8832
],
"parameters": {
"columns": {
"value": {
"status": "\u23f3 Pending Approval",
"orderId": "={{ $('Prepare Order Data').item.json.orderId }}"
},
"schema": [
{
"id": "orderId",
"type": "string",
"display": true,
"required": false,
"displayName": "orderId",
"defaultMatch": true,
"canBeUsedToMatch": true
},
{
"id": "status",
"type": "string",
"display": true,
"required": false,
"displayName": "status",
"defaultMatch": false,
"canBeUsedToMatch": false
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"orderId"
]
},
"options": {},
"operation": "update",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "Orders"
},
"documentId": {
"__rl": true,
"mode": "url",
"value": "YOUR_GOOGLE_SHEET_URL"
},
"authentication": "serviceAccount"
},
"typeVersion": 4.4
},
{
"id": "3a4c6ca1-1ef5-413a-b409-e63f29dac9e3",
"name": "Send Approval Request",
"type": "n8n-nodes-base.gmail",
"position": [
28048,
8832
],
"parameters": {
"sendTo": "={{ $('Prepare Order Data').item.json.approverEmail }}",
"message": "=<h2 style=\"color:#1a1a1a\">Order Approval Required</h2>\n<p>The following order exceeds your approval threshold and requires your review:</p>\n<table style=\"border-collapse:collapse;margin-bottom:16px\">\n <tr><td style=\"padding:6px 16px 6px 0;color:#666\"><b>Order ID</b></td><td>#{{ $('Prepare Order Data').item.json.orderId }}</td></tr>\n <tr><td style=\"padding:6px 16px 6px 0;color:#666\"><b>Customer</b></td><td>{{ $('Prepare Order Data').item.json.customerName }}</td></tr>\n <tr><td style=\"padding:6px 16px 6px 0;color:#666\"><b>Amount</b></td><td><strong>${{ $('Prepare Order Data').item.json.amount }}</strong></td></tr>\n</table>\n<p>\n <a href=\"{{ $('Prepare Order Data').item.json.n8nWebhookBaseUrl }}/order-approval?orderId={{ $('Prepare Order Data').item.json.orderId }}&action=approve&token={{ $('Prepare Order Data').item.json.approvalToken }}\" style=\"background:#22c55e;color:white;padding:12px 28px;text-decoration:none;border-radius:6px;font-weight:bold;display:inline-block\">\u2705 Approve</a>\n \n <a href=\"{{ $('Prepare Order Data').item.json.n8nWebhookBaseUrl }}/order-approval?orderId={{ $('Prepare Order Data').item.json.orderId }}&action=reject&token={{ $('Prepare Order Data').item.json.approvalToken }}\" style=\"background:#ef4444;color:white;padding:12px 28px;text-decoration:none;border-radius:6px;font-weight:bold;display:inline-block\">\u274c Reject</a>\n</p>\n<p style=\"color:#999;font-size:12px\">Each link can only be used once. Multiple orders can be reviewed independently \u2014 clicking one link does not affect any others.</p>",
"options": {},
"subject": "=\ud83d\udd14 Approval Required: Order #{{ $('Prepare Order Data').item.json.orderId }} \u2014 ${{ $('Prepare Order Data').item.json.amount }}"
},
"typeVersion": 2.1,
"continueOnFail": true
},
{
"id": "fbdf392e-2014-45d7-b032-2dcd80e41822",
"name": "Mark as Auto-Approved",
"type": "n8n-nodes-base.googleSheets",
"position": [
27616,
9232
],
"parameters": {
"columns": {
"value": {
"status": "\u2705 Auto-Approved",
"orderId": "={{ $('Prepare Order Data').item.json.orderId }}"
},
"schema": [
{
"id": "orderId",
"type": "string",
"display": true,
"required": false,
"displayName": "orderId",
"defaultMatch": true,
"canBeUsedToMatch": true
},
{
"id": "status",
"type": "string",
"display": true,
"required": false,
"displayName": "status",
"defaultMatch": false,
"canBeUsedToMatch": false
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"orderId"
]
},
"options": {},
"operation": "update",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "Orders"
},
"documentId": {
"__rl": true,
"mode": "url",
"value": "YOUR_GOOGLE_SHEET_URL"
},
"authentication": "serviceAccount"
},
"typeVersion": 4.4
},
{
"id": "684ce42a-21a1-4279-b963-77826f2bb11b",
"name": "Prepare LINE Notification (Auto)",
"type": "n8n-nodes-base.set",
"position": [
27824,
9232
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "line-auto-01",
"name": "lineMessage",
"type": "string",
"value": "=\u2705 Order auto-approved!\nOrder: #{{ $('Prepare Order Data').item.json.orderId }}\nCustomer: {{ $('Prepare Order Data').item.json.customerName }}\nAmount: ${{ $('Prepare Order Data').item.json.amount }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "c96e6c80-c9aa-4982-93f3-a391a0980bb3",
"name": "LINE Push \u2013 Auto Approved",
"type": "n8n-nodes-base.httpRequest",
"position": [
28048,
9232
],
"parameters": {
"url": "https://api.line.me/v2/bot/message/push",
"body": "={{ JSON.stringify({ to: $('Prepare Order Data').item.json.lineUserId, messages: [{ type: 'text', text: $json.lineMessage }] }) }}",
"method": "POST",
"options": {},
"sendBody": true,
"contentType": "raw",
"authentication": "genericCredentialType",
"rawContentType": "application/json",
"genericAuthType": "httpBearerAuth"
},
"typeVersion": 4.2,
"continueOnFail": true
},
{
"id": "00faff5c-fc18-4d61-92f2-0a81d969e569",
"name": "Webhook \u2013 Approval Handler",
"type": "n8n-nodes-base.webhook",
"position": [
26288,
9616
],
"parameters": {
"path": "order-approval",
"options": {},
"responseMode": "responseNode"
},
"typeVersion": 2
},
{
"id": "25532be8-3a02-4255-a0d6-51a9eb80a188",
"name": "Extract Approval Params",
"type": "n8n-nodes-base.set",
"position": [
26512,
9616
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "ext-01",
"name": "orderId",
"type": "string",
"value": "={{ $json.query.orderId }}"
},
{
"id": "ext-02",
"name": "action",
"type": "string",
"value": "={{ $json.query.action }}"
},
{
"id": "ext-03",
"name": "webhookToken",
"type": "string",
"value": "={{ $json.query.token }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "f3ffa329-3af4-4ee6-a892-46505e36e32f",
"name": "Read Order from Sheets",
"type": "n8n-nodes-base.googleSheets",
"position": [
26736,
9616
],
"parameters": {
"options": {},
"filtersUI": {
"values": [
{
"lookupValue": "={{ $json.orderId }}",
"lookupColumn": "orderId"
}
]
},
"sheetName": {
"__rl": true,
"mode": "name",
"value": "Orders"
},
"documentId": {
"__rl": true,
"mode": "url",
"value": "YOUR_GOOGLE_SHEET_URL"
},
"authentication": "serviceAccount"
},
"typeVersion": 4.4
},
{
"id": "b578a9c1-0b51-4aef-b7cf-c70a73492837",
"name": "Token Valid?",
"type": "n8n-nodes-base.if",
"position": [
26944,
9616
],
"parameters": {
"conditions": {
"string": [
{
"value1": "={{ $json.approvalToken }}",
"value2": "={{ $('Extract Approval Params').item.json.webhookToken }}"
}
]
}
},
"typeVersion": 1
},
{
"id": "855ef536-e483-4db3-b969-1c2160aed595",
"name": "Respond \u2013 Invalid Token",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
27168,
9808
],
"parameters": {
"options": {
"responseCode": 403,
"responseHeaders": {
"entries": [
{
"name": "Content-Type",
"value": "text/html"
}
]
}
},
"respondWith": "text",
"responseBody": "<!DOCTYPE html><html><body style=\"font-family:sans-serif;text-align:center;padding:60px\"><h2 style=\"color:#ef4444\">\u274c Invalid or Expired Link</h2><p>This approval link is invalid. Please contact your administrator.</p></body></html>"
},
"typeVersion": 1.1
},
{
"id": "4c7ab7e6-1686-4f72-b711-3a497a9caf2f",
"name": "Already Processed?",
"type": "n8n-nodes-base.if",
"position": [
27168,
9616
],
"parameters": {
"conditions": {
"string": [
{
"value1": "={{ $json.status }}",
"value2": "\u23f3 Pending Approval",
"operation": "notEqual"
}
]
}
},
"typeVersion": 1
},
{
"id": "5be37fa2-70c9-4c8a-b00e-8343689996e6",
"name": "Respond \u2013 Already Processed",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
27408,
9440
],
"parameters": {
"options": {
"responseCode": 200,
"responseHeaders": {
"entries": [
{
"name": "Content-Type",
"value": "text/html"
}
]
}
},
"respondWith": "text",
"responseBody": "=<!DOCTYPE html><html><body style=\"font-family:sans-serif;text-align:center;padding:60px\"><h2 style=\"color:#f59e0b\">\u26a0\ufe0f Already Processed</h2><p>Order #{{ $('Extract Approval Params').item.json.orderId }} has already been reviewed.</p><p>Current status: <strong>{{ $json.status }}</strong></p></body></html>"
},
"typeVersion": 1.1
},
{
"id": "d1b28499-b7eb-485a-8ca8-ec11208cbe8a",
"name": "Approve or Reject?",
"type": "n8n-nodes-base.if",
"position": [
27392,
9616
],
"parameters": {
"conditions": {
"string": [
{
"value1": "={{ $('Extract Approval Params').item.json.action }}",
"value2": "approve"
}
]
}
},
"typeVersion": 1
},
{
"id": "7b4adf5f-f036-4c9d-8b4a-ad341d96e16c",
"name": "Mark as Approved",
"type": "n8n-nodes-base.googleSheets",
"position": [
27632,
9600
],
"parameters": {
"columns": {
"value": {
"status": "\u2705 Approved",
"orderId": "={{ $json.orderId }}",
"reviewedAt": "={{ $now.toFormat('yyyy-MM-dd HH:mm:ss') }}"
},
"schema": [
{
"id": "orderId",
"type": "string",
"display": true,
"required": false,
"displayName": "orderId",
"defaultMatch": true,
"canBeUsedToMatch": true
},
{
"id": "status",
"type": "string",
"display": true,
"required": false,
"displayName": "status",
"defaultMatch": false,
"canBeUsedToMatch": false
},
{
"id": "reviewedAt",
"type": "string",
"display": true,
"required": false,
"displayName": "reviewedAt",
"defaultMatch": false,
"canBeUsedToMatch": false
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"orderId"
]
},
"options": {},
"operation": "update",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "Orders"
},
"documentId": {
"__rl": true,
"mode": "url",
"value": "YOUR_GOOGLE_SHEET_URL"
},
"authentication": "serviceAccount"
},
"typeVersion": 4.4
},
{
"id": "779352ed-2b2e-427d-abe4-48bf86fb5a8d",
"name": "Prepare LINE Notification (Approved)",
"type": "n8n-nodes-base.set",
"position": [
27840,
9600
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "line-appr-01",
"name": "lineMessage",
"type": "string",
"value": "=\u2705 Order approved!\nOrder: #{{ $json.orderId }}\nCustomer: {{ $json.customerName }}\nAmount: ${{ $json.amount }}"
},
{
"id": "line-appr-02",
"name": "lineUserId",
"type": "string",
"value": "={{ $json.lineUserId }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "3f6148b2-e810-4704-ae6e-9c0a17684420",
"name": "LINE Push \u2013 Approved",
"type": "n8n-nodes-base.httpRequest",
"position": [
28064,
9600
],
"parameters": {
"url": "https://api.line.me/v2/bot/message/push",
"body": "={{ JSON.stringify({ to: $json.lineUserId, messages: [{ type: 'text', text: $json.lineMessage }] }) }}",
"method": "POST",
"options": {},
"sendBody": true,
"contentType": "raw",
"authentication": "genericCredentialType",
"rawContentType": "application/json",
"genericAuthType": "httpBearerAuth"
},
"typeVersion": 4.2,
"continueOnFail": true
},
{
"id": "6e08156e-62f0-4dd6-b9eb-3bf72e198209",
"name": "Respond \u2013 Order Approved",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
28288,
9600
],
"parameters": {
"options": {
"responseCode": 200,
"responseHeaders": {
"entries": [
{
"name": "Content-Type",
"value": "text/html"
}
]
}
},
"respondWith": "text",
"responseBody": "=<!DOCTYPE html><html><body style=\"font-family:sans-serif;text-align:center;padding:60px\"><h2 style=\"color:#22c55e\">\u2705 Order Approved</h2><p>Order #{{ $('Read Order from Sheets').item.json.orderId }} has been approved.</p><p>A LINE notification has been sent.</p></body></html>"
},
"typeVersion": 1.1
},
{
"id": "90f76d61-27bc-456b-bc3f-067ad2f534ba",
"name": "Mark as Rejected",
"type": "n8n-nodes-base.googleSheets",
"position": [
27632,
9808
],
"parameters": {
"columns": {
"value": {
"status": "\u274c Rejected",
"orderId": "={{ $json.orderId }}",
"reviewedAt": "={{ $now.toFormat('yyyy-MM-dd HH:mm:ss') }}"
},
"schema": [
{
"id": "orderId",
"type": "string",
"display": true,
"required": false,
"displayName": "orderId",
"defaultMatch": true,
"canBeUsedToMatch": true
},
{
"id": "status",
"type": "string",
"display": true,
"required": false,
"displayName": "status",
"defaultMatch": false,
"canBeUsedToMatch": false
},
{
"id": "reviewedAt",
"type": "string",
"display": true,
"required": false,
"displayName": "reviewedAt",
"defaultMatch": false,
"canBeUsedToMatch": false
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"orderId"
]
},
"options": {},
"operation": "update",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "Orders"
},
"documentId": {
"__rl": true,
"mode": "url",
"value": "YOUR_GOOGLE_SHEET_URL"
},
"authentication": "serviceAccount"
},
"typeVersion": 4.4
},
{
"id": "9cfedf3f-f33e-4051-892c-20572209b68d",
"name": "Prepare LINE Notification (Rejected)",
"type": "n8n-nodes-base.set",
"position": [
27840,
9808
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "line-rej-01",
"name": "lineMessage",
"type": "string",
"value": "=\u274c Order rejected.\nOrder: #{{ $('Read Order from Sheets').item.json.orderId }}\nCustomer: {{ $('Read Order from Sheets').item.json.customerName }}\nAmount: ${{ $('Read Order from Sheets').item.json.amount }}"
},
{
"id": "line-rej-02",
"name": "lineUserId",
"type": "string",
"value": "={{ $('Read Order from Sheets').item.json.lineUserId }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "f4313550-714b-4eb8-9a53-3c0b6ad02529",
"name": "LINE Push \u2013 Rejected",
"type": "n8n-nodes-base.httpRequest",
"position": [
28064,
9808
],
"parameters": {
"url": "https://api.line.me/v2/bot/message/push",
"body": "={{ JSON.stringify({ to: $json.lineUserId, messages: [{ type: 'text', text: $json.lineMessage }] }) }}",
"method": "POST",
"options": {},
"sendBody": true,
"contentType": "raw",
"authentication": "genericCredentialType",
"rawContentType": "application/json",
"genericAuthType": "httpBearerAuth"
},
"typeVersion": 4.2,
"continueOnFail": true
},
{
"id": "fc99ddab-933c-47bb-a923-7cf50ce07c5e",
"name": "Respond \u2013 Order Rejected",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
28288,
9808
],
"parameters": {
"options": {
"responseCode": 200,
"responseHeaders": {
"entries": [
{
"name": "Content-Type",
"value": "text/html"
}
]
}
},
"respondWith": "text",
"responseBody": "=<!DOCTYPE html><html><body style=\"font-family:sans-serif;text-align:center;padding:60px\"><h2 style=\"color:#ef4444\">\u274c Order Rejected</h2><p>Order #{{ $('Read Order from Sheets').item.json.orderId }} has been rejected.</p><p>The status has been updated in the system.</p></body></html>"
},
"typeVersion": 1.1
}
],
"active": false,
"settings": {
"timezone": "Asia/Taipei",
"binaryMode": "separate",
"callerPolicy": "workflowsFromSameOwner",
"timeSavedMode": "fixed",
"availableInMCP": false,
"executionOrder": "v1"
},
"versionId": "216415cf-cc14-4183-add9-a579bf83d47a",
"connections": {
"Config": {
"main": [
[
{
"node": "Prepare Order Data",
"type": "main",
"index": 0
}
]
]
},
"New order?": {
"main": [
[
{
"node": "Config",
"type": "main",
"index": 0
}
]
]
},
"Token Valid?": {
"main": [
[
{
"node": "Already Processed?",
"type": "main",
"index": 0
}
],
[
{
"node": "Respond \u2013 Invalid Token",
"type": "main",
"index": 0
}
]
]
},
"Needs Approval?": {
"main": [
[
{
"node": "Build Approval Links",
"type": "main",
"index": 0
}
],
[
{
"node": "Mark as Auto-Approved",
"type": "main",
"index": 0
}
]
]
},
"Mark as Approved": {
"main": [
[
{
"node": "Prepare LINE Notification (Approved)",
"type": "main",
"index": 0
}
]
]
},
"Mark as Rejected": {
"main": [
[
{
"node": "Prepare LINE Notification (Rejected)",
"type": "main",
"index": 0
}
]
]
},
"Already Processed?": {
"main": [
[
{
"node": "Respond \u2013 Already Processed",
"type": "main",
"index": 0
}
],
[
{
"node": "Approve or Reject?",
"type": "main",
"index": 0
}
]
]
},
"Approve or Reject?": {
"main": [
[
{
"node": "Mark as Approved",
"type": "main",
"index": 0
}
],
[
{
"node": "Mark as Rejected",
"type": "main",
"index": 0
}
]
]
},
"Prepare Order Data": {
"main": [
[
{
"node": "Write Approval Token",
"type": "main",
"index": 0
}
]
]
},
"Build Approval Links": {
"main": [
[
{
"node": "Mark as Pending Approval",
"type": "main",
"index": 0
}
]
]
},
"Write Approval Token": {
"main": [
[
{
"node": "Needs Approval?",
"type": "main",
"index": 0
}
]
]
},
"Google Sheets Trigger": {
"main": [
[
{
"node": "New order?",
"type": "main",
"index": 0
}
]
]
},
"Mark as Auto-Approved": {
"main": [
[
{
"node": "Prepare LINE Notification (Auto)",
"type": "main",
"index": 0
}
]
]
},
"LINE Push \u2013 Approved": {
"main": [
[
{
"node": "Respond \u2013 Order Approved",
"type": "main",
"index": 0
}
]
]
},
"LINE Push \u2013 Rejected": {
"main": [
[
{
"node": "Respond \u2013 Order Rejected",
"type": "main",
"index": 0
}
]
]
},
"Read Order from Sheets": {
"main": [
[
{
"node": "Token Valid?",
"type": "main",
"index": 0
}
]
]
},
"Extract Approval Params": {
"main": [
[
{
"node": "Read Order from Sheets",
"type": "main",
"index": 0
}
]
]
},
"Mark as Pending Approval": {
"main": [
[
{
"node": "Send Approval Request",
"type": "main",
"index": 0
}
]
]
},
"Webhook \u2013 Approval Handler": {
"main": [
[
{
"node": "Extract Approval Params",
"type": "main",
"index": 0
}
]
]
},
"Prepare LINE Notification (Auto)": {
"main": [
[
{
"node": "LINE Push \u2013 Auto Approved",
"type": "main",
"index": 0
}
]
]
},
"Prepare LINE Notification (Approved)": {
"main": [
[
{
"node": "LINE Push \u2013 Approved",
"type": "main",
"index": 0
}
]
]
},
"Prepare LINE Notification (Rejected)": {
"main": [
[
{
"node": "LINE Push \u2013 Rejected",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Automatically processes new orders added to Google Sheets. Small orders are approved instantly; large orders trigger an HTML email with one-click Approve / Reject links — each handled by an independent webhook, so multiple orders can be reviewed simultaneously without blocking…
Source: https://n8n.io/workflows/13712/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Hiring teams often struggle with document follow-ups, offer letter generation, and stakeholder communication. Manual checks, email back-and-forth, and missing files slow down hiring and create chaos d
This workflow turns a Google Sheet into an automated certificate-issuing pipeline. The moment a new completion row is added — whether by your training team, a Zap, or a quiz platform — a branded PDF/A
This workflow automates the end-to-end process of generating and sending client payment links using Google Sheets and Stripe.
This template is ideal for HR teams, startup founders, operations leads, remote-first companies, and freelancers managing onboarding manually or across multiple tools.
Build a Phone Agent to qualify outbound leads and inbound calls with RetellAI -vide. Uses stickyNote, googleSheetsTrigger, twilio, httpRequest. Event-driven trigger; 18 nodes.