AutomationFlowsWeb Scraping › Process Testimonials with Gpt-4 & Auto-generate Social Media Cards with…

Process Testimonials with Gpt-4 & Auto-generate Social Media Cards with…

Original n8n title: Process Testimonials with Gpt-4 & Auto-generate Social Media Cards with Google Sheets

ByJitesh Dugar @jiteshdugar on n8n.io

This comprehensive workflow automates the entire testimonial collection and publishing process, from submission to social media-ready content. It uses AI to enhance testimonials, generates beautiful branded cards, and implements an approval system before posting. ✅ Webhook-based…

Webhook trigger★★★★★ complexityAI-powered34 nodesOpenAIN8N Nodes HtmlcsstoimageHTTP RequestGoogle DriveGoogle SheetsSlack
Web Scraping Trigger: Webhook Nodes: 34 Complexity: ★★★★★ AI nodes: yes Added:

This workflow corresponds to n8n.io template #10135 — we link there as the canonical source.

This workflow follows the Google Drive → 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 →

Download .json
{
  "id": "",
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "name": "Automated testimonial workflow",
  "tags": [],
  "nodes": [
    {
      "id": "4665148e-044d-44bc-982e-e253eb60f173",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -496,
        -64
      ],
      "parameters": {
        "color": 7,
        "width": 389,
        "height": 392,
        "content": "## \ud83d\ude80 WORKFLOW START: TESTIMONIAL COLLECTION\n\n**Purpose:** Entry point for testimonial submissions\n\n**Expected Data:**\n- name or Name\n- designation/Designation/role\n- testimonial_text/testimonial/Testimonial\n- photo_url/Photo/photo (optional)\n- email/Email (optional)\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "0edf9e7e-bd71-42da-b028-2ea8b7c8439b",
      "name": "Webhook Trigger",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -304,
        192
      ],
      "parameters": {
        "path": "testimonial-webhook",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "lastNode"
      },
      "typeVersion": 1.1
    },
    {
      "id": "3d5b7bac-6955-4c7b-bfbf-525b4b01416e",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -80,
        -96
      ],
      "parameters": {
        "color": 7,
        "width": 357,
        "height": 411,
        "content": "## \u2705 DATA VALIDATION & CLEANING\n\n**What it does:**\n- Extracts data from webhook payload\n- Validates required fields (name, testimonial)\n- Sets default values for missing fields\n- Generates fallback avatars using UI Avatars API\n- Removes HTML tags and extra spaces\n- Prevents XSS attacks"
      },
      "typeVersion": 1
    },
    {
      "id": "e2b98cfd-f458-491f-adf9-951daae6c164",
      "name": "Data Validation",
      "type": "n8n-nodes-base.code",
      "position": [
        64,
        192
      ],
      "parameters": {
        "jsCode": "// Data Validation Code for n8n\n// This code cleans and validates testimonial data from the webhook\n\nconst items = $input.all();\nconst cleanedItems = [];\n\nfor (const item of items) {\n  // Access the body data from webhook\n  const data = item.json.body || item.json;\n  \n  // Extract and clean fields\n  let name = (data.name || data.Name || '').trim();\n  let designation = (data.designation || data.Designation || data.role || '').trim();\n  let testimonial = (data.testimonial_text || data.testimonial || data.Testimonial || '').trim();\n  let photoUrl = (data.photo_url || data.Photo || data.photo || '').trim();\n  let email = (data.email || data.Email || '').trim();\n  \n  // Validation: Check required fields\n  if (!name || name.length < 2) {\n    name = 'Anonymous User';\n  }\n  \n  if (!testimonial || testimonial.length < 10) {\n    // Skip invalid testimonials\n    console.log('Skipping invalid testimonial - too short or empty');\n    continue;\n  }\n  \n  // Set defaults for optional fields\n  if (!designation) {\n    designation = 'Valued Customer';\n  }\n  \n  // Generate fallback avatar if no photo provided\n  if (!photoUrl || !photoUrl.startsWith('http')) {\n    photoUrl = 'https://ui-avatars.com/api/?name=' + encodeURIComponent(name) + '&size=400&background=4F46E5&color=fff&bold=true';\n  }\n  \n  // Clean testimonial text - remove extra spaces and HTML tags\n  testimonial = testimonial\n    .replace(/\\s+/g, ' ')    // Replace multiple spaces with single space\n    .replace(/[<>]/g, '')     // Remove < and > to prevent XSS\n    .trim();\n  \n  // Create clean object with all data\n  cleanedItems.push({\n    json: {\n      name: name,\n      designation: designation,\n      testimonial: testimonial,\n      photoUrl: photoUrl,\n      email: email,\n      timestamp: new Date().toISOString(),\n      originalLength: testimonial.length,\n      source: 'webhook'\n    }\n  });\n}\n\n// If no valid testimonials found, throw error\nif (cleanedItems.length === 0) {\n  throw new Error('No valid testimonials found after validation');\n}\n\nreturn cleanedItems;"
      },
      "typeVersion": 2
    },
    {
      "id": "825f049a-a2d5-47e2-83e7-57b319193a2e",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        288,
        -48
      ],
      "parameters": {
        "color": 7,
        "width": 295,
        "height": 379,
        "content": "## \ud83e\udd16 AI ENHANCEMENT SETUP\n\n**AI Instructions:**\n- Fix grammar and spelling\n- Keep natural and conversational\n- Maintain enthusiasm\n- No fake details\n- Similar length to original\n"
      },
      "typeVersion": 1
    },
    {
      "id": "6f77780d-a9e7-44a3-834b-89f41fa40219",
      "name": "Set AI Prompt",
      "type": "n8n-nodes-base.set",
      "position": [
        368,
        192
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "prompt-assignment",
              "name": "prompt",
              "type": "string",
              "value": "=You are a professional editor. Polish the following testimonial to be grammatically perfect while maintaining the original tone, enthusiasm, and authenticity. Keep it natural and conversational. Do not add fake details or change the meaning. Keep the length similar to the original.\n\nOriginal testimonial:\n{{ $json.testimonial }}\n\nReturn ONLY the improved testimonial text, nothing else. No quotes, no preamble."
            }
          ]
        }
      },
      "typeVersion": 3.3
    },
    {
      "id": "b9b9ee52-9122-4c55-90f3-cdfbda8bfcfc",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        608,
        -32
      ],
      "parameters": {
        "color": 7,
        "width": 327,
        "height": 347,
        "content": "## \ud83e\udde0 OPENAI PROCESSING\n\n**Model:** GPT-4 Turbo Preview\n\n**What happens:**\n1. Sends prompt + testimonial to OpenAI\n2. AI improves grammar and readability\n3. Preserves original meaning and tone\n4. Returns polished testimonial\n"
      },
      "typeVersion": 1
    },
    {
      "id": "5fdf887b-e1aa-4122-8ede-1ef586072a06",
      "name": "OpenAI Enhancement",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        640,
        192
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4-turbo-preview",
          "cachedResultName": "GPT-4-TURBO-PREVIEW"
        },
        "options": {
          "maxTokens": 300,
          "temperature": 0.3
        },
        "messages": {
          "values": [
            {
              "content": "={{ $json.prompt }}"
            }
          ]
        }
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "d3b64d7c-c54e-4eeb-bec6-9b56ac3dba07",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        944,
        -16
      ],
      "parameters": {
        "color": 7,
        "width": 343,
        "height": 331,
        "content": "## \ud83d\udce6 EXTRACT AI RESPONSE\n\n**What it does:**\n- Extracts enhanced text from OpenAI response\n- Handles multiple response formats\n- Merges with original data\n- Tracks enhancement metadata\n"
      },
      "typeVersion": 1
    },
    {
      "id": "2f0c7201-a5e4-4b3a-be91-0f200f8f249f",
      "name": "Extract AI Response",
      "type": "n8n-nodes-base.code",
      "position": [
        1040,
        192
      ],
      "parameters": {
        "jsCode": "// Extract AI Response Code (Updated for OpenAI format)\n// This extracts the enhanced testimonial from OpenAI and merges with previous data\n\nconst items = $input.all();\n\nreturn items.map((item) => {\n  // Get the original data from Data Validation node\n  const previousData = $('Data Validation').item.json;\n  \n  // Extract AI response from OpenAI's response format\n  // OpenAI returns: [{ message: { content: \"text\" } }]\n  let aiResponse;\n  \n  if (Array.isArray(item.json) && item.json[0]?.message?.content) {\n    // Handle array format from OpenAI\n    aiResponse = item.json[0].message.content;\n  } else if (item.json.message?.content) {\n    // Handle object format\n    aiResponse = item.json.message.content;\n  } else if (item.json.choices?.[0]?.message?.content) {\n    // Handle choices format\n    aiResponse = item.json.choices[0].message.content;\n  } else if (item.json.text) {\n    // Handle simple text format\n    aiResponse = item.json.text;\n  } else {\n    // Fallback to original testimonial if extraction fails\n    aiResponse = previousData.testimonial;\n  }\n  \n  // Return merged data with both original and enhanced testimonial\n  return {\n    json: {\n      ...previousData,                              // Keep all original data\n      originalTestimonial: previousData.testimonial,  // Save original\n      testimonial: aiResponse.trim(),                 // Use enhanced version\n      enhanced: true,                                 // Mark as enhanced\n      aiModel: 'gpt-4-turbo'                         // Track which model used\n    }\n  };\n});"
      },
      "typeVersion": 2
    },
    {
      "id": "7ec4da11-b438-4877-be02-f967719aef64",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1296,
        -96
      ],
      "parameters": {
        "color": 7,
        "width": 272,
        "height": 427,
        "content": "## \ud83c\udfa8 HTML TEMPLATE GENERATION\n\n**What it does:**\n- Creates beautiful testimonial card\n- Responsive design with gradient background\n- Professional styling with Inter font\n- 5-star rating display\n- Customer photo with fallback initials\n- XSS protection"
      },
      "typeVersion": 1
    },
    {
      "id": "b57bee88-1bc0-4bd2-b6cc-73ff96b56797",
      "name": "Generate HTML Template",
      "type": "n8n-nodes-base.code",
      "position": [
        1344,
        192
      ],
      "parameters": {
        "jsCode": "// Generate HTML Template Code - FIXED VERSION\n// Creates a beautiful branded testimonial card with proper image handling\n\nconst items = $input.all();\n\nreturn items.map(item => {\n  const data = item.json;\n  \n  // Function to escape HTML special characters (prevent XSS)\n  const escapeHtml = (text) => {\n    return text\n      .replace(/&/g, '&amp;')\n      .replace(/</g, '&lt;')\n      .replace(/>/g, '&gt;')\n      .replace(/\"/g, '&quot;')\n      .replace(/'/g, '&#039;');\n  };\n  \n  // Get initials for fallback\n  const getInitials = (name) => {\n    return name\n      .split(' ')\n      .map(n => n[0])\n      .join('')\n      .toUpperCase()\n      .substring(0, 2);\n  };\n  \n  const initials = getInitials(data.name);\n  \n  // Generate the HTML testimonial card\n  const html = `<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap\" rel=\"stylesheet\">\n  <style>\n    * {\n      margin: 0;\n      padding: 0;\n      box-sizing: border-box;\n    }\n    \n    body {\n      width: 800px;\n      height: 600px;\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n      font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n      padding: 40px;\n    }\n    \n    .testimonial-card {\n      background: white;\n      border-radius: 24px;\n      padding: 48px;\n      max-width: 700px;\n      box-shadow: 0 20px 60px rgba(0,0,0,0.3);\n      position: relative;\n      overflow: hidden;\n    }\n    \n    .quote-icon {\n      position: absolute;\n      top: 30px;\n      left: 40px;\n      font-size: 80px;\n      color: #667eea;\n      opacity: 0.15;\n      font-family: Georgia, serif;\n      line-height: 1;\n    }\n    \n    .star-rating {\n      color: #fbbf24;\n      font-size: 24px;\n      margin-bottom: 20px;\n      position: relative;\n      z-index: 1;\n      letter-spacing: 4px;\n    }\n    \n    .testimonial-text {\n      font-size: 20px;\n      line-height: 1.7;\n      color: #2d3748;\n      margin-bottom: 32px;\n      position: relative;\n      z-index: 1;\n      font-style: italic;\n    }\n    \n    .author-section {\n      display: flex;\n      align-items: center;\n      gap: 20px;\n      position: relative;\n      z-index: 1;\n    }\n    \n    .author-photo-wrapper {\n      width: 72px;\n      height: 72px;\n      border-radius: 50%;\n      border: 4px solid #667eea;\n      box-shadow: 0 4px 12px rgba(0,0,0,0.1);\n      overflow: hidden;\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n      flex-shrink: 0;\n    }\n    \n    .author-photo {\n      width: 100%;\n      height: 100%;\n      object-fit: cover;\n      display: block;\n    }\n    \n    .author-initials {\n      display: none;\n      font-size: 28px;\n      font-weight: 700;\n      color: white;\n      user-select: none;\n    }\n    \n    .author-photo-wrapper.fallback .author-photo {\n      display: none;\n    }\n    \n    .author-photo-wrapper.fallback .author-initials {\n      display: flex;\n    }\n    \n    .author-info h3 {\n      font-size: 22px;\n      color: #1a202c;\n      margin-bottom: 4px;\n      font-weight: 700;\n    }\n    \n    .author-info p {\n      font-size: 16px;\n      color: #718096;\n      font-weight: 500;\n    }\n    \n    .brand-logo {\n      position: absolute;\n      bottom: 30px;\n      right: 40px;\n      font-size: 14px;\n      color: #a0aec0;\n      font-weight: 600;\n      letter-spacing: 1px;\n      text-transform: uppercase;\n    }\n    \n    .decorative-circle {\n      position: absolute;\n      width: 200px;\n      height: 200px;\n      border-radius: 50%;\n      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n      opacity: 0.05;\n      bottom: -100px;\n      right: -100px;\n    }\n  </style>\n  <script>\n    function handleImageError(img) {\n      var wrapper = img.parentElement;\n      wrapper.classList.add('fallback');\n    }\n  </script>\n</head>\n<body>\n  <div class=\"testimonial-card\">\n    <div class=\"quote-icon\">\"</div>\n    <div class=\"decorative-circle\"></div>\n    \n    <div class=\"star-rating\">\n      \u2605\u2605\u2605\u2605\u2605\n    </div>\n    \n    <div class=\"testimonial-text\">\n      \"${escapeHtml(data.testimonial)}\"\n    </div>\n    \n    <div class=\"author-section\">\n      <div class=\"author-photo-wrapper\">\n        <img \n          src=\"${data.photoUrl}\" \n          alt=\"${escapeHtml(data.name)}\" \n          class=\"author-photo\" \n          onerror=\"handleImageError(this)\"\n        />\n        <div class=\"author-initials\">${initials}</div>\n      </div>\n      <div class=\"author-info\">\n        <h3>${escapeHtml(data.name)}</h3>\n        <p>${escapeHtml(data.designation)}</p>\n      </div>\n    </div>\n    \n    <div class=\"brand-logo\">\n      YOUR BRAND\n    </div>\n  </div>\n</body>\n</html>`;\n  \n  // Generate a unique filename\n  const fileName = `${data.name.replace(/\\s+/g, '_')}_Testimonial_${new Date().getTime()}.png`;\n  \n  // Return the HTML and filename\n  return {\n    json: {\n      ...data,           // Keep all previous data\n      html: html,        // Add HTML template\n      fileName: fileName // Add filename for later use\n    }\n  };\n});"
      },
      "typeVersion": 2
    },
    {
      "id": "50e726ce-705a-4bc4-a98e-f0653a26ad47",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1584,
        -80
      ],
      "parameters": {
        "color": 7,
        "width": 256,
        "height": 427,
        "content": "## \ud83d\udcf8 HTML TO IMAGE CONVERSION\n\n**Service:**(https://htmlcsstoimg.com)\n\n**What it does:**\n- Converts HTML template to PNG image\n- Renders at 800x600px\n- Returns image URL"
      },
      "typeVersion": 1
    },
    {
      "id": "abad8276-3b3e-4414-ad1d-7c4105e86d03",
      "name": "HTML/CSS to Image",
      "type": "n8n-nodes-htmlcsstoimage.htmlCssToImage",
      "position": [
        1632,
        192
      ],
      "parameters": {
        "html_content": "={{ $json.html }}"
      },
      "credentials": {
        "htmlcsstoimgApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "1c06771d-8cb7-4870-b3a1-a6960ffd0029",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1856,
        -80
      ],
      "parameters": {
        "color": 7,
        "width": 288,
        "height": 411,
        "content": "## \u2b07\ufe0f DOWNLOAD IMAGE\n\n**What it does:**\n- Fetches PNG from URL\n- Downloads as binary file\n- Prepares for Google Drive upload\n\n**HTTP Request Settings:**\n- Method: GET\n- URL: From previous node\n- Response: Binary file format\n"
      },
      "typeVersion": 1
    },
    {
      "id": "94929251-c783-4ede-9710-6aff9fc59ea1",
      "name": "Download Image",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1920,
        192
      ],
      "parameters": {
        "url": "={{ $json.image_url }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "b79caf76-ec04-4a57-90d7-7ae3ff802e2e",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2160,
        -112
      ],
      "parameters": {
        "color": 7,
        "width": 288,
        "height": 443,
        "content": "## \u2601\ufe0f GOOGLE DRIVE UPLOAD\n\n**What it does:**\n- Uploads PNG to Google Drive\n- Saves to \"testimonial data\" folder\n- Uses dynamic filename from HTML node\n- Returns shareable link and Drive ID\n\n**File Name Format:**\nCustomerName_Testimonial_Timestamp.png\n"
      },
      "typeVersion": 1
    },
    {
      "id": "e028a171-81b8-4120-b443-414fbd77dc46",
      "name": "Upload to Google Drive",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        2208,
        192
      ],
      "parameters": {
        "name": "={{ $('Generate HTML Template').item.json.fileName }}",
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive",
          "cachedResultUrl": "https://drive.google.com/drive/my-drive",
          "cachedResultName": "My Drive"
        },
        "options": {},
        "folderId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_DRIVE_FOLDER_ID",
          "cachedResultUrl": "https://drive.google.com/drive/folders/YOUR_GOOGLE_DRIVE_FOLDER_ID",
          "cachedResultName": "testimonial data"
        }
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "7990f760-83ad-483c-adea-2f9eac6020d7",
      "name": "Update Google Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2544,
        192
      ],
      "parameters": {
        "columns": {
          "value": {
            "Name": "={{ $('Generate HTML Template').item.json.name }}",
            "Email": "={{ $('Generate HTML Template').item.json.email }}",
            "Source": "={{ $('Generate HTML Template').item.json.source }}",
            "Status": "Pending Approval",
            "Drive ID": "={{ $json.id }}",
            "Enhanced": "={{ $('Generate HTML Template').item.json.enhanced ? 'Yes' : 'No' }}",
            "Timestamp": "={{ $('Generate HTML Template').item.json.timestamp }}",
            "Image Link": "={{ $json.webViewLink }}",
            "Designation": "={{ $('Generate HTML Template').item.json.designation }}",
            "Testimonial": "={{ $('Generate HTML Template').item.json.testimonial }}",
            "Original Length": "={{ $('Generate HTML Template').item.json.originalLength }}",
            "Original Testimonial": "={{ $('Generate HTML Template').item.json.originalTestimonial }}"
          },
          "schema": [
            {
              "id": "Timestamp",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Designation",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Designation",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Testimonial",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Testimonial",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Original Testimonial",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Original Testimonial",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Image Link",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Image Link",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Drive ID",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Drive ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Email",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Original Length",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Original Length",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Enhanced",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Enhanced",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Source",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Source",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Posted to Social",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Posted to Social",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Posted At",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Posted At",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Drive ID"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1358321955,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEETS_ID_HERE/edit#gid=1358321955",
          "cachedResultName": "Testimonial Database"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_SHEETS_ID_HERE",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEETS_ID_HERE/edit?usp=drivesdk",
          "cachedResultName": "Feedback Log"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "496bcc78-6f79-4719-aaf6-14f83c27e13b",
      "name": "Sticky Note10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2768,
        -48
      ],
      "parameters": {
        "color": 7,
        "width": 272,
        "height": 395,
        "content": "## \ud83d\udce2 SLACK NOTIFICATION\n\n**Notification Includes:**\n\u2713 Customer name, role, email\n\u2713 Enhanced testimonial\n\u2713 Original testimonial (comparison)\n\u2713 Image preview link\n\u2713 Database status\n\u2713 Action checklist\n"
      },
      "typeVersion": 1
    },
    {
      "id": "1baf7d5d-478f-4d8c-9fe1-4b0a1947109e",
      "name": "Send Slack Notification",
      "type": "n8n-nodes-base.slack",
      "position": [
        2832,
        192
      ],
      "parameters": {
        "text": "=\ud83c\udf89 *New Testimonial Received!*\n\n*Customer Details:*\n- Name: {{ $('Generate HTML Template').item.json.name }}\n- Role: {{ $('Generate HTML Template').item.json.designation }}\n- Email: {{ $('Generate HTML Template').item.json.email }}\n\n*Testimonial:*\n_\"{{ $('Generate HTML Template').item.json.testimonial }}\"_\n\n*Original:*\n_\"{{ $('Generate HTML Template').item.json.originalTestimonial }}\"_\n\n*Image Preview:*\n{{ $('Upload to Google Drive').item.json.webViewLink }}\n\n*Database:*\nStatus: {{ $json.Status }}\nSheet: [View Testimonial Database](https://docs.google.com/spreadsheets/d/YOUR_SHEET_ID/edit)\n\n*Actions Required:*\n1. Review the testimonial and image\n2. Update status in Google Sheet to \"Approved\" or \"Rejected\"\n3. Approved testimonials will auto-post to social media\n\n---\n_Submitted via webhook at {{ $('Generate HTML Template').item.json.timestamp }}_",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "SLACK_CHANNEL_ID",
          "cachedResultName": "SLACK_CHANNEL_NAME"
        },
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "f95c7659-6e59-468b-9a53-4047a8e2f337",
      "name": "Sticky Note11",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1184,
        512
      ],
      "parameters": {
        "color": 7,
        "width": 368,
        "height": 411,
        "content": "## \u23f0 SCHEDULE TRIGGER - APPROVAL CHECKER\n\n**Purpose:** Automated approval monitoring\n\n**Trigger:** Every 5 minutes\n\n**What it does:**\n- Checks Google Sheet for approved testimonials\n- Identifies testimonials not yet posted\n- Triggers social media posting workflow"
      },
      "typeVersion": 1
    },
    {
      "id": "93107afd-1e32-4f86-928a-baf2e9c5a24c",
      "name": "Every 5 Minutes",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        1280,
        784
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "75ff904f-6a28-459a-999c-f959985088ee",
      "name": "Sticky Note12",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1568,
        576
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 347,
        "content": "## \ud83d\udcd6 READ DATABASE\n\n**Data Retrieved:**\n- All testimonial records\n- Status column (Approved/Pending/Rejected)\n- Posted to Social column\n- Drive ID for matching\n"
      },
      "typeVersion": 1
    },
    {
      "id": "f5180c82-5bc1-4676-9e57-08e65ff741ae",
      "name": "Read Google Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1616,
        784
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1358321955,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID/edit#gid=1358321955",
          "cachedResultName": "Testimonial Database"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_SHEET_ID",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID/edit?usp=drivesdk",
          "cachedResultName": "Feedback Log"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "2a5196a0-de9c-48cc-8e48-805c2228ddf7",
      "name": "Sticky Note13",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1904,
        528
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 395,
        "content": "## \u2714\ufe0f CONDITIONAL FILTER\n\n**What it does:**\n- Filters testimonials based on conditions\n- Only passes approved testimonials\n- Excludes already posted ones\n\n**Logic:**\n- TRUE path: Ready to post\n- FALSE path: Skip (not approved or already posted)\n"
      },
      "typeVersion": 1
    },
    {
      "id": "f516c987-5dac-4580-abf1-a29c111e5b5e",
      "name": "IF Approved & Not Posted",
      "type": "n8n-nodes-base.if",
      "position": [
        1984,
        784
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "status-check",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.Status }}",
              "rightValue": "Approved"
            },
            {
              "id": "not-posted",
              "operator": {
                "type": "string",
                "operation": "empty",
                "singleValue": true
              },
              "leftValue": "={{ $json['Posted to Social'] }}",
              "rightValue": "Yes"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "96ecfaa0-9c6d-4711-941c-4cdeb8397026",
      "name": "Sticky Note14",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2240,
        432
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 395,
        "content": "## \ud83d\udcf1 SOCIAL MEDIA READY NOTIFICATION\n\n**Notification Contains:**\n\u2713 Customer details\n\u2713 Enhanced testimonial\n\u2713 Original testimonial\n\u2713 Image preview link\n\u2713 **Ready-to-post social media text**\n\u2713 Hashtags included\n"
      },
      "typeVersion": 1
    },
    {
      "id": "9284d544-2cf9-4d67-830e-ef3de1990efb",
      "name": "Notify Ready to Post",
      "type": "n8n-nodes-base.slack",
      "position": [
        2304,
        672
      ],
      "parameters": {
        "text": "=\u2705 *Testimonial Ready for Social Media!*\n\n*Customer Details:*\n- Name: {{ $json.Name }}\n- Role: {{ $json.Designation }}\n- Email: {{ $json.Email }}\n\n*Testimonial:*\n_\"{{ $json.Testimonial }}\"_\n\n*Original:*\n_\"{{ $json['Original Testimonial'] }}\"_\n\n*View Image:*\n{{ $json['Image Link'] }}\n\n---\n\n*\ud83d\udcf1 Ready-to-Post Social Media Text:*\n\n\ud83c\udf1f Hear what our clients say!\n\n\"{{ $json.Testimonial }}\"\n\n\u2014 {{ $json.Name }}, {{ $json.Designation }}\n\n#ClientSuccess #Testimonial #CustomerReview\n\n---\n\n*Actions Required:*\n1. Copy the text above\n2. Post to Twitter & LinkedIn manually\n3. This testimonial will be marked as processed\n\n_Automated by n8n \u2022 {{ $now.format('MMM DD, YYYY @ HH:mm') }}_",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "SLACK_CHANNEL_ID",
          "cachedResultName": "SLACK_CHANNEL_NAME"
        },
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "e63a79cc-2313-4d65-a349-b261dd17fecd",
      "name": "Sticky Note15",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2592,
        512
      ],
      "parameters": {
        "color": 7,
        "width": 336,
        "height": 315,
        "content": "## \ud83d\udd04 MARK AS PROCESSED\n\n**What it does:**\n- Updates Google Sheet record\n- Marks testimonial as processed\n- Adds timestamp\n\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "6b7e87eb-7958-4bab-bde9-db1df08c3148",
      "name": "Mark as Posted",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2656,
        672
      ],
      "parameters": {
        "columns": {
          "value": {
            "Drive ID": "={{ $('IF Approved & Not Posted').item.json[\"Drive ID\"] }}",
            "Posted At": "={{ $now.toISO() }}",
            "Posted to Social": "Ready - Manual Post Required"
          },
          "schema": [
            {
              "id": "Timestamp",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Designation",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Designation",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Testimonial",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Testimonial",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Original Testimonial",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Original Testimonial",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Image Link",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Image Link",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Drive ID",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Drive ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Email",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Original Length",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Original Length",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Enhanced",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Enhanced",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Source",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Source",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Posted to Social",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Posted to Social",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Posted At",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Posted At",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Drive ID"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1358321955,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEETS_ID_HERE/edit#gid=1358321955",
          "cachedResultName": "Testimonial Database"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_SHEETS_ID_HERE",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEETS_ID_HERE/edit?usp=drivesdk",
          "cachedResultName": "Feedback Log"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "bf68641b-18b6-4fe7-ba2e-bf418f44aef8",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2464,
        -32
      ],
      "parameters": {
        "color": 7,
        "width": 288,
        "height": 363,
        "content": "## \ud83d\udcca DATABASE UPDATE\n\n**Data Saved:**\n\u2713 Timestamp, Name, Designation, Email\n\u2713 Original + Enhanced testimonial\n\u2713 Image link (shareable)\n\u2713 Status: \"Pending Approval\"\n\u2713 Enhancement metadata\n\u2713 Source tracking\n"
      },
      "typeVersion": 1
    },
    {
      "id": "564a5664-46e7-417c-b01c-ffc7f761184b",
      "name": "Sticky Note16",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -896,
        -128
      ],
      "parameters": {
        "color": 7,
        "width": 368,
        "height": 560,
        "content": "## \ud83d\udccb **SETUP CREDENTIALS FIRST**\n\n### **Required API Credentials:**\n\n#### 1. **OpenAI API** \ud83e\udd16\n- Sign up at: https://platform.openai.com\n- Get API key from dashboard\n\n#### 2. **HTML/CSS to Image API** \ud83d\udcf8\n- Sign up at: https://htmlcsstoimg.com\n\n#### 3. **Google Drive OAuth2** \u2601\ufe0f\n- Enable Google Drive API in Google Cloud Console\n\n#### 4. **Google Sheets OAuth2** \ud83d\udcca\n- Use same Google Cloud project\n\n#### 5. **Slack API** \ud83d\udcac\n- Create Slack App at: https://api.slack.com/apps\n"
      },
      "typeVersion": 1
    },
    {
      "id": "4be49892-0a67-4eb2-8cc9-c18b92559a76",
      "name": "Sticky Note17",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1312,
        -48
      ],
      "parameters": {
        "color": 7,
        "width": 384,
        "height": 480,
        "content": "## \ud83e\uddea **TEST WORKFLOW**\n\n**Set Headers:**\n   - `Content-Type`: `application/json`\n**Paste this in Body (raw JSON):**\n\n```json\n{\n    \"name\": \"Sarah Johnson\",\n    \"designation\": \"Marketing Director\",\n    \"photo_url\": \"https://i.pravatar.cc/400?img=5\",\n    \"testimonial_text\": \"Working with this team was amazing!\",\n    \"email\": \"sample@gmail.com\"\n}\n```\n\n### **Expected Result:**\n\u2705 Testimonial processed in ~15-30 seconds\n\u2705 Image uploaded to Google Drive\n\u2705 Entry added to Google Sheets\n\u2705 Slack notification sent\n\n"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "",
  "connections": {
    "Set AI Prompt": {
      "main": [
        [
          {
            "node": "OpenAI Enhancement",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Image": {
      "main": [
        [
          {
            "node": "Upload to Google Drive",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Data Validation": {
      "main": [
        [
          {
            "node": "Set AI Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Every 5 Minutes": {
      "main": [
        [
          {
            "node": "Read Google Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook Trigger": {
      "main": [
        [
          {
            "node": "Data Validation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTML/CSS to Image": {
      "main": [
        [
          {
            "node": "Download Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Google Sheet": {
      "main": [
        [
          {
            "node": "IF Approved & Not Posted",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Enhancement": {
      "main": [
        [
          {
            "node": "Extract AI Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract AI Response": {
      "main": [
        [
          {
            "node": "Generate HTML Template",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Google Sheet": {
      "main": [
        [
          {
            "node": "Send Slack Notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Notify Ready to Post": {
      "main": [
        [
          {
            "node": "Mark as Posted",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate HTML Template": {
      "main": [
        [
          {
            "node": "HTML/CSS to Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload to Google Drive": {
      "main": [
        [
          {
            "node": "Update Google Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF Approved & Not Posted": {
      "main": [
        [
          {
            "node": "Notify Ready to Post",
            "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.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This comprehensive workflow automates the entire testimonial collection and publishing process, from submission to social media-ready content. It uses AI to enhance testimonials, generates beautiful branded cards, and implements an approval system before posting. ✅ Webhook-based…

Source: https://n8n.io/workflows/10135/ — original creator credit. Request a take-down →

More Web Scraping workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Web Scraping

Automatically transform resolved support tickets into professional, AI-powered PDF documentation with complete tracking and team notifications.

OpenAI, Google Drive, Google Sheets +3
Web Scraping

Simplify employee performance reviews with AI-powered automation. This workflow transforms raw feedback and evaluation inputs into clear, structured, and professional performance review summaries — sa

OpenAI, Gmail, HTTP Request +3
Web Scraping

This workflow automates the entire pre-issuance process of workshop participation certificates. When an attendee submits a registration form via a webhook, the workflow validates the data, verifies th

Google Drive, Gmail, Google Sheets +4
Web Scraping

Automated Email Verification & Digital Health Card Generator

HTTP Request, Google Drive, Gmail +5
Web Scraping

Fully automated meeting documentation workflow that uses AI to transform raw transcripts into professional PDFs and actionable tasks. AI-powered summary generation (GPT-4) Automatic action item extrac

OpenAI, N8N Nodes Htmlcsstopdf, Gmail +4