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": "Openthai.ai Content Generator",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "openthai-ai",
"options": {}
},
"id": "webhook-trigger",
"name": "Webhook Trigger",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1,
"position": [
250,
300
]
},
{
"parameters": {
"jsCode": "// S7: Context Compression \u2014 \u0e2a\u0e23\u0e49\u0e32\u0e07 Context Card 5 fields\nconst input = $input.first().json;\n\nconst contextCard = {\n product_name: input.product_name || '',\n category: input.category || 'otop',\n key_features: input.features || [],\n target_audience: input.audience || '\u0e04\u0e19\u0e44\u0e17\u0e22\u0e17\u0e31\u0e48\u0e27\u0e44\u0e1b',\n unique_selling_point: input.usp || '',\n hook_preference: input.hook_type || 'auto'\n};\n\n// Compress to ~150 chars for efficient context\nconst compressed = `${contextCard.product_name}|${contextCard.category}|${contextCard.target_audience}|${contextCard.hook_preference}`;\n\nreturn {\n contextCard,\n compressed,\n timestamp: new Date().toISOString()\n};"
},
"id": "context-card",
"name": "S7: Context Card",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
450,
200
]
},
{
"parameters": {
"url": "https://sheets.googleapis.com/v4/spreadsheets/YOUR_SHEET_ID/values/Products!A:Z",
"authentication": "genericCredentialType",
"genericAuthType": "httpQueryAuth",
"options": {}
},
"id": "knowledge-base",
"name": "S8: Knowledge Base",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
450,
400
],
"notes": "\u0e40\u0e1b\u0e25\u0e35\u0e48\u0e22\u0e19 YOUR_SHEET_ID \u0e40\u0e1b\u0e47\u0e19 Google Sheet ID \u0e02\u0e2d\u0e07\u0e04\u0e38\u0e13"
},
{
"parameters": {
"jsCode": "// S1+S3+S5: Prompt Assembly\n// \u0e23\u0e27\u0e21 RCCF Prompt + Master Prompt + Category System Prompt\n\nconst context = $('S7: Context Card').first().json;\nconst knowledge = $('S8: Knowledge Base').first().json;\n\n// S5: Category-specific system prompts\nconst systemPrompts = {\n food: '\u0e04\u0e38\u0e13\u0e40\u0e1b\u0e47\u0e19\u0e1c\u0e39\u0e49\u0e40\u0e0a\u0e35\u0e48\u0e22\u0e27\u0e0a\u0e32\u0e0d\u0e2a\u0e23\u0e49\u0e32\u0e07\u0e04\u0e2d\u0e19\u0e40\u0e17\u0e19\u0e15\u0e4c TikTok \u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e2d\u0e32\u0e2b\u0e32\u0e23\u0e44\u0e17\u0e22 \u0e40\u0e19\u0e49\u0e19: \u0e23\u0e2a\u0e0a\u0e32\u0e15\u0e34, \u0e2a\u0e39\u0e15\u0e23\u0e42\u0e1a\u0e23\u0e32\u0e13, \u0e27\u0e31\u0e15\u0e16\u0e38\u0e14\u0e34\u0e1a\u0e17\u0e49\u0e2d\u0e07\u0e16\u0e34\u0e48\u0e19',\n craft: '\u0e04\u0e38\u0e13\u0e40\u0e1b\u0e47\u0e19\u0e1c\u0e39\u0e49\u0e40\u0e0a\u0e35\u0e48\u0e22\u0e27\u0e0a\u0e32\u0e0d\u0e2a\u0e23\u0e49\u0e32\u0e07\u0e04\u0e2d\u0e19\u0e40\u0e17\u0e19\u0e15\u0e4c TikTok \u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e2b\u0e31\u0e15\u0e16\u0e01\u0e23\u0e23\u0e21 \u0e40\u0e19\u0e49\u0e19: \u0e1d\u0e35\u0e21\u0e37\u0e2d\u0e0a\u0e48\u0e32\u0e07, \u0e20\u0e39\u0e21\u0e34\u0e1b\u0e31\u0e0d\u0e0d\u0e32, \u0e01\u0e23\u0e30\u0e1a\u0e27\u0e19\u0e01\u0e32\u0e23\u0e1c\u0e25\u0e34\u0e15',\n herb: '\u0e04\u0e38\u0e13\u0e40\u0e1b\u0e47\u0e19\u0e1c\u0e39\u0e49\u0e40\u0e0a\u0e35\u0e48\u0e22\u0e27\u0e0a\u0e32\u0e0d\u0e2a\u0e23\u0e49\u0e32\u0e07\u0e04\u0e2d\u0e19\u0e40\u0e17\u0e19\u0e15\u0e4c TikTok \u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e2a\u0e21\u0e38\u0e19\u0e44\u0e1e\u0e23 \u0e40\u0e19\u0e49\u0e19: \u0e2a\u0e23\u0e23\u0e1e\u0e04\u0e38\u0e13, \u0e27\u0e34\u0e18\u0e35\u0e43\u0e0a\u0e49, \u0e1c\u0e25\u0e25\u0e31\u0e1e\u0e18\u0e4c',\n textile: '\u0e04\u0e38\u0e13\u0e40\u0e1b\u0e47\u0e19\u0e1c\u0e39\u0e49\u0e40\u0e0a\u0e35\u0e48\u0e22\u0e27\u0e0a\u0e32\u0e0d\u0e2a\u0e23\u0e49\u0e32\u0e07\u0e04\u0e2d\u0e19\u0e40\u0e17\u0e19\u0e15\u0e4c TikTok \u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e1c\u0e49\u0e32\u0e17\u0e2d \u0e40\u0e19\u0e49\u0e19: \u0e25\u0e27\u0e14\u0e25\u0e32\u0e22, \u0e01\u0e23\u0e30\u0e1a\u0e27\u0e19\u0e01\u0e32\u0e23\u0e22\u0e49\u0e2d\u0e21, heritage',\n global: '\u0e04\u0e38\u0e13\u0e40\u0e1b\u0e47\u0e19\u0e1c\u0e39\u0e49\u0e40\u0e0a\u0e35\u0e48\u0e22\u0e27\u0e0a\u0e32\u0e0d\u0e2a\u0e23\u0e49\u0e32\u0e07\u0e04\u0e2d\u0e19\u0e40\u0e17\u0e19\u0e15\u0e4c TikTok \u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e2a\u0e34\u0e19\u0e04\u0e49\u0e32\u0e17\u0e31\u0e48\u0e27\u0e44\u0e1b \u0e40\u0e19\u0e49\u0e19: \u0e04\u0e27\u0e32\u0e21\u0e04\u0e38\u0e49\u0e21\u0e04\u0e48\u0e32, \u0e1f\u0e35\u0e40\u0e08\u0e2d\u0e23\u0e4c\u0e40\u0e14\u0e48\u0e19, \u0e40\u0e17\u0e23\u0e19\u0e14\u0e4c'\n};\n\nconst category = context.contextCard?.category || 'global';\nconst systemPrompt = systemPrompts[category] || systemPrompts.global;\n\n// S3: Master Prompt\nconst masterPrompt = `\n\u3010OUTPUT FORMAT\u3011\n\u0e2a\u0e23\u0e49\u0e32\u0e07\u0e04\u0e2d\u0e19\u0e40\u0e17\u0e19\u0e15\u0e4c TikTok 30 \u0e27\u0e34\u0e19\u0e32\u0e17\u0e35:\n1. Hook (3 \u0e27\u0e34): \u0e14\u0e36\u0e07\u0e04\u0e27\u0e32\u0e21\u0e2a\u0e19\u0e43\u0e08\u0e17\u0e31\u0e19\u0e17\u0e35\n2. Story (20 \u0e27\u0e34): \u0e40\u0e19\u0e37\u0e49\u0e2d\u0e2b\u0e32\u0e2b\u0e25\u0e31\u0e01\n3. CTA (7 \u0e27\u0e34): Call to Action\n\n\u0e1e\u0e23\u0e49\u0e2d\u0e21 Caption + 10 Hashtags\n\n\u0e15\u0e2d\u0e1a\u0e40\u0e1b\u0e47\u0e19 JSON \u0e40\u0e17\u0e48\u0e32\u0e19\u0e31\u0e49\u0e19:\n{\n \"script\": {\"hook\": \"...\", \"story\": \"...\", \"cta\": \"...\"},\n \"caption\": \"...\",\n \"hashtags\": [\"...\"],\n \"hook_type\": \"...\",\n \"why_it_works\": \"...\"\n}\n`;\n\n// S1: RCCF Prompt (Role, Context, Constraint, Format)\nconst userPrompt = `\n\u3010\u0e2a\u0e34\u0e19\u0e04\u0e49\u0e32\u3011${context.contextCard?.product_name}\n\u3010\u0e2b\u0e21\u0e27\u0e14\u0e2b\u0e21\u0e39\u0e48\u3011${category}\n\u3010\u0e01\u0e25\u0e38\u0e48\u0e21\u0e40\u0e1b\u0e49\u0e32\u0e2b\u0e21\u0e32\u0e22\u3011${context.contextCard?.target_audience}\n\u3010Hook \u0e17\u0e35\u0e48\u0e15\u0e49\u0e2d\u0e07\u0e01\u0e32\u0e23\u3011${context.contextCard?.hook_preference}\n\n${masterPrompt}\n`;\n\nreturn {\n systemPrompt,\n userPrompt,\n context: context.contextCard,\n iteration: 1\n};"
},
"id": "prompt-assembly",
"name": "S1+S3+S5: Prompt Assembly",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
700,
300
]
},
{
"parameters": {
"url": "https://api.anthropic.com/v1/messages",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"model\": \"claude-sonnet-4-5-20251001\",\n \"max_tokens\": 2000,\n \"system\": \"{{ $json.systemPrompt }}\",\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"{{ $json.userPrompt }}\"\n }\n ]\n}",
"options": {}
},
"id": "claude-generate",
"name": "Claude Generate",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
950,
300
],
"notes": "\u0e15\u0e31\u0e49\u0e07\u0e04\u0e48\u0e32 HTTP Header Auth: x-api-key = YOUR_ANTHROPIC_API_KEY"
},
{
"parameters": {
"jsCode": "// S6: AI Critic \u2014 \u0e43\u0e2b\u0e49\u0e04\u0e30\u0e41\u0e19\u0e19\u0e41\u0e25\u0e30 feedback\nconst response = $input.first().json;\nconst content = JSON.parse(response.content[0].text);\n\n// \u0e04\u0e33\u0e19\u0e27\u0e13\u0e04\u0e30\u0e41\u0e19\u0e19\u0e15\u0e32\u0e21\u0e40\u0e01\u0e13\u0e11\u0e4c\nfunction calculateScore(content) {\n let score = 0;\n \n // Hook strength (30%)\n const hookLength = content.script?.hook?.length || 0;\n if (hookLength > 20 && hookLength < 100) score += 3;\n \n // Story flow (25%)\n const storyLength = content.script?.story?.length || 0;\n if (storyLength > 100 && storyLength < 500) score += 2.5;\n \n // CTA clarity (20%)\n const ctaLength = content.script?.cta?.length || 0;\n if (ctaLength > 20 && ctaLength < 100) score += 2;\n \n // Hashtags (15%)\n const hashtagCount = content.hashtags?.length || 0;\n if (hashtagCount >= 8) score += 1.5;\n \n // Format compliance (10%)\n if (content.hook_type && content.why_it_works) score += 1;\n \n return Math.min(10, score);\n}\n\nconst criticScore = calculateScore(content);\n\nreturn {\n content,\n criticScore,\n passed: criticScore >= 7,\n feedback: criticScore >= 7 \n ? '\u0e04\u0e2d\u0e19\u0e40\u0e17\u0e19\u0e15\u0e4c\u0e1c\u0e48\u0e32\u0e19\u0e21\u0e32\u0e15\u0e23\u0e10\u0e32\u0e19' \n : '\u0e15\u0e49\u0e2d\u0e07\u0e1b\u0e23\u0e31\u0e1a\u0e1b\u0e23\u0e38\u0e07: ' + (criticScore < 5 ? 'Hook \u0e2d\u0e48\u0e2d\u0e19' : '\u0e40\u0e1e\u0e34\u0e48\u0e21 detail'),\n iteration: $('S1+S3+S5: Prompt Assembly').first().json.iteration\n};"
},
"id": "ai-critic",
"name": "S6: AI Critic",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1200,
300
]
},
{
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{ $json.passed }}",
"value2": true
}
],
"number": [
{
"value1": "={{ $json.iteration }}",
"operation": "smaller",
"value2": 4
}
]
},
"combineOperation": "any"
},
"id": "if-gate",
"name": "IF: Score \u2265 7 or Max 3x",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [
1400,
300
]
},
{
"parameters": {
"jsCode": "// S2: Taste Check \u2014 \u0e15\u0e23\u0e27\u0e08\u0e2a\u0e2d\u0e1a\u0e01\u0e31\u0e1a reference library\nconst input = $input.first().json;\nconst content = input.content;\n\n// \u0e15\u0e31\u0e27\u0e2d\u0e22\u0e48\u0e32\u0e07 taste check (\u0e43\u0e19\u0e08\u0e23\u0e34\u0e07\u0e08\u0e30\u0e40\u0e17\u0e35\u0e22\u0e1a\u0e01\u0e31\u0e1a reference lib)\nconst tasteChecks = {\n hasEmoji: /[\\u{1F300}-\\u{1F9FF}]/u.test(content.caption || ''),\n hashtagCount: (content.hashtags?.length || 0) >= 8,\n hookNotGeneric: !['\u0e23\u0e39\u0e49\u0e44\u0e2b\u0e21', '\u0e40\u0e04\u0e22\u0e40\u0e2b\u0e47\u0e19'].some(w => content.script?.hook?.includes(w)),\n ctaHasAction: ['\u0e01\u0e14', 'follow', '\u0e25\u0e2d\u0e07', '\u0e0b\u0e37\u0e49\u0e2d', '\u0e2a\u0e31\u0e48\u0e07'].some(w => \n (content.script?.cta || '').toLowerCase().includes(w)\n )\n};\n\nconst tasteScore = Object.values(tasteChecks).filter(Boolean).length * 2.5;\n\nreturn {\n ...input,\n tasteScore,\n tasteChecks\n};"
},
"id": "taste-check",
"name": "S2: Taste Check",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1600,
200
]
},
{
"parameters": {
"jsCode": "// S9: Learning Layer \u2014 \u0e2d\u0e18\u0e34\u0e1a\u0e32\u0e22 why it works\nconst input = $input.first().json;\n\nconst learning = {\n hook_type: input.content.hook_type || 'auto',\n why_it_works: input.content.why_it_works || 'Hook \u0e19\u0e35\u0e49\u0e43\u0e0a\u0e49\u0e44\u0e14\u0e49\u0e40\u0e1e\u0e23\u0e32\u0e30\u0e14\u0e36\u0e07\u0e04\u0e27\u0e32\u0e21\u0e2a\u0e19\u0e43\u0e08\u0e17\u0e31\u0e19\u0e17\u0e35',\n next_try: `\u0e25\u0e2d\u0e07\u0e43\u0e0a\u0e49 Hook \u0e41\u0e1a\u0e1a ${['story', 'process', 'contrast', 'question', 'transformation'].filter(h => h !== input.content.hook_type)[0]} \u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e2a\u0e34\u0e19\u0e04\u0e49\u0e32\u0e19\u0e35\u0e49`\n};\n\nreturn {\n content: input.content,\n quality: {\n critic_score: input.criticScore,\n taste_score: input.tasteScore,\n iterations: input.iteration\n },\n learning\n};"
},
"id": "learning-layer",
"name": "S9: Learning Layer",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1800,
200
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify($json) }}",
"options": {}
},
"id": "response",
"name": "Response",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [
2000,
200
]
},
{
"parameters": {
"jsCode": "// Retry loop \u2014 \u0e40\u0e1e\u0e34\u0e48\u0e21 iteration \u0e41\u0e25\u0e30 retry\nconst input = $input.first().json;\n\nreturn {\n ...input,\n iteration: input.iteration + 1,\n retry: true\n};"
},
"id": "retry-loop",
"name": "Retry Loop",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1600,
400
]
}
],
"connections": {
"Webhook Trigger": {
"main": [
[
{
"node": "S7: Context Card",
"type": "main",
"index": 0
},
{
"node": "S8: Knowledge Base",
"type": "main",
"index": 0
}
]
]
},
"S7: Context Card": {
"main": [
[
{
"node": "S1+S3+S5: Prompt Assembly",
"type": "main",
"index": 0
}
]
]
},
"S8: Knowledge Base": {
"main": [
[
{
"node": "S1+S3+S5: Prompt Assembly",
"type": "main",
"index": 0
}
]
]
},
"S1+S3+S5: Prompt Assembly": {
"main": [
[
{
"node": "Claude Generate",
"type": "main",
"index": 0
}
]
]
},
"Claude Generate": {
"main": [
[
{
"node": "S6: AI Critic",
"type": "main",
"index": 0
}
]
]
},
"S6: AI Critic": {
"main": [
[
{
"node": "IF: Score \u2265 7 or Max 3x",
"type": "main",
"index": 0
}
]
]
},
"IF: Score \u2265 7 or Max 3x": {
"main": [
[
{
"node": "S2: Taste Check",
"type": "main",
"index": 0
}
],
[
{
"node": "Retry Loop",
"type": "main",
"index": 0
}
]
]
},
"S2: Taste Check": {
"main": [
[
{
"node": "S9: Learning Layer",
"type": "main",
"index": 0
}
]
]
},
"S9: Learning Layer": {
"main": [
[
{
"node": "Response",
"type": "main",
"index": 0
}
]
]
},
"Retry Loop": {
"main": [
[
{
"node": "S1+S3+S5: Prompt Assembly",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
},
"staticData": null,
"tags": [
"openthai-ai",
"tiktok",
"content-generation"
],
"triggerCount": 0,
"updatedAt": "2026-04-01T00:00:00.000Z",
"versionId": "1.0.0"
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Openthai.ai Content Generator. Uses httpRequest. Webhook trigger; 11 nodes.
Source: https://github.com/occylthailand-ai/openthai-ai/blob/a54555497095210210ff6ecc34ce8c68a523f147/n8n-workflows/openthai-ai-workflow.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.
This n8n template provides enterprise-level version control for your workflows using GitHub integration. Stop losing hours to broken workflows and manual exports – get proper commit history, visual di
This flow creates dummy files for every item added in your *Arrs (Radarr/Sonarr) with the tag .
eek-Go v2 (Batch-Then-Review). Uses httpRequest. Webhook trigger; 75 nodes.
This workflow receives webhook requests from a content calendar and uses the X API v2 to publish text posts, threads, image/video posts, and polls, as well as delete existing posts and run a credentia
This workflow acts as a central API gateway for all technical indicator agents in the Binance Spot Market Quant AI system. It listens for incoming webhook requests and dynamically routes them to the c