This workflow corresponds to n8n.io template #16105 — we link there as the canonical source.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"id": "v631KAaxCoXPZYnO",
"name": "[TEMPLATE] AI Viral Script Generator + Whisper SRT",
"tags": [],
"nodes": [
{
"id": "webhook-1",
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"onError": "continueRegularOutput",
"position": [
240,
400
],
"parameters": {
"path": "viral-script-srt",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2.1
},
{
"id": "respond-1",
"name": "Respond",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
1664,
400
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={{ $json }}"
},
"typeVersion": 1.5
},
{
"id": "gpt-1",
"name": "Generate Script",
"type": "n8n-nodes-base.httpRequest",
"position": [
464,
400
],
"parameters": {
"url": "https://api.openai.com/v1/chat/completions",
"method": "POST",
"options": {},
"jsonBody": "={{ JSON.stringify({ model: 'gpt-4o-mini', temperature: 0.8, messages: [{ role: 'system', content: 'You write punchline-first viral YouTube Shorts scripts. Structure: CLAIM (contrarian punchline, no setup, no \"did you know\") -> PROOF (specific verifiable fact with numbers or named studies) -> ESCALATION (stack 1-2 more facts). Cut hard on the strongest fact. No closer, no rhetorical question, no \"so next time...\". Plain ASCII only, no emojis, no markdown. Output ONLY the script text - no labels, no preamble.' }, { role: 'user', content: 'Write a ' + (($('Webhook').first().json.body.words) || 80) + '-word viral Short script on this topic: ' + (($('Webhook').first().json.body.topic) || 'a surprising fact about the human body') }] }) }}",
"sendBody": true,
"specifyBody": "json",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "openAiApi"
},
"typeVersion": 4.4
},
{
"id": "extract-1",
"name": "Extract Script",
"type": "n8n-nodes-base.code",
"position": [
688,
400
],
"parameters": {
"jsCode": "const raw = $input.first().json.choices[0].message.content || '';\nconst script = raw.trim();\nif (!script) throw new Error('GPT returned empty script');\nreturn [{ json: { script } }];"
},
"typeVersion": 2
},
{
"id": "tts-1",
"name": "Generate TTS Audio",
"type": "n8n-nodes-base.httpRequest",
"position": [
912,
400
],
"parameters": {
"url": "https://api.openai.com/v1/audio/speech",
"method": "POST",
"options": {
"response": {
"response": {
"responseFormat": "file"
}
}
},
"jsonBody": "={{ JSON.stringify({ model: 'tts-1', input: $('Extract Script').first().json.script, voice: ($('Webhook').first().json.body.voice) || 'alloy', response_format: 'mp3' }) }}",
"sendBody": true,
"specifyBody": "json",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "openAiApi"
},
"typeVersion": 4.4
},
{
"id": "whisper-1",
"name": "Transcribe Whisper",
"type": "n8n-nodes-base.httpRequest",
"position": [
1120,
400
],
"parameters": {
"url": "https://api.openai.com/v1/audio/transcriptions",
"method": "POST",
"options": {},
"sendBody": true,
"contentType": "multipart-form-data",
"authentication": "predefinedCredentialType",
"bodyParameters": {
"parameters": [
{
"name": "model",
"value": "whisper-1"
},
{
"name": "response_format",
"value": "verbose_json"
},
{
"name": "timestamp_granularities[]",
"value": "word"
},
{
"name": "file",
"parameterType": "formBinaryData",
"inputDataFieldName": "data"
}
]
},
"nodeCredentialType": "openAiApi"
},
"typeVersion": 4.4
},
{
"id": "srt-1",
"name": "Build SRT",
"type": "n8n-nodes-base.code",
"position": [
1344,
400
],
"parameters": {
"jsCode": "const item = $input.first();\nconst words = (item.json && item.json.words) || [];\nconst script = $('Extract Script').first().json.script;\nconst audioBase64 = item.binary && item.binary.data ? item.binary.data.data : '';\n\nfunction toSrtTime(s) {\n const h = Math.floor(s / 3600);\n const m = Math.floor((s % 3600) / 60);\n const sec = Math.floor(s % 60);\n const ms = Math.round((s - Math.floor(s)) * 1000);\n return String(h).padStart(2, '0') + ':' + String(m).padStart(2, '0') + ':' + String(sec).padStart(2, '0') + ',' + String(ms).padStart(3, '0');\n}\n\nconst HARD_MAX = 5;\nconst IDEAL = 3;\nconst TAIL_HOLD = 0.5;\nconst breaks = /[.!?,;:]$/;\n\nconst cues = [];\nlet buf = [];\nfor (let i = 0; i < words.length; i++) {\n const w = words[i];\n buf.push(w);\n const isLast = i === words.length - 1;\n const endsBreak = breaks.test(w.word);\n if (isLast || (buf.length >= 2 && endsBreak) || buf.length >= HARD_MAX) {\n cues.push({ start: buf[0].start, end: buf[buf.length - 1].end, text: buf.map(b => b.word).join(' ').trim() });\n buf = [];\n } else if (buf.length >= IDEAL && words[i + 1] && /^[A-Z]/.test(words[i + 1].word)) {\n cues.push({ start: buf[0].start, end: buf[buf.length - 1].end, text: buf.map(b => b.word).join(' ').trim() });\n buf = [];\n }\n}\n\nlet srt = '';\nfor (let i = 0; i < cues.length; i++) {\n const c = cues[i];\n const next = cues[i + 1];\n const holdEnd = next ? next.start : (c.end + TAIL_HOLD);\n srt += (i + 1) + '\\n' + toSrtTime(c.start) + ' --> ' + toSrtTime(holdEnd) + '\\n' + c.text + '\\n\\n';\n}\n\nreturn [{ json: { script, srt, audioBase64, durationSeconds: words.length ? words[words.length - 1].end : 0 } }];"
},
"typeVersion": 2
},
{
"id": "sticky-intro",
"name": "Sticky Overview",
"type": "n8n-nodes-base.stickyNote",
"position": [
-688,
-160
],
"parameters": {
"width": 760,
"height": 800,
"content": "## AI Viral Script Generator + Whisper SRT\n\nOne webhook call returns everything you need to caption a YouTube Short: a punchline-first script, a narration MP3 (base64), and a frame-accurate SRT aligned to YOUR audio via Whisper word-level timestamps (perfect lip-sync). Drop the SRT into any editor, Shotstack, FFmpeg, etc.\n\n**In** (POST JSON): topic (required), words (default 80, ~30s), voice (alloy/echo/fable/onyx/nova/shimmer). **Out**: { script, srt, audioBase64, durationSeconds }.\n\n### How it works\n\n1. Webhook receives topic, word count and voice.\n2. GPT-4o-mini writes a punchline-first script (CLAIM -> PROOF -> ESCALATION, no closer).\n3. OpenAI TTS turns it into a narration MP3.\n4. Whisper word-timestamps the audio; Build SRT chunks it into TikTok-style cues.\n\n### Setup steps\n\n- [ ] Create an OpenAI credential (key at platform.openai.com/api-keys).\n- [ ] Select it in all 3 OpenAI nodes: Generate Script, Generate TTS Audio, Transcribe Whisper.\n\n### Try it\n\nClick Listen for test event on the Webhook, then POST:\n```json\n{\n \"topic\": \"Why octopuses have three hearts\",\n \"words\": 80,\n \"voice\": \"nova\"\n}\n```\nReturns script + SRT in ~10s. Decode audioBase64 to a .mp3 file.\n\n### Customization\n\n~$0.012 per call (GPT-4o-mini + tts-1 + whisper-1). Tune the SRT chunking constants (HARD_MAX, IDEAL, TAIL_HOLD) at the top of Build SRT. Save the MP3 by adding a Write Binary File or Cloudinary node after Generate TTS Audio."
},
"typeVersion": 1
},
{
"id": "sticky-setup",
"name": "Sticky Script",
"type": "n8n-nodes-base.stickyNote",
"position": [
432,
128
],
"parameters": {
"color": 7,
"width": 400,
"height": 360,
"content": "## Generate the script\n\nGPT-4o-mini writes a punchline-first viral Short script; Extract Script pulls the text and checks it is non-empty."
},
"typeVersion": 1
},
{
"id": "sticky-test",
"name": "Sticky Receive",
"type": "n8n-nodes-base.stickyNote",
"position": [
160,
128
],
"parameters": {
"color": 7,
"width": 220,
"height": 360,
"content": "## Receive the request\n\nWebhook accepts a POST with topic, words and voice. See the overview note for a test payload."
},
"typeVersion": 1
},
{
"id": "sticky-srt",
"name": "Sticky Captions",
"type": "n8n-nodes-base.stickyNote",
"position": [
1088,
128
],
"parameters": {
"color": 7,
"width": 450,
"height": 360,
"content": "## Transcribe and build captions\n\nWhisper word-timestamps the audio; Build SRT chunks words into Shorts-style cues (sentence boundaries respected, no flicker)."
},
"typeVersion": 1
},
{
"id": "sticky-audio",
"name": "Sticky Audio",
"type": "n8n-nodes-base.stickyNote",
"position": [
864,
128
],
"parameters": {
"color": 7,
"width": 180,
"height": 360,
"content": "## Create narration audio\n\ntts-1 outputs the MP3 as binary `data`, carried through to the response."
},
"typeVersion": 1
},
{
"id": "1e20dbb7-70ee-4576-859e-548b8a16eaa1",
"name": "Sticky Respond",
"type": "n8n-nodes-base.stickyNote",
"position": [
1552,
128
],
"parameters": {
"color": 7,
"width": 220,
"height": 360,
"content": "## Return the result\n\nResponds with { script, srt, audioBase64, durationSeconds }."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"executionOrder": "v1",
"saveManualExecutions": true,
"saveExecutionProgress": true,
"saveDataErrorExecution": "all",
"saveDataSuccessExecution": "all"
},
"versionId": "2a822b5c-45c9-4773-99b2-29c64aad742c",
"connections": {
"Webhook": {
"main": [
[
{
"node": "Generate Script",
"type": "main",
"index": 0
}
]
]
},
"Build SRT": {
"main": [
[
{
"node": "Respond",
"type": "main",
"index": 0
}
]
]
},
"Extract Script": {
"main": [
[
{
"node": "Generate TTS Audio",
"type": "main",
"index": 0
}
]
]
},
"Generate Script": {
"main": [
[
{
"node": "Extract Script",
"type": "main",
"index": 0
}
]
]
},
"Generate TTS Audio": {
"main": [
[
{
"node": "Transcribe Whisper",
"type": "main",
"index": 0
}
]
]
},
"Transcribe Whisper": {
"main": [
[
{
"node": "Build SRT",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow exposes a webhook that generates a punchline-first YouTube Shorts script with OpenAI, turns it into an MP3 voiceover using OpenAI Text-to-Speech, then transcribes that audio with Whisper to return word-timed SRT captions plus the audio as base64. Receives a POST…
Source: https://n8n.io/workflows/16105/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This n8n template provides enterprise-level version control for your workflows using GitHub integration. Stop losing hours to broken workflows and manual exports – get proper commit history, visual di
This flow creates dummy files for every item added in your *Arrs (Radarr/Sonarr) with the tag .
eek-Go v2 (Batch-Then-Review). Uses httpRequest. Webhook trigger; 75 nodes.
This workflow receives webhook requests from a content calendar and uses the X API v2 to publish text posts, threads, image/video posts, and polls, as well as delete existing posts and run a credentia
This workflow acts as a central API gateway for all technical indicator agents in the Binance Spot Market Quant AI system. It listens for incoming webhook requests and dynamically routes them to the c