AutomationFlowsEmail & Gmail › Echo Brand Voice Analysis (processor) - Task-074 Dec 10 Fix

Echo Brand Voice Analysis (processor) - Task-074 Dec 10 Fix

Echo Brand Voice Analysis (Processor) - TASK-074 Dec 10 Fix. Uses formTrigger, httpRequest, executeWorkflowTrigger, moveBinaryData. Event-driven trigger; 40 nodes.

Event trigger★★★★★ complexity40 nodesForm TriggerHTTP RequestExecute Workflow TriggerMove Binary DataGmail
Email & Gmail Trigger: Event Nodes: 40 Complexity: ★★★★★ Added:

This workflow follows the Execute Workflow Trigger → 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
{
  "name": "Echo Brand Voice Analysis (Processor) - TASK-074 Dec 10 Fix",
  "nodes": [
    {
      "parameters": {
        "path": "echo-brand-voice-trigger",
        "formTitle": "Echo Brand Voice Analysis",
        "formDescription": "Analyze writing samples to create a brand voice profile for AI agents.",
        "formFields": {
          "values": [
            {
              "fieldLabel": "Voice Type",
              "fieldType": "dropdown",
              "fieldOptions": {
                "values": [
                  {
                    "option": "personal"
                  },
                  {
                    "option": "company"
                  },
                  {
                    "option": "combined"
                  }
                ]
              },
              "requiredField": true
            },
            {
              "fieldLabel": "Person Name",
              "placeholder": "e.g., Tyler Fisk (required for personal/combined)"
            },
            {
              "fieldLabel": "Company Name",
              "placeholder": "e.g., Hattie B's (required for company/combined)"
            },
            {
              "fieldLabel": "Writing Samples",
              "fieldType": "textarea",
              "placeholder": "Paste writing samples here (emails, transcripts, social posts). More samples = better analysis.",
              "requiredField": true
            },
            {
              "fieldLabel": "Company Materials",
              "fieldType": "textarea",
              "placeholder": "For company/combined: Paste brand guidelines, style guide, or marketing copy."
            },
            {
              "fieldLabel": "Skip QC Validation",
              "fieldType": "dropdown",
              "fieldOptions": {
                "values": [
                  {
                    "option": "No (Recommended)"
                  },
                  {
                    "option": "Yes (Save ~$0.03)"
                  }
                ]
              }
            }
          ]
        },
        "options": {}
      },
      "id": "8ecc78ac-790e-4c44-8360-175e04a96ea2",
      "name": "Manual Trigger",
      "type": "n8n-nodes-base.formTrigger",
      "typeVersion": 2.1,
      "position": [
        -1104,
        1056
      ],
      "notes": "Entry point for brand voice analysis.\n\nVoice Types:\n- personal: Analyze individual's voice\n- company: Analyze company brand voice\n- combined: Personal voice at company context"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "claude-key",
              "name": "claude_api_key",
              "value": "REPLACE_WITH_YOUR_ANTHROPIC_API_KEY",
              "type": "string"
            },
            {
              "id": "analyzer-prompt",
              "name": "analyzer_prompt",
              "value": "See assets/prompt-library/agents/echo-analyzer.md for full prompt",
              "type": "string"
            },
            {
              "id": "formatter-prompt",
              "name": "formatter_prompt",
              "value": "<!-- Echo Brand Voice Formatter v1.0 -->\n<version_info>\n<name>Echo - Brand Voice Formatter</name>\n<version>1.0</version>\n<date>2025-11-27</date>\n<phase>3 (XML Generation)</phase>\n</version_info>\n\n<role>\nYou are a precision formatter that transforms JSON brand voice analysis into optimized XML snippets. Your output will be injected directly into AI agent system prompts, so every word must serve a purpose.\n\nYou prioritize:\n- Token efficiency (\u22641000 words strict limit)\n- Actionable specificity (AI can implement your guidelines)\n- Structured consistency (XML schema must be followed exactly)\n</role>\n\n<input_format>\nYou will receive a JSON object from the Echo Analyzer containing:\n- voice_type, subject information\n- analysis_summary\n- linguistic_profile\n- vocabulary_compilation\n- dos_and_donts\n- emotional_range\n- contextual_variations\n- golden_threads\n- quantitative_anchors\n- conflict_resolutions (for combined type only)\n</input_format>\n\n<output_schema>\n## Required XML Structure\n\nYour output MUST follow this exact schema:\n\n<BrandVoice type=\"[personal|company|combined]\" subject=\"[Name]\" company=\"[Company or null]\">\n\n  <summary>\n    [100-150 word essence of this voice. Must be specific enough that\n    reading this alone gives a clear sense of the voice character.]\n  </summary>\n\n  <golden_threads>\n    <thread priority=\"1\">[Core motivation - weave throughout content]</thread>\n    <thread priority=\"2\">[Secondary motivation]</thread>\n    <thread priority=\"3\">[Tertiary motivation if applicable]</thread>\n  </golden_threads>\n\n  <key_characteristics>\n    <characteristic>\n      <name>[Characteristic name]</name>\n      <description>[Brief description with example]</description>\n    </characteristic>\n    <!-- 3-5 characteristics -->\n  </key_characteristics>\n\n  <voice_attributes>\n    <tone>[Primary tone descriptor - secondary descriptor]</tone>\n    <diction>\n      <use>[Comma-separated words and phrases TO USE]</use>\n      <avoid>[Comma-separated words and phrases TO AVOID]</avoid>\n    </diction>\n    <sentence_style>[Description of sentence structure patterns]</sentence_style>\n    <narrative_stance>[Person, voice, storytelling pattern]</narrative_stance>\n  </voice_attributes>\n\n  <language_patterns>\n    <pattern>[Specific pattern with example from samples]</pattern>\n    <!-- 3-5 patterns -->\n  </language_patterns>\n\n  <emotional_expression>\n    <primary_emotions>[Comma-separated emotions]</primary_emotions>\n    <intensity>[low|medium|high]</intensity>\n    <methods>[How emotions are expressed linguistically]</methods>\n  </emotional_expression>\n\n  <dos_and_donts>\n    <do>[Action] - [Brief justification]</do>\n    <!-- 3-5 dos -->\n    <dont>[Avoidance] - [Brief justification]</dont>\n    <!-- 3-5 don'ts -->\n  </dos_and_donts>\n\n  <contextual_variations>\n    <context type=\"formal\">\n      [How voice adapts for formal contexts]\n    </context>\n    <context type=\"casual\">\n      [How voice adapts for casual contexts]\n    </context>\n  </contextual_variations>\n\n  <!-- Only for combined type -->\n  <constraint_hierarchy type=\"combined\">\n    <rule>Company constraints are HARD boundaries - never violate</rule>\n    <rule>Personal patterns apply WITHIN company boundaries</rule>\n    <conflicts>\n      <conflict>\n        <personal>[Pattern that conflicts]</personal>\n        <company>[Constraint it violates]</company>\n        <resolution>[How to handle]</resolution>\n      </conflict>\n    </conflicts>\n  </constraint_hierarchy>\n\n  <purpose>\n    [1-2 sentence primary goal of this voice in AI communications]\n  </purpose>\n\n</BrandVoice>\n</output_schema>\n\n<formatting_rules>\n## Critical Formatting Rules\n\n1. **Word Limit**: Maximum 1000 words. Count before finalizing.\n\n2. **Specificity Over Generality**:\n   - BAD: \"Use a friendly tone\"\n   - GOOD: \"Use contractions, first-person plural ('we'), and end sentences with enthusiasm\"\n\n3. **Examples Are Required**: Every pattern should include at least one example phrase or word.\n\n4. **Quantify When Possible**:\n   - BAD: \"Mix sentence lengths\"\n   - GOOD: \"Alternate between short (5-8 words) and medium (12-18 words) sentences\"\n\n5. **Golden Threads Must Be Concrete**:\n   - BAD: \"Cares about customers\"\n   - GOOD: \"Financial security for the family - reference savings, investment, long-term value\"\n\n6. **Dos/Don'ts Must Be Actionable**:\n   - BAD: \"Don't be boring\"\n   - GOOD: \"Don't use passive voice - always use active constructions\"\n\n7. **No Filler**: Every word must add value. Cut phrases like:\n   - \"It's important to note that...\"\n   - \"When writing, remember to...\"\n   - \"The voice is characterized by...\"\n</formatting_rules>\n\n<instructions>\n## Formatting Process\n\n1. **Parse Input JSON**: Extract all relevant fields from the analyzer output.\n\n2. **Distill to Essentials**: The analyzer provides comprehensive data. Your job is to compress to the most impactful elements.\n\n3. **Prioritize by Impact**:\n   - Golden threads (used for narrative weaving)\n   - Dos/Don'ts (direct AI instructions)\n   - Voice attributes (tone, diction)\n   - Language patterns (specific examples)\n\n4. **Apply Schema**: Format into the exact XML structure required.\n\n5. **Word Count Check**: Count words. If over 1000, cut:\n   - Redundant examples\n   - Secondary characteristics\n   - Verbose descriptions\n\n6. **Validate Structure**: Ensure all required tags are present and properly nested.\n\n7. **Output XML Only**: Return only the XML block. No introduction, no explanation.\n</instructions>\n\n<quality_check>\nBefore outputting, verify:\n- [ ] Word count \u22641000\n- [ ] All required XML tags present\n- [ ] At least 3 golden threads\n- [ ] At least 3 dos AND 3 don'ts\n- [ ] Specific examples included\n- [ ] No generic filler phrases\n- [ ] Proper XML nesting and closure\n</quality_check>",
              "type": "string"
            },
            {
              "id": "validator-prompt",
              "name": "validator_prompt",
              "value": "See assets/prompt-library/agents/echo-validator.md for full prompt",
              "type": "string"
            },
            {
              "id": "min-words",
              "name": "min_word_count",
              "value": 500,
              "type": "number"
            }
          ]
        },
        "options": {}
      },
      "id": "b0bd702a-2bb5-453f-b756-971f076af931",
      "name": "Configuration",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -880,
        1056
      ],
      "notes": "CONFIGURE HERE:\n\n1. Replace REPLACE_WITH_YOUR_ANTHROPIC_API_KEY with your actual API key from console.anthropic.com\n2. Copy prompts from assets/prompt-library/agents/echo-*.md\n3. Adjust min_word_count if needed"
    },
    {
      "parameters": {
        "jsCode": "// Validate input fields\n// Handle both Manual Trigger (form) and Normalize External Input (Execute Workflow) paths\nlet input;\ntry {\n  input = $('Manual Trigger').first().json;\n} catch (e) {\n  // Called via Execute Workflow - use Normalize External Input\n  input = $('Normalize External Input').first().json;\n}\n\nconst minWords = $('Configuration').first().json.min_word_count || 500;\nconst errors = [];\nconst config = $('Configuration').first().json;\n\nconst voiceType = input['Voice Type'] || '';\nconst personName = input['Person Name'] || '';\nconst companyName = input['Company Name'] || '';\nconst writingSamples = input['Writing Samples'] || '';\nconst companyMaterials = input['Company Materials'] || '';\nconst skipQC = input['Skip QC Validation'] === 'Yes (Save ~$0.03)';\n\n// Check voice type\nif (!['personal', 'company', 'combined'].includes(voiceType)) {\n  errors.push({\n    field: 'Voice Type',\n    message: 'Must select personal, company, or combined'\n  });\n}\n\n// Check required names based on voice type\nif (voiceType === 'personal' && !personName.trim()) {\n  errors.push({\n    field: 'Person Name',\n    message: 'Person Name required for personal voice type'\n  });\n}\n\nif (voiceType === 'company' && !companyName.trim()) {\n  errors.push({\n    field: 'Company Name',\n    message: 'Company Name required for company voice type'\n  });\n}\n\nif (voiceType === 'combined') {\n  if (!personName.trim()) {\n    errors.push({\n      field: 'Person Name',\n      message: 'Person Name required for combined voice type'\n    });\n  }\n  if (!companyName.trim()) {\n    errors.push({\n      field: 'Company Name',\n      message: 'Company Name required for combined voice type'\n    });\n  }\n}\n\n// Check writing samples\nif (!writingSamples.trim()) {\n  errors.push({\n    field: 'Writing Samples',\n    message: 'Writing samples are required'\n  });\n} else {\n  const wordCount = writingSamples.split(/\\s+/).length;\n  if (wordCount < minWords) {\n    errors.push({\n      field: 'Writing Samples',\n      message: `Minimum ${minWords} words required. Found ${wordCount} words.`\n    });\n  }\n}\n\n// Check company materials for company/combined\nif (['company', 'combined'].includes(voiceType) && !companyMaterials.trim()) {\n  // Warning, not error\n  console.log('Warning: Company materials recommended for company/combined voice type');\n}\n\n// Preserve student_email from Normalize External Input\nconst studentEmail = input.student_email || 'your-email@example.com';\n\n// Return result\nif (errors.length > 0) {\n  return [{\n    json: {\n      validation_passed: false,\n      errors: errors,\n      message: 'Input validation failed',\n      student_email: studentEmail\n    }\n  }];\n}\n\n// Validation passed - normalize inputs\nreturn [{\n  json: {\n    validation_passed: true,\n    voice_type: voiceType,\n    student_name: personName.trim(),\n    company_name: companyName.trim(),\n    writing_samples: writingSamples.trim(),\n    company_materials: companyMaterials.trim(),\n    skip_qc: skipQC,\n    word_count: writingSamples.split(/\\s+/).length,\n    claude_api_key: config.claude_api_key,\n    analyzer_prompt: config.analyzer_prompt,\n    formatter_prompt: config.formatter_prompt,\n    validator_prompt: config.validator_prompt,\n    min_word_count: config.min_word_count,\n    retry_count: 0,\n    student_email: studentEmail\n  }\n}];"
      },
      "id": "75e6dc14-b0a2-4cb1-ae1c-8e16c9a8c84e",
      "name": "Validate Input",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -656,
        1056
      ],
      "notes": "Validates:\n- Voice type selection\n- Required names for each type\n- Minimum word count in samples\n- Company materials for company/combined"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "validation-check",
              "leftValue": "={{ $json.validation_passed }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "f95cb263-ff27-4165-8904-df086b2bc4f7",
      "name": "Validation OK?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        -432,
        1056
      ],
      "notes": "Routes based on validation:\n- TRUE: Continue to Option Router\n- FALSE: Return error"
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict"
                },
                "conditions": [
                  {
                    "id": "personal-check",
                    "leftValue": "={{ $json.voice_type }}",
                    "rightValue": "personal",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ]
              },
              "renameOutput": true,
              "outputKey": "personal"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict"
                },
                "conditions": [
                  {
                    "id": "company-check",
                    "leftValue": "={{ $json.voice_type }}",
                    "rightValue": "company",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ]
              },
              "renameOutput": true,
              "outputKey": "company"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict"
                },
                "conditions": [
                  {
                    "id": "combined-check",
                    "leftValue": "={{ $json.voice_type }}",
                    "rightValue": "combined",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ]
              },
              "renameOutput": true,
              "outputKey": "combined"
            }
          ]
        },
        "options": {}
      },
      "id": "3b8d6037-df09-4e4c-98b6-247fdd15e1a9",
      "name": "Option Router",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3,
      "position": [
        -208,
        928
      ],
      "notes": "Routes to appropriate analysis path:\n- personal: Individual voice analysis\n- company: Brand voice analysis\n- combined: Hybrid analysis"
    },
    {
      "parameters": {
        "jsCode": "// Build personal voice analysis prompt\nconst input = $input.first().json;\nconst analyzerPrompt = $('Configuration').first().json.analyzer_prompt;\n\n// Personal-specific context\nconst context = `## Personal Voice Analysis Mode\n\nYou are analyzing writing samples to extract **${input.student_name}'s personal voice**.\n\n**Goal**: Create a voice profile that makes AI write exactly like ${input.student_name} - capturing their unique personality, quirks, and communication style.\n\n**Input Focus**:\n- Personal emails, messages, transcripts\n- Social media posts\n- Any authentic communication in their natural voice\n\n**Output Focus**:\n- Individual linguistic patterns\n- Personal vocabulary and phrases\n- Emotional expression style\n- Personality traits through language`;\n\nreturn [{\n  json: {\n    ...input,\n    analysis_context: context,\n    system_prompt: analyzerPrompt\n  }\n}];"
      },
      "id": "b5bf4092-3351-4eaf-b8eb-33c26861aeb8",
      "name": "Build Personal Prompt",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        16,
        752
      ],
      "notes": "Builds personal voice analysis context"
    },
    {
      "parameters": {
        "jsCode": "// Build company voice analysis prompt\nconst input = $input.first().json;\nconst analyzerPrompt = $('Configuration').first().json.analyzer_prompt;\n\n// Company-specific context\nconst context = `## Company Voice Analysis Mode\n\nYou are analyzing materials to extract the **${input.company_name} brand voice**.\n\n**Goal**: Create a voice profile usable by any employee writing as this company - consistent, professional, and aligned with brand values.\n\n**Input Focus**:\n- Brand style guides\n- Marketing copy and website content\n- Approved customer communications\n- Social media guidelines\n\n**Output Focus**:\n- Company tone and personality\n- Brand-specific vocabulary\n- Communication guidelines\n- Consistency requirements across channels`;\n\nreturn [{\n  json: {\n    ...input,\n    analysis_context: context,\n    system_prompt: analyzerPrompt\n  }\n}];"
      },
      "id": "470f9081-10de-4660-99db-fe32a97ec8a2",
      "name": "Build Company Prompt",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        16,
        944
      ],
      "notes": "Builds company voice analysis context"
    },
    {
      "parameters": {
        "jsCode": "// Build combined voice analysis prompt\nconst input = $input.first().json;\nconst analyzerPrompt = $('Configuration').first().json.analyzer_prompt;\n\n// Combined-specific context\nconst context = `## Combined Voice Analysis Mode\n\nYou are analyzing samples to extract **${input.student_name}'s voice when representing ${input.company_name}**.\n\n**Goal**: Create a hybrid profile - ${input.student_name}'s authentic personality filtered through ${input.company_name}'s brand constraints.\n\n**Input Focus**:\n- ${input.student_name}'s personal writing samples\n- ${input.company_name}'s brand guidelines/materials\n- (Optional) Past communications as company representative\n\n**Output Focus**:\n- Personal patterns that complement company voice\n- Where personal style enhances brand communication\n- Conflict resolution between personal and company preferences\n- Contextual switching guidance (internal vs external)\n\n**Constraint Hierarchy**:\n1. Company constraints are HARD boundaries (never violate)\n2. Personal patterns applied WITHIN those boundaries\n3. When conflict exists: Company wins, but FLAG the conflict`;\n\nreturn [{\n  json: {\n    ...input,\n    analysis_context: context,\n    system_prompt: analyzerPrompt\n  }\n}];"
      },
      "id": "6ee1465d-6306-483f-8a2c-b0785d464b21",
      "name": "Build Combined Prompt",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        16,
        1136
      ],
      "notes": "Builds combined voice analysis context with constraint hierarchy"
    },
    {
      "parameters": {
        "jsCode": "// Merge paths back together\nconst items = $input.all();\nif (items.length === 0) {\n  throw new Error('No input received');\n}\nreturn items;"
      },
      "id": "578fccde-ef86-4a07-aea2-cf1da962f8e0",
      "name": "Merge Paths",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        240,
        944
      ],
      "notes": "Merges the three voice type paths back to single flow"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $('Configuration').first().json.claude_api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"claude-sonnet-4-5-20250929\",\n  \"max_tokens\": 4000,\n  \"temperature\": 0.3,\n  \"system\": {{ JSON.stringify($('Configuration').first().json.formatter_prompt) }},\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify('Generate an XML brand voice snippet from the following analysis data. Output ONLY the XML starting with <BrandVoice> and ending with </BrandVoice>.\\n\\nAnalysis Data:\\n' + JSON.stringify($json, null, 2)) }}\n    }\n  ]\n}",
        "options": {
          "response": {
            "response": {
              "fullResponse": true,
              "responseFormat": "json"
            }
          }
        }
      },
      "id": "a42e6d7a-7e4d-497e-963d-59313de12d6b",
      "name": "Phase 3: XML Generation",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        912,
        848
      ],
      "notes": "Phase 3: Generate XML brand voice snippet.\n\nModel: Claude Sonnet 4.5\nTemp: 0.3 (low for consistent formatting)\nMax Tokens: 2000"
    },
    {
      "parameters": {
        "jsCode": "// Extract XML from formatter response\nconst input = $('Merge Analyses').first().json;\nconst httpResponse = $input.first().json;\nconst response = httpResponse.body || httpResponse;\n\n// Check for API errors\nif (response.error) {\n  return [{\n    json: {\n      ...input,\n      phase3_success: false,\n      phase3_error: response.error.message || 'Claude API error'\n    }\n  }];\n}\n\n// Extract text from Claude response\nconst content = response.content?.[0]?.text || '';\n\n// Look for XML in response\nconst xmlMatch = content.match(/<BrandVoice[\\s\\S]*<\\/BrandVoice>/);\nconst xmlOutput = xmlMatch ? xmlMatch[0] : content;\n\n// Count words in XML\nconst wordCount = xmlOutput.split(/\\s+/).length;\n\nreturn [{\n  json: {\n    ...input,\n    phase3_success: !!xmlMatch,\n    brand_voice_xml: xmlOutput,\n    xml_word_count: wordCount,\n    phase3_error: xmlMatch ? null : 'Could not extract XML from response'\n  }\n}];"
      },
      "id": "73dedb60-6887-44e4-abba-0c9516762618",
      "name": "Parse XML",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1136,
        848
      ],
      "notes": "Extracts XML brand voice snippet from formatter response"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "always-skip-qc",
              "leftValue": "={{ true }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "abc6f163-9676-4aba-8b15-541c97864f8d",
      "name": "Skip QC?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1360,
        848
      ],
      "notes": "TEMPORARY: Always skip QC for demo.\n\nOriginal condition was: $json.skip_qc === true\n\nTo restore: Change leftValue back to \"={{ $json.skip_qc }}\""
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $('Configuration').first().json.claude_api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"claude-sonnet-4-5-20250929\",\n  \"max_tokens\": 2000,\n  \"temperature\": 0,\n  \"system\": {{ JSON.stringify($('Configuration').first().json.validator_prompt) }},\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify('Validate the following brand voice profile. Output your validation report as JSON.\\n\\n' + $json.brand_voice_xml) }}\n    }\n  ]\n}",
        "options": {
          "response": {
            "response": {
              "fullResponse": true,
              "responseFormat": "json"
            }
          }
        }
      },
      "id": "d8066cfd-c5c0-4a99-8ee6-2b9d78d6a07f",
      "name": "Phase 4: QC Validation",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1584,
        912
      ],
      "notes": "Phase 4: Quality validation with emulation test.\n\nModel: Claude Sonnet 4.5\nTemp: 0.0 (CRITICAL - deterministic)\nMax Tokens: 2000"
    },
    {
      "parameters": {
        "jsCode": "// Parse validation response and determine pass/fail\nconst input = $('Parse XML').first().json;\nconst httpResponse = $input.first().json;\nconst response = httpResponse.body || httpResponse;\n\n// Check for API errors\nif (response.error) {\n  return [{\n    json: {\n      ...input,\n      validation_result: 'ERROR',\n      validation_error: response.error.message\n    }\n  }];\n}\n\n// Extract validation JSON\nconst content = response.content?.[0]?.text || '';\nlet validationJson = null;\n\ntry {\n  const jsonMatch = content.match(/\\{[\\s\\S]*\\}/);\n  if (jsonMatch) {\n    validationJson = JSON.parse(jsonMatch[0]);\n  }\n} catch (e) {\n  console.log('Validation JSON parse error:', e.message);\n}\n\nconst result = validationJson?.validation_result || 'UNKNOWN';\nconst corrections = validationJson?.corrections_needed || [];\n\nreturn [{\n  json: {\n    ...input,\n    validation_result: result,\n    validation_details: validationJson,\n    corrections_needed: corrections,\n    qc_passed: result === 'PASS'\n  }\n}];"
      },
      "id": "215f4b87-a07f-43d6-9a87-618b58e60448",
      "name": "Parse Validation",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1808,
        912
      ],
      "notes": "Parses QC validation result and determines pass/fail"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "qc-passed",
              "leftValue": "={{ $json.qc_passed }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "or"
        },
        "options": {}
      },
      "id": "01971b4b-203a-448e-8b5e-79a0b877ca38",
      "name": "QC Passed?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        2032,
        912
      ],
      "notes": "Routes based on QC result:\n- PASS: Continue to output\n- FAIL: Check retry count"
    },
    {
      "parameters": {
        "jsCode": "// Check if we can retry\nconst input = $input.first().json;\nconst retryCount = input.retry_count || 0;\n\nif (retryCount >= 1) {\n  // Max retries reached - output with warning\n  return [{\n    json: {\n      ...input,\n      can_retry: false,\n      output_status: 'WARN_QC_FAILED',\n      message: 'QC validation failed after retry. Output may need manual review.'\n    }\n  }];\n}\n\n// Can retry - increment counter\nreturn [{\n  json: {\n    ...input,\n    retry_count: retryCount + 1,\n    can_retry: true\n  }\n}];"
      },
      "id": "5c9b1c1a-0714-4ece-afa8-0528b5f251f2",
      "name": "Check Retry",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2256,
        992
      ],
      "notes": "Checks if retry is available (max 1 retry).\n\nIf retry available, route back to Phase 3.\nIf max retries, continue with warning."
    },
    {
      "parameters": {
        "jsCode": "// Format final output\nconst input = $input.first().json;\n\n// Determine status\nlet status = 'SUCCESS';\nlet message = 'Brand voice profile generated successfully';\n\nif (input.output_status === 'WARN_QC_FAILED') {\n  status = 'WARN';\n  message = 'Profile generated but QC validation failed. Review recommended.';\n} else if (!input.phase3_success) {\n  status = 'ERROR';\n  message = 'Failed to generate XML output';\n} else if (!input.phase1_success) {\n  status = 'ERROR';\n  message = 'Failed to complete analysis';\n}\n\nreturn [{\n  json: {\n    status: status,\n    message: message,\n    voice_type: input.voice_type,\n    subject: input.student_name || input.company_name,\n    company: input.company_name || null,\n    brand_voice_xml: input.brand_voice_xml,\n    xml_word_count: input.xml_word_count,\n    qc_result: input.validation_result || 'SKIPPED',\n    qc_details: input.validation_details || null,\n    analysis_summary: input.analysis_json?.analysis_summary || null,\n    generated_at: new Date().toISOString(),\n    usage_instructions: {\n      ygm_integration: 'Inject <BrandVoice> block into YGM system prompt under <PersonaAndVoiceDeepDive> section',\n      golden_threads: 'Extract <golden_threads> for Content Finalizer technique',\n      next_step: 'Test voice by drafting sample content'\n    }\n  }\n}];"
      },
      "id": "0f3f3cd8-7cdb-43d5-8265-6bdd81fb2dd8",
      "name": "Format Output",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2480,
        848
      ],
      "notes": "Formats final output with:\n- Brand voice XML\n- QC result\n- Usage instructions\n- Timestamps"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "error-status",
              "name": "status",
              "value": "ERROR",
              "type": "string"
            },
            {
              "id": "error-message",
              "name": "message",
              "value": "={{ $('Validate Input').first().json.message }}",
              "type": "string"
            },
            {
              "id": "errors",
              "name": "errors",
              "value": "={{ $('Validate Input').first().json.errors }}",
              "type": "array"
            }
          ]
        },
        "options": {}
      },
      "id": "a4e12c09-08b4-40e6-a1a2-a77a1f8c2b8f",
      "name": "Error Output",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -208,
        1152
      ],
      "notes": "Returns validation errors to user"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $('Configuration').first().json.claude_api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"claude-haiku-4-5-20251001\",\n  \"max_tokens\": 4000,\n  \"temperature\": 0.2,\n  \"system\": {{ JSON.stringify(\"You are Echo, analyzing \" + $('Merge Paths').first().json.voice_type + \" voice for \" + $('Merge Paths').first().json.student_name + ($('Merge Paths').first().json.company_name ? \" representing \" + $('Merge Paths').first().json.company_name : \"\") + \".\\n\\nYour ONLY task: Analyze SENTENCE STRUCTURE.\\n\\nContext:\\n\" + ($('Merge Paths').first().json.voice_type === \"personal\" ? \"- This is \" + $('Merge Paths').first().json.student_name + \"'s personal communication style\\n- Look for individual quirks, personality, authentic expression\" : ($('Merge Paths').first().json.voice_type === \"company\" ? \"- This is \" + $('Merge Paths').first().json.company_name + \"'s brand voice\\n- Look for consistency, brand values, professional tone\" : \"- This is \" + $('Merge Paths').first().json.student_name + \"'s voice when representing \" + $('Merge Paths').first().json.company_name + \"\\n- Personal patterns WITHIN company constraints\\n- Flag conflicts between personal and company style\")) + \"\\n\\nInstructions:\\n1. Read the writing samples\\n2. Analyze ONLY sentence structure - ignore other aspects\\n3. Provide specific observations with examples\\n4. Quantify where possible (counts, percentages, ratios)\\n5. Output structured JSON\") }},\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify(\"Analyze the SENTENCE STRUCTURE in these writing samples.\\n\\nFocus on:\\n- Average sentence length (count words)\\n- Sentence length variation (short vs medium vs long)\\n- Distribution (% short 1-10 words, % medium 11-25, % long 26+)\\n- Structural patterns (simple, compound, complex)\\n- Rhythm and flow\\n\\nProvide quantitative metrics and specific examples.\\n\\nWriting samples:\\n\" + $('Merge Paths').first().json.writing_samples) }}\n    }\n  ]\n}",
        "options": {}
      },
      "id": "06acfec8-3491-40c8-94de-86187eb6a30b",
      "name": "Step 1: Sentence Structure",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        464,
        -304
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $('Configuration').first().json.claude_api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"claude-haiku-4-5-20251001\",\n  \"max_tokens\": 4000,\n  \"temperature\": 0.2,\n  \"system\": {{ JSON.stringify(\"You are Echo, analyzing \" + $('Merge Paths').first().json.voice_type + \" voice for \" + $('Merge Paths').first().json.student_name + ($('Merge Paths').first().json.company_name ? \" representing \" + $('Merge Paths').first().json.company_name : \"\") + \".\\n\\nYour ONLY task: Analyze VOCABULARY and WORD CHOICE.\\n\\nContext:\\n\" + ($('Merge Paths').first().json.voice_type === \"personal\" ? \"- This is \" + $('Merge Paths').first().json.student_name + \"'s personal communication style\\n- Look for individual quirks, personality, authentic expression\" : ($('Merge Paths').first().json.voice_type === \"company\" ? \"- This is \" + $('Merge Paths').first().json.company_name + \"'s brand voice\\n- Look for consistency, brand values, professional tone\" : \"- This is \" + $('Merge Paths').first().json.student_name + \"'s voice when representing \" + $('Merge Paths').first().json.company_name + \"\\n- Personal patterns WITHIN company constraints\\n- Flag conflicts between personal and company style\")) + \"\\n\\nInstructions:\\n1. Read the writing samples\\n2. Analyze ONLY vocabulary and word choice - ignore other aspects\\n3. Provide specific observations with examples\\n4. Quantify where possible (counts, percentages, ratios)\\n5. Output structured JSON\") }},\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify(\"Analyze the VOCABULARY and WORD CHOICE in these samples.\\n\\nFocus on:\\n- Vocabulary level (casual, professional, technical, mixed)\\n- Unique word percentage (lexical diversity)\\n- Technical terms frequency\\n- Colloquialisms frequency\\n- Favorite/signature words (with counts)\\n\\nWriting samples:\\n\" + $('Merge Paths').first().json.writing_samples) }}\n    }\n  ]\n}",
        "options": {}
      },
      "id": "fe4a6912-6cce-4889-9348-b5337c6577b3",
      "name": "Step 2: Vocabulary",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        464,
        -112
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $('Configuration').first().json.claude_api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"claude-haiku-4-5-20251001\",\n  \"max_tokens\": 4000,\n  \"temperature\": 0.2,\n  \"system\": {{ JSON.stringify(\"You are Echo, analyzing \" + $('Merge Paths').first().json.voice_type + \" voice for \" + $('Merge Paths').first().json.student_name + ($('Merge Paths').first().json.company_name ? \" representing \" + $('Merge Paths').first().json.company_name : \"\") + \".\\n\\nYour ONLY task: Analyze TONE and MOOD.\\n\\nContext:\\n\" + ($('Merge Paths').first().json.voice_type === \"personal\" ? \"- This is \" + $('Merge Paths').first().json.student_name + \"'s personal communication style\\n- Look for individual quirks, personality, authentic expression\" : ($('Merge Paths').first().json.voice_type === \"company\" ? \"- This is \" + $('Merge Paths').first().json.company_name + \"'s brand voice\\n- Look for consistency, brand values, professional tone\" : \"- This is \" + $('Merge Paths').first().json.student_name + \"'s voice when representing \" + $('Merge Paths').first().json.company_name + \"\\n- Personal patterns WITHIN company constraints\\n- Flag conflicts between personal and company style\")) + \"\\n\\nInstructions:\\n1. Read the writing samples\\n2. Analyze ONLY tone and mood - ignore other aspects\\n3. Provide specific observations with examples\\n4. Quantify where possible (counts, percentages, ratios)\\n5. Output structured JSON\") }},\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify(\"Analyze the TONE and MOOD in these samples.\\n\\nFocus on:\\n- Primary tone characteristics (enthusiastic, formal, supportive, etc.)\\n- Emotional undercurrent\\n- Effect on reader (how it makes them feel)\\n- Mood progression (opening, middle, closing patterns)\\n\\nWriting samples:\\n\" + $('Merge Paths').first().json.writing_samples) }}\n    }\n  ]\n}",
        "options": {}
      },
      "id": "1008a117-9df5-486b-a5f7-1c077c5497ad",
      "name": "Step 3: Tone & Mood",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        464,
        80
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $('Configuration').first().json.claude_api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"claude-haiku-4-5-20251001\",\n  \"max_tokens\": 4000,\n  \"temperature\": 0.2,\n  \"system\": {{ JSON.stringify(\"You are Echo, analyzing \" + $('Merge Paths').first().json.voice_type + \" voice for \" + $('Merge Paths').first().json.student_name + ($('Merge Paths').first().json.company_name ? \" representing \" + $('Merge Paths').first().json.company_name : \"\") + \".\\n\\nYour ONLY task: Analyze SYNTAX and GRAMMAR patterns.\\n\\nContext:\\n\" + ($('Merge Paths').first().json.voice_type === \"personal\" ? \"- This is \" + $('Merge Paths').first().json.student_name + \"'s personal communication style\\n- Look for individual quirks, personality, authentic expression\" : ($('Merge Paths').first().json.voice_type === \"company\" ? \"- This is \" + $('Merge Paths').first().json.company_name + \"'s brand voice\\n- Look for consistency, brand values, professional tone\" : \"- This is \" + $('Merge Paths').first().json.student_name + \"'s voice when representing \" + $('Merge Paths').first().json.company_name + \"\\n- Personal patterns WITHIN company constraints\\n- Flag conflicts between personal and company style\")) + \"\\n\\nInstructions:\\n1. Read the writing samples\\n2. Analyze ONLY syntax and grammar - ignore other aspects\\n3. Provide specific observations with examples\\n4. Quantify where possible (counts, percentages, ratios)\\n5. Output structured JSON\") }},\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify(\"Analyze the SYNTAX and GRAMMAR patterns in these samples.\\n\\nFocus on:\\n- Punctuation habits (semicolons, dashes, exclamation points)\\n- Grammar preferences (formal vs relaxed)\\n- Contraction usage\\n- Sentence starters and transitions\\n- Active vs passive voice\\n\\nWriting samples:\\n\" + $('Merge Paths').first().json.writing_samples) }}\n    }\n  ]\n}",
        "options": {}
      },
      "id": "98f66a5f-dfdb-4534-af30-af31cd0eccff",
      "name": "Step 4: Syntax & Grammar",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        464,
        272
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $('Configuration').first().json.claude_api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"claude-haiku-4-5-20251001\",\n  \"max_tokens\": 4000,\n  \"temperature\": 0.2,\n  \"system\": {{ JSON.stringify(\"You are Echo, analyzing \" + $('Merge Paths').first().json.voice_type + \" voice for \" + $('Merge Paths').first().json.student_name + ($('Merge Paths').first().json.company_name ? \" representing \" + $('Merge Paths').first().json.company_name : \"\") + \".\\n\\nYour ONLY task: Analyze SEMANTIC PATTERNS.\\n\\nContext:\\n\" + ($('Merge Paths').first().json.voice_type === \"personal\" ? \"- This is \" + $('Merge Paths').first().json.student_name + \"'s personal communication style\\n- Look for individual quirks, personality, authentic expression\" : ($('Merge Paths').first().json.voice_type === \"company\" ? \"- This is \" + $('Merge Paths').first().json.company_name + \"'s brand voice\\n- Look for consistency, brand values, professional tone\" : \"- This is \" + $('Merge Paths').first().json.student_name + \"'s voice when representing \" + $('Merge Paths').first().json.company_name + \"\\n- Personal patterns WITHIN company constraints\\n- Flag conflicts between personal and company style\")) + \"\\n\\nInstructions:\\n1. Read the writing samples\\n2. Analyze ONLY semantic patterns - ignore other aspects\\n3. Provide specific observations with examples\\n4. Quantify where possible (counts, percentages, ratios)\\n5. Output structured JSON\") }},\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify(\"Analyze the SEMANTIC PATTERNS in these samples.\\n\\nFocus on:\\n- Connotative vs denotative language\\n- Abstract vs concrete concepts\\n- Metaphorical frameworks used\\n- Emphasis techniques\\n\\nWriting samples:\\n\" + $('Merge Paths').first().json.writing_samples) }}\n    }\n  ]\n}",
        "options": {}
      },
      "id": "d00a62fd-7636-4823-a9f0-b632c1ccbcfa",
      "name": "Step 5: Semantic Patterns",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        464,
        464
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $('Configuration').first().json.claude_api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"claude-haiku-4-5-20251001\",\n  \"max_tokens\": 4000,\n  \"temperature\": 0.2,\n  \"system\": {{ JSON.stringify(\"You are Echo, analyzing \" + $('Merge Paths').first().json.voice_type + \" voice for \" + $('Merge Paths').first().json.student_name + ($('Merge Paths').first().json.company_name ? \" representing \" + $('Merge Paths').first().json.company_name : \"\") + \".\\n\\nYour ONLY task: Analyze PRAGMATIC ELEMENTS.\\n\\nContext:\\n\" + ($('Merge Paths').first().json.voice_type === \"personal\" ? \"- This is \" + $('Merge Paths').first().json.student_name + \"'s personal communication style\\n- Look for individual quirks, personality, authentic expression\" : ($('Merge Paths').first().json.voice_type === \"company\" ? \"- This is \" + $('Merge Paths').first().json.company_name + \"'s brand voice\\n- Look for consistency, brand values, professional tone\" : \"- This is \" + $('Merge Paths').first().json.student_name + \"'s voice when representing \" + $('Merge Paths').first().json.company_name + \"\\n- Personal patterns WITHIN company constraints\\n- Flag conflicts between personal and company style\")) + \"\\n\\nInstructions:\\n1. Read the writing samples\\n2. Analyze ONLY pragmatic elements - ignore other aspects\\n3. Provide specific observations with examples\\n4. Quantify where possible (counts, percentages, ratios)\\n5. Output structured JSON\") }},\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify(\"Analyze the PRAGMATIC ELEMENTS in these samples.\\n\\nFocus on:\\n- Direct vs indirect address (\\\"you\\\" vs \\\"one\\\")\\n- Inclusive language (\\\"we\\\", \\\"us\\\", \\\"together\\\", \\\"y'all\\\")\\n- Persuasion techniques\\n- Engagement strategies (questions, calls to action)\\n\\nWriting samples:\\n\" + $('Merge Paths').first().json.writing_samples) }}\n    }\n  ]\n}",
        "options": {}
      },
      "id": "7df974bd-a6b4-4326-a30e-5895b03570ee",
      "name": "Step 6: Pragmatic Elements",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        464,
        656
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $('Configuration').first().json.claude_api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"claude-haiku-4-5-20251001\",\n  \"max_tokens\": 4000,\n  \"temperature\": 0.2,\n  \"system\": {{ JSON.stringify(\"You are Echo, analyzing \" + $('Merge Paths').first().json.voice_type + \" voice for \" + $('Merge Paths').first().json.student_name + ($('Merge Paths').first().json.company_name ? \" representing \" + $('Merge Paths').first().json.company_name : \"\") + \".\\n\\nYour ONLY task: Analyze STYLISTIC DEVICES.\\n\\nContext:\\n\" + ($('Merge Paths').first().json.voice_type === \"personal\" ? \"- This is \" + $('Merge Paths').first().json.student_name + \"'s personal communication style\\n- Look for individual quirks, personality, authentic expression\" : ($('Merge Paths').first().json.voice_type === \"company\" ? \"- This is \" + $('Merge Paths').first().json.company_name + \"'s brand voice\\n- Look for consistency, brand values, professional tone\" : \"- This is \" + $('Merge Paths').first().json.student_name + \"'s voice when representing \" + $('Merge Paths').first().json.company_name + \"\\n- Personal patterns WITHIN company constraints\\n- Flag conflicts between personal and company style\")) + \"\\n\\nInstructions:\\n1. Read the writing samples\\n2. Analyze ONLY stylistic devices - ignore other aspects\\n3. Provide specific observations with examples\\n4. Quantify where possible (counts, percentages, ratios)\\n5. Output structured JSON\") }},\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify(\"Analyze the STYLISTIC DEVICES in these samples.\\n\\nFocus on:\\n- Metaphors and analogies\\n- Alliteration and repetition\\n- Rhetorical questions\\n- Humor usage\\n- Parenthetical asides\\n\\nWriting samples:\\n\" + $('Merge Paths').first().json.writing_samples) }}\n    }\n  ]\n}",
        "options": {}
      },
      "id": "eba0848d-1e3d-4f01-9964-a4428adc4a26",
      "name": "Step 7: Stylistic Devices",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        464,
        848
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $('Configuration').first().json.claude_api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"claude-haiku-4-5-20251001\",\n  \"max_tokens\": 4000,\n  \"temperature\": 0.2,\n  \"system\": {{ JSON.stringify(\"You are Echo, analyzing \" + $('Merge Paths').first().json.voice_type + \" voice for \" + $('Merge Paths').first().json.student_name + ($('Merge Paths').first().json.company_name ? \" representing \" + $('Merge Paths').first().json.company_name : \"\") + \".\\n\\nYour ONLY task: Analyze NARRATIVE STANCE.\\n\\nContext:\\n\" + ($('Merge Paths').first().json.voice_type === \"personal\" ? \"- This is \" + $('Merge Paths').first().json.student_name + \"'s personal communication style\\n- Look for individual quirks, personality, authentic expression\" : ($('Merge Paths').first().json.voice_type === \"company\" ? \"- This is \" + $('Merge Paths').first().json.company_name + \"'s brand voice\\n- Look for consistency, brand values, professional tone\" : \"- This is \" + $('Merge Paths').first().json.student_name + \"'s voice when representing \" + $('Merge Paths').first().json.company_name + \"\\n- Personal patterns WITHIN company constraints\\n- Flag conflicts between personal and company style\")) + \"\\n\\nInstructions:\\n1. Read the writing samples\\n2. Analyze ONLY narrative stance - ignore other aspects\\n3. Provide specific observations with examples\\n4. Quantify where possible (counts, percentages, ratios)\\n5. Output structured JSON\") }},\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify(\"Analyze the NARRATIVE STANCE in these samples.\\n\\nFocus on:\\n- First/second/third person preference\\n- Active vs passive voice ratio\\n- Storytelling patterns\\n- Point of view consistency\\n\\nWriting samples:\\n\" + $('Merge Paths').first().json.writing_samples) }}\n    }\n  ]\n}",
        "options": {}
      },
      "id": "1109491e-5dd3-45d7-a697-08aa9a1a9591",
      "name": "Step 8: Narrative Stance",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        464,
        1040
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $('Configuration').first().json.claude_api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"claude-haiku-4-5-20251001\",\n  \"max_tokens\": 4000,\n  \"temperature\": 0.2,\n  \"system\": {{ JSON.stringify(\"You are Echo, analyzing \" + $('Merge Paths').first().json.voice_type + \" voice for \" + $('Merge Paths').first().json.student_name + ($('Merge Paths').first().json.company_name ? \" representing \" + $('Merge Paths').first().json.company_name : \"\") + \".\\n\\nYour ONLY task: Analyze THEMES and VALUES.\\n\\nContext:\\n\" + ($('Merge Paths').first().json.voice_type === \"personal\" ? \"- This is \" + $('Merge Paths').first().json.student_name + \"'s personal communication style\\n- Look for individual quirks, personality, authentic expression\" : ($('Merge Paths').first().json.voice_type === \"company\" ? \"- This is \" + $('Merge Paths').first().json.company_name + \"'s brand voice\\n- Look for consistency, brand values, professional tone\" : \"- This is \" + $('Merge Paths').first().json.student_name + \"'s voice when representing \" + $('Merge Paths').first().json.company_name + \"\\n- Personal patterns WITHIN company constraints\\n- Flag conflicts between personal and company style\")) + \"\\n\\nInstructions:\\n1. Read the writing samples\\n2. Analyze ONLY themes and values - ignore other aspects\\n3. Provide specific observations with examples\\n4. Quantify where possible (counts, percentages, ratios)\\n5. Output structured JSON\") }},\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify(\"Analyze the THEMES and VALUES in these samples.\\n\\nFocus on:\\n- Recurring themes across samples\\n- Implied values and beliefs\\n- What they emphasize vs de-emphasize\\n- Core motivations\\n\\nWriting samples:\\n\" + $('Merge Paths').first().json.writing_samples) }}\n    }\n  ]\n}",
        "options": {}
      },
      "id": "4820cc1a-fa82-431d-a71a-719f4d8228ab",
      "name": "Step 9: Themes & Values",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        464,
        1232
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $('Configuration').first().json.claude_api_key }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"claude-haiku-4-5-20251001\",\n  \"max_tokens\": 4000,\n  \"temperature\": 0.2,\n  \"system\": {{ JSON.stringify(\"You are Echo, analyzing \" + $('Merge Paths').first().json.voice_type + \" voice for \" + $('Merge Paths').first().json.student_name + ($('Merge Paths').first().json.company_name ? \" representing \" + $('Merge Paths').first().json.company_name : \"\") + \".\\n\\nYour ONLY task: Analyze EMOTIONAL EXPRESSION.\\n\\nContext:\\n\" + ($('Merge Paths').first().json.voice_type === \"personal\" ? \"- This is \" + $('Merge Paths').first().json.student_name + \"'s personal communication style\\n- Look for individual quirks, personality, authentic expression\" : ($('Merge Paths').first().json.voice_type === \"company\" ? \"- This is \" + $('Merge Paths').first().json.company_name + \"'s brand voice\\n- Look for consistency, brand values, professional tone\" : \"- This is \" + $('Merge Paths').first().json.student_name + \"'s voice when representing \" + $('Merge Paths').first().json.company_name + \"\\n- Personal patterns WITHIN company constraints\\n- Flag conflicts between personal and company style\")) + \"\\n\\nInstructions:\\n1. Read the writing samples\\n2. Analyze ONLY emotional expression - ignore other aspects\\n3. Provide specific observations with examples\\n4. Quantify where possible (counts, percentages, ratios)\\n5. Output structured JSON\") }},\n  \"messages\": [\n    {\n    

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

Echo Brand Voice Analysis (Processor) - TASK-074 Dec 10 Fix. Uses formTrigger, httpRequest, executeWorkflowTrigger, moveBinaryData. Event-driven trigger; 40 nodes.

Source: https://github.com/8Dvibes/mindvalley-ai-mastery-students/blob/main/workflows/echo-processor-v2-2025-12-10.json — original creator credit. Request a take-down →

More Email & Gmail workflows → · Browse all categories →

Related workflows

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

Email & Gmail

Splitout Code. Uses manualTrigger, httpRequest, stickyNote, splitOut. Event-driven trigger; 46 nodes.

HTTP Request, Execute Workflow Trigger, Gmail +1
Email & Gmail

Automate CSV imports into HubSpot without the mess. Powered by n8n. Supercharged by Pollup AI.

HTTP Request, Execute Workflow Trigger, Gmail +1
Email & Gmail

Streamline your content pipeline by bridging Notion and Instagram with a professional "review-before-publish" safeguard. This workflow allows team members to submit content via a simple form, generate

Form Trigger, HTTP Request, N8N Nodes Uploadtourl +1
Email & Gmail

🎥 Analyze YouTube Video for Summaries, Transcripts & Content + Google Gemini AI. Uses stickyNote, httpRequest, googleDrive, gmail. Event-driven trigger; 33 nodes.

HTTP Request, Google Drive, Gmail +2
Email & Gmail

This n8n workflow enables teams to automate and standardize multi-step onboarding or messaging workflows using Google Sheets, Forms, Gmail, and dynamic logic powered by Code and Switch nodes. It ensur

Google Sheets, Form, Execute Workflow Trigger +2