{
  "id": "V09FW4Rn4Nicl5de",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Generate complete drama scripts with AI from topic and genre via webhook",
  "tags": [],
  "nodes": [
    {
      "id": "337a40fc-8e3a-4e59-bb80-3c8daee41b92",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2000,
        -480
      ],
      "parameters": {
        "width": 980,
        "height": 1096,
        "content": "## Generate complete drama scripts with AI from topic and genre via webhook\n\nThis workflow turns a simple topic and genre into a complete, production-ready drama script \u2014 characters, act and scene breakdowns, full dialogue with stage directions, emotional beats, themes, and production notes \u2014 all returned as structured JSON via webhook.\n\n### Who's it for\n- Screenwriters and playwrights who want a fast first draft to react to and revise\n- Theatre groups, film students, and content creators needing rapid script prototypes\n- Apps or bots that need on-demand scripted content (interactive fiction, training scenarios, roleplay content)\n- Writers' rooms looking for brainstorm material or alternate scene takes\n\n### How it works\n1. Caller sends a POST request with a topic, genre, and optional act count/tone\n2. Code validates the input and maps the genre to tone, pacing, and style guidelines\n3. AI generates a story outline: title, logline, themes, full character profiles, and an act/scene breakdown\n4. AI expands that outline into a complete script \u2014 full dialogue and stage directions per scene, with emotional beats noted\n5. AI generates production notes: staging, lighting/sound ideas, costume/set direction, casting notes, runtime estimate\n6. Everything is merged into one structured JSON object\n7. The request is logged to Google Sheets for tracking\n8. Webhook returns the complete script as JSON to the caller\n\n### How to set up\n1. Import this workflow into n8n\n2. Add your OpenAI credentials to all three OpenAI Chat Model nodes\n3. Add Google Sheets OAuth2 credentials and replace YOUR_SHEET_ID in the logging node\n4. Activate the workflow and POST a test request with topic, genre, numberOfActs, and tone\n\n### Requirements\n- OpenAI API key (GPT-4.1 or above recommended for long-form dialogue)\n- Google Sheets with OAuth2 credentials (optional, for logging)\n\n### How to customize\n- Adjust genre style guidelines in Code - Validate & Configure Genre Style\n- Change default act/scene counts per genre\n- Swap the three-stage AI pipeline for a single mega-prompt if you prefer fewer tokens/lower cost\n- Add a fourth AI stage for a cold open / teaser scene\n- Swap Google Sheets logging for Airtable or Notion\n- Add a translation step to deliver scripts in other languages\n\n### Google Sheets Column Layout\nSet up your ScriptRequests tab with these headers in row 1:\nRequest ID | Date | Timestamp | Topic | Genre | Title | Number Of Acts | Number Of Scenes | Word Count | Status"
      },
      "typeVersion": 1
    },
    {
      "id": "8dfb1239-a8d5-4d63-a0ef-95843fa0b993",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3120,
        -464
      ],
      "parameters": {
        "color": 3,
        "width": 932,
        "height": 720,
        "content": "## 1. Trigger & Input Validation\n\nAccepts script requests via webhook POST.\n\nRequired POST fields: topic, genre. Optional: numberOfActs (default 3), tone (default matches genre), targetLength (short / medium / full-length).\n\nThe Set node normalizes all fields and generates a unique requestId and timestamp.\n\nThe Code node maps the genre to a set of style guidelines (tone descriptors, pacing notes, typical scene count) and clamps numberOfActs to a sane range (1\u20135).\n\nIf topic or genre is missing, the Filter routes to an error handler that immediately returns a clear error response to the caller instead of calling the AI."
      },
      "typeVersion": 1
    },
    {
      "id": "e7ec77d3-c760-4438-a4a2-15868757f36b",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4080,
        -464
      ],
      "parameters": {
        "color": 3,
        "width": 748,
        "height": 900,
        "content": "## 2. Outline & Character Generation\n\nThe first AI stage takes the topic, genre, and style guidelines and produces the creative blueprint for the whole script:\n\n- Title and one-line logline\n- Core themes\n- Setting\n- Full character profiles (name, role, age, description, want vs. need, arc)\n- Act-by-act, scene-by-scene breakdown with a short summary and emotional beat for each scene\n\nThe AI is instructed to respond with JSON only, which the following Code node parses safely (falling back gracefully if the model wraps the JSON in text or code fences)."
      },
      "typeVersion": 1
    },
    {
      "id": "d2e1b283-cf7b-42da-821d-5af1caa91f06",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4896,
        -448
      ],
      "parameters": {
        "color": 3,
        "width": 836,
        "height": 848,
        "content": "## 3. Full Script Expansion\n\nThe second AI stage takes the parsed outline (characters + act/scene breakdown) and writes the actual script.\n\nFor every scene it produces:\n- A scene heading and location\n- Full stage directions\n- Complete character dialogue, line by line, with any parenthetical actor direction\n- A short note on the emotional beat being played\n\nThe model is instructed to keep every character's voice consistent with their profile from stage one, and to return everything as structured JSON so it can be merged programmatically later rather than left as loose prose."
      },
      "typeVersion": 1
    },
    {
      "id": "a9766b31-78c8-4b59-ab3f-d872a453c14f",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5792,
        -480
      ],
      "parameters": {
        "color": 3,
        "width": 1120,
        "height": 800,
        "content": "## 4. Production Notes, Merge & Delivery\n\nThe third AI stage adds a director's-eye layer on top of the finished script: staging suggestions, lighting/sound design ideas, costume and set notes, casting suggestions, an estimated runtime, and any content notes worth flagging.\n\nCode - Format Final Script Output merges the outline, full script, and production notes into a single JSON object and computes basic stats like total scene count and word count.\n\nLog to Google Sheet Tracker appends one audit row per request (continueOnFail is on, so a Sheets outage never blocks the response).\n\nWebhook Response - Send Script returns the complete script package as JSON to the original caller."
      },
      "typeVersion": 1
    },
    {
      "id": "8bd32094-8484-4307-82c2-557cc5f9c4f0",
      "name": "Webhook - Script Generation Request",
      "type": "n8n-nodes-base.webhook",
      "position": [
        3248,
        0
      ],
      "parameters": {
        "path": "generate-drama-script",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 1.1
    },
    {
      "id": "7765ac69-2e3a-4d4c-acb5-5882a5021f33",
      "name": "Set - Prepare Script Parameters",
      "type": "n8n-nodes-base.set",
      "position": [
        3472,
        0
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "name": "topic",
              "type": "string",
              "value": "={{ $json.topic || $json.body?.topic || '' }}"
            },
            {
              "name": "genre",
              "type": "string",
              "value": "={{ ($json.genre || $json.body?.genre || '').toLowerCase() }}"
            },
            {
              "name": "numberOfActs",
              "type": "number",
              "value": "={{ $json.numberOfActs || $json.body?.numberOfActs || 3 }}"
            },
            {
              "name": "tone",
              "type": "string",
              "value": "={{ $json.tone || $json.body?.tone || '' }}"
            },
            {
              "name": "targetLength",
              "type": "string",
              "value": "={{ $json.targetLength || $json.body?.targetLength || 'short' }}"
            },
            {
              "name": "requestId",
              "type": "string",
              "value": "={{ 'SCRIPT-' + Date.now().toString() }}"
            },
            {
              "name": "requestTimestamp",
              "type": "string",
              "value": "={{ new Date().toISOString() }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "84ec6d40-1e11-41c7-98cd-dc75a14de61e",
      "name": "Code - Validate & Configure Genre Style",
      "type": "n8n-nodes-base.code",
      "position": [
        3904,
        0
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Validate input and map genre to style guidelines\nconst item = $input.item.json;\n\nconst topic = (item.topic || '').trim();\nconst genre = (item.genre || '').trim();\n\nconst genreStyles = {\n  tragedy: { tone: 'somber, weighty, inevitable', pacing: 'slow-building with a devastating climax', defaultScenesPerAct: 3 },\n  comedy: { tone: 'light, witty, fast-talking', pacing: 'quick, punchy scenes with comic timing', defaultScenesPerAct: 4 },\n  thriller: { tone: 'tense, suspicious, high-stakes', pacing: 'escalating tension with cliffhanger scene breaks', defaultScenesPerAct: 4 },\n  romance: { tone: 'warm, yearning, emotionally intimate', pacing: 'slow-burn with tender turning points', defaultScenesPerAct: 3 },\n  'historical drama': { tone: 'formal, sweeping, era-authentic', pacing: 'measured with rich period detail', defaultScenesPerAct: 3 },\n  'sci-fi drama': { tone: 'reflective, speculative, human-centered', pacing: 'idea-driven with grounded emotional stakes', defaultScenesPerAct: 3 },\n  'family drama': { tone: 'intimate, layered, bittersweet', pacing: 'conversational with slow-reveal secrets', defaultScenesPerAct: 3 },\n  courtroom: { tone: 'sharp, procedural, morally charged', pacing: 'argument-and-reveal structure', defaultScenesPerAct: 4 },\n  mystery: { tone: 'curious, guarded, clue-driven', pacing: 'steady reveals building to a twist', defaultScenesPerAct: 4 },\n  default: { tone: 'grounded and emotionally honest', pacing: 'balanced rise and fall across acts', defaultScenesPerAct: 3 }\n};\n\nconst style = genreStyles[genre] || genreStyles.default;\nconst tone = item.tone && item.tone.length > 0 ? item.tone : style.tone;\n\nlet numberOfActs = parseInt(item.numberOfActs, 10);\nif (isNaN(numberOfActs) || numberOfActs < 1) numberOfActs = 3;\nif (numberOfActs > 5) numberOfActs = 5;\n\nconst hasValidInput = topic.length > 0 && genre.length > 0;\n\nreturn {\n  json: {\n    ...item,\n    topic,\n    genre,\n    tone,\n    numberOfActs,\n    scenesPerAct: style.defaultScenesPerAct,\n    pacingNotes: style.pacing,\n    hasValidInput\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "859be8fc-a9e8-4293-94f3-77a40d522fde",
      "name": "Filter - Valid Input",
      "type": "n8n-nodes-base.filter",
      "position": [
        4128,
        0
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "conditions": [
            {
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.hasValidInput }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "75feecf0-7d37-4482-b499-f34fd7bac5e5",
      "name": "AI - Generate Outline & Characters",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        4352,
        0
      ],
      "parameters": {
        "text": "=You are a professional dramaturg. Create a complete story blueprint for a {{ $json.genre }} script about: \"{{ $json.topic }}\".\n\nTone: {{ $json.tone }}\nPacing notes: {{ $json.pacingNotes }}\nNumber of acts: {{ $json.numberOfActs }}\nScenes per act (target): {{ $json.scenesPerAct }}\n\nRespond with ONLY valid JSON, no commentary, no code fences, in exactly this shape:\n{\n  \"title\": string,\n  \"logline\": string,\n  \"themes\": string[],\n  \"setting\": string,\n  \"characters\": [ { \"name\": string, \"role\": string, \"age\": string, \"description\": string, \"wantVsNeed\": string, \"arc\": string } ],\n  \"actBreakdown\": [ { \"actNumber\": number, \"actTitle\": string, \"scenes\": [ { \"sceneNumber\": number, \"sceneTitle\": string, \"location\": string, \"summary\": string, \"charactersPresent\": string[], \"emotionalBeat\": string } ] } ]\n}",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 1.6
    },
    {
      "id": "d5c2262f-37f4-46bc-8bec-31cb78737c3a",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        4432,
        224
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1"
        },
        "options": {},
        "builtInTools": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "611ebfd1-52c2-40c0-8a25-c0ae7b3e6f06",
      "name": "Code - Parse Outline JSON",
      "type": "n8n-nodes-base.code",
      "position": [
        4704,
        0
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Parse the outline JSON from the AI response, tolerating stray text/code fences\nconst item = $input.item.json;\nconst raw = item.output || item.response || item.text || '';\n\nlet cleaned = raw.trim();\ncleaned = cleaned.replace(/^```json/i, '').replace(/^```/, '').replace(/```$/, '').trim();\n\nlet outline;\ntry {\n  outline = JSON.parse(cleaned);\n} catch (e) {\n  const match = cleaned.match(/\\{[\\s\\S]*\\}/);\n  outline = match ? JSON.parse(match[0]) : { error: 'Failed to parse outline JSON', raw: cleaned };\n}\n\nreturn {\n  json: {\n    requestId: $('Set - Prepare Script Parameters').item.json.requestId,\n    requestTimestamp: $('Set - Prepare Script Parameters').item.json.requestTimestamp,\n    topic: $('Set - Prepare Script Parameters').item.json.topic,\n    genre: $('Set - Prepare Script Parameters').item.json.genre,\n    tone: $('Set - Prepare Script Parameters').item.json.tone,\n    numberOfActs: $('Set - Prepare Script Parameters').item.json.numberOfActs,\n    outline\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "9d4ba1ba-a6a6-4005-a2d8-56b4201aef79",
      "name": "AI - Generate Full Script",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        4928,
        0
      ],
      "parameters": {
        "text": "=You are a professional playwright. Using this story outline, write the complete script.\n\nOutline JSON:\n{{ JSON.stringify($json.outline) }}\n\nInstructions:\n- Write every scene listed in the actBreakdown in full, in the same order.\n- Keep each character's voice consistent with their profile.\n- Include full stage directions and complete dialogue, plus any short parenthetical actor direction on individual lines where useful.\n- Do not skip or summarize any scene \u2014 write it out completely.\n\nRespond with ONLY valid JSON, no commentary, no code fences, in exactly this shape:\n{\n  \"acts\": [\n    {\n      \"actNumber\": number,\n      \"actTitle\": string,\n      \"scenes\": [\n        {\n          \"sceneNumber\": number,\n          \"sceneTitle\": string,\n          \"location\": string,\n          \"stageDirections\": string,\n          \"dialogue\": [ { \"character\": string, \"line\": string, \"direction\": string } ],\n          \"emotionalBeat\": string\n        }\n      ]\n    }\n  ]\n}",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 1.6
    },
    {
      "id": "51655281-106f-4d75-b681-b6dd0a03b3ab",
      "name": "OpenAI Chat Model1",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        5008,
        224
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1"
        },
        "options": {},
        "builtInTools": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "5ff3a2bb-5746-4923-81ae-f3d221398edd",
      "name": "Code - Parse Full Script JSON",
      "type": "n8n-nodes-base.code",
      "position": [
        5280,
        0
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Parse the full script JSON from the AI response\nconst item = $input.item.json;\nconst raw = item.output || item.response || item.text || '';\n\nlet cleaned = raw.trim();\ncleaned = cleaned.replace(/^```json/i, '').replace(/^```/, '').replace(/```$/, '').trim();\n\nlet script;\ntry {\n  script = JSON.parse(cleaned);\n} catch (e) {\n  const match = cleaned.match(/\\{[\\s\\S]*\\}/);\n  script = match ? JSON.parse(match[0]) : { error: 'Failed to parse script JSON', raw: cleaned };\n}\n\nconst prior = $('Code - Parse Outline JSON').item.json;\n\nreturn {\n  json: {\n    ...prior,\n    script\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "a3c37a6d-8ab5-46e8-8e06-532a913835ee",
      "name": "AI - Generate Production Notes",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        5504,
        0
      ],
      "parameters": {
        "text": "=You are an experienced theatre/film director. Based on this script's genre, tone, and themes, write concise production notes.\n\nGenre: {{ $json.genre }}\nTone: {{ $json.tone }}\nThemes: {{ JSON.stringify($json.outline?.themes) }}\nTitle: {{ $json.outline?.title }}\nNumber of scenes: {{ ($json.script?.acts || []).reduce((sum, a) => sum + (a.scenes?.length || 0), 0) }}\n\nRespond with ONLY valid JSON, no commentary, no code fences, in exactly this shape:\n{\n  \"stagingNotes\": string,\n  \"lightingAndSound\": string,\n  \"costumeAndSetDesign\": string,\n  \"castingNotes\": string,\n  \"estimatedRuntimeMinutes\": number,\n  \"contentNotes\": string\n}",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 1.6
    },
    {
      "id": "799d6f19-f381-43e7-9d84-8d65ae93db43",
      "name": "OpenAI Chat Model2",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        5584,
        224
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1-mini"
        },
        "options": {},
        "builtInTools": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "fee1db7c-4258-4b36-865c-30638ff13f66",
      "name": "Code - Format Final Script Output",
      "type": "n8n-nodes-base.code",
      "position": [
        5856,
        0
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Merge outline + full script + production notes into one final payload\nconst item = $input.item.json;\nconst raw = item.output || item.response || item.text || '';\n\nlet cleaned = raw.trim();\ncleaned = cleaned.replace(/^```json/i, '').replace(/^```/, '').replace(/```$/, '').trim();\n\nlet productionNotes;\ntry {\n  productionNotes = JSON.parse(cleaned);\n} catch (e) {\n  const match = cleaned.match(/\\{[\\s\\S]*\\}/);\n  productionNotes = match ? JSON.parse(match[0]) : { error: 'Failed to parse production notes JSON', raw: cleaned };\n}\n\nconst prior = $('Code - Parse Full Script JSON').item.json;\nconst acts = prior.script?.acts || [];\n\nlet sceneCount = 0;\nlet wordCount = 0;\nfor (const act of acts) {\n  for (const scene of (act.scenes || [])) {\n    sceneCount += 1;\n    wordCount += (scene.stageDirections || '').split(/\\s+/).filter(Boolean).length;\n    for (const line of (scene.dialogue || [])) {\n      wordCount += (line.line || '').split(/\\s+/).filter(Boolean).length;\n    }\n  }\n}\n\nreturn {\n  json: {\n    success: true,\n    requestId: prior.requestId,\n    requestTimestamp: prior.requestTimestamp,\n    topic: prior.topic,\n    genre: prior.genre,\n    tone: prior.tone,\n    title: prior.outline?.title,\n    logline: prior.outline?.logline,\n    themes: prior.outline?.themes,\n    setting: prior.outline?.setting,\n    characters: prior.outline?.characters,\n    numberOfActs: prior.numberOfActs,\n    numberOfScenes: sceneCount,\n    wordCount,\n    acts,\n    productionNotes,\n    logDate: new Date().toISOString().split('T')[0],\n    logTimestamp: new Date().toISOString()\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "9f9d4666-e13d-4fbf-a33f-1824ba29bdf8",
      "name": "Wait - Pre-Delivery Buffer",
      "type": "n8n-nodes-base.wait",
      "position": [
        6080,
        0
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "5328aff8-e41d-43f1-8396-e8ff413b3454",
      "name": "Log to Google Sheet Tracker",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        6304,
        96
      ],
      "parameters": {
        "url": "https://sheets.googleapis.com/v4/spreadsheets/YOUR_SHEET_ID/values/ScriptRequests!A1:append?valueInputOption=USER_ENTERED",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"values\": [[\n    \"{{ $json.requestId }}\",\n    \"{{ $json.logDate }}\",\n    \"{{ $json.logTimestamp }}\",\n    \"{{ $json.topic }}\",\n    \"{{ $json.genre }}\",\n    \"{{ $json.title }}\",\n    \"{{ $json.numberOfActs }}\",\n    \"{{ $json.numberOfScenes }}\",\n    \"{{ $json.wordCount }}\",\n    \"Generated\"\n  ]]\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googleSheetsOAuth2Api"
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2,
      "continueOnFail": true
    },
    {
      "id": "4db15747-b517-4b19-b724-449c1d48fe91",
      "name": "Webhook Response - Send Script",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        6304,
        -96
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ success: true, requestId: $json.requestId, title: $json.title, logline: $json.logline, themes: $json.themes, setting: $json.setting, characters: $json.characters, numberOfActs: $json.numberOfActs, numberOfScenes: $json.numberOfScenes, wordCount: $json.wordCount, acts: $json.acts, productionNotes: $json.productionNotes }) }}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "f5eee6f0-0003-4d1d-8d67-c283a8dc404a",
      "name": "Wait For Data",
      "type": "n8n-nodes-base.wait",
      "position": [
        3680,
        0
      ],
      "parameters": {},
      "typeVersion": 1.1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "9ad9a9c1-5903-4fa6-a459-c0b942256d11",
  "connections": {
    "Wait For Data": {
      "main": [
        [
          {
            "node": "Code - Validate & Configure Genre Style",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI - Generate Outline & Characters",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model1": {
      "ai_languageModel": [
        [
          {
            "node": "AI - Generate Full Script",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model2": {
      "ai_languageModel": [
        [
          {
            "node": "AI - Generate Production Notes",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Filter - Valid Input": {
      "main": [
        [
          {
            "node": "AI - Generate Outline & Characters",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI - Generate Full Script": {
      "main": [
        [
          {
            "node": "Code - Parse Full Script JSON",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code - Parse Outline JSON": {
      "main": [
        [
          {
            "node": "AI - Generate Full Script",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait - Pre-Delivery Buffer": {
      "main": [
        [
          {
            "node": "Webhook Response - Send Script",
            "type": "main",
            "index": 0
          },
          {
            "node": "Log to Google Sheet Tracker",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code - Parse Full Script JSON": {
      "main": [
        [
          {
            "node": "AI - Generate Production Notes",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI - Generate Production Notes": {
      "main": [
        [
          {
            "node": "Code - Format Final Script Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set - Prepare Script Parameters": {
      "main": [
        [
          {
            "node": "Wait For Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code - Format Final Script Output": {
      "main": [
        [
          {
            "node": "Wait - Pre-Delivery Buffer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI - Generate Outline & Characters": {
      "main": [
        [
          {
            "node": "Code - Parse Outline JSON",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook - Script Generation Request": {
      "main": [
        [
          {
            "node": "Set - Prepare Script Parameters",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code - Validate & Configure Genre Style": {
      "main": [
        [
          {
            "node": "Filter - Valid Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}