AutomationFlowsAI & RAG › Streamline Content Creation with Gpt-4o and One-click Human Review Approvals

Streamline Content Creation with Gpt-4o and One-click Human Review Approvals

ByCheng Siong Chin @cschin on n8n.io

Automate AI content creation from request to approval. While AI writes quickly, human review often delays delivery—and multiple tools create workflow gaps and version confusion. This unified solution streamlines the entire process, enabling teams to produce quality content at…

Event trigger★★★★☆ complexityAI-powered23 nodesForm TriggerChain LlmOpenAI ChatGoogle SheetsGmail
AI & RAG Trigger: Event Nodes: 23 Complexity: ★★★★☆ AI nodes: yes Added:

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

This workflow follows the Chainllm → Form Trigger 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": "QQvh5D3K5kyo0IgA",
  "name": "AI Content Generation via OpenAI GPT-4o with Human Review and One-Click Approval Workflow",
  "tags": [],
  "nodes": [
    {
      "id": "ce14c76d-7e1a-49e3-a072-7fd02ddc0419",
      "name": "\ud83d\udce5 Content Request Form",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        0,
        0
      ],
      "parameters": {
        "path": "79adff4f-bffa-47ef-9e28-6bad05d94d89",
        "options": {},
        "formTitle": "\ud83c\udfa8 AI Content Request",
        "formFields": {
          "values": [
            {
              "fieldType": "dropdown",
              "fieldLabel": "Content Type",
              "fieldOptions": {
                "values": [
                  {
                    "option": "Blog Post"
                  },
                  {
                    "option": "Social Media Post"
                  },
                  {
                    "option": "Email Newsletter"
                  },
                  {
                    "option": "Product Description"
                  },
                  {
                    "option": "Video Script"
                  },
                  {
                    "option": "Press Release"
                  },
                  {
                    "option": "Case Study"
                  },
                  {
                    "option": "Landing Page Copy"
                  }
                ]
              },
              "requiredField": true
            },
            {
              "fieldLabel": "Topic / Title",
              "requiredField": true
            },
            {
              "fieldLabel": "Target Audience",
              "requiredField": true
            },
            {
              "fieldType": "textarea",
              "fieldLabel": "Key Points / Requirements",
              "requiredField": true
            },
            {
              "fieldType": "dropdown",
              "fieldLabel": "Tone of Voice",
              "fieldOptions": {
                "values": [
                  {
                    "option": "Professional"
                  },
                  {
                    "option": "Casual & Friendly"
                  },
                  {
                    "option": "Technical & Expert"
                  },
                  {
                    "option": "Inspirational"
                  },
                  {
                    "option": "Humorous"
                  },
                  {
                    "option": "Educational"
                  }
                ]
              },
              "requiredField": true
            },
            {
              "fieldType": "dropdown",
              "fieldLabel": "Word Count",
              "fieldOptions": {
                "values": [
                  {
                    "option": "Short (300-500 words)"
                  },
                  {
                    "option": "Medium (800-1200 words)"
                  },
                  {
                    "option": "Long (1500-2500 words)"
                  }
                ]
              }
            },
            {
              "fieldType": "email",
              "fieldLabel": "Reviewer Email",
              "requiredField": true
            },
            {
              "fieldType": "dropdown",
              "fieldLabel": "Priority",
              "fieldOptions": {
                "values": [
                  {
                    "option": "\ud83d\udd34 Urgent (24h)"
                  },
                  {
                    "option": "\ud83d\udfe1 Normal (3 days)"
                  },
                  {
                    "option": "\ud83d\udfe2 Low (1 week)"
                  }
                ]
              }
            }
          ]
        },
        "formDescription": "Submit your content requirements and let AI create the first draft. You'll review and approve before publishing."
      },
      "typeVersion": 2.1
    },
    {
      "id": "5b21fbcc-a5c0-4b3a-a9e1-5cb05d591bd2",
      "name": "\ud83e\udd16 Generate AI Content",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        464,
        0
      ],
      "parameters": {
        "text": "=You are an expert content writer specializing in {{ $json.contentType }}.\n\n**Assignment:**\nCreate engaging {{ $json.contentType }} content about: {{ $json.topic }}\n\n**Target Audience:** {{ $json.audience }}\n\n**Tone:** {{ $json.tone }}\n\n**Word Count Target:** {{ $json.wordTarget }} words\n\n**Requirements:**\n{{ $json.requirements }}\n\n**Instructions:**\n1. Research the topic thoroughly\n2. Write in the specified tone consistently\n3. Include compelling headlines/hooks\n4. Add relevant examples and data points\n5. Include a clear call-to-action\n6. Optimize for readability (short paragraphs, subheadings)\n7. If blog post, include SEO-optimized meta description\n\n**Format:**\nTitle: [Compelling title here]\n\nMeta Description: [If applicable]\n\n[Main content body]\n\nCTA: [Call to action]\n\n---\n\nBegin writing now:",
        "promptType": "define"
      },
      "typeVersion": 1.4
    },
    {
      "id": "7e5c33e0-c201-4287-a0bf-2fdf64f3a86c",
      "name": "OpenAI GPT-4o",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        464,
        144
      ],
      "parameters": {
        "model": "gpt-4o",
        "options": {
          "maxTokens": 3000,
          "temperature": 0.7
        }
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "a08011d4-30c4-49d1-a80c-2197e058952e",
      "name": "\ud83d\udd17 Generate Review Links",
      "type": "n8n-nodes-base.code",
      "position": [
        912,
        224
      ],
      "parameters": {
        "jsCode": "// Generate review links (webhook URLs for approve/reject/edit)\nconst baseUrl = $env.WEBHOOK_URL || 'https://your-n8n-instance.com';\nconst requestId = $json.requestId;\n\nconst reviewLinks = {\n  approve: `${baseUrl}/webhook/content-review?action=approve&id=${requestId}`,\n  reject: `${baseUrl}/webhook/content-review?action=reject&id=${requestId}`,\n  requestEdit: `${baseUrl}/webhook/content-review?action=edit&id=${requestId}`\n};\n\nreturn [{\n  json: {\n    ...$json,\n    reviewLinks: reviewLinks\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "7420d0ad-1c5c-4c51-9f88-eb13e92dddb8",
      "name": "\ud83d\udce7 Create Review Email",
      "type": "n8n-nodes-base.code",
      "position": [
        912,
        464
      ],
      "parameters": {
        "jsCode": "// Create beautiful HTML email for review\nconst data = $input.first().json;\n\nconst html = `\n<!DOCTYPE html>\n<html>\n<head>\n  <style>\n    body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 0; padding: 0; background: #f4f4f4; }\n    .container { max-width: 700px; margin: 20px auto; background: white; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }\n    .header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; text-align: center; }\n    .header h1 { margin: 0; font-size: 28px; }\n    .badge { display: inline-block; background: rgba(255,255,255,0.2); padding: 5px 15px; border-radius: 20px; font-size: 14px; margin-top: 10px; }\n    .content { padding: 30px; }\n    .meta { background: #f8f9fa; padding: 20px; border-radius: 8px; margin-bottom: 20px; }\n    .meta-item { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #e0e0e0; }\n    .meta-item:last-child { border-bottom: none; }\n    .meta-label { font-weight: 600; color: #666; }\n    .meta-value { color: #333; }\n    .quality-score { text-align: center; padding: 20px; background: ${data.stats.qualityScore >= 80 ? '#d1fae5' : data.stats.qualityScore >= 60 ? '#fef3c7' : '#fee2e2'}; border-radius: 8px; margin: 20px 0; }\n    .quality-number { font-size: 48px; font-weight: bold; color: ${data.stats.qualityScore >= 80 ? '#065f46' : data.stats.qualityScore >= 60 ? '#92400e' : '#991b1b'}; margin: 0; }\n    .draft-content { background: #fafafa; padding: 25px; border-left: 4px solid #667eea; border-radius: 4px; margin: 20px 0; max-height: 500px; overflow-y: auto; }\n    .draft-content pre { white-space: pre-wrap; font-family: inherit; line-height: 1.6; }\n    .actions { display: flex; gap: 15px; margin-top: 30px; }\n    .btn { flex: 1; padding: 15px 30px; text-align: center; text-decoration: none; border-radius: 6px; font-weight: 600; font-size: 16px; transition: all 0.3s; }\n    .btn-approve { background: #10b981; color: white; }\n    .btn-approve:hover { background: #059669; }\n    .btn-edit { background: #f59e0b; color: white; }\n    .btn-edit:hover { background: #d97706; }\n    .btn-reject { background: #ef4444; color: white; }\n    .btn-reject:hover { background: #dc2626; }\n    .footer { background: #f8f9fa; padding: 20px; text-align: center; color: #666; font-size: 14px; }\n  </style>\n</head>\n<body>\n  <div class=\"container\">\n    <div class=\"header\">\n      <h1>\u2728 AI Content Ready for Review</h1>\n      <span class=\"badge\">${data.requestId}</span>\n    </div>\n    \n    <div class=\"content\">\n      <h2>\ud83d\udccb Content Details</h2>\n      <div class=\"meta\">\n        <div class=\"meta-item\">\n          <span class=\"meta-label\">Content Type:</span>\n          <span class=\"meta-value\">${data.contentType}</span>\n        </div>\n        <div class=\"meta-item\">\n          <span class=\"meta-label\">Topic:</span>\n          <span class=\"meta-value\">${data.topic}</span>\n        </div>\n        <div class=\"meta-item\">\n          <span class=\"meta-label\">Target Audience:</span>\n          <span class=\"meta-value\">${data.audience}</span>\n        </div>\n        <div class=\"meta-item\">\n          <span class=\"meta-label\">Tone:</span>\n          <span class=\"meta-value\">${data.tone}</span>\n        </div>\n        <div class=\"meta-item\">\n          <span class=\"meta-label\">Word Count:</span>\n          <span class=\"meta-value\">${data.stats.wordCount} words (${data.stats.readingTime} min read)</span>\n        </div>\n        <div class=\"meta-item\">\n          <span class=\"meta-label\">Priority:</span>\n          <span class=\"meta-value\">${data.priority}</span>\n        </div>\n      </div>\n\n      <div class=\"quality-score\">\n        <p style=\"margin: 0 0 10px 0; color: #666; font-weight: 600;\">AI Quality Score</p>\n        <p class=\"quality-number\">${data.stats.qualityScore}/100</p>\n        <p style=\"margin: 10px 0 0 0; color: #666; font-size: 14px;\">\n          ${data.stats.qualityScore >= 80 ? '\ud83c\udf89 Excellent Quality' : data.stats.qualityScore >= 60 ? '\u2705 Good Quality' : '\u26a0\ufe0f Needs Review'}\n        </p>\n      </div>\n\n      <h3>\ud83d\udcdd Generated Content Draft</h3>\n      <div class=\"draft-content\">\n        <pre>${data.aiDraft}</pre>\n      </div>\n\n      <h3>\ud83c\udfac Take Action</h3>\n      <p style=\"color: #666;\">Review the content above and choose an action:</p>\n      \n      <div class=\"actions\">\n        <a href=\"${data.reviewLinks.approve}\" class=\"btn btn-approve\">\u2705 Approve & Publish</a>\n        <a href=\"${data.reviewLinks.requestEdit}\" class=\"btn btn-edit\">\u270f\ufe0f Request Changes</a>\n        <a href=\"${data.reviewLinks.reject}\" class=\"btn btn-reject\">\u274c Reject</a>\n      </div>\n\n      <p style=\"margin-top: 20px; padding: 15px; background: #f0f9ff; border-left: 4px solid #3b82f6; border-radius: 4px; font-size: 14px;\">\n        <strong>\ud83d\udca1 Tip:</strong> Click the buttons above to take action instantly. The content will be automatically updated in your tracking sheet.\n      </p>\n    </div>\n\n    <div class=\"footer\">\n      <p>Generated by AI Content Workflow</p>\n      <p style=\"font-size: 12px; color: #999;\">Request ID: ${data.requestId} | ${new Date(data.generatedAt).toLocaleString()}</p>\n    </div>\n  </div>\n</body>\n</html>\n`;\n\nreturn [{\n  json: {\n    ...data,\n    htmlEmail: html\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "f5f4e6d0-3105-477a-93d4-bbcf8fe56424",
      "name": "\ud83d\udd14 Review Action Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        64,
        400
      ],
      "parameters": {
        "path": "f077ff3f-1a68-4c38-a010-527f8d519e96",
        "options": {},
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "251d2073-ffc5-4f28-998a-465c79afcf17",
      "name": "Check: Approve?",
      "type": "n8n-nodes-base.if",
      "position": [
        224,
        304
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "caseSensitive": false,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "action_approve",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.query.action }}",
              "rightValue": "approve"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "66ad077c-5a6a-4ab5-8051-4ad012bbc2f1",
      "name": "Check: Reject?",
      "type": "n8n-nodes-base.if",
      "position": [
        224,
        496
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "caseSensitive": false,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "action_reject",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.query.action }}",
              "rightValue": "reject"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "05890ffb-139d-4b9f-9357-bfd0a020d49f",
      "name": "\u2705 Update: Approved",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        448,
        256
      ],
      "parameters": {
        "columns": {
          "value": {
            "Status": "approved",
            "Approved At": "={{ $now.toISO() }}",
            "Final Content": "={{ $json.aiDraft || 'See AI Draft column' }}",
            "Review Status": "Approved"
          },
          "mappingMode": "defineBelow"
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Content Requests"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "8c174f9c-5452-4cf4-82b0-6cd1c4a4362c",
      "name": "\u274c Update: Rejected",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        448,
        560
      ],
      "parameters": {
        "columns": {
          "value": {
            "Status": "rejected",
            "Rejected At": "={{ $now.toISO() }}",
            "Review Status": "Rejected"
          },
          "mappingMode": "defineBelow"
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Content Requests"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "8766daca-9d03-43d1-84e7-310fb437d2b4",
      "name": "\u270f\ufe0f Update: Needs Edit",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        448,
        400
      ],
      "parameters": {
        "columns": {
          "value": {
            "Status": "revision_requested",
            "Review Status": "Needs Changes",
            "Revision Requested At": "={{ $now.toISO() }}"
          },
          "mappingMode": "defineBelow"
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Content Requests"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "ca6fd14f-4f48-4203-9173-c2b67049699c",
      "name": "Respond: Approved",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        672,
        256
      ],
      "parameters": {
        "options": {},
        "respondWith": "text",
        "responseBody": "=<!DOCTYPE html>\n<html>\n<head>\n  <style>\n    body { font-family: sans-serif; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }\n    .card { background: white; padding: 50px; border-radius: 12px; text-align: center; box-shadow: 0 10px 40px rgba(0,0,0,0.2); max-width: 500px; }\n    .icon { font-size: 72px; margin-bottom: 20px; }\n    h1 { color: #333; margin: 0 0 10px 0; }\n    p { color: #666; line-height: 1.6; }\n    .btn { display: inline-block; margin-top: 20px; padding: 12px 30px; background: #667eea; color: white; text-decoration: none; border-radius: 6px; font-weight: 600; }\n  </style>\n</head>\n<body>\n  <div class=\"card\">\n    <div class=\"icon\">\u2705</div>\n    <h1>Content Approved!</h1>\n    <p>The content has been marked as approved and is ready for publishing.</p>\n    <p><strong>Request ID:</strong> {{ $json.query.id }}</p>\n    <a href=\"#\" class=\"btn\">Close This Tab</a>\n  </div>\n</body>\n</html>"
      },
      "typeVersion": 1.1
    },
    {
      "id": "3f097f0e-c27f-4d86-8f12-8ddfca48ce6e",
      "name": "Respond: Edit Requested",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        672,
        400
      ],
      "parameters": {
        "options": {},
        "respondWith": "text",
        "responseBody": "=<!DOCTYPE html>\n<html>\n<head>\n  <style>\n    body { font-family: sans-serif; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); }\n    .card { background: white; padding: 50px; border-radius: 12px; text-align: center; box-shadow: 0 10px 40px rgba(0,0,0,0.2); max-width: 500px; }\n    .icon { font-size: 72px; margin-bottom: 20px; }\n    h1 { color: #333; margin: 0 0 10px 0; }\n    p { color: #666; line-height: 1.6; }\n  </style>\n</head>\n<body>\n  <div class=\"card\">\n    <div class=\"icon\">\u270f\ufe0f</div>\n    <h1>Changes Requested</h1>\n    <p>The content has been marked for revision. The team will be notified.</p>\n    <p><strong>Request ID:</strong> {{ $json.query.id }}</p>\n  </div>\n</body>\n</html>"
      },
      "typeVersion": 1.1
    },
    {
      "id": "ace69455-eb47-4db3-b047-899dfdaaf93c",
      "name": "Respond: Rejected",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        672,
        560
      ],
      "parameters": {
        "options": {},
        "respondWith": "text",
        "responseBody": "=<!DOCTYPE html>\n<html>\n<head>\n  <style>\n    body { font-family: sans-serif; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); }\n    .card { background: white; padding: 50px; border-radius: 12px; text-align: center; box-shadow: 0 10px 40px rgba(0,0,0,0.2); max-width: 500px; }\n    .icon { font-size: 72px; margin-bottom: 20px; }\n    h1 { color: #333; margin: 0 0 10px 0; }\n    p { color: #666; line-height: 1.6; }\n  </style>\n</head>\n<body>\n  <div class=\"card\">\n    <div class=\"icon\">\u274c</div>\n    <h1>Content Rejected</h1>\n    <p>The content has been rejected. It will not be published.</p>\n    <p><strong>Request ID:</strong> {{ $json.query.id }}</p>\n  </div>\n</body>\n</html>"
      },
      "typeVersion": 1.1
    },
    {
      "id": "d0048863-ec2f-4219-9a9e-1461234d9243",
      "name": "Log to Tracking Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        336,
        0
      ],
      "parameters": {
        "columns": {
          "value": {
            "Tone": "={{ $json.tone }}",
            "Topic": "={{ $json.topic }}",
            "Status": "={{ $json.status }}",
            "AI Draft": "",
            "Audience": "={{ $json.audience }}",
            "Priority": "={{ $json.priority }}",
            "Reviewer": "={{ $json.reviewerEmail }}",
            "Created At": "={{ $json.createdAt }}",
            "Request ID": "={{ $json.requestId }}",
            "Word Count": "={{ $json.wordCount }}",
            "Content Type": "={{ $json.contentType }}",
            "Published At": "",
            "Final Content": "",
            "Review Status": ""
          },
          "mappingMode": "defineBelow"
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Content Requests"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "e0142757-2a6d-41ca-805e-e8e703c0a260",
      "name": "Prepare Request Data",
      "type": "n8n-nodes-base.set",
      "position": [
        160,
        0
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "request_id",
              "name": "requestId",
              "type": "string",
              "value": "={{ 'REQ-' + $now.format('yyyyMMdd-HHmmss') }}"
            },
            {
              "id": "created_at",
              "name": "createdAt",
              "type": "string",
              "value": "={{ $now.toISO() }}"
            },
            {
              "id": "status",
              "name": "status",
              "type": "string",
              "value": "pending_generation"
            },
            {
              "id": "word_target",
              "name": "wordTarget",
              "type": "number",
              "value": "={{ $json.wordCount === 'Short (300-500 words)' ? 400 : $json.wordCount === 'Medium (800-1200 words)' ? 1000 : 2000 }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "9fff0e38-434d-4fe6-bb69-1c705391c1e6",
      "name": "Update Sheet with Draft",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        896,
        0
      ],
      "parameters": {
        "columns": {
          "value": {
            "Status": "={{ $json.status }}",
            "AI Draft": "={{ $json.aiDraft }}",
            "Word Count": "={{ $json.stats.wordCount }}",
            "Generated At": "={{ $json.generatedAt }}",
            "Quality Score": "={{ $json.stats.qualityScore }}"
          },
          "mappingMode": "defineBelow"
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Content Requests"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "1fb00659-6ff0-49a8-bb4a-b2f603c5fd4c",
      "name": "Process Generated Content",
      "type": "n8n-nodes-base.code",
      "position": [
        720,
        0
      ],
      "parameters": {
        "jsCode": "// Extract and structure the generated content\nconst generatedText = $input.first().json.output || $input.first().json.response || $input.first().json.text;\nconst requestData = $('Prepare Request Data').first().json;\n\n// Word count\nconst wordCount = generatedText.split(/\\s+/).length;\n\n// Extract title (first line usually)\nconst lines = generatedText.split('\\n').filter(line => line.trim());\nconst title = lines[0].replace(/^(Title:|#)\\s*/i, '').trim();\n\n// Calculate quality score (simple heuristic)\nfunction calculateQualityScore(text) {\n  let score = 70; // Base score\n  \n  // Length check\n  const words = text.split(/\\s+/).length;\n  if (words >= requestData.wordTarget * 0.8 && words <= requestData.wordTarget * 1.2) {\n    score += 10;\n  }\n  \n  // Has subheadings\n  if (text.match(/^#{1,3}\\s|^\\*\\*.*\\*\\*$/m)) {\n    score += 5;\n  }\n  \n  // Has bullet points or numbered lists\n  if (text.match(/^[-*]\\s|^\\d+\\.\\s/m)) {\n    score += 5;\n  }\n  \n  // Has CTA\n  if (text.toLowerCase().includes('call to action') || text.toLowerCase().includes('cta:')) {\n    score += 5;\n  }\n  \n  // Not too repetitive (check for repeated phrases)\n  const sentences = text.split(/[.!?]+/);\n  const uniqueSentences = new Set(sentences);\n  if (uniqueSentences.size / sentences.length > 0.8) {\n    score += 5;\n  }\n  \n  return Math.min(score, 100);\n}\n\nconst qualityScore = calculateQualityScore(generatedText);\n\n// Extract key stats\nconst stats = {\n  wordCount: wordCount,\n  paragraphCount: generatedText.split('\\n\\n').length,\n  hasTitle: title.length > 0,\n  hasCTA: generatedText.toLowerCase().includes('cta:') || generatedText.toLowerCase().includes('call to action'),\n  qualityScore: qualityScore,\n  readingTime: Math.ceil(wordCount / 200) // Average reading speed\n};\n\nreturn [{\n  json: {\n    ...requestData,\n    aiDraft: generatedText,\n    title: title,\n    stats: stats,\n    generatedAt: new Date().toISOString(),\n    status: 'pending_review'\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "1c312090-0c64-4fd8-b3ce-77e404cae043",
      "name": "\u2709\ufe0f Send Review Request",
      "type": "n8n-nodes-base.gmail",
      "position": [
        896,
        672
      ],
      "parameters": {
        "sendTo": "={{ $json.reviewerEmail }}",
        "message": "={{ $json.htmlEmail }}",
        "options": {},
        "subject": "=\ud83c\udfa8 AI Content Ready: {{ $json.title }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "c2ce5185-a1b4-4458-af16-1f62a4dc23e9",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -480,
        400
      ],
      "parameters": {
        "color": 3,
        "width": 416,
        "height": 208,
        "content": "### Workflow Stages\n1. Submit a form with topic, tone, and keywords.\n2. GPT-4o generates content and assigns a quality score (0\u2013100).\n3. Reviewer receives an email to approve, edit, or reject the draft.\n4. All review actions are automatically logged in Google Sheets with timestamps and approval status.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "38ecff9e-9415-40ad-b55f-b2b0ddfbc9b2",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1072,
        160
      ],
      "parameters": {
        "color": 5,
        "width": 656,
        "height": 336,
        "content": "\n## Requirements\nn8n v1.0+ instance, OpenAI API key with GPT-4o access, and Google Workspace with OAuth2 credentials.\n\n## Custom options\nChoose model: gpt-4o, 4o-mini, or 3.5-turbo. Adjust score weights: Readability 40%, Keywords 30%, Length 30%.\n\n## Key benefits\nGenerate drafts 99% faster and approve content 95% quicker. Centralized tracking ensures clarity and accountability."
      },
      "typeVersion": 1
    },
    {
      "id": "11b0f0a9-331b-4a9f-80a0-baa02193d7b3",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -480,
        0
      ],
      "parameters": {
        "width": 416,
        "height": 384,
        "content": "## How it works\nSubmit a form with your topic, tone, and keywords. GPT-4o generates the content and assigns a quality score (0\u2013100). The reviewer receives an email to approve, edit, or reject the draft\u2014all actions are automatically logged in Google Sheets for tracking and audit purposes.\n\n## Setup steps\n1. Import the workflow JSON file into n8n\n2. Connect your OpenAI and Google account credentials\n3. Update three variables in the workflow: SHEET_ID (your Google Sheets document ID), REVIEWER_EMAIL (recipient for review notifications), and WEBHOOK_URL (for form submissions)\n4. Test the workflow with a sample submission"
      },
      "typeVersion": 1
    },
    {
      "id": "f4b1b128-2ef8-4e8b-b09c-10cbfb665bae",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1072,
        0
      ],
      "parameters": {
        "color": 4,
        "width": 656,
        "height": 128,
        "content": "## Overview\nAutomate AI content creation from request to approval. While AI writes quickly, human review often delays delivery\u2014and multiple tools create workflow gaps and version confusion. This unified solution streamlines the entire process, enabling teams to produce quality content at scale with transparent tracking."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "3c523822-afaf-4a71-989e-a4640aee4178",
  "connections": {
    "OpenAI GPT-4o": {
      "ai_languageModel": [
        [
          {
            "node": "\ud83e\udd16 Generate AI Content",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Check: Reject?": {
      "main": [
        [
          {
            "node": "\u274c Update: Rejected",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check: Approve?": {
      "main": [
        [
          {
            "node": "\u2705 Update: Approved",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "\u270f\ufe0f Update: Needs Edit",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Request Data": {
      "main": [
        [
          {
            "node": "Log to Tracking Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u2705 Update: Approved": {
      "main": [
        [
          {
            "node": "Respond: Approved",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u274c Update: Rejected": {
      "main": [
        [
          {
            "node": "Respond: Rejected",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log to Tracking Sheet": {
      "main": [
        [
          {
            "node": "\ud83e\udd16 Generate AI Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Sheet with Draft": {
      "main": [
        [
          {
            "node": "\ud83d\udd17 Generate Review Links",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udce7 Create Review Email": {
      "main": [
        [
          {
            "node": "\u2709\ufe0f Send Review Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83e\udd16 Generate AI Content": {
      "main": [
        [
          {
            "node": "Process Generated Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Generated Content": {
      "main": [
        [
          {
            "node": "Update Sheet with Draft",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u270f\ufe0f Update: Needs Edit": {
      "main": [
        [
          {
            "node": "Respond: Edit Requested",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udce5 Content Request Form": {
      "main": [
        [
          {
            "node": "Prepare Request Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd14 Review Action Webhook": {
      "main": [
        [
          {
            "node": "Check: Approve?",
            "type": "main",
            "index": 0
          },
          {
            "node": "Check: Reject?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd17 Generate Review Links": {
      "main": [
        [
          {
            "node": "\ud83d\udce7 Create Review Email",
            "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

Automate AI content creation from request to approval. While AI writes quickly, human review often delays delivery—and multiple tools create workflow gaps and version confusion. This unified solution streamlines the entire process, enabling teams to produce quality content at…

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

More AI & RAG workflows → · Browse all categories →

Related workflows

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

AI & RAG

The workflow runs every hour with a randomized delay of 5–20 minutes to help distribute load. It records the exact date and time a lead is emailed so you can track outreach. Follow-ups are automatical

Google Sheets, Agent, OpenAI Chat +5
AI & RAG

This workflow is perfect for graphic designers, creative agencies, marketing teams, or freelancers who regularly use AI-generated images in their projects. It's specifically beneficial for teams that

Google Sheets, Google Drive, HTTP Request +5
AI & RAG

AI Agents Vs AI Workflow. Uses lmChatOpenAi, gmailTrigger, gmail, gmailTool. Event-driven trigger; 30 nodes.

OpenAI Chat, Gmail Trigger, Gmail +7
AI & RAG

It works with both Thai and English business cards and even includes an optional step to draft greeting emails automatically.

OpenAI Chat, Agent, Chain Llm +5
AI & RAG

This n8n template automates targeted lead discovery, AI-driven data structuring, and personalized cold-email sending at controlled intervals. It’s ideal for sales teams, founders, and agencies that wa

Google Sheets, Form Trigger, Chain Llm +6