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 →
{
"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.
gmailOAuth2
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 →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Splitout Code. Uses manualTrigger, httpRequest, stickyNote, splitOut. Event-driven trigger; 46 nodes.
Automate CSV imports into HubSpot without the mess. Powered by n8n. Supercharged by Pollup AI.
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
🎥 Analyze YouTube Video for Summaries, Transcripts & Content + Google Gemini AI. Uses stickyNote, httpRequest, googleDrive, gmail. Event-driven trigger; 33 nodes.
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