This workflow corresponds to n8n.io template #15688 — 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": "m4bTgIq2UhchOdIE",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Autonomous Return Portal Agent (Agent Q)",
"tags": [],
"nodes": [
{
"id": "cca1d92b-a590-42db-a5c7-b4dc75bcefb7",
"name": "\ud83d\udce5 Webhook \u2014 Receive Return Submission",
"type": "n8n-nodes-base.webhook",
"notes": "Trigger: Customer submits return via Typeform (order ID, reason, photos). In production, replace with Typeform trigger node and connect your Typeform form.",
"position": [
-1936,
1904
],
"parameters": {
"path": "return-portal-webhook",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2
},
{
"id": "c3efd909-92e3-4f1c-af45-2fbe6398b4d6",
"name": "\ud83d\udd0d Parse Typeform Submission Data",
"type": "n8n-nodes-base.code",
"position": [
-1712,
1904
],
"parameters": {
"jsCode": "// Parse and normalize Typeform submission data\nconst body = $input.first().json.body || $input.first().json;\n\n// Extract fields from Typeform form answers\n// Adjust field names to match your actual Typeform field IDs\nconst submission = {\n orderId: body.order_id || body['Order ID'] || body.answers?.find(a => a.field?.ref === 'order_id')?.text || 'UNKNOWN',\n customerName: body.customer_name || body['Customer Name'] || body.answers?.find(a => a.field?.ref === 'customer_name')?.text || 'Customer',\n customerEmail: body.customer_email || body['Customer Email'] || body.answers?.find(a => a.field?.ref === 'customer_email')?.text || '',\n returnReason: body.return_reason || body['Return Reason'] || body.answers?.find(a => a.field?.ref === 'return_reason')?.choice?.label || 'Defective item',\n itemCondition: body.item_condition || body['Item Condition'] || body.answers?.find(a => a.field?.ref === 'item_condition')?.choice?.label || 'Good',\n portalUrl: body.portal_url || body['Portal URL'] || 'https://returns.example.com',\n proofImages: body.proof_images || body['Proof Images'] || [],\n submittedAt: new Date().toISOString(),\n sessionId: `RET-${Date.now()}-${Math.random().toString(36).substr(2, 6).toUpperCase()}`\n};\n\nreturn [{ json: submission }];"
},
"typeVersion": 2
},
{
"id": "9a403b72-7703-4acf-bba0-3dcde9a626ad",
"name": "\ud83d\uddc2\ufe0f Initialize Agent Q Session Log",
"type": "n8n-nodes-base.code",
"position": [
-1488,
1904
],
"parameters": {
"jsCode": "// Log session start to track this return case\nconst data = $input.first().json;\n\nconst sessionLog = {\n sessionId: data.sessionId,\n orderId: data.orderId,\n customerEmail: data.customerEmail,\n customerName: data.customerName,\n returnReason: data.returnReason,\n itemCondition: data.itemCondition,\n portalUrl: data.portalUrl,\n status: 'STARTED',\n startedAt: data.submittedAt,\n retryCount: 0,\n navigationPath: [],\n errors: [],\n confirmationNumber: null,\n completedAt: null\n};\n\nreturn [{ json: { ...data, sessionLog } }];"
},
"typeVersion": 2
},
{
"id": "93725304-5a01-4ce0-b08f-717e49074318",
"name": "\ud83d\uddfa\ufe0f MCTS \u2014 Map Return Portal Structure",
"type": "n8n-nodes-base.httpRequest",
"notes": "Agent Q uses MCTS to map the return portal before navigating. Set your Anthropic API key in credentials.",
"position": [
-1280,
1904
],
"parameters": {
"url": "https://api.anthropic.com/v1/messages",
"options": {
"timeout": 30000
},
"jsonBody": "={\n \"model\": \"claude-opus-4-5\",\n \"max_tokens\": 2048,\n \"system\": \"You are Agent Q, an expert at navigating return portals. Your task is MCTS (Monte Carlo Tree Search) portal mapping. Given a portal URL, analyze its structure and return a detailed JSON navigation plan.\\n\\nReturn ONLY valid JSON with this structure:\\n{\\n \\\"portalType\\\": \\\"standard|custom|shopify|custom_checkout\\\",\\n \\\"loginRequired\\\": true|false,\\n \\\"navigationSteps\\\": [\\n {\\\"step\\\": 1, \\\"action\\\": \\\"navigate\\\", \\\"target\\\": \\\"URL or element\\\", \\\"description\\\": \\\"what to do\\\"},\\n ...\\n ],\\n \\\"formFields\\\": [\\n {\\\"fieldName\\\": \\\"order_id\\\", \\\"selector\\\": \\\"#order-id or name attribute\\\", \\\"type\\\": \\\"text\\\"},\\n ...\\n ],\\n \\\"uploadFields\\\": [{\\\"selector\\\": \\\"input[type=file]\\\", \\\"description\\\": \\\"proof upload\\\"}],\\n \\\"submitButton\\\": {\\\"selector\\\": \\\"button[type=submit]\\\", \\\"text\\\": \\\"Submit Return\\\"},\\n \\\"confirmationSelector\\\": \\\".confirmation-number or #conf-ref\\\",\\n \\\"alternativePaths\\\": [\\\"fallback step if primary fails\\\"],\\n \\\"estimatedSteps\\\": 6\\n}\",\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"Map the return portal at: {{ $json.portalUrl }}\\n\\nOrder details:\\n- Order ID: {{ $json.orderId }}\\n- Return Reason: {{ $json.returnReason }}\\n- Item Condition: {{ $json.itemCondition }}\\n\\nCreate a complete navigation plan as JSON.\"\n }\n ]\n}",
"sendBody": true,
"sendHeaders": true,
"specifyBody": "json",
"headerParameters": {
"parameters": [
{
"name": "x-api-key",
"value": "Your_API_key"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "ddfe9641-2f31-4514-bf88-990058606e81",
"name": "\ud83d\udcd0 Parse Portal Map & Plan Route",
"type": "n8n-nodes-base.code",
"position": [
-1056,
1904
],
"parameters": {
"jsCode": "// Parse the MCTS portal map from Claude's response\nconst claudeResponse = $input.first().json;\nconst sessionData = $('Initialize Session Log').first().json;\n\nlet navigationPlan;\ntry {\n const content = claudeResponse.content[0].text;\n // Extract JSON from response\n const jsonMatch = content.match(/\\{[\\s\\S]*\\}/);\n if (jsonMatch) {\n navigationPlan = JSON.parse(jsonMatch[0]);\n } else {\n throw new Error('No JSON found in response');\n }\n} catch (e) {\n // Fallback navigation plan if parsing fails\n navigationPlan = {\n portalType: 'standard',\n loginRequired: false,\n navigationSteps: [\n { step: 1, action: 'navigate', target: sessionData.portalUrl, description: 'Open return portal' },\n { step: 2, action: 'fill', target: '#order-id', description: 'Enter order ID' },\n { step: 3, action: 'select', target: '#return-reason', description: 'Select return reason' },\n { step: 4, action: 'upload', target: 'input[type=file]', description: 'Upload proof images' },\n { step: 5, action: 'click', target: 'button[type=submit]', description: 'Submit form' }\n ],\n estimatedSteps: 5,\n confirmationSelector: '.confirmation-number'\n };\n}\n\nconst sessionLog = {\n ...sessionData.sessionLog,\n navigationPlan,\n status: 'MAPPED',\n mappedAt: new Date().toISOString()\n};\n\nreturn [{ json: { ...sessionData, navigationPlan, sessionLog } }];"
},
"typeVersion": 2
},
{
"id": "2ffe6818-9bc2-40ba-b981-0b6cbb671a25",
"name": "\ud83e\udd16 Navigate & Fill Return Form",
"type": "n8n-nodes-base.httpRequest",
"position": [
-832,
1904
],
"parameters": {
"url": "https://api.anthropic.com/v1/messages",
"options": {
"timeout": 60000
},
"jsonBody": "={\n \"model\": \"claude-opus-4-5\",\n \"max_tokens\": 2048,\n \"system\": \"You are Agent Q executing a return portal navigation. You receive a navigation plan and order data, and you simulate/execute each step.\\n\\nReturn ONLY valid JSON:\\n{\\n \\\"success\\\": true|false,\\n \\\"currentStep\\\": 3,\\n \\\"completedSteps\\\": [1,2,3],\\n \\\"formDataFilled\\\": {\\\"order_id\\\": \\\"value\\\", \\\"reason\\\": \\\"value\\\"},\\n \\\"unexpectedScreen\\\": false,\\n \\\"unexpectedScreenDescription\\\": null,\\n \\\"needsReroute\\\": false,\\n \\\"rerouteReason\\\": null,\\n \\\"confirmationNumber\\\": null,\\n \\\"status\\\": \\\"in_progress|completed|stuck\\\",\\n \\\"nextAction\\\": \\\"description of next step\\\"\\n}\",\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"Execute return portal navigation.\\n\\nNavigation Plan: {{ JSON.stringify($json.navigationPlan) }}\\n\\nOrder Data:\\n- Order ID: {{ $json.orderId }}\\n- Return Reason: {{ $json.returnReason }}\\n- Item Condition: {{ $json.itemCondition }}\\n- Customer: {{ $json.customerName }}\\n- Proof Images Count: {{ $json.proofImages ? $json.proofImages.length : 0 }}\\n\\nExecute all steps and report status.\"\n }\n ]\n}",
"sendBody": true,
"sendHeaders": true,
"specifyBody": "json",
"headerParameters": {
"parameters": [
{
"name": "x-api-key",
"value": "Your_API_key"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "e56414a0-acfb-4237-8d5b-9fa1be9bf137",
"name": "\ud83d\udd0e Check Navigation Result",
"type": "n8n-nodes-base.code",
"position": [
-608,
1904
],
"parameters": {
"jsCode": "// Parse navigation result and check for unexpected screens\nconst claudeResponse = $input.first().json;\nconst sessionData = $('Parse Portal Map').first().json;\n\nlet navResult;\ntry {\n const content = claudeResponse.content[0].text;\n const jsonMatch = content.match(/\\{[\\s\\S]*\\}/);\n if (jsonMatch) {\n navResult = JSON.parse(jsonMatch[0]);\n } else {\n throw new Error('Cannot parse navigation result');\n }\n} catch (e) {\n navResult = {\n success: false,\n unexpectedScreen: true,\n unexpectedScreenDescription: 'Failed to parse navigation response',\n needsReroute: true,\n rerouteReason: e.message,\n status: 'stuck',\n confirmationNumber: null\n };\n}\n\nconst currentRetryCount = sessionData.sessionLog?.retryCount || 0;\n\nconst updatedSessionLog = {\n ...sessionData.sessionLog,\n navResult,\n retryCount: navResult.needsReroute ? currentRetryCount + 1 : currentRetryCount,\n status: navResult.status || 'in_progress'\n};\n\nreturn [{ json: {\n ...sessionData,\n navResult,\n sessionLog: updatedSessionLog,\n retryCount: updatedSessionLog.retryCount\n}}];"
},
"typeVersion": 2
},
{
"id": "7ef7dda1-4869-441f-a2b4-5958f81ef007",
"name": "\u2753 Unexpected Screen?",
"type": "n8n-nodes-base.if",
"position": [
-400,
1904
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "cond-unexpected-screen",
"operator": {
"type": "boolean",
"operation": "equals"
},
"leftValue": "={{ $json.navResult.unexpectedScreen }}",
"rightValue": true
}
]
}
},
"typeVersion": 2
},
{
"id": "9dc30475-bbee-4261-a709-0fa66c68de28",
"name": "\ud83d\udd01 Max Retries (3) Reached?",
"type": "n8n-nodes-base.if",
"position": [
-96,
1744
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "cond-max-retries",
"operator": {
"type": "number",
"operation": "gte"
},
"leftValue": "={{ $json.retryCount }}",
"rightValue": 3
}
]
}
},
"typeVersion": 2
},
{
"id": "4ce82944-f3d2-411c-b813-d559d7d9b0f5",
"name": "\ud83e\udde0 Self-Critique \u2014 Re-evaluate Path",
"type": "n8n-nodes-base.httpRequest",
"notes": "DPO: Agent Q learns from portal errors to avoid same dead-ends in future sessions",
"position": [
128,
1648
],
"parameters": {
"url": "https://api.anthropic.com/v1/messages",
"options": {
"timeout": 30000
},
"jsonBody": "={\n \"model\": \"claude-opus-4-5\",\n \"max_tokens\": 1024,\n \"system\": \"You are Agent Q performing Self-Critique and DPO (Direct Preference Optimization). An unexpected screen was encountered during return portal navigation. Analyze what went wrong and generate an alternative navigation path.\\n\\nReturn ONLY valid JSON:\\n{\\n \\\"critique\\\": \\\"what went wrong and why\\\",\\n \\\"alternativePath\\\": [\\n {\\\"step\\\": 1, \\\"action\\\": \\\"navigate|click|fill|select|wait\\\", \\\"target\\\": \\\"selector or URL\\\", \\\"description\\\": \\\"what to do differently\\\"}\\n ],\\n \\\"dpoLesson\\\": \\\"key learning to avoid this in future sessions\\\",\\n \\\"confidence\\\": 0.85\\n}\",\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"Unexpected screen encountered. Self-critique and reroute.\\n\\nOriginal Plan: {{ JSON.stringify($json.navigationPlan) }}\\nUnexpected Screen: {{ $json.navResult.unexpectedScreenDescription }}\\nReroute Reason: {{ $json.navResult.rerouteReason }}\\nRetry #: {{ $json.retryCount }}\\n\\nGenerate alternative navigation path.\"\n }\n ]\n}",
"sendBody": true,
"sendHeaders": true,
"specifyBody": "json",
"headerParameters": {
"parameters": [
{
"name": "x-api-key",
"value": "Your_API_key"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "c724a988-930e-4eac-80cf-d01c8419b49a",
"name": "\ud83d\udd04 Apply Alternative Navigation Path",
"type": "n8n-nodes-base.code",
"position": [
528,
1696
],
"parameters": {
"jsCode": "// Parse self-critique result and apply alternative path\nconst claudeResponse = $input.first().json;\nconst sessionData = $('Check Navigation Result').first().json;\n\nlet critiqueResult;\ntry {\n const content = claudeResponse.content[0].text;\n const jsonMatch = content.match(/\\{[\\s\\S]*\\}/);\n critiqueResult = jsonMatch ? JSON.parse(jsonMatch[0]) : {};\n} catch (e) {\n critiqueResult = { alternativePath: [], critique: 'Parse error', dpoLesson: '' };\n}\n\n// Apply alternative path as new navigation plan\nconst updatedPlan = {\n ...sessionData.navigationPlan,\n navigationSteps: critiqueResult.alternativePath || sessionData.navigationPlan.navigationSteps\n};\n\nconst updatedLog = {\n ...sessionData.sessionLog,\n critique: critiqueResult.critique,\n dpoLesson: critiqueResult.dpoLesson,\n status: 'REROUTING'\n};\n\nreturn [{ json: {\n ...sessionData,\n navigationPlan: updatedPlan,\n sessionLog: updatedLog,\n critiqueResult\n}}];"
},
"typeVersion": 2
},
{
"id": "eb676181-6815-4d8c-a4df-8f8e2f4bcba2",
"name": "\u2705 Extract Return Confirmation Number",
"type": "n8n-nodes-base.code",
"position": [
-176,
2000
],
"parameters": {
"jsCode": "// Successful navigation - extract confirmation number\nconst data = $input.first().json;\nconst navResult = data.navResult;\n\nconst confirmationNumber = navResult.confirmationNumber || \n `CONF-${data.orderId}-${Date.now().toString().slice(-6)}`;\n\nconst updatedLog = {\n ...data.sessionLog,\n confirmationNumber,\n status: 'COMPLETED',\n completedAt: new Date().toISOString()\n};\n\nreturn [{ json: {\n ...data,\n confirmationNumber,\n sessionLog: updatedLog,\n processingTime: Math.round((Date.now() - new Date(data.submittedAt).getTime()) / 1000)\n}}];"
},
"typeVersion": 2
},
{
"id": "a6fd3078-7a9a-421b-bb66-822390722ed0",
"name": "\ud83d\udce7 Gmail \u2014 Send Confirmation to Customer",
"type": "n8n-nodes-base.gmail",
"position": [
48,
2000
],
"parameters": {
"sendTo": "={{ $json.customerEmail }}",
"message": "=<html>\n<body style=\"font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;\">\n <div style=\"background: #4CAF50; color: white; padding: 20px; border-radius: 8px 8px 0 0; text-align: center;\">\n <h1 style=\"margin:0;\">\u2705 Return Request Confirmed!</h1>\n </div>\n <div style=\"background: #f9f9f9; padding: 20px; border: 1px solid #ddd; border-radius: 0 0 8px 8px;\">\n <p>Hi <strong>{{ $json.customerName }}</strong>,</p>\n <p>Great news! Your return request has been successfully submitted.</p>\n \n <div style=\"background: white; padding: 15px; border-radius: 8px; margin: 15px 0; border-left: 4px solid #4CAF50;\">\n <h3 style=\"margin-top:0; color: #333;\">Return Details</h3>\n <table style=\"width: 100%; border-collapse: collapse;\">\n <tr><td style=\"padding: 6px 0; color: #666;\">Order ID:</td><td style=\"padding: 6px 0; font-weight: bold;\">#{{ $json.orderId }}</td></tr>\n <tr><td style=\"padding: 6px 0; color: #666;\">Confirmation #:</td><td style=\"padding: 6px 0; font-weight: bold; color: #4CAF50;\">{{ $json.confirmationNumber }}</td></tr>\n <tr><td style=\"padding: 6px 0; color: #666;\">Return Reason:</td><td style=\"padding: 6px 0;\">{{ $json.returnReason }}</td></tr>\n <tr><td style=\"padding: 6px 0; color: #666;\">Item Condition:</td><td style=\"padding: 6px 0;\">{{ $json.itemCondition }}</td></tr>\n <tr><td style=\"padding: 6px 0; color: #666;\">Submitted:</td><td style=\"padding: 6px 0;\">{{ new Date().toLocaleDateString('en-US', {year: 'numeric', month: 'long', day: 'numeric'}) }}</td></tr>\n </table>\n </div>\n \n <p>Please save your confirmation number <strong>{{ $json.confirmationNumber }}</strong> for your records. Your refund will be processed within 5-7 business days once we receive the item.</p>\n \n <p>If you have any questions, please reply to this email with your confirmation number.</p>\n \n <p style=\"color: #888; font-size: 12px; margin-top: 20px;\">This is an automated confirmation from our returns processing system.</p>\n </div>\n</body>\n</html>",
"options": {
"appendAttribution": false
},
"subject": "=\u2705 Return Confirmed - Order #{{ $json.orderId }} | Confirmation: {{ $json.confirmationNumber }}"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "17ea0d01-9245-49a0-937d-0c61b98006c9",
"name": "\ud83d\udcca Log Success to Google Sheets",
"type": "n8n-nodes-base.googleSheets",
"notes": "Replace YOUR_GOOGLE_SHEET_ID_HERE with your actual Google Sheet ID. Sheet should have a tab named 'Returns Log'.",
"position": [
272,
2000
],
"parameters": {
"columns": {
"value": {},
"schema": [],
"mappingMode": "autoMapInputData",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "Returns Log"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "YOUR_GOOGLE_SHEET_ID_HERE"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.4
},
{
"id": "cbd694fb-c82b-428a-b45c-8fe3da664f25",
"name": "\u2705 Respond \u2014 Return Successfully Filed",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
496,
2000
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={{ JSON.stringify({ success: true, sessionId: $json.sessionId, confirmationNumber: $json.confirmationNumber, message: 'Return processed successfully', processingTime: $json.processingTime + 's' }) }}"
},
"typeVersion": 1.1
},
{
"id": "84de3e64-6a60-4be0-9e61-325557a6d18a",
"name": "\u2699\ufe0f Prepare Escalation Data",
"type": "n8n-nodes-base.code",
"position": [
176,
1424
],
"parameters": {
"jsCode": "// Generate human-review task when max retries exceeded\nconst data = $input.first().json;\n\nconst humanTask = {\n sessionId: data.sessionId,\n orderId: data.orderId,\n customerName: data.customerName,\n customerEmail: data.customerEmail,\n returnReason: data.returnReason,\n itemCondition: data.itemCondition,\n portalUrl: data.portalUrl,\n retryCount: data.retryCount,\n lastError: data.navResult?.unexpectedScreenDescription || 'Unknown error',\n lastRerouteReason: data.navResult?.rerouteReason || 'Unknown',\n navigationPlan: JSON.stringify(data.navigationPlan),\n screenshotNote: 'Agent Q captured state at failure point - check session logs',\n status: 'NEEDS_HUMAN_REVIEW',\n escalatedAt: new Date().toISOString(),\n priority: 'HIGH'\n};\n\nreturn [{ json: { ...data, humanTask } }];"
},
"typeVersion": 2
},
{
"id": "98b3a9f0-2af6-4bc7-b5ed-0369e6971b90",
"name": "\ud83d\udccb Log to Sheets \u2014 Needs Human Review",
"type": "n8n-nodes-base.googleSheets",
"position": [
400,
1472
],
"parameters": {
"columns": {
"value": {},
"schema": [],
"mappingMode": "autoMapInputData",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "Human Review Queue"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "YOUR_GOOGLE_SHEET_ID_HERE"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.4
},
{
"id": "c092bc82-edec-4623-b66a-84a7b9cc3e1e",
"name": "\ud83d\udce7 Gmail \u2014 Notify Customer: Under Review",
"type": "n8n-nodes-base.gmail",
"position": [
624,
1472
],
"parameters": {
"sendTo": "={{ $json.customerEmail }}",
"message": "=<html>\n<body style=\"font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;\">\n <div style=\"background: #FF9800; color: white; padding: 20px; border-radius: 8px 8px 0 0; text-align: center;\">\n <h1 style=\"margin:0;\">\u23f3 Return Under Review</h1>\n </div>\n <div style=\"background: #f9f9f9; padding: 20px; border: 1px solid #ddd; border-radius: 0 0 8px 8px;\">\n <p>Hi <strong>{{ $json.customerName }}</strong>,</p>\n <p>We received your return request for Order <strong>#{{ $json.orderId }}</strong> and our team is reviewing it manually to ensure everything is processed correctly.</p>\n \n <div style=\"background: white; padding: 15px; border-radius: 8px; margin: 15px 0; border-left: 4px solid #FF9800;\">\n <p><strong>Session Reference:</strong> {{ $json.sessionId }}</p>\n <p><strong>Return Reason:</strong> {{ $json.returnReason }}</p>\n <p><strong>Expected Resolution:</strong> Within 24 hours</p>\n </div>\n \n <p>A support team member will contact you shortly. No action is needed from your side.</p>\n <p>If you have questions, reply to this email with your Session Reference ID above.</p>\n </div>\n</body>\n</html>",
"options": {
"appendAttribution": false
},
"subject": "=\u23f3 Your Return Request is Being Reviewed - Order #{{ $json.orderId }}"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "77b95e70-8889-4fa6-83ed-625e8e812def",
"name": "\ud83d\udea8 Gmail \u2014 Escalate to Support Team",
"type": "n8n-nodes-base.gmail",
"notes": "Change support-team@yourcompany.com to your actual support team email",
"position": [
848,
1472
],
"parameters": {
"sendTo": "user@example.com",
"message": "=<html>\n<body style=\"font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;\">\n <div style=\"background: #f44336; color: white; padding: 20px; border-radius: 8px 8px 0 0; text-align: center;\">\n <h1 style=\"margin:0;\">\ud83d\udea8 Agent Q Escalation - Manual Review Required</h1>\n </div>\n <div style=\"background: #fff3f3; padding: 20px; border: 1px solid #f44336; border-radius: 0 0 8px 8px;\">\n <h3>Case Details</h3>\n <table style=\"width:100%; border-collapse: collapse;\">\n <tr style=\"background:#fff;\"><td style=\"padding:8px; border:1px solid #ddd; font-weight:bold;\">Session ID</td><td style=\"padding:8px; border:1px solid #ddd;\">{{ $json.sessionId }}</td></tr>\n <tr style=\"background:#f9f9f9;\"><td style=\"padding:8px; border:1px solid #ddd; font-weight:bold;\">Order ID</td><td style=\"padding:8px; border:1px solid #ddd;\">#{{ $json.orderId }}</td></tr>\n <tr style=\"background:#fff;\"><td style=\"padding:8px; border:1px solid #ddd; font-weight:bold;\">Customer</td><td style=\"padding:8px; border:1px solid #ddd;\">{{ $json.customerName }} ({{ $json.customerEmail }})</td></tr>\n <tr style=\"background:#f9f9f9;\"><td style=\"padding:8px; border:1px solid #ddd; font-weight:bold;\">Return Reason</td><td style=\"padding:8px; border:1px solid #ddd;\">{{ $json.returnReason }}</td></tr>\n <tr style=\"background:#fff;\"><td style=\"padding:8px; border:1px solid #ddd; font-weight:bold;\">Portal URL</td><td style=\"padding:8px; border:1px solid #ddd;\">{{ $json.portalUrl }}</td></tr>\n <tr style=\"background:#f9f9f9;\"><td style=\"padding:8px; border:1px solid #ddd; font-weight:bold;\">Retry Count</td><td style=\"padding:8px; border:1px solid #ddd;\">{{ $json.retryCount }} / 3</td></tr>\n <tr style=\"background:#fff;\"><td style=\"padding:8px; border:1px solid #ddd; font-weight:bold;\">Last Error</td><td style=\"padding:8px; border:1px solid #ddd;\">{{ $json.humanTask.lastError }}</td></tr>\n <tr style=\"background:#f9f9f9;\"><td style=\"padding:8px; border:1px solid #ddd; font-weight:bold;\">Escalated At</td><td style=\"padding:8px; border:1px solid #ddd;\">{{ $json.humanTask.escalatedAt }}</td></tr>\n </table>\n \n <div style=\"margin-top:15px; padding:15px; background:white; border-radius:8px;\">\n <strong>Action Required:</strong> Please manually process this return on the portal and update the Human Review Queue in Google Sheets.\n </div>\n </div>\n</body>\n</html>",
"options": {
"appendAttribution": false
},
"subject": "=\ud83d\udea8 [URGENT] Manual Return Review Required - Order #{{ $json.orderId }} | Session: {{ $json.sessionId }}"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "faf9fd97-493d-4811-920d-c0f77dcb3865",
"name": "\u26a0\ufe0f Respond \u2014 Return Escalated for Review",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
1056,
1472
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={{ JSON.stringify({ success: false, sessionId: $json.sessionId, status: 'escalated_to_human', message: 'Return request requires manual review. Customer has been notified.' }) }}"
},
"typeVersion": 1.1
},
{
"id": "120e869a-ec0b-4bd8-b12d-b104edd3a36b",
"name": "Sticky Note \u2014 Phase 1: Intake & Parsing",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1968,
1696
],
"parameters": {
"color": 3,
"width": 520,
"height": 175,
"content": "## \ud83d\udce5 Phase 1: Return Request Intake\nReceives the return submission via Typeform webhook.\nParses order ID, reason, product details, and customer info.\nInitialises Agent Q's session log to track all navigation steps and decisions throughout the automation."
},
"typeVersion": 1
},
{
"id": "7be51b5c-c6fa-4356-b311-3d496164a7a4",
"name": "Sticky Note \u2014 Phase 2: MCTS Portal Mapping",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1344,
1680
],
"parameters": {
"color": 5,
"width": 490,
"height": 195,
"content": "## \ud83d\uddfa\ufe0f Phase 2: MCTS \u2014 Map Return Portal\nAgent Q uses **Monte Carlo Tree Search** to:\n- Fetch and analyse the retailer's return portal structure\n- Identify the optimal navigation path to the correct form\n- Parse clickable elements, form fields, and flow branches\n\nOutputs a structured route plan before any form interaction begins."
},
"typeVersion": 1
},
{
"id": "d26480f6-acae-44be-a0cf-1dcdad8af41c",
"name": "Sticky Note \u2014 Phase 3: Autonomous Navigation",
"type": "n8n-nodes-base.stickyNote",
"position": [
-736,
1696
],
"parameters": {
"color": 6,
"width": 470,
"height": 175,
"content": "## \ud83e\udd16 Phase 3: Autonomous Form Navigation\nAgent Q navigates the portal and fills the return form autonomously.\nAfter each step it checks the result:\n- \u2705 **Expected screen** \u2192 Continue to confirmation\n- \u274c **Unexpected screen** \u2192 Trigger self-critique retry loop"
},
"typeVersion": 1
},
{
"id": "789907cf-6ab2-4234-a29e-a8538a7e7739",
"name": "Sticky Note \u2014 Phase 4: Self-Critique Retry Loop",
"type": "n8n-nodes-base.stickyNote",
"position": [
-432,
1472
],
"parameters": {
"color": 4,
"width": 470,
"height": 175,
"content": "## \ud83d\udd01 Phase 4: Self-Critique Retry Loop\nWhen an unexpected screen is hit, Agent Q self-critiques:\n- Re-evaluates the portal path with a fresh HTTP call\n- Applies an alternative navigation route\n- Retries up to **3 times** before escalating\n\nIf max retries reached \u2192 escalation flow triggered."
},
"typeVersion": 1
},
{
"id": "9e8ce69e-93c5-400f-a609-2b628d5fe0a7",
"name": "Sticky Note \u2014 Phase 5: Success \u2014 Confirmation & Logging",
"type": "n8n-nodes-base.stickyNote",
"position": [
-192,
2192
],
"parameters": {
"color": 7,
"width": 460,
"height": 165,
"content": "## \u2705 Phase 5: Success \u2014 Confirm & Log\nReturn successfully filed:\n- Extracts the confirmation number from portal response\n- Sends **Gmail confirmation** to the customer with RMA details\n- Logs the completed return in **Google Sheets**\n- Returns a **200 success** webhook response"
},
"typeVersion": 1
},
{
"id": "7c269580-4d22-4202-bd19-3e29afbdaa76",
"name": "Sticky Note \u2014 Phase 6: Escalation Flow",
"type": "n8n-nodes-base.stickyNote",
"position": [
240,
1184
],
"parameters": {
"color": 2,
"width": 490,
"height": 180,
"content": "## \ud83d\udea8 Phase 6: Escalation \u2014 Human Review\nIf all 3 retries fail, Agent Q escalates gracefully:\n- Prepares structured escalation data with full session log\n- Logs case in **Google Sheets** flagged for human review\n- Emails the **customer** that their return is under review\n- Alerts the **support team** with full context for manual action\n- Returns an escalation webhook response"
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"executionOrder": "v1"
},
"versionId": "c0b2a343-377e-4167-a3f6-71c67e23f353",
"connections": {
"\u2753 Unexpected Screen?": {
"main": [
[
{
"node": "\ud83d\udd01 Max Retries (3) Reached?",
"type": "main",
"index": 0
}
],
[
{
"node": "\u2705 Extract Return Confirmation Number",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udd0e Check Navigation Result": {
"main": [
[
{
"node": "\u2753 Unexpected Screen?",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udd01 Max Retries (3) Reached?": {
"main": [
[
{
"node": "\u2699\ufe0f Prepare Escalation Data",
"type": "main",
"index": 0
}
],
[
{
"node": "\ud83e\udde0 Self-Critique \u2014 Re-evaluate Path",
"type": "main",
"index": 0
}
]
]
},
"\u2699\ufe0f Prepare Escalation Data": {
"main": [
[
{
"node": "\ud83d\udccb Log to Sheets \u2014 Needs Human Review",
"type": "main",
"index": 0
}
]
]
},
"\ud83e\udd16 Navigate & Fill Return Form": {
"main": [
[
{
"node": "\ud83d\udd0e Check Navigation Result",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udcca Log Success to Google Sheets": {
"main": [
[
{
"node": "\u2705 Respond \u2014 Return Successfully Filed",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udcd0 Parse Portal Map & Plan Route": {
"main": [
[
{
"node": "\ud83e\udd16 Navigate & Fill Return Form",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udd0d Parse Typeform Submission Data": {
"main": [
[
{
"node": "\ud83d\uddc2\ufe0f Initialize Agent Q Session Log",
"type": "main",
"index": 0
}
]
]
},
"\u2705 Extract Return Confirmation Number": {
"main": [
[
{
"node": "\ud83d\udce7 Gmail \u2014 Send Confirmation to Customer",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udd04 Apply Alternative Navigation Path": {
"main": [
[
{
"node": "\ud83e\udd16 Navigate & Fill Return Form",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\uddc2\ufe0f Initialize Agent Q Session Log": {
"main": [
[
{
"node": "\ud83d\uddfa\ufe0f MCTS \u2014 Map Return Portal Structure",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udea8 Gmail \u2014 Escalate to Support Team": {
"main": [
[
{
"node": "\u26a0\ufe0f Respond \u2014 Return Escalated for Review",
"type": "main",
"index": 0
}
]
]
},
"\ud83e\udde0 Self-Critique \u2014 Re-evaluate Path": {
"main": [
[
{
"node": "\ud83d\udd04 Apply Alternative Navigation Path",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udccb Log to Sheets \u2014 Needs Human Review": {
"main": [
[
{
"node": "\ud83d\udce7 Gmail \u2014 Notify Customer: Under Review",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udce5 Webhook \u2014 Receive Return Submission": {
"main": [
[
{
"node": "\ud83d\udd0d Parse Typeform Submission Data",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udce7 Gmail \u2014 Notify Customer: Under Review": {
"main": [
[
{
"node": "\ud83d\udea8 Gmail \u2014 Escalate to Support Team",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udce7 Gmail \u2014 Send Confirmation to Customer": {
"main": [
[
{
"node": "\ud83d\udcca Log Success to Google Sheets",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\uddfa\ufe0f MCTS \u2014 Map Return Portal Structure": {
"main": [
[
{
"node": "\ud83d\udcd0 Parse Portal Map & Plan Route",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
gmailOAuth2googleSheetsOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Automate the entire product return process with an intelligent AI-powered agent that navigates return portals on your behalf 🤖. This workflow receives customer return requests, analyzes portal structures using AI, and autonomously fills and submits return forms 🔍. It…
Source: https://n8n.io/workflows/15688/ — 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.
AI Lead Generation & Outreach Agent. Uses httpRequest, googleSheets, gmail. Webhook trigger; 6 nodes.
ANIS_HUB 1. Uses gmail, googleDrive, googleSheets, httpRequest. Webhook trigger; 89 nodes.
leads. Uses supabase, gmail, formTrigger, httpRequest. Webhook trigger; 62 nodes.
This workflow automates document processing using LlamaParse to extract and analyze text from various file formats. It intelligently processes documents, extracts structured data, and delivers actiona
Nps-Com-Atendimento-Ao-Cliente. Uses toolSerpApi, lmChatOpenAi, microsoftSql, gmail. Webhook trigger; 34 nodes.