This workflow corresponds to n8n.io template #16368 — we link there as the canonical source.
This workflow follows the Emailsend → HTTP Request 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 →
{
"meta": {
"templateId": "community-template",
"templateCredsSetupCompleted": false
},
"name": "HubSpot Ticket",
"tags": [],
"nodes": [
{
"id": "82b3827b-7e5e-4859-a80d-e88478d248f8",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-464,
64
],
"parameters": {
"width": 480,
"height": 832,
"content": "## HubSpot Ticket\n\n### How it works\n\n1. Webhook trigger receives initial data and sorts it.\n2. Checks are performed to validate data authenticity using Google Captcha.\n3. Contact is created or updated in HubSpot.\n4. Existing ticket status is checked and conditionally handled.\n5. New ticket might be created and notifications are sent via email.\n6. Final responses are built and sent to appropriate stakeholders.\n\n### Setup steps\n\n- [ ] Ensure HubSpot API credentials are set up in the nodes\n- [ ] Configure Webhook to listen to the correct endpoint\n- [ ] Set up Google reCAPTCHA credentials\n- [ ] Ensure email credentials are correctly set up in the email nodes\n"
},
"typeVersion": 1
},
{
"id": "2db91629-d8a7-4174-9049-d937483644a8",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
96,
288
],
"parameters": {
"color": 7,
"width": 352,
"height": 304,
"content": "## Webhook and data sorting\n\nStarts workflow by receiving and sorting incoming data"
},
"typeVersion": 1
},
{
"id": "a9709205-2c78-4ef0-bc87-7aa6b70f1839",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
368,
624
],
"parameters": {
"color": 7,
"width": 400,
"height": 272,
"content": "## Captcha verification\n\nValidates data using Google reCAPTCHA"
},
"typeVersion": 1
},
{
"id": "898a6467-92ae-47dc-8e64-56572a7540f5",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
544,
288
],
"parameters": {
"color": 7,
"width": 416,
"height": 304,
"content": "## Contact creation and query\n\nCreates or updates HubSpot contact and checks for existing ticket"
},
"typeVersion": 1
},
{
"id": "815e11e5-c75f-4115-880f-c60b3b75e8c3",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
992,
304
],
"parameters": {
"color": 7,
"width": 640,
"height": 272,
"content": "## Check and handle ticket status\n\nProcesses and determines ticket status for further actions"
},
"typeVersion": 1
},
{
"id": "e67403d8-4966-478d-9a87-34ff918aee4e",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
1664,
192
],
"parameters": {
"color": 7,
"width": 464,
"height": 688,
"content": "## Create ticket and send notifications\n\nCreates a new ticket, executes conditional logic, and sends emails"
},
"typeVersion": 1
},
{
"id": "4fd3e115-ac92-4a57-a1e3-3850586c064a",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
2160,
400
],
"parameters": {
"color": 7,
"width": 624,
"height": 464,
"content": "## Final response and email flow\n\nHandles final response setup and sends final emails"
},
"typeVersion": 1
},
{
"id": "9137206a-c40b-49d6-b169-d96eec5f494a",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
2176,
64
],
"parameters": {
"color": 7,
"width": 432,
"height": 304,
"content": "## Additional code processing\n\nExecutes custom JavaScript code for specific logic"
},
"typeVersion": 1
},
{
"id": "f936b22e-9f2c-4e46-995d-4a05f9a26e8b",
"name": "Create or update a contact1",
"type": "n8n-nodes-base.hubspot",
"position": [
592,
416
],
"parameters": {
"email": "={{ $('Data Sorting1').item.json.email }}",
"options": {},
"authentication": "appToken",
"additionalFields": {
"city": "={{ $('Data Sorting1').item.json.City }}",
"lastName": "={{ $('Data Sorting1').item.json.lastname }}",
"firstName": "={{ $('Data Sorting1').item.json.firstname }}",
"mobilePhoneNumber": "={{ $('Data Sorting1').item.json.phone }}"
}
},
"typeVersion": 2.1
},
{
"id": "1a19c17c-776b-4b17-bc7f-39ada3f9f230",
"name": "Data Sorting1",
"type": "n8n-nodes-base.set",
"position": [
304,
416
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "279ae7b0-2724-43f8-a38b-3178b17874a4",
"name": "email",
"type": "string",
"value": "={{$json.body.email}}"
},
{
"id": "1780a02a-6f83-4ccc-a782-5fd5dffba2ca",
"name": "firstname",
"type": "string",
"value": "={{$json.body.first_name}}"
},
{
"id": "e826fb03-160d-457e-bdb0-74c4c4589eae",
"name": "lastname",
"type": "string",
"value": "={{$json.body.last_name}}"
},
{
"id": "52ec8651-18d8-46ca-873b-51c0a57833bf",
"name": "phone",
"type": "string",
"value": "={{$json.body.phone}}"
},
{
"id": "fc32c29a-3f12-4b8f-a01e-b6ac8c8ed42d",
"name": "remarks",
"type": "string",
"value": "={{$json.body.remarks}}"
},
{
"id": "7ffa8aca-fced-4863-a7e6-09e0b4ea0afc",
"name": "order_id",
"type": "string",
"value": "={{$json.body.order_no}}"
},
{
"id": "cbc202bb-7f07-44b7-b3fe-e920a019e54f",
"name": "City",
"type": "string",
"value": "={{$json.body.city}}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "810cb0af-cf81-42fb-b350-8380d41dfe61",
"name": "Code in JavaScript2",
"type": "n8n-nodes-base.code",
"position": [
1040,
416
],
"parameters": {
"jsCode": "// items[0].json.results is the array from associations\nconst ids = (items[0].json.results || []).map(r => ({ id: String(r.toObjectId || r.id) }));\nreturn [{ json: { inputs: ids } }];"
},
"typeVersion": 2
},
{
"id": "1dc86d50-9b31-49d3-91f2-e24ba11f30bb",
"name": "If1",
"type": "n8n-nodes-base.if",
"position": [
1488,
416
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "or",
"conditions": [
{
"id": "ee017bf5-6153-4c5a-85f9-7e6589fdd943",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{ String($json.results[0].properties.hs_pipeline_stage) }}",
"rightValue": "1"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "ec92d885-3090-430f-ae6b-0f506a2d86f1",
"name": "Create a ticket1",
"type": "n8n-nodes-base.hubspot",
"position": [
1712,
512
],
"parameters": {
"stageId": "1",
"resource": "ticket",
"pipelineId": "0",
"ticketName": "={{ $('Create or update a contact1').item.json.properties.hs_full_name_or_email.value }}",
"authentication": "appToken",
"additionalFields": {
"source": "AI Agent",
"priority": "MEDIUM",
"description": "={{$node[\"Data Sorting1\"].json[\"remarks\"]}} & my order No. is {{$node[\"Data Sorting1\"].json[\"order_id\"]}}",
"ticketOwnerId": 47981429,
"associatedContactIds": "={{ [ String($item(0).$node['Create or update a contact1'].json.id || $item(0).$node['Create or update a contact1'].json.vid) ] }}"
}
},
"typeVersion": 2.2
},
{
"id": "609e45de-6391-4273-aa06-626f738e07ef",
"name": "Code in JavaScript3",
"type": "n8n-nodes-base.code",
"position": [
1712,
320
],
"parameters": {
"jsCode": "/**\n * Code in JavaScript3 \u2014 duplicate ticket branch\n * - Works in Webhook-driven flows (no Form Trigger)\n * - Safely fetches data from earlier nodes by name\n */\n\n/* ====== CONFIG: put your node names here ====== */\nconst FORM_NODE = \"Data Sorting1\"; // node that holds mapped form fields\nconst HS_NODE = \"Find Ticket Status1\"; // node that returned the ticket search/status\nconst DEFAULT_TO = \"support@example.com\";\n\n/* ====== Safe helpers ====== */\nfunction safeFirstFrom(nodeName) {\n try {\n const arr = $items(nodeName, 0) || [];\n const j = arr[0]?.json ?? {};\n return Array.isArray(j) ? (j[0] ?? {}) : j;\n } catch {\n return {};\n }\n}\n\nfunction extractHsId(hsJson) {\n if (!hsJson) return null;\n // common HubSpot shapes\n if (hsJson.id) return String(hsJson.id);\n if (hsJson.properties?.hs_ticket_id) return String(hsJson.properties.hs_ticket_id);\n\n // search API: { results: [ { id, properties: { hs_object_id } } ] }\n if (Array.isArray(hsJson.results) && hsJson.results.length) {\n return String(hsJson.results[0].id || hsJson.results[0].properties?.hs_object_id || \"\");\n }\n // sometimes the whole execution passes an array of results\n if (Array.isArray(hsJson) && hsJson[0]?.results?.length) {\n const r0 = hsJson[0].results[0];\n return String(r0.id || r0.properties?.hs_object_id || \"\");\n }\n return null;\n}\n\nconst s = (v) => (v == null ? \"\" : String(v));\nconst tr = (label, value) =>\n `<tr><td><strong>${label}</strong></td><td>${value ? String(value) : \"\"}</td></tr>`;\n\n/* ====== Read inputs from earlier nodes ====== */\nconst form = safeFirstFrom(FORM_NODE); // your mapped fields live here\nconst hsResp = safeFirstFrom(HS_NODE); // HubSpot status/search response\n\n// Adapt to your mapped keys (from your Shopify form)\nconst firstName = s(form.first_name || form[\"First Name\"]);\nconst lastName = s(form.last_name || form[\"Last Name\"]);\nconst orderNo = s(form.order_no || form[\"Order No.\"]);\nconst mobile = s(form.phone || form[\"Mobile Number\"]);\nconst email = s(form.email || form[\"Email\"]);\nconst remarks = s(form.remarks || form[\"Remarks\"]);\n\nconst hsIdRaw = extractHsId(hsResp);\nconst hasOpen = Boolean(hsIdRaw);\nconst hsId = hasOpen ? String(hsIdRaw) : \"N/A\";\nconst clientName = [firstName, lastName].filter(Boolean).join(\" \");\n\n/* ====== Email content ====== */\nconst subject = `Duplicate \u2013 ${clientName || \"Unknown\"} \u2013 HS #${hsId} (Order ${orderNo || \"N/A\"})`;\n\nconst textBody = `\nHey Team,\n\nFollowing person is trying to lodge a complaint, but this complaint number is already open in HubSpot.\n\nComplaint No.: ${hsId}\n\nFirst Name: ${firstName}\nLast Name: ${lastName}\nOrder No.: ${orderNo}\nMobile Number: ${mobile}\nEmail: ${email}\n${remarks ? `Remarks: ${remarks}` : \"\"}\n\nThank You\nRegards,\nAI Agent\nYour Company\nCreated with n8n\n---\nThis email was sent automatically with n8n\n`.trim();\n\nconst htmlBody = `\n<div style=\"font-family:Arial,Helvetica,sans-serif;line-height:1.5;font-size:14px;color:#222\">\n <p>Hey Team,</p>\n <p>Following person is trying to lodge a complaint, but this complaint number is already open in HubSpot.</p>\n <p><strong>Complaint No.:</strong> ${hsId}</p>\n <table cellpadding=\"8\" cellspacing=\"0\" border=\"0\" style=\"border-collapse:collapse;background:#fafafa\">\n ${tr(\"First Name\", firstName)}\n ${tr(\"Last Name\", lastName)}\n ${tr(\"Order No.\", orderNo)}\n ${tr(\"Mobile Number\", mobile)}\n ${tr(\"Email\", email)}\n ${remarks ? tr(\"Remarks\", remarks) : \"\"}\n </table>\n <br/>\n <p>Thank You<br/>Regards,<br/><strong>AI Agent</strong><br/>Your Company</p>\n <div style=\"text-align:center; margin-top:20px; font-size:12px; color:#666;\">\n Created with \u2764\ufe0f by <strong>Your Name</strong> | <a href=\"https://example.com\" style=\"color:#666;\">example.com</a>\n </div>\n</div>\n`.trim();\n\n/* ====== Output for next nodes ====== */\nreturn [\n {\n json: {\n isDuplicate: hasOpen,\n hs_object_id: hsIdRaw,\n to: DEFAULT_TO,\n subject,\n text: textBody,\n html: htmlBody,\n form,\n hubspot: hsResp\n }\n }\n];"
},
"typeVersion": 2
},
{
"id": "07ca6e08-b0d4-444c-bea4-cd1267a9fdbc",
"name": "Send email1",
"type": "n8n-nodes-base.emailSend",
"position": [
1936,
320
],
"parameters": {
"html": "={{ $json.html }}",
"options": {
"appendAttribution": false
},
"subject": "={{$json.subject}}",
"toEmail": "support@example.com",
"fromEmail": "no-reply@example.com"
},
"typeVersion": 2.1
},
{
"id": "db53046f-4b2b-45b0-9b20-cb5da8f5a4e4",
"name": "Find Ticket Status1",
"type": "n8n-nodes-base.httpRequest",
"position": [
1264,
416
],
"parameters": {
"url": "https://api.hubapi.com/crm/v3/objects/tickets/batch/read",
"method": "POST",
"options": {},
"jsonBody": "={{ ({\n properties: [\"hs_pipeline_stage\"],\n inputs: ($json.inputs || []).map(i => ({ \n id: String(i.id)\n }))\n}) }}",
"sendBody": true,
"specifyBody": "json",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "hubspotAppToken"
},
"typeVersion": 4.2
},
{
"id": "1bef42bc-2fe6-4d56-bbaf-403cfca342ec",
"name": "Find if Ticket Exit1",
"type": "n8n-nodes-base.httpRequest",
"position": [
816,
416
],
"parameters": {
"url": "=https://api.hubapi.com/crm/v4/objects/contacts/{{$json.id || $json.vid}}/associations/tickets?limit=100",
"options": {},
"authentication": "predefinedCredentialType",
"nodeCredentialType": "hubspotAppToken"
},
"typeVersion": 4.2
},
{
"id": "c5101dc0-279f-4098-94ef-cc3a80ad5f52",
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"position": [
144,
416
],
"parameters": {
"path": "hubspot-ticket-webhook",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2.1
},
{
"id": "a2d1e26e-bbd3-4d09-927e-40f69a1c7c15",
"name": "Respond to Webhook",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
2208,
512
],
"parameters": {
"options": {
"responseCode": 200
},
"respondWith": "text",
"responseBody": "={{$json[\"html\"]}}"
},
"typeVersion": 1.4,
"alwaysOutputData": false
},
{
"id": "e192bae0-57a0-42ef-bdbd-71cc2acd9f28",
"name": "Code in JavaScript4",
"type": "n8n-nodes-base.code",
"position": [
1920,
512
],
"parameters": {
"jsCode": "/**\n * JavaScript4\n * Build final success HTML for NEW ticket\n * - Gets ticketId from \"Create a ticket1\" node (HubSpot create ticket)\n * - Shows thank you, ticket number, and Track button\n */\n\nfunction safeGetTicketId() {\n // 1. Try current item first (in case n8n is passing Create a ticket1 output directly)\n if ($json?.objectId) {\n return String($json.objectId);\n }\n\n // 2. Try to look up by node name \"Create a ticket1\"\n try {\n const created = $items(\"Create a ticket1\", 0, 0).json;\n if (created?.objectId) {\n return String(created.objectId);\n }\n } catch (e) {\n // ignore, we'll fall through\n }\n\n // 3. Try some other common shapes you've used before just in case\n if ($json?.ticket_number) return String($json.ticket_number);\n if ($json?.id) return String($json.id);\n if ($json?.results?.[0]?.id) return String($json.results[0].id);\n\n // nothing found\n return \"\u2014\";\n}\n\nconst ticketId = safeGetTicketId();\n\n// Build name (fallback to \"Customer\")\nconst first =\n $('Data Sorting1').first().json.firstname ||\n $json.body?.first_name ||\n \"\";\n\nconst last =\n $('Data Sorting1').first().json.lastname ||\n $json.body?.last_name ||\n \"\";\n\nconst name = [first, last].filter(Boolean).join(\" \") || \"Customer\";\n\n// Build pretty HTML\nconst html = `\n<div style=\"font-family:system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;padding:16px 0;\">\n <div style=\"max-width:780px;border-radius:14px;border:1px solid #d1fae5;background:#ecfdf5;box-shadow:0 10px 30px rgba(0,0,0,.07);padding:20px 24px;display:flex;align-items:flex-start;gap:16px;\">\n <div style=\"flex-shrink:0;width:36px;height:36px;border-radius:999px;background:#10b981;color:#fff;font-weight:700;line-height:36px;text-align:center;font-size:16px;\">\u2713</div>\n\n <div style=\"font-size:15px;line-height:1.5;color:#065f46;flex:1;min-width:0;\">\n <div style=\"font-size:16px;font-weight:700;margin-bottom:6px;color:#065f46;\">\n Complaint Submitted\n </div>\n\n <div style=\"margin:0 0 10px 0;color:#065f46;\">\n Thank you <strong>${name}</strong>.<br/>\n Your ticket number is\n <span style=\"display:inline-block;background:#065f46;color:#fff;border-radius:8px;padding:4px 8px;font-size:14px;font-weight:600;\">\n ${ticketId}\n </span>.\n </div>\n\n <div style=\"font-size:14px;color:#065f46;margin:0 0 14px;\">\n Please save this number. Our support team will contact you soon.<br/>\n You can also WhatsApp us at <strong>YOUR_SUPPORT_PHONE</strong>.\n </div>\n\n <div style=\"margin-top:8px;\">\n <a\n href=\"https://example.com/track-your-ticket?ticket=${encodeURIComponent(ticketId)}\"\n target=\"_blank\"\n rel=\"noopener\"\n style=\"display:inline-block;background:#065f46;color:#fff;text-decoration:none;border-radius:8px;padding:8px 12px;font-size:14px;font-weight:600;\">\n Track Complaint Status\n </a>\n </div>\n </div>\n </div>\n</div>\n`;\n\n// Send HTML forward to Respond to Webhook\nreturn [\n {\n json: {\n html,\n ticket_id: ticketId,\n customer_name: name,\n },\n },\n];\n"
},
"typeVersion": 2
},
{
"id": "c3b4228b-dffa-4c12-b2f7-a5c06572780b",
"name": "Respond to Webhook1",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
2464,
192
],
"parameters": {
"options": {
"responseCode": 200
},
"respondWith": "text",
"responseBody": "={{$json[\"html\"]}}"
},
"typeVersion": 1.4,
"alwaysOutputData": false
},
{
"id": "394bcdec-3c41-457d-9f19-015f09fae4f0",
"name": "Code in JavaScript5",
"type": "n8n-nodes-base.code",
"position": [
2224,
192
],
"parameters": {
"jsCode": "/**\n * JavaScript5 \u2014 Duplicate Ticket (Already Logged)\n * -------------------------------------------------------------\n * \u2705 Matches design of \"Complaint Submitted\" success card\n * \u2705 Pulls existing ticket number from Code in JavaScript3\n * \u2705 Prevents new ticket creation\n * \u2705 Sends clean HTML to Respond to Webhook\n * -------------------------------------------------------------\n */\n\nlet js3 = {};\ntry {\n js3 = $items(\"Code in JavaScript3\", 0, 0).json || {};\n} catch (e) {\n js3 = {};\n}\n\n// Extract ticket number safely\nconst ticketId =\n $('Code in JavaScript3').first().json.hs_object_id ||\n (js3.hubspot?.results?.[0]?.id) ||\n (js3.hubspot?.results?.[0]?.properties?.hs_object_id) ||\n js3.ticket_number ||\n \"\u2014\";\n\n// Extract name if available\nconst first =\n $('Code in JavaScript3').first().json.form.firstname ||\n js3.body?.first_name ||\n js3.form?.firstname ||\n \"\";\n\nconst last =\n $('Code in JavaScript3').first().json.form.lastname||\n js3.body?.last_name ||\n js3.form?.lastname ||\n \"\";\n\nconst name = [first, last].filter(Boolean).join(\" \") || \"Customer\";\n\n// -------------------------------------------------------------\n// Build clean HTML response (same style as new ticket)\n// -------------------------------------------------------------\nconst html = `\n<div style=\"font-family:system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;padding:16px 0;\">\n <div style=\"max-width:780px;border-radius:14px;border:1px solid #d1fae5;background:#ecfdf5;\n box-shadow:0 10px 30px rgba(0,0,0,.07);padding:20px 24px;display:flex;align-items:flex-start;gap:16px;\">\n <div style=\"flex-shrink:0;width:36px;height:36px;border-radius:999px;background:#10b981;color:#fff;\n font-weight:700;line-height:36px;text-align:center;font-size:16px;\">\u2713</div>\n\n <div style=\"font-size:15px;line-height:1.5;color:#065f46;flex:1;min-width:0;\">\n <div style=\"font-size:16px;font-weight:700;margin-bottom:6px;color:#065f46;\">\n Complaint Already Logged\n </div>\n\n <div style=\"margin:0 0 10px 0;color:#065f46;\">\n Thank you <strong>${name}</strong>.<br/>\n We found an active complaint already opened under your details.<br/>\n Your current ticket number is\n <span style=\"display:inline-block;background:#065f46;color:#fff;border-radius:8px;padding:4px 8px;\n font-size:14px;font-weight:600;\">\n ${ticketId}\n </span>.\n </div>\n\n <div style=\"font-size:14px;color:#065f46;margin:0 0 14px;\">\n Our team is already working on this case. You do not need to submit again.<br/>\n We will contact you soon. You can also WhatsApp us at <strong>YOUR_SUPPORT_PHONE</strong>.\n </div>\n\n <div style=\"margin-top:8px;\">\n <a href=\"https://example.com/track-your-ticket?ticket=${encodeURIComponent(ticketId)}\"\n target=\"_blank\"\n rel=\"noopener\"\n style=\"display:inline-block;background:#065f46;color:#fff;text-decoration:none;border-radius:8px;\n padding:8px 12px;font-size:14px;font-weight:600;\">\n Track Complaint Status\n </a>\n </div>\n </div>\n </div>\n</div>\n`;\n\n// -------------------------------------------------------------\n// Build final JSON payload for Respond to Webhook\n// -------------------------------------------------------------\nreturn [\n {\n json: {\n success: true,\n status_type: \"existing\", // critical flag for frontend\n ticket_number: ticketId,\n customer_name: name,\n html,\n },\n },\n];\n"
},
"typeVersion": 2
},
{
"id": "758bf65a-21fa-4c6a-b842-69df88c72372",
"name": "HTTP Request1",
"type": "n8n-nodes-base.httpRequest",
"position": [
1984,
704
],
"parameters": {
"url": "=https://api.hubapi.com/crm/v3/objects/tickets/{{$node[\"Create a ticket1\"].json.objectId}}",
"method": "PATCH",
"options": {},
"sendBody": true,
"sendHeaders": true,
"authentication": "predefinedCredentialType",
"bodyParameters": {
"parameters": [
{
"name": "properties[city]",
"value": "={{ $('Data Sorting1').item.json.City }}"
}
]
},
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"nodeCredentialType": "hubspotAppToken"
},
"typeVersion": 4.3
},
{
"id": "27bfc157-c7b6-49cb-a510-2401ee00c0ce",
"name": "Google-captcha",
"type": "n8n-nodes-base.httpRequest",
"position": [
416,
736
],
"parameters": {
"url": "https://www.google.com/recaptcha/api/siteverify",
"method": "POST",
"options": {},
"sendBody": true,
"contentType": "multipart-form-data",
"bodyParameters": {
"parameters": [
{
"name": "secret",
"value": "REPLACE_WITH_RECAPTCHA_SECRET"
},
{
"name": "response",
"value": "={{ $('Webhook').item.json.body['g-recaptcha-response'] }}"
}
]
}
},
"typeVersion": 4.3
},
{
"id": "5009187c-f526-4a83-b8f6-79b0d8df7702",
"name": "If2",
"type": "n8n-nodes-base.if",
"position": [
624,
736
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "6b244d76-11dc-41cd-9e40-565a88efc6cc",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.success }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "139a7398-d021-4fa8-8f58-82393cd62b1e",
"name": "Build Email Payload (New)",
"type": "n8n-nodes-base.code",
"position": [
2416,
512
],
"parameters": {
"jsCode": "// ===============================================\n// Build Email Payload (Customer + CS Team)\n// Inputs:\n// - \"Data Sorting1\" => form / Shopify data\n// - \"Create a ticket1\" => HubSpot ticket response\n// Mode: Run Once for All Items\n// ===============================================\n\n// ---------- Helper ----------\nfunction asString(v) {\n if (v === null || v === undefined) return '';\n const t = typeof v;\n\n if (t === 'string' || t === 'number' || t === 'boolean') {\n return String(v);\n }\n if (Array.isArray(v)) {\n return v.map(asString).join(', ');\n }\n if (t === 'object') {\n if (Object.prototype.hasOwnProperty.call(v, 'value')) {\n return asString(v.value);\n }\n return JSON.stringify(v);\n }\n return String(v);\n}\n\n// =============== 1. Grab data from previous nodes ===============\nconst form = $('Data Sorting1').item.json || {};\nconst ticket = $('Create a ticket1').item.json || {};\n\n// HubSpot properties object (v3 style)\nconst props = ticket.properties || {};\n\n// Robust ticket ID resolution\nconst ticketId =\n ticket.id ||\n ticket.objectId ||\n ticket.hs_object_id ||\n asString(props.hs_ticket_id) ||\n 'N/A';\n\n// Ticket name / subject\nconst ticketNameRaw =\n props.subject ||\n props.ticketname ||\n props.ticket_name ||\n `Ticket #${ticketId}`;\nconst ticketName = asString(ticketNameRaw);\n\n// HubSpot portal Id (set your real portal ID here)\nconst portalId = ticket.portalId || 'YOUR_HUBSPOT_PORTAL_ID';\n\n// HubSpot ticket URL\nconst ticketUrl =\n ticketId !== 'N/A'\n ? `https://app.hubspot.com/contacts/${portalId}/ticket/${ticketId}`\n : 'N/A';\n\n// Customer info from form\nconst firstName = form.firstname || '';\nconst lastName = form.lastname || '';\nconst fullName = `${firstName} ${lastName}`.trim() || 'Customer';\n\nconst orderNo = form.order_id || form.order || 'N/A';\nconst city = form.City || form.city || 'N/A';\nconst phone = form.phone || 'N/A';\nconst remarks = form.remarks || 'N/A';\n\n// Any extra meta flags coming from previous nodes (optional)\nconst meta = $input.item.json || {};\nconst rawPriority = props.hs_ticket_priority || meta.ticketPriority || 'Medium';\nconst ticketPriority = asString(rawPriority).toUpperCase() || 'MEDIUM';\nconst isSpam = meta.isSpam === true;\nconst spamLabel = isSpam ? 'SPAM' : 'clean';\n\n// Priority badge colour\nlet priorityColor = '#10b981'; // green = low/normal\nif (ticketPriority === 'MEDIUM') priorityColor = '#f97316'; // orange\nif (['HIGH', 'URGENT', 'CRITICAL'].includes(ticketPriority)) {\n priorityColor = '#ef4444'; // red\n}\n\n// Enhanced Your Company colour palette\nconst primaryDark = '#004039';\nconst primaryLight = '#f0f9f7';\nconst accentGold = '#d4af37';\nconst bgLight = '#f8fafc';\nconst cardBg = '#ffffff';\nconst borderColor = '#e2e8f0';\nconst textDark = '#1e293b';\nconst textMedium = '#475569';\nconst textLight = '#64748b';\n\nconst logoUrl =\n 'https://example.com/cdn/shop/files/logo-hsG_ac5cd6f5-1c48-49f6-a2f3-4dc1bdc25120.png?v=1688646128';\n\n// ADD THIS LINE: Define ticketTitleForHeader\nconst ticketTitleForHeader = ticketId !== 'N/A' ? `Ticket #${ticketId}` : 'New Customer Complaint';\n\n// =============== 2. CUSTOMER EMAIL (Enhanced UI with Tracking) ===============\nconst customerSubject =\n `Your Company Ticket ${ticketId !== 'N/A' ? `#${ticketId}` : ''} \u2013 Complaint Received`;\n\n// Ticket tracking URL - IMPORTANT: Make sure this URL works!\nconst trackUrl = ticketId !== 'N/A'\n ? `https://example.com/track-your-ticket?ticket_id=${encodeURIComponent(ticketId)}`\n : 'https://example.com/track-your-ticket';\n\nconst customerBody = `\n<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Your Company Ticket #${ticketId} - Complaint Received</title>\n <style>\n @media only screen and (max-width: 600px) {\n .container {\n width: 100% !important;\n padding: 16px !important;\n }\n .header-content {\n text-align: center !important;\n padding: 20px !important;\n }\n .ticket-card {\n padding: 24px 20px !important;\n }\n .action-button {\n display: block !important;\n width: 100% !important;\n text-align: center !important;\n margin: 10px 0 !important;\n }\n .info-grid {\n grid-template-columns: 1fr !important;\n gap: 16px !important;\n }\n }\n </style>\n</head>\n<body style=\"margin:0; padding:0; background:#f8fafc; font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;\">\n\n <!-- Top Decorative Strip -->\n <div style=\"background:linear-gradient(90deg, ${primaryDark} 0%, #00594e 100%); height:6px;\"></div>\n\n <!-- Main Container -->\n <div class=\"container\" style=\"max-width:640px; margin:0 auto; padding:32px 20px;\">\n \n <!-- Header -->\n <div style=\"text-align:center; margin-bottom:40px;\">\n <div style=\"margin-bottom:24px;\">\n <img src=\"${logoUrl}\" alt=\"Your Company\" style=\"height:42px; display:block; margin:0 auto;\">\n </div>\n <div style=\"font-size:14px; font-weight:600; color:${primaryDark}; letter-spacing:0.1em; text-transform:uppercase; margin-bottom:8px;\">\n Customer Care Center\n </div>\n </div>\n <!-- ==================== GREETING SECTION ==================== -->\n <div style=\"margin-bottom:40px; text-align:center;\">\n <p style=\"margin:0 0 16px 0; font-size:17px; color:${textMedium}; line-height:1.6;\">\n Dear <strong style=\"color:${primaryDark}; font-size:19px;\">${fullName}</strong>,\n </p>\n <p style=\"margin:0; font-size:16px; color:${textMedium}; line-height:1.7;\">\n Thank you for reaching out to Customer Care. Your complaint has been logged into our system and is now being processed.\n </p>\n </div>\n\n <!-- ==================== BIG TICKET ID + TRACK SECTION ==================== -->\n <!-- ==================== BIG TICKET ID + TRACK SECTION (Fixed & Polished) ==================== -->\n<div style=\"text-align:center; margin:48px 0; padding:48px 32px; background:#ffffff; border-radius:20px; border:4px solid ${primaryDark}; box-shadow:0 10px 30px rgba(0,64,57,0.08);\">\n\n <!-- Large Ticket ID -->\n <div style=\"font-size:56px; font-weight:800; font-family:'Courier New', monospace; color:${primaryDark}; letter-spacing:0.08em; margin-bottom:20px; line-height:1;\">\n #${ticketId}\n </div>\n\n <!-- Heading -->\n <h2 style=\"margin:0 0 16px 0; font-size:26px; font-weight:700; color:${primaryDark};\">\n Track Your Complaint\n </h2>\n\n <!-- Description -->\n <p style=\"margin:0 auto 36px auto; font-size:16px; color:${textMedium}; max-width:460px; line-height:1.6;\">\n Use the button below to check real-time updates on your complaint status anytime.\n </p>\n\n <!-- BIG VISIBLE BUTTON \u2013 uses table for 100% email client compatibility -->\n <div style=\"margin:20px 0;\">\n <table role=\"presentation\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" align=\"center\">\n <tr>\n <td align=\"center\" bgcolor=\"${primaryDark}\" style=\"border-radius:14px; box-shadow:0 8px 25px rgba(0,64,57,0.35);\">\n <a href=\"${trackUrl}\"\n target=\"_blank\"\n style=\"display:inline-block; padding:20px 68px; font-size:19px; font-weight:700; color:#ffffff; text-decoration:none; border-radius:14px; background:linear-gradient(135deg, ${primaryDark} 0%, #006955 100%);\">\n Track Complaint Status\n </a>\n </td>\n </tr>\n </table>\n </div>\n\n <!-- Direct link fallback (small) -->\n <p style=\"margin:28px 0 0 0; font-size:13px; color:${textLight};\">\n <a href=\"${trackUrl}\" style=\"color:${primaryDark}; text-decoration:underline;\" target=\"_blank\">\n ${trackUrl}\n </a>\n </p>\n\n</div>\n\n <!-- What Happens Next Section -->\n <div style=\"background:linear-gradient(135deg, #fefce8 0%, #fef3c7 100%); border-radius:16px; padding:32px; margin-bottom:32px; border:1px solid #fde68a; position:relative; overflow:hidden;\">\n <!-- Decorative corner -->\n <div style=\"position:absolute; top:0; right:0; width:80px; height:80px; background:${accentGold}; opacity:0.1; border-radius:0 0 0 80px;\"></div>\n \n <h3 style=\"margin:0 0 20px 0; font-size:20px; font-weight:700; color:#92400e; display:flex; align-items:center; gap:12px;\">\n <span style=\"background:#92400e; color:white; width:40px; height:40px; border-radius:10px; display:inline-flex; align-items:center; justify-content:center; font-size:20px;\">\ud83d\udccb</span>\n What Happens Next?\n </h3>\n \n <div style=\"position:relative; z-index:1;\">\n <div style=\"display:flex; align-items:flex-start; gap:16px; margin-bottom:20px; padding-bottom:20px; border-bottom:1px solid rgba(146, 64, 14, 0.1);\">\n <div style=\"flex-shrink:0; width:32px; height:32px; background:#92400e; color:white; border-radius:50%; display:flex; align-items:center; justify-content:center; font-weight:bold;\">1</div>\n <div>\n <div style=\"font-size:16px; font-weight:600; color:#92400e; margin-bottom:4px;\">\n Initial Review\n </div>\n <div style=\"font-size:15px; color:#78350f; line-height:1.5;\">\n Our team will review your complaint within <strong>24-48 hours</strong>\n </div>\n </div>\n </div>\n \n <div style=\"display:flex; align-items:flex-start; gap:16px; margin-bottom:20px; padding-bottom:20px; border-bottom:1px solid rgba(146, 64, 14, 0.1);\">\n <div style=\"flex-shrink:0; width:32px; height:32px; background:#92400e; color:white; border-radius:50%; display:flex; align-items:center; justify-content:center; font-weight:bold;\">2</div>\n <div>\n <div style=\"font-size:16px; font-weight:600; color:#92400e; margin-bottom:4px;\">\n Additional Information\n </div>\n <div style=\"font-size:15px; color:#78350f; line-height:1.5;\">\n You may get a call from our customer team if more information is required\n </div>\n </div>\n </div>\n \n <div style=\"display:flex; align-items:flex-start; gap:16px;\">\n <div style=\"flex-shrink:0; width:32px; height:32px; background:#92400e; color:white; border-radius:50%; display:flex; align-items:center; justify-content:center; font-weight:bold;\">3</div>\n <div>\n <div style=\"font-size:16px; font-weight:600; color:#92400e; margin-bottom:4px;\">\n Track Progress\n </div>\n <div style=\"font-size:15px; color:#78350f; line-height:1.5;\">\n You can track progress anytime using the <strong>Track Complaint Status</strong> button above\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Need Help Section -->\n <div style=\"text-align:center; padding-top:32px; border-top:1px solid ${borderColor};\">\n <h3 style=\"margin:0 0 24px 0; font-size:18px; font-weight:600; color:${textDark};\">\n Need Immediate Assistance?\n </h3>\n <div style=\"display:flex; justify-content:center; gap:24px; flex-wrap:wrap;\">\n <a href=\"https://wa.me/YOUR_SUPPORT_PHONE\"\n style=\"display:inline-flex; align-items:center; gap:12px; background:#25D366; color:white; padding:14px 28px; border-radius:10px; text-decoration:none; font-size:15px; font-weight:600; transition:all 0.3s ease;\"\n onmouseover=\"this.style.transform='translateY(-2px)'; this.style.boxShadow='0 6px 20px rgba(37, 211, 102, 0.3)';\"\n onmouseout=\"this.style.transform='translateY(0)'; this.style.boxShadow='none';\">\n <span style=\"font-size:20px;\">\ud83d\udcac</span>\n <span>WhatsApp Support</span>\n </a>\n \n </div>\n <p style=\"margin:20px 0 0 0; font-size:14px; color:${textLight};\">\n Support Hours: Monday - Saturday, 9:00 AM - 5:30 PM\n </p>\n </div>\n </div>\n </div>\n\n <!-- Footer -->\n <div style=\"text-align:center; padding-top:32px; border-top:1px solid ${borderColor};\">\n <div style=\"margin-bottom:20px;\">\n <img src=\"${logoUrl}\" alt=\"Your Company\" style=\"height:30px; opacity:0.8;\">\n </div>\n \n <p style=\"margin:0 0 16px 0; font-size:15px; color:${textDark}; font-weight:500;\">\n Customer Care Team\n </p>\n \n <p style=\"margin:0 0 24px 0; font-size:14px; color:${textMedium}; line-height:1.5; max-width:560px; margin-left:auto; margin-right:auto;\">\n We're committed to providing you with the best possible service. Thank you for choosing Your Company.\n </p>\n \n <div style=\"font-size:12px; color:${textLight};\">\n <p style=\"margin:0 0 8px 0;\">\n This is an automated message. Please do not reply to this email.<br>\n For any queries, please contact us via WhatsApp or call support.\n </p>\n <p style=\"margin:0;\">\n \u00a9 ${new Date().getFullYear()} Your Company. All rights reserved.\n </p>\n </div>\n </div>\n\n </div>\n\n <!-- Bottom Decorative Strip -->\n <div style=\"background:linear-gradient(90deg, ${primaryDark} 0%, #00594e 100%); height:6px;\"></div>\n\n</body>\n</html>\n`.trim();\n\n// =============== 3. INTERNAL TEAM EMAIL ===============\nconst teamSubject =\n `\ud83d\udea8 New Customer Complaint \u2013 ${city} \u2013 ${fullName} (${ticketTitleForHeader})`;\n\nconst teamBody = `\n<!DOCTYPE html>\n<html>\n <head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>New Customer Complaint - ${ticketId}</title>\n </head>\n <body style=\"margin:0; padding:0; background:#f8fafc; font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\">\n \n <!-- Main Container -->\n <div style=\"max-width:700px; margin:0 auto; padding:24px 16px;\">\n \n <!-- Header Card -->\n <div style=\"background:linear-gradient(135deg, ${primaryDark} 0%, #00594e 100%); border-radius:16px 16px 0 0; padding:28px 32px; color:#ffffff; box-shadow:0 4px 20px rgba(0, 64, 57, 0.15);\">\n <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\">\n <tr>\n <td style=\"vertical-align:middle;\">\n <img src=\"${logoUrl}\" alt=\"Your Company\" style=\"height:32px; display:block;\" />\n </td>\n <td style=\"text-align:right; vertical-align:middle;\">\n <div style=\"background:${priorityColor}; padding:8px 20px; border-radius:999px; font-size:12px; font-weight:700; letter-spacing:0.05em; display:inline-block; box-shadow:0 2px 8px rgba(0,0,0,0.2);\">\n ${ticketPriority} PRIORITY\n </div>\n </td>\n </tr>\n </table>\n \n <div style=\"margin-top:24px;\">\n <h1 style=\"margin:0; font-size:24px; font-weight:700; line-height:1.3;\">\n New Customer Complaint Received\n </h1>\n <div style=\"margin-top:8px; font-size:14px; opacity:0.9; display:flex; align-items:center; gap:12px;\">\n <span>\ud83d\udccd ${city}</span>\n <span style=\"opacity:0.6;\">\u2022</span>\n <span>\ud83d\udc64 ${fullName}</span>\n <span style=\"opacity:0.6;\">\u2022</span>\n <span>#${ticketId}</span>\n </div>\n </div>\n </div>\n \n <!-- Content Card -->\n <div style=\"background:#ffffff; border-radius:0 0 16px 16px; padding:32px; box-shadow:0 4px 20px rgba(0, 0, 0, 0.05); border:1px solid ${borderColor}; border-top:none;\">\n \n <!-- Quick Info Grid -->\n <div style=\"display:grid; grid-template-columns:repeat(auto-fit, minmax(200px, 1fr)); gap:20px; margin-bottom:32px;\">\n <div style=\"background:${primaryLight}; padding:20px; border-radius:12px; border-left:4px solid ${primaryDark};\">\n <div style=\"font-size:11px; font-weight:600; color:${textLight}; text-transform:uppercase; letter-spacing:0.05em; margin-bottom:6px;\">\n Customer\n </div>\n <div style=\"font-size:16px; font-weight:600; color:${textDark};\">\n ${fullName}\n </div>\n <div style=\"font-size:13px; color:${textMedium}; margin-top:4px;\">\n ${form.email}\n </div>\n </div>\n \n <div style=\"background:#fef7e6; padding:20px; border-radius:12px; border-left:4px solid ${accentGold};\">\n <div style=\"font-size:11px; font-weight:600; color:${textLight}; text-transform:uppercase; letter-spacing:0.05em; margin-bottom:6px;\">\n Order & Location\n </div>\n <div style=\"font-size:16px; font-weight:600; color:${textDark};\">\n Order #${orderNo}\n </div>\n <div style=\"font-size:13px; color:${textMedium}; margin-top:4px;\">\n ${city} \u2022 ${phone}\n </div>\n </div>\n </div>\n \n <!-- Ticket Details Section -->\n <div style=\"margin-bottom:32px;\">\n <div style=\"display:flex; align-items:center; margin-bottom:20px; padding-bottom:12px; border-bottom:1px solid ${borderColor};\">\n <div style=\"width:24px; height:24px; background:${primaryDark}; border-radius:6px; display:flex; align-items:center; justify-content:center; margin-right:12px;\">\n <span style=\"color:white; font-size:14px; font-weight:bold;\">\ud83d\udccb</span>\n </div>\n <h2 style=\"margin:0; font-size:18px; font-weight:600; color:${textDark};\">\n Ticket Details\n </h2>\n </div>\n \n <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"font-size:14px;\">\n <tr>\n <td style=\"padding:12px 0; color:${textMedium}; width:140px; border-bottom:1px solid #f1f5f9;\">\n Ticket ID\n </td>\n <td style=\"padding:12px 0; font-weight:500; color:${textDark}; border-bottom:1px solid #f1f5f9;\">\n ${ticketId}\n </td>\n </tr>\n <tr>\n <td style=\"padding:12px 0; color:${textMedium}; border-bottom:1px solid #f1f5f9;\">\n Subject\n </td>\n <td style=\"padding:12px 0; font-weight:500; color:${textDark}; border-bottom:1px solid #f1f5f9;\">\n ${ticketName}\n </td>\n </tr>\n <tr>\n <td style=\"padding:12px 0; color:${textMedium}; border-bottom:1px solid #f1f5f9;\">\n HubSpot Link\n </td>\n <td style=\"padding:12px 0; border-bottom:1px solid #f1f5f9;\">\n ${ticketUrl !== 'N/A'\n ? `<a href=\"${ticketUrl}\" style=\"color:#3b82f6; text-decoration:none; font-weight:500; display:inline-flex; align-items:center;\">\n <span style=\"margin-right:6px;\">\ud83d\udd17</span> \n View in HubSpot\n </a>`\n : 'N/A'}\n </td>\n </tr>\n <tr>\n <td style=\"padding:12px 0; color:${textMedium};\">\n Spam Status\n </td>\n <td style=\"padding:12px 0;\">\n <span style=\"background:${isSpam ? '#fee2e2' : '#dcfce7'}; color:${isSpam ? '#991b1b' : '#166534'}; padding:4px 12px; border-radius:999px; font-size:12px; font-weight:600;\">\n ${spamLabel}\n </span>\n </td>\n </tr>\n </table>\n </div>\n \n <!-- Customer Remarks Section -->\n <div>\n <div style=\"display:flex; align-items:center; margin-bottom:16px;\">\n <div style=\"width:24px; height:24px; background:${primaryDark}; border-radius:6px; display:flex; align-items:center; justify-content:center; margin-right:12px;\">\n <span style=\"color:white; font-size:14px; font-weight:bold;\">\ud83d\udcac</span>\n </div>\n <h2 style=\"margin:0; font-size:18px; font-weight:600; color:${textDark};\">\n Customer Remarks\n </h2>\n </div>\n \n <div style=\"background:${bgLight}; padding:24px; border-radius:12px; border:1px solid ${borderColor};\">\n <div style=\"font-size:15px; line-height:1.6; color:${textDark}; white-space:pre-line;\">\n ${remarks}\n </div>\n </div>\n </div>\n \n <!-- Action Required -->\n <div style=\"margin-top:32px; padding:20px; background:linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%); border-radius:12px; border:1px solid #bae6fd;\">\n <div style=\"display:flex; align-items:flex-start; gap:12px;\">\n <div style=\"flex-shrink:0; width:24px; height:24px; background:#0ea5e9; border-radius:50%; display:flex; align-items:center; justify-content:center;\">\n <span style=\"color:white; font-size:12px;\">\u26a1</span>\n </div>\n <div>\n <div style=\"font-size:14px; font-weight:600; color:#0369a1; margin-bottom:4px;\">\n Action Required\n </div>\n <div style=\"font-size:13px; color:#0c4a6e;\">\n Please assign this ticket to the relevant showroom or product line owner in HubSpot and update the public reply for the customer.\n </div>\n </div>\n </div>\n </div>\n </div>\n \n <!-- Footer -->\n <div style=\"text-align:center; margin-top:24px; padding-top:24px; border-top:1px solid ${borderColor};\">\n <div style=\"font-size:12px; color:${textLight};\">\n Auto-generated via Customer Care Workflow \u2022 ${new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}\n </div>\n </div>\n </div>\n </body>\n</html>\n`.trim();\n\n// =============== 4. Return payload for Email nodes ===============\nreturn [{\n json: {\n customerEmail: form.email,\n customerSubject,\n customerBody,\n teamEmail: 'support@example.com', // change if needed\n teamSubject,\n teamBody\n }\n}];"
},
"typeVersion": 2
},
{
"id": "15d9fc36-94c9-4b90-ba08-ff395947854e",
"name": "Send Email \u2013 Customer",
"type": "n8n-nodes-base.gmail",
"position": [
2624,
512
],
"parameters": {
"sendTo": "={{ $json.customerEmail }}",
"message": "={{ $json.customerBody }}",
"options": {
"appendAttribution": false
},
"subject": "={{ $json.customerSubject }}"
},
"typeVersion": 2.1
},
{
"id": "cc706420-3fcf-4d50-9c3e-c35879428389",
"name": "Send Email \u2013 CS Team",
"type": "n8n-nodes-base.gmail",
"position": [
2640,
704
],
"parameters": {
"sendTo": "={{ $json.teamEmail }}",
"message": "={{ $json.teamBody }}",
"options": {
"appendAttribution": false
},
"subject": "={{ $json.teamSubject }}"
},
"typeVersion": 2.1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"executionOrder": "v1"
},
"nodeGroups": [],
"connections": {
"If1": {
"main": [
[
{
"node": "Code in JavaScript3",
"type": "main",
"index": 0
}
],
[
{
"node": "Create a ticket1",
"type": "main",
"index": 0
}
]
]
},
"If2": {
"main": [
[
{
"node": "Create or update a contact1",
"type": "main",
"index": 0
}
],
[
{
"node": "Respond to Webhook",
"type": "main",
"index": 0
}
]
]
},
"Webhook": {
"main": [
[
{
"node": "Data Sorting1",
"type": "main",
"index": 0
}
]
]
},
"Send email1": {
"main": [
[
{
"node": "Code in JavaScript5",
"type": "main",
"index": 0
}
]
]
},
"Data Sorting1": {
"main": [
[
{
"node": "Google-captcha",
"type": "main",
"index": 0
}
]
]
},
"Google-captcha": {
"main": [
[
{
"node": "If2",
"type": "main",
"index": 0
}
]
]
},
"Create a ticket1": {
"main": [
[
{
"node": "Code in JavaScript4",
"type": "main",
"index": 0
},
{
"node": "HTTP Request1",
"type": "main",
"index": 0
}
]
]
},
"Respond to Webhook": {
"main": [
[
{
"node": "Build Email Payload (New)",
"type": "main",
"index": 0
}
]
]
},
"Code in JavaScript2": {
"main": [
[
{
"node": "Find Ticket Status1",
"type": "main",
"index": 0
}
]
]
},
"Code in JavaScript3": {
"main": [
[
{
"node": "Send email1",
"type": "main",
"index": 0
}
]
]
},
"Code in JavaScript4": {
"main": [
[
{
"node": "Respond to Webhook",
"type": "main",
"index": 0
}
]
]
},
"Code in JavaScript5": {
"main": [
[
{
"node": "Respond to Webhook1",
"type": "main",
"index": 0
}
]
]
},
"Find Ticket Status1": {
"main": [
[
{
"node": "If1",
"type": "main",
"index": 0
}
]
]
},
"Find if Ticket Exit1": {
"main": [
[
{
"node": "Code in JavaScript2",
"type": "main",
"index": 0
}
]
]
},
"Send Email \u2013 Customer": {
"main": [
[]
]
},
"Build Email Payload (New)": {
"main": [
[
{
"node": "Send Email \u2013 Customer",
"type": "main",
"index": 0
},
{
"node": "Send Email \u2013 CS Team",
"type": "main",
"index": 0
}
]
]
},
"Create or update a contact1": {
"main": [
[
{
"node": "Find if Ticket Exit1",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This webhook-based workflow validates form submissions with Google reCAPTCHA, creates or updates a contact in HubSpot, checks for an existing open ticket, and either creates a new HubSpot ticket or flags it as a duplicate, then emails support and returns an HTML response to the…
Source: https://n8n.io/workflows/16368/ — 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.
Receive booking requests via webhook with automatic validation, duplicate detection, availability checking, confirmation emails, Google Calendar sync, and Slack notifications.
Signup Intake → CRM triage. Uses formTrigger, googleSheets, telegram, telegramTrigger. Webhook trigger; 28 nodes.
This workflow automatically handles every resolved Jira bug by verifying the fix, notifying the customer, updating HubSpot, commenting on the Jira issue, alerting the team on Slack, and logging everyt
Automate short-term trading research by generating high-quality trade ideas using MCP (Market Context Protocol) signals and AI-powered analysis. 📈🤖 This workflow evaluates market context, catalysts, m
This workflow runs on a schedule to monitor HubSpot deals with upcoming contract expiry dates. It filters deals that are 30, 60, or 90 days away from expiration and processes each one individually. Ba