{
  "nodes": [
    {
      "id": "4db758a2-824e-4ed2-a6ed-311e9fca7dca",
      "name": "Receive Video URL",
      "type": "n8n-nodes-base.webhook",
      "position": [
        736,
        288
      ],
      "parameters": {
        "path": "43129e0a-92b3-4f28-b2a8-97ef86d287c6",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "374c8d7e-bb40-4991-bf27-17e3f1c1e649",
      "name": "Submit Transcription \u2014 Wayin",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1008,
        288
      ],
      "parameters": {
        "url": "https://wayinvideo-api.wayin.ai/api/v2/transcripts",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"video_url\": \"{{ $json.body.url }}\",\n  \"target_lang\": \"en\"\n} ",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer YOUR_TOKEN_HERE"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "x-wayinvideo-api-version",
              "value": "v2"
            }
          ]
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "eceab34e-fb90-4989-a3e9-5619a06a0c9b",
      "name": "Poll Transcription Status",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1216,
        288
      ],
      "parameters": {
        "url": "=https://wayinvideo-api.wayin.ai/api/v2/transcripts/results/{{ $('Submit Transcription \u2014 Wayin').item.json.data.id }}",
        "options": {},
        "jsonBody": "{\n  \"video_url\": \"https://www.youtube.com/watch?v=JkJOZewGTU4\",\n  \"target_lang\": \"en\"\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer YOUR_TOKEN_HERE"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "x-wayinvideo-api-version",
              "value": "v2"
            }
          ]
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "5b344110-2c51-46b1-a574-ef7dd964fe96",
      "name": "Wait 5 Seconds",
      "type": "n8n-nodes-base.wait",
      "position": [
        1440,
        288
      ],
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "7a1496a8-2633-4b21-9f5b-a4b40f3a3737",
      "name": "Is Transcription Complete?",
      "type": "n8n-nodes-base.if",
      "position": [
        1632,
        288
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "41193fb2-1c7d-415b-bbef-7aa182df19ab",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.data.status }}",
              "rightValue": "SUCCEEDED"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "409711de-cb37-4cf1-9110-3052a6e45639",
      "name": "Process Transcript Data",
      "type": "n8n-nodes-base.code",
      "position": [
        1904,
        272
      ],
      "parameters": {
        "jsCode": "const pollResult = $input.first().json;\nconst segments = pollResult.data?.transcript || [];\n\nif (segments.length === 0) {\n  throw new Error('Transcript empty!');\n}\n\nconst fullText = segments\n  .map(s => s.text.trim())\n  .join(' ');\n\nconst wordCount = fullText.split(/\\s+/).length;\n\nconst durationMs = segments[segments.length - 1]?.end || 0;\nconst durationMin = Math.round(durationMs / 60000);\n\nreturn [{\n  json: {\n    full_transcript: fullText,\n    word_count: wordCount,\n    total_segments: segments.length,\n    duration_minutes: durationMin\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "4083e98c-4150-43bc-b98e-5031a9cba167",
      "name": "Generate SEO Blog Post",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        2320,
        272
      ],
      "parameters": {
        "text": "={{ $json.full_transcript }}",
        "options": {
          "systemMessage": "=You are an expert SEO content writer. Transform video transcripts into \nhigh-quality SEO blog posts. Return ONLY valid raw JSON \u2014 no markdown, \nno backticks, no explanation.\n\nVideo Title: {{ $('Submit Transcription \u2014 Wayin').item.json.data.name }}\nTranscript ({{ $json.word_count }} words | {{ $json.duration_minutes }} min):\n---\n{{ $json.full_transcript }}\n---\n\nReturn ONLY this JSON:\n{\n  \"seo_title\": \"55-60 char title with keyword\",\n  \"meta_description\": \"155 char meta with CTA\",\n  \"slug\": \"url-slug\",\n  \"focus_keyword\": \"main keyword\",\n  \"secondary_keywords\": [\"kw2\",\"kw3\",\"kw4\"],\n  \"introduction\": \"3 hook paragraphs\",\n  \"sections\": [{\"h2\": \"heading\", \"content\": \"300+ words\"}],\n  \"key_takeaways\": [\"p1\",\"p2\",\"p3\",\"p4\",\"p5\"],\n  \"faq\": [{\"question\": \"Q?\", \"answer\": \"answer\"}],\n  \"conclusion\": \"2 paragraphs with CTA\",\n  \"read_time\": \"X min read\",\n  \"tags\": [\"tag1\",\"tag2\",\"tag3\"]\n}"
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 3.1
    },
    {
      "id": "58973e0c-b8de-4ae6-b26b-afb5ca3bc21d",
      "name": "GPT Model \u2014 Blog Generator",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        2320,
        448
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini"
        },
        "options": {},
        "builtInTools": {}
      },
      "typeVersion": 1.3
    },
    {
      "id": "cab08fad-09de-4855-9bd8-452de4bfbd31",
      "name": "Parse Blog JSON Output",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        2496,
        480
      ],
      "parameters": {
        "autoFix": true,
        "jsonSchemaExample": "{\n  \"seo_title\": \"string\",\n  \"meta_description\": \"string\",\n  \"slug\": \"string\",\n  \"focus_keyword\": \"string\",\n  \"secondary_keywords\": [\"string\"],\n  \"introduction\": \"string\",\n  \"sections\": [\n    {\n      \"h2\": \"string\",\n      \"content\": \"string\"\n    }\n  ],\n  \"key_takeaways\": [\"string\"],\n  \"faq\": [\n    {\n      \"question\": \"string\",\n      \"answer\": \"string\"\n    }\n  ],\n  \"conclusion\": \"string\",\n  \"read_time\": \"string\",\n  \"tags\": [\"string\"]\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "d37fc4bf-ef2e-44a4-9c88-3e3c0879fa79",
      "name": "GPT Model \u2014 Output Parser",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        2496,
        656
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini"
        },
        "options": {},
        "builtInTools": {}
      },
      "typeVersion": 1.3
    },
    {
      "id": "07bf8d0a-99c3-4385-a25f-7103bf017113",
      "name": "Save to Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        3024,
        272
      ],
      "parameters": {
        "columns": {
          "value": {
            "Date": "={{ $now.toFormat('dd MMMM yyyy') }}",
            "Slug": "={{ $json.output.slug }}",
            "Tags": "={{ $json.output.tags.map((t, i) => (i + 1) + '. ' + t).join('\\n') }}",
            "Status": "Draft",
            "Read Time": "={{ $json.output.read_time }}",
            "SEO Title": "={{ $json.output.seo_title }}",
            "Video URL": "={{ $('Receive Video URL').item.json.body.url }}",
            "Word Count": "={{ $('Process Transcript Data').item.json.word_count }}",
            "Focus Keyword": "={{ $json.output.focus_keyword }}",
            "Duration (min)": "={{ $('Process Transcript Data').item.json.duration_minutes }}",
            "Meta Description": "={{ $json.output.meta_description }}",
            "Secondary Keywords": "={{ $json.output.secondary_keywords.map(k => '\u2022 ' + k).join('\\n') }}"
          },
          "schema": [
            {
              "id": "Date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Video URL",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Video URL",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "SEO Title",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "SEO Title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Slug",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Slug",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Focus Keyword",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Focus Keyword",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Meta Description",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Meta Description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Secondary Keywords",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Secondary Keywords",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Read Time",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Read Time",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Tags",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Tags",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Word Count",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Word Count",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Duration (min)",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Duration (min)",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_DOCUMENT_ID/edit#gid=0",
          "cachedResultName": "SEO BLog Post"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_SHEET_DOCUMENT_ID",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_DOCUMENT_ID/edit?usp=drivesdk",
          "cachedResultName": "Wayin \u2014 Dynamic Video to SEO Blog Post"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "5e78d275-841b-4a9b-a751-c1952a3d477c",
      "name": "Return Success Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        3280,
        272
      ],
      "parameters": {
        "options": {},
        "respondWith": "allIncomingItems"
      },
      "typeVersion": 1.5
    },
    {
      "id": "1f0b8c25-6c78-44cb-99af-cb0aa3d85bfb",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        672,
        48
      ],
      "parameters": {
        "color": 7,
        "width": 272,
        "height": 288,
        "content": "## 1. Receive Video URL\n\nWebhook listens for a POST request containing the video URL.\n\n**Method:** POST\n**Body:** `{ \"url\": \"https://youtube.com/watch?v=...\" }`"
      },
      "typeVersion": 1
    },
    {
      "id": "afd89d2f-58f5-4ab1-be51-981488de28f2",
      "name": "\ud83d\udccb Workflow Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        176,
        -128
      ],
      "parameters": {
        "width": 452,
        "height": 836,
        "content": "**Video \u2192 SEO Blog Post \u2014 Automated Pipeline**\n\nAutomatically convert any video URL into a fully structured SEO blog post using **Wayin AI transcription** + **GPT-4o-mini**. Paste a video link, get a complete draft in Google Sheets \u2014 title, slug, meta, sections, FAQs, tags and more.\n\n**How it works**\n1. Webhook receives a video URL via POST request\n2. Wayin API transcribes the video to English text\n3. Workflow polls every 5 seconds until transcription succeeds\n4. JavaScript code joins segments and calculates word count & duration\n5. AI Agent (GPT-4o-mini) writes a full SEO blog post from the transcript\n6. Structured Output Parser validates and enforces strict JSON schema\n7. Final blog draft is saved as a new row in Google Sheets\n\n**Setup**\n\u2610 Add Wayin API key in `Submit Transcription \u2014 Wayin` and `Poll Transcription Status` nodes\n\u2610 Add OpenAI API credentials to `GPT Model \u2014 Blog Generator` and `GPT Model \u2014 Output Parser`\n\u2610 Connect Google Sheets OAuth in `Save to Google Sheets`\n\u2610 Replace `YOUR_GOOGLE_SHEET_DOCUMENT_ID` with your actual Sheet ID\n\u2610 Create sheet with columns: `Date` | `Video URL` | `SEO Title` | `Slug` | `Focus Keyword` | `Meta Description` | `Secondary Keywords` | `Read Time` | `Tags` | `Word Count` | `Duration (min)` | `Status`\n\n**Customize**\n- **Language:** Change `target_lang` in Wayin node to transcribe non-English videos\n- **AI Model:** Swap `gpt-4o-mini` with `gpt-4o` for higher quality output\n- **Blog Structure:** Edit system prompt in `Generate SEO Blog Post` to add/remove sections\n- **Output:** Replace Google Sheets with a WordPress or Notion node to auto-publish\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "e71a31a5-d57f-441d-b247-0b658ed74ad8",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1008,
        48
      ],
      "parameters": {
        "color": 7,
        "width": 304,
        "height": 288,
        "content": "## 2. Send to Wayin for Transcription\n\nSubmits the video URL to WayinVideo AI \u2014 an accurate multilingual transcription API \u2014 and receives a unique job ID in return.\n\n\ud83d\udd17 Learn more: https://wayin.ai"
      },
      "typeVersion": 1
    },
    {
      "id": "088f724d-af62-4ce5-812f-6886cc244554",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1392,
        48
      ],
      "parameters": {
        "color": 7,
        "width": 384,
        "height": 288,
        "content": "## 3. Poll Until Transcription Completes\n\nFetches job status every 5 seconds using the job ID.\n\n- \u2705 Status = SUCCEEDED \u2192 move forward\n- \ud83d\udd01 Any other status \u2192 wait and re-poll"
      },
      "typeVersion": 1
    },
    {
      "id": "7ce27e12-cfc3-42fa-9e95-3cc9c2324d71",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1824,
        48
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 272,
        "content": "## 4. Process Transcript Data\n\nJoins all transcript segments into one clean text block.\nCalculates total word count and video duration in minutes."
      },
      "typeVersion": 1
    },
    {
      "id": "9fff704a-8e67-4499-b7de-a1fa8b1a0a27",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2176,
        32
      ],
      "parameters": {
        "color": 7,
        "width": 656,
        "height": 288,
        "content": "## 5. Generate SEO Blog Post with AI\n\nAI Agent (GPT-4o-mini) writes a full structured blog post from the transcript:\n\n- SEO title, meta description, URL slug\n- H2 sections with 300+ words each\n- Key takeaways, FAQs, conclusion with CTA\n- Read time and tags\n\nStructured Output Parser validates and auto-fixes the JSON schema."
      },
      "typeVersion": 1
    },
    {
      "id": "37954791-54e9-42b1-8dbb-c64518f70daf",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2880,
        32
      ],
      "parameters": {
        "color": 7,
        "width": 560,
        "height": 288,
        "content": "## 6. Save Draft & Respond\n\nAppends the complete blog post as a new row in Google Sheets.\n\n- Status set to: Draft\n- Includes: date, video URL, word count, read time\n- Webhook returns success confirmation to the caller"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Wait 5 Seconds": {
      "main": [
        [
          {
            "node": "Is Transcription Complete?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Receive Video URL": {
      "main": [
        [
          {
            "node": "Submit Transcription \u2014 Wayin",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save to Google Sheets": {
      "main": [
        [
          {
            "node": "Return Success Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate SEO Blog Post": {
      "main": [
        [
          {
            "node": "Save to Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Blog JSON Output": {
      "ai_outputParser": [
        [
          {
            "node": "Generate SEO Blog Post",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Process Transcript Data": {
      "main": [
        [
          {
            "node": "Generate SEO Blog Post",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Poll Transcription Status": {
      "main": [
        [
          {
            "node": "Wait 5 Seconds",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Transcription Complete?": {
      "main": [
        [
          {
            "node": "Process Transcript Data",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Poll Transcription Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GPT Model \u2014 Output Parser": {
      "ai_languageModel": [
        [
          {
            "node": "Parse Blog JSON Output",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "GPT Model \u2014 Blog Generator": {
      "ai_languageModel": [
        [
          {
            "node": "Generate SEO Blog Post",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Submit Transcription \u2014 Wayin": {
      "main": [
        [
          {
            "node": "Poll Transcription Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}