This workflow follows the GitHub → HTTP Request 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 →
{
"updatedAt": "2026-05-18T20:02:38.897Z",
"createdAt": "2026-04-14T20:06:33.236Z",
"id": "NKyH6etI3cF8En7n",
"name": "Addendo \u2014 Blog Automatico Don Jacinto Nahual",
"description": null,
"active": true,
"isArchived": false,
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 6 * * 1,3,5"
}
]
},
"timezone": "America/Los_Angeles"
},
"id": "schedule-trigger",
"name": "Schedule Trigger - 6am LA L/M/V",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [
224,
304
]
},
{
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// Lista maestra de keywords del nicho esoterico para Don Jacinto Nahual\n// Estrategia: keywords de alto volumen en espanol (mercado hispano USA)\n\nconst keywordsBase = [\n 'brujo cerca de mi',\n 'amarres de amor',\n 'como quitarme una brujeria',\n 'curandero cerca de mi',\n 'limpia espiritual',\n 'hechizos de amor',\n 'brujeria',\n 'tarot gratis',\n 'lectura de cartas',\n 'como saber si me hicieron brujeria',\n 'ritual de amor',\n 'santeria',\n 'magia negra',\n 'videncia gratis',\n 'como hacer un amarre de amor',\n // 15 expansion 2026-05-18 \u2014 Fase 2.2/2.2b\n 'palo santo',\n 'ofrenda',\n 'horoscopo',\n 'agua bendita',\n 'sahumerio',\n 'limpia con huevo',\n 'amuletos de proteccion',\n 'despojo',\n 'chacras',\n 'mal de ojo',\n 'oraciones poderosas',\n 'botanica los angeles',\n 'botanica santa ana',\n 'botanica san pedro',\n 'botanica san bernardino'\n];\n\nreturn {\n json: {\n cliente: 'don-jacinto-nahual',\n sitio_web: 'donjacintonahual.com',\n repositorio: 'AddendoGrowthPartner/don-jacinto-nahual',\n nicho: 'servicios esotericos - brujo curandero',\n idioma: 'espanol',\n mercado: 'hispanos en USA',\n keywords_base: keywordsBase,\n fecha_ejecucion: new Date().toISOString()\n }\n};"
},
"id": "config-cliente",
"name": "Configuracion Cliente",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
464,
304
]
},
{
"parameters": {
"method": "POST",
"url": "https://api.dataforseo.com/v3/keywords_data/google/search_volume/live",
"authentication": "genericCredentialType",
"genericAuthType": "httpBasicAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "=[\n {\n \"keywords\": {{ JSON.stringify($json.keywords_base) }},\n \"language_code\": \"es\",\n \"location_name\": \"United States\",\n \"sort_by\": \"search_volume\"\n }\n]",
"options": {
"timeout": 30000
}
},
"id": "dataforseo-volumen",
"name": "DataForSEO - Buscar Keywords",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
624,
304
],
"credentials": {
"httpBasicAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"url": "https://api.github.com/repos/AddendoGrowthPartner/don-jacinto-nahual/contents/src/pages/blog",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "githubApi",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Accept",
"value": "application/vnd.github+json"
}
]
},
"options": {
"timeout": 15000
}
},
"id": "github-listar-articulos",
"name": "GitHub - Listar Articulos",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
832,
304
],
"credentials": {
"githubApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Leer DataForSEO desde nodo por referencia y lista GitHub desde input\nconst dataforseoResp = $('DataForSEO - Buscar Keywords').first().json;\nconst result = dataforseoResp?.tasks?.[0]?.result || [];\n\n// Filtrar por volumen y dificultad\nconst keywordsFiltradas = result\n .filter(kw => {\n const volume = kw.search_volume || 0;\n const difficulty = kw.keyword_difficulty;\n return volume >= 100 && (difficulty == null || difficulty < 70);\n })\n .map(kw => ({\n keyword: kw.keyword,\n volume: kw.search_volume,\n difficulty: kw.keyword_difficulty || 0,\n cpc: kw.cpc || 0,\n competition: kw.competition || 0\n }));\n\nkeywordsFiltradas.sort((a, b) => b.volume - a.volume);\n\n// Normalizar output de GitHub (lectura explicita por referencia)\n// F1 (15 mayo 2026): rewire Fix B cambio $input a Redis - Listar Slugs Publicados.\n// El loop original leia $input esperando archivos GitHub y procesaba el objeto Redis\n// como basura. Lectura explicita restaura defensa en profundidad GitHub.\nconst ghRaw = $('GitHub - Listar Articulos').all().map(i => i.json);\nconst githubFiles = (ghRaw.length === 1 && Array.isArray(ghRaw[0])) ? ghRaw[0] : ghRaw;\n\nfunction slugify(s) {\n return s.toLowerCase()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .replace(/[^a-z0-9\\s-]/g, '')\n .replace(/\\s+/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '');\n}\n\n// Construir set de slugs publicados en los ultimos 7 dias\nconst ahora = Date.now();\nconst sieteDiasMs = 7 * 24 * 60 * 60 * 1000;\nconst slugsBloqueados = new Set();\n// HIBRIDO Opcion B (15 mayo 2026): merge slugs persistidos en Redis al set bloqueado\n// Source: nodo upstream 'Redis - Listar Slugs Publicados' (KEYS dj:slug_pub:* getValues=false)\n// Output shape: [{ keys: ['dj:slug_pub:a', 'dj:slug_pub:b', ...] }]\n// TTL nativo 7d en Redis: keys viejas ya expiraron, aqui solo agregamos slug bloqueado.\n// GitHub list (loop debajo) se mantiene como defensa en profundidad.\ntry {\n const redisRaw = $('Redis - Listar Slugs Publicados').first().json;\n const redisKeyList = (redisRaw && Array.isArray(redisRaw.keys)) ? redisRaw.keys : [];\n for (const key of redisKeyList) {\n if (typeof key === 'string' && key.startsWith('dj:slug_pub:')) {\n slugsBloqueados.add(key.replace(/^dj:slug_pub:/, ''));\n }\n }\n} catch (e) {\n // Redis no disponible o cero keys: fallback puro al GitHub list (defensa en profundidad)\n}\n\nfor (const f of githubFiles) {\n const name = f?.name;\n if (!name || !name.endsWith('.astro')) continue;\n const base = name.replace(/\\.astro$/, '');\n const m = base.match(/^(.+)-(\\d{4}-\\d{2}-\\d{2})$/);\n if (!m) {\n slugsBloqueados.add(base);\n continue;\n }\n const slug = m[1];\n const fechaPub = Date.parse(m[2]);\n if (!isNaN(fechaPub) && (ahora - fechaPub) <= sieteDiasMs) {\n slugsBloqueados.add(slug);\n }\n}\n\n// Elegir la keyword con mayor volumen cuyo slug NO este bloqueado\nconst keywordSeleccionada = keywordsFiltradas.find(\n kw => !slugsBloqueados.has(slugify(kw.keyword))\n);\n\nif (!keywordSeleccionada) {\n throw new Error('No hay keywords disponibles no publicadas en los ultimos 7 dias');\n}\n\nconst slug = slugify(keywordSeleccionada.keyword);\nconst fecha = new Date().toISOString().split('T')[0];\nconst nombreArchivo = `${slug}-${fecha}.astro`;\n\nreturn [{\n json: {\n keyword_seleccionada: keywordSeleccionada.keyword,\n volumen: keywordSeleccionada.volume,\n dificultad: keywordSeleccionada.difficulty,\n cpc: keywordSeleccionada.cpc,\n slug: slug,\n nombre_archivo: nombreArchivo,\n fecha: fecha,\n cliente: 'don-jacinto-nahual',\n sitio_web: 'donjacintonahual.com',\n url_articulo: `https://donjacintonahual.com/blog/${slug}/`,\n slugs_bloqueados: [...slugsBloqueados]\n }\n}];"
},
"id": "seleccionar-keyword",
"name": "Seleccionar Mejor Keyword",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
944,
304
]
},
{
"parameters": {
"url": "=https://api.freepik.com/v1/resources?term={{ encodeURIComponent($json.keyword_seleccionada) }}&page=1&limit=10&filters[content_type][photo]=1&filters[license][premium]=1&filters[orientation][landscape]=1&order=relevance",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Accept-Language",
"value": "es-MX"
}
]
},
"options": {
"response": {
"response": {
"neverError": true,
"responseFormat": "json"
}
},
"timeout": 15000
}
},
"id": "stock-buscar",
"name": "Magnific Stock - Buscar",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1104,
304
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"continueOnFail": true
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose"
},
"conditions": [
{
"id": "stock-total-check",
"leftValue": "={{ $json.meta?.total ?? 0 }}",
"rightValue": 0,
"operator": {
"type": "number",
"operation": "gt"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "if-stock-ok",
"name": "IF Stock OK",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
1264,
304
]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "1",
"name": "imagen_url",
"type": "string",
"value": "={{ $json.data[0].image.source.url }}"
},
{
"id": "2",
"name": "imagen_alt",
"type": "string",
"value": "={{ ($json.data[0].title || '').slice(0, 120) }}"
},
{
"id": "3",
"name": "imagen_fuente",
"type": "string",
"value": "magnific_stock"
},
{
"id": "4",
"name": "imagen_fotografo",
"type": "string",
"value": "={{ $json.data[0].author?.name || '' }}"
}
]
},
"includeOtherFields": true,
"options": {}
},
"id": "set-imagen-stock",
"name": "Set Imagen (Stock)",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1424,
144
]
},
{
"parameters": {
"url": "=https://api.pexels.com/v1/search?query={{ encodeURIComponent($json.keyword_seleccionada) }}&per_page=1&locale=es-MX",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"options": {
"timeout": 15000
}
},
"id": "pexels-buscar-imagen",
"name": "Pexels - Buscar Imagen",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1424,
464
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose"
},
"conditions": [
{
"id": "pexels-photos-check",
"leftValue": "={{ ($json.photos || []).length }}",
"rightValue": 0,
"operator": {
"type": "number",
"operation": "gt"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "if-pexels-ok",
"name": "IF Pexels OK",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
1584,
464
]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "1",
"name": "imagen_url",
"type": "string",
"value": "={{ $json.photos[0].src.large }}"
},
{
"id": "2",
"name": "imagen_alt",
"type": "string",
"value": "={{ $json.photos[0].alt || ($('Seleccionar Mejor Keyword').item.json.keyword_seleccionada + ' \u2014 orientacion espiritual') }}"
},
{
"id": "3",
"name": "imagen_fuente",
"type": "string",
"value": "pexels"
},
{
"id": "4",
"name": "imagen_fotografo",
"type": "string",
"value": "={{ $json.photos[0].photographer || '' }}"
}
]
},
"includeOtherFields": true,
"options": {}
},
"id": "set-imagen-pexels",
"name": "Set Imagen (Pexels)",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1744,
304
]
},
{
"parameters": {
"method": "POST",
"url": "http://127.0.0.1:3000/invoke",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({\n prompt: `Construye el mejor prompt text-to-image fotorrealista, widescreen 16:9, sin texto, sin personas, ambiente espiritual con luz dorada calida, para la keyword: ${$('Seleccionar Mejor Keyword').item.json.keyword_seleccionada}. Devuelve SOLO el prompt final en ingles, una sola linea, sin explicacion.`,\n skill_path: \"/home/ubuntu/addendo-website/.claude/agents/skills-globales/diseno-imagen.md\",\n model: \"claude-sonnet-4-6\"\n}) }}",
"options": {
"response": {
"response": {
"neverError": true,
"responseFormat": "json"
}
},
"timeout": 30000
}
},
"id": "daemon-build-mystic",
"name": "Daemon Build Mystic Prompt",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1744,
624
],
"continueOnFail": true
},
{
"parameters": {
"method": "POST",
"url": "https://api.magnific.com/v1/ai/mystic",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "httpHeaderAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({\n prompt: ($('Daemon Build Mystic Prompt').item.json.response?.result || 'spiritual aura with golden light, no text, no people, professional photography, widescreen').toString().trim(),\n aspect_ratio: \"widescreen_16_9\",\n model: \"realism\",\n resolution: \"2k\"\n}) }}",
"options": {
"response": {
"response": {
"neverError": true,
"responseFormat": "json"
}
},
"timeout": 15000
}
},
"id": "mystic-submit",
"name": "HTTP Mystic Submit",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1904,
624
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"continueOnFail": true
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "1",
"name": "mystic_task_id",
"type": "string",
"value": "={{ $json.data?.task_id || '' }}"
}
]
},
"includeOtherFields": true,
"options": {}
},
"id": "set-mystic-taskid",
"name": "Set Extract task_id",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
2064,
624
]
},
{
"parameters": {
"amount": 30
},
"id": "wait-mystic-1",
"name": "Wait 30s (1)",
"type": "n8n-nodes-base.wait",
"typeVersion": 1.1,
"position": [
2544,
624
]
},
{
"parameters": {
"url": "=https://api.magnific.com/v1/ai/mystic/{{ $('Set Extract task_id').item.json.mystic_task_id }}",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "httpHeaderAuth",
"options": {
"response": {
"response": {
"neverError": true,
"responseFormat": "json"
}
},
"timeout": 15000
}
},
"id": "mystic-poll-1",
"name": "HTTP Mystic Poll 1",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2704,
624
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"continueOnFail": true
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose"
},
"conditions": [
{
"id": "status-completed",
"leftValue": "={{ $json.data?.status }}",
"rightValue": "COMPLETED",
"operator": {
"type": "string",
"operation": "equals"
}
},
{
"id": "no-nsfw",
"leftValue": "={{ ($json.data?.has_nsfw || [false])[0] === true }}",
"rightValue": "false",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "if-mystic-done-1",
"name": "IF Mystic Done 1",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
2864,
624
]
},
{
"parameters": {
"amount": 25
},
"id": "wait-mystic-2",
"name": "Wait 25s (2)",
"type": "n8n-nodes-base.wait",
"typeVersion": 1.1,
"position": [
3024,
624
]
},
{
"parameters": {
"url": "=https://api.magnific.com/v1/ai/mystic/{{ $('Set Extract task_id').item.json.mystic_task_id }}",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "httpHeaderAuth",
"options": {
"response": {
"response": {
"neverError": true,
"responseFormat": "json"
}
},
"timeout": 15000
}
},
"id": "mystic-poll-2",
"name": "HTTP Mystic Poll 2",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
3184,
624
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"continueOnFail": true
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose"
},
"conditions": [
{
"id": "status-completed",
"leftValue": "={{ $json.data?.status }}",
"rightValue": "COMPLETED",
"operator": {
"type": "string",
"operation": "equals"
}
},
{
"id": "no-nsfw",
"leftValue": "={{ ($json.data?.has_nsfw || [false])[0] === true }}",
"rightValue": "false",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "if-mystic-done-2",
"name": "IF Mystic Done 2",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
3344,
624
]
},
{
"parameters": {
"amount": 25
},
"id": "wait-mystic-3",
"name": "Wait 25s (3)",
"type": "n8n-nodes-base.wait",
"typeVersion": 1.1,
"position": [
3504,
624
]
},
{
"parameters": {
"url": "=https://api.magnific.com/v1/ai/mystic/{{ $('Set Extract task_id').item.json.mystic_task_id }}",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "httpHeaderAuth",
"options": {
"response": {
"response": {
"neverError": true,
"responseFormat": "json"
}
},
"timeout": 15000
}
},
"id": "mystic-poll-3",
"name": "HTTP Mystic Poll 3",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
3664,
624
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"continueOnFail": true
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose"
},
"conditions": [
{
"id": "status-completed",
"leftValue": "={{ $json.data?.status }}",
"rightValue": "COMPLETED",
"operator": {
"type": "string",
"operation": "equals"
}
},
{
"id": "no-nsfw",
"leftValue": "={{ ($json.data?.has_nsfw || [false])[0] === true }}",
"rightValue": "false",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "if-mystic-done-3",
"name": "IF Mystic Done 3",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
3824,
624
]
},
{
"parameters": {
"method": "POST",
"url": "https://api.cloudinary.com/v1_1/dokzw376u/image/upload",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "httpHeaderAuth",
"sendBody": true,
"contentType": "form-urlencoded",
"bodyParameters": {
"parameters": [
{
"name": "file",
"value": "={{ $json.data.generated[0] }}"
},
{
"name": "folder",
"value": "blog/don-jacinto/mystic"
},
{
"name": "public_id",
"value": "={{ $('Seleccionar Mejor Keyword').item.json.slug }}-{{ $('Seleccionar Mejor Keyword').item.json.fecha }}"
},
{
"name": "overwrite",
"value": "true"
}
]
},
"options": {
"response": {
"response": {
"neverError": true,
"responseFormat": "json"
}
},
"timeout": 30000
}
},
"id": "cloudinary-upload",
"name": "Cloudinary Upload (Mystic)",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
3968,
464
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"continueOnFail": true
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "1",
"name": "imagen_url",
"type": "string",
"value": "={{ $json.secure_url }}"
},
{
"id": "2",
"name": "imagen_alt",
"type": "string",
"value": "={{ $('Seleccionar Mejor Keyword').item.json.keyword_seleccionada + ' \u2014 orientacion espiritual' }}"
},
{
"id": "3",
"name": "imagen_fuente",
"type": "string",
"value": "magnific_mystic"
},
{
"id": "4",
"name": "imagen_fotografo",
"type": "string",
"value": ""
}
]
},
"includeOtherFields": true,
"options": {}
},
"id": "set-imagen-mystic",
"name": "Set Imagen (Mystic)",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
4128,
464
]
},
{
"parameters": {
"method": "POST",
"url": "http://127.0.0.1:3000/invoke",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({\n prompt: `Construye el mejor prompt text-to-image para Fal flux/dev, ambiente espiritual, luz dorada calida, fotografia profesional, sin texto, sin personas, para keyword: ${$('Seleccionar Mejor Keyword').item.json.keyword_seleccionada}. Devuelve SOLO el prompt en ingles, una linea.`,\n skill_path: \"/home/ubuntu/addendo-website/.claude/agents/skills-globales/diseno-imagen.md\",\n model: \"claude-sonnet-4-6\"\n}) }}",
"options": {
"response": {
"response": {
"neverError": true,
"responseFormat": "json"
}
},
"timeout": 30000
}
},
"id": "daemon-build-fal",
"name": "Daemon Build Fal Prompt",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
3648,
832
],
"continueOnFail": true
},
{
"parameters": {
"method": "POST",
"url": "https://queue.fal.run/fal-ai/flux/dev",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "httpHeaderAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({\n prompt: ($('Daemon Build Fal Prompt').item.json.response?.result || 'spiritual aura golden light no text professional').toString().trim(),\n image_size: \"landscape_16_9\",\n num_images: 1\n}) }}",
"options": {
"response": {
"response": {
"neverError": true,
"responseFormat": "json"
}
},
"timeout": 15000
}
},
"id": "fal-submit",
"name": "HTTP Fal Submit",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
3808,
832
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"continueOnFail": true
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "1",
"name": "fal_status_url",
"type": "string",
"value": "={{ $json.status_url || '' }}"
},
{
"id": "2",
"name": "fal_response_url",
"type": "string",
"value": "={{ $json.response_url || '' }}"
},
{
"id": "3",
"name": "fal_request_id",
"type": "string",
"value": "={{ $json.request_id || '' }}"
}
]
},
"includeOtherFields": true,
"options": {}
},
"id": "set-fal-urls",
"name": "Set Extract URLs",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
3968,
832
]
},
{
"parameters": {
"amount": 10
},
"id": "wait-fal-1",
"name": "Wait 10s (Fal)",
"type": "n8n-nodes-base.wait",
"typeVersion": 1.1,
"position": [
4128,
832
]
},
{
"parameters": {
"url": "={{ $('Set Extract URLs').item.json.fal_status_url }}",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "httpHeaderAuth",
"options": {
"response": {
"response": {
"neverError": true,
"responseFormat": "json"
}
},
"timeout": 15000
}
},
"id": "fal-poll-status",
"name": "HTTP Fal Poll Status",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
4288,
832
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"continueOnFail": true
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose"
},
"conditions": [
{
"id": "fal-status-completed",
"leftValue": "={{ $json.status }}",
"rightValue": "COMPLETED",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "if-fal-done",
"name": "IF Fal Done",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
4448,
832
]
},
{
"parameters": {
"url": "={{ $('Set Extract URLs').item.json.fal_response_url }}",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "httpHeaderAuth",
"options": {
"response": {
"response": {
"neverError": true,
"responseFormat": "json"
}
},
"timeout": 15000
}
},
"id": "fal-get-result",
"name": "HTTP Fal Get Result",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
4608,
752
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"continueOnFail": true
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "1",
"name": "imagen_url",
"type": "string",
"value": "={{ $json.images?.[0]?.url || '/images/altar-espiritual.webp' }}"
},
{
"id": "2",
"name": "imagen_alt",
"type": "string",
"value": "={{ $('Seleccionar Mejor Keyword').item.json.keyword_seleccionada + ' \u2014 orientacion espiritual' }}"
},
{
"id": "3",
"name": "imagen_fuente",
"type": "string",
"value": "fal_flux_dev"
},
{
"id": "4",
"name": "imagen_fotografo",
"type": "string",
"value": ""
}
]
},
"includeOtherFields": true,
"options": {}
},
"id": "set-imagen-fal",
"name": "Set Imagen (Fal)",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
4768,
752
]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "1",
"name": "imagen_url",
"type": "string",
"value": "/images/altar-espiritual.webp"
},
{
"id": "2",
"name": "imagen_alt",
"type": "string",
"value": "={{ $('Seleccionar Mejor Keyword').item.json.keyword_seleccionada + ' \u2014 orientacion espiritual' }}"
},
{
"id": "3",
"name": "imagen_fuente",
"type": "string",
"value": "placeholder"
},
{
"id": "4",
"name": "imagen_fotografo",
"type": "string",
"value": ""
}
]
},
"includeOtherFields": true,
"options": {}
},
"id": "set-imagen-placeholder",
"name": "Set Imagen (Placeholder)",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
4768,
928
]
},
{
"parameters": {},
"id": "merge-imagen",
"name": "Merge Imagen",
"type": "n8n-nodes-base.merge",
"typeVersion": 3.1,
"position": [
4960,
608
]
},
{
"parameters": {
"operation": "get",
"key": "cb:NKyH6etI3cF8En7n:non_stock_streak",
"options": {}
},
"id": "redis-get-counter",
"name": "Redis Get Counter",
"type": "n8n-nodes-base.redis",
"typeVersion": 1,
"position": [
5120,
608
],
"credentials": {
"redis": {
"name": "<your credential>"
}
},
"onError": "continueRegularOutput"
},
{
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// Circuit breaker counter: incrementa si imagen_fuente NO es stock/pexels; reset si lo es; alerta a partir de 3 consecutivos\nconst item = $input.item.json;\nconst imagen_fuente = $('Merge Imagen').item.json.imagen_fuente || 'placeholder';\n\nconst priorRaw = $('Redis Get Counter').item.json?.value;\nconst currentCounter = parseInt((priorRaw || '0').toString(), 10) || 0;\n\nlet counter_new;\nlet should_alert = false;\n\nif (imagen_fuente === 'magnific_stock' || imagen_fuente === 'pexels') {\n counter_new = 0;\n} else {\n counter_new = currentCounter + 1;\n if (counter_new >= 3) {\n should_alert = true;\n }\n}\n\nreturn { json: { ...item, counter_new, should_alert, prior_counter: currentCounter } };"
},
"id": "code-counter-logic",
"name": "Code Counter Logic",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
5280,
608
]
},
{
"parameters": {
"operation": "set",
"key": "cb:NKyH6etI3cF8En7n:non_stock_streak",
"value": "={{ $json.counter_new.toString() }}",
"keyType": "string"
},
"id": "redis-set-counter",
"name": "Redis Set Counter",
"type": "n8n-nodes-base.redis",
"typeVersion": 1,
"position": [
5440,
464
],
"credentials": {
"redis": {
"name": "<your credential>"
}
},
"onError": "continueRegularOutput"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose"
},
"conditions": [
{
"id": "should-alert",
"leftValue": "={{ $json.should_alert }}",
"rightValue": "true",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "if-should-alert",
"name": "IF should_alert",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
5440,
752
]
},
{
"parameters": {
"method": "POST",
"url": "http://127.0.0.1:5678/webhook/addendo-pulse-alert",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "httpHeaderAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({\n source: \"workflow.don-jacinto.circuit-breaker\",\n severity: \"warning\",\n summary: \"3+ runs consecutivos sin Stock/Pexels \u2014 fallback AI dominante\",\n details: {\n consecutive_non_stock: $json.counter_new,\n last_imagen_fuente: $json.imagen_fuente,\n keyword: $('Seleccionar Mejor Keyword').item.json.keyword_seleccionada,\n fecha: $('Seleccionar Mejor Keyword').item.json.fecha\n }\n}) }}",
"options": {
"response": {
"response": {
"neverError": true,
"responseFormat": "json"
}
},
"timeout": 5000
}
},
"id": "http-pulse-alert",
"name": "HTTP Pulse Alert",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
5600,
752
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"continueOnFail": true
},
{
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const item = $input.item.json;\nconst meta = $('Seleccionar Mejor Keyword').item.json;\nconst imagen_url = item.imagen_url || '/images/altar-espiritual.webp';\nconst imagen_alt = item.imagen_alt || `${meta.keyword_seleccionada} \u2014 orientacion espiritual`;\nconst imagen_fuente = item.imagen_fuente || 'placeholder';\nconst imagen_fotografo = item.imagen_fotografo || null;\nreturn { json: { ...meta, imagen_url, imagen_alt, imagen_fuente, imagen_fotografo } };"
},
"id": "combinar-datos-imagen",
"name": "Combinar Datos + Imagen",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
5600,
608
]
},
{
"parameters": {
"method": "POST",
"url": "http://127.0.0.1:3000/invoke",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({\n prompt: `Vas a escribir un articulo de blog informativo y util para una persona hispana que vive en Estados Unidos y que esta buscando en Google AHORA MISMO informacion sobre: \"${$json.keyword_seleccionada}\"\n\nEsta persona no esta buscando \"conocer a un brujo\". Esta buscando entender algo que le esta pasando \u2014 un dolor, un miedo, una duda, una situacion que la rebasa. Quizas enfermo sin explicacion medica. Quizas su pareja se fue de un dia para otro. Quizas su negocio se vino abajo sin razon. Quizas sospecha que alguien le hizo un mal. Tu articulo debe ser EL recurso que le ayude a entender lo que vive y a saber que puede hacer.\n\n=== ESTRATEGIA DE CONTENIDO ===\n\nEl articulo NO es sobre Don Jacinto Nahual. Es un articulo informativo, empatico y util sobre el TEMA. Don Jacinto aparece SOLO al final, como un recurso de ayuda, en la seccion de CTA.\n\nEl cuerpo del articulo:\n1. Reconoce el problema real que vive el lector (desde la primera linea)\n2. Explica el tema con informacion culturalmente relevante para la comunidad hispana en USA, usando la tradicion esoterica mexicana, caribe\u00f1a y centroamericana como marco\n3. Tono: empatico, cercano, como un consejero espiritual que entiende el dolor \u2014 NO sensacionalista, NO catastrofico, NO prometiendo soluciones magicas\n4. Da orientacion practica: se\u00f1ales a observar, errores comunes, diferencias entre problemas espirituales y problemas medicos/emocionales que requieren otro tipo de ayuda\n5. Usa ejemplos anonimos de situaciones tipicas (\"hay personas que llegan despues de meses de sentir X\u2026\", sin inventar testimonios con nombres)\n\nTemas que el blog cubre (usa esto para orientar el angulo del articulo):\n- Enfermedades inexplicables o males fisicos sin diagnostico medico claro\n- Rompimientos de pareja, infidelidad, alejamiento del ser amado\n- Fracasos de negocios repentinos e inexplicables\n- Perdidas economicas y malas rachas financieras prolongadas\n- Personas que sospechan ser victimas de brujeria o trabajos en su contra\n- Proteccion contra energias negativas y fuerzas oscuras\n- Mal de ojo, envidia, hechizos\n- Como reconocer si alguien hizo da\u00f1o espiritual\n- Limpias espirituales, su proposito y cuando tienen sentido\n- Rituales de proteccion y fortalecimiento del espiritu\n\n=== DATOS DE ENTRADA ===\n- Keyword principal: ${$json.keyword_seleccionada}\n- Volumen mensual: ${$json.volumen}\n- Slug: ${$json.slug}\n- Fecha: ${$json.fecha}\n- URL imagen hero (usar tal cual): ${$json.imagen_url}\n- Alt sugerido: ${$json.imagen_alt}\n\n=== PERFIL DE DON JACINTO (usar SOLO en la seccion final de CTA) ===\n- Nombre: Don Jacinto Nahual\n- Origen: Catemaco, Veracruz, Mexico \u2014 region reconocida por su herencia espiritual\n- Experiencia: mas de 30 a\u00f1os\n- Conocimiento ancestral transmitido por generaciones\n- Servicios: limpias con hierbas y rezos, orientacion espiritual, proteccion energetica, armonizacion del entorno\n- Atiende en el sur de California: Los Angeles, Santa Monica, Inglewood, East LA, Huntington Park, Long Beach, Compton, Gardena, Downey, San Bernardino, Santa Ana, Anaheim, Irvine, Ontario\n- Atencion: presencial con cita previa, tambien orientacion por llamada o mensaje\n- Filosofia: el trabajo espiritual es disciplina, fe y fortalecimiento del espiritu \u2014 no magia instantanea ni resultados garantizados\n\n=== PROHIBIDO (descalifica el articulo) ===\n- NO mencionar Houston, Chicago, Miami, Nueva York ni ninguna ciudad fuera del sur de California\n- NO mencionar a Don Jacinto en introduccion ni en el cuerpo \u2014 solo en la seccion final\n- NO prometer curaciones medicas, resultados garantizados, ni efectos inmediatos\n- NO usar \"el mejor\", \"el unico\", \"100% efectivo\", \"infalible\"\n- NO inventar testimonios con nombres, edades o detalles verificables\n- NO sugerir que el lector abandone tratamiento medico\n\n=== FORMATO DE SALIDA (CRITICO) ===\n\nDevuelve UN UNICO archivo .astro completo. NO envuelvas en code fences, NO a\u00f1adas explicaciones, NO uses markdown. Devuelve SOLO el contenido del archivo .astro exactamente en este formato:\n\n---\nimport BlogPostLayout from '../../layouts/BlogPostLayout.astro';\n---\n<BlogPostLayout\n title=\"[titulo SEO max 60 caracteres, incluye keyword, orientado al problema del lector]\"\n metaTitle=\"[titulo SEO] | Don Jacinto Nahual\"\n description=\"[meta description 150-160 caracteres con keyword, empatica]\"\n heroImg=\"${$json.imagen_url}\"\n heroAlt=\"[alt SEO basado en ${$json.imagen_alt} e incluyendo la keyword]\"\n category=\"[Brujeria / Limpias / Proteccion / Amarres / Mal de Ojo / Orientacion Espiritual]\"\n date=\"${$json.fecha}\"\n readTime=\"[X min de lectura]\"\n h1=\"[H1 con keyword principal, orientado al problema]\"\n>\n <p>\n [Primer parrafo: reconoce el problema que vive el lector. Incluye la keyword en las primeras 60 palabras. NO menciones a Don Jacinto aqui.]\n </p>\n\n <h2>[H2 que aborda el tema desde el angulo del problema]</h2>\n <p>[...]</p>\n <p>[...]</p>\n\n <h2>[H2 con sub-keyword]</h2>\n <p>[...]</p>\n <h3>[H3 interno cuando aplique]</h3>\n <p>[...]</p>\n\n [6-8 secciones H2 total, todas orientadas a informar/ayudar al lector]\n\n <h2>Preguntas Frecuentes</h2>\n <h3>[Pregunta 1 que realmente se hace alguien con este problema]</h3>\n <p>[Respuesta 1]</p>\n [Minimo 6 preguntas]\n\n <h2>Cuando buscar orientacion de un especialista</h2>\n <p>\n [Aqui y SOLO aqui presenta a Don Jacinto Nahual, curandero de Catemaco, Veracruz con mas de 30 a\u00f1os de experiencia. Menciona 2-3 ciudades del sur de California relevantes al tema (Los Angeles y 1-2 mas). Invita a consulta presencial con cita previa u orientacion por llamada/mensaje. Cierra recordando su filosofia de disciplina, fe y fortalecimiento del espiritu \u2014 no magia instantanea.]\n </p>\n</BlogPostLayout>\n\n=== REGLAS ESTRICTAS DEL FORMATO ===\n- Todo el contenido dentro de <BlogPostLayout>...</BlogPostLayout>\n- Etiquetas HTML permitidas: <p>, <h2>, <h3>, <strong>, <em>, <ul>/<li>\n- NO sintaxis markdown (sin #, ##, **, -)\n- NO code fences en ninguna parte\n- Atributos siempre con comillas dobles; en el cuerpo puedes usar apostrofes normales\n- La primera linea DEBE ser exactamente: ---\n- Despues del cierre --- del bloque import, sigue <BlogPostLayout>\n\n=== REQUISITOS VERIFICABLES ===\n1. Minimo 1,800 palabras dentro del <BlogPostLayout>\n2. El nombre \"Don Jacinto\" aparece SOLO en la ultima seccion (\"Cuando buscar orientacion\u2026\"), NO antes\n3. \"Catemaco, Veracruz\" aparece SOLO en esa misma ultima seccion\n4. En esa ultima seccion se mencionan 2-3 ciudades del sur de California: Los Angeles (obligatoria) + 1-2 mas de la lista (Santa Monica, Long Beach, Inglewood, East LA, Huntington Park, Compton, Gardena, Downey, San Bernardino, Santa Ana, Anaheim, Irvine, Ontario), elegidas por pertinencia al tema\n5. Keyword principal en h1, primer <p>, ultimo <p>, minimo 10 veces natural en el cuerpo\n6. Keywords secundarias del nicho segun el tema: limpias, rezos, hierbas, proteccion, mal de ojo, envidia, trabajos, amarres, curanderismo\n7. Tono empatico desde la primera linea, NO promocional hasta la seccion final\n8. Parrafos cortos (3-4 lineas max), variando longitud de oraciones\n9. Sin cliches de IA: \"en el mundo de hoy\", \"sin duda alguna\", \"es importante destacar\", \"en conclusion\"\n\nDEVUELVE SOLO EL ARCHIVO .astro. Nada antes, nada despues.`,\n skill_path: \"/home/ubuntu/addendo-website/.claude/agents/skills-globales/copywriting-seo.md\",\n additional_skill_paths: [\"/home/ubuntu/addendo-website/.claude/agents/skills-globales/seo.md\"],\n model: \"claude-sonnet-4-6\"\n}) }}",
"options": {
"response": {
"response": {
"neverError": true,
"responseFormat": "json"
}
},
"timeout": 740000
}
},
"id": "daemon-write-article",
"name": "Daemon Write Article",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
5760,
608
],
"continueOnFail": true
},
{
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// Daemon Write Article devuelve { response: { result: \"<article>\" }, ... }\n// Adaptar al shape esperado y mantener el resto de la logica intacta\nconst item = $input.item.json;\n\n// Fix B firmado CEO 13 mayo 2026: detectar errores del daemon expl\u00edcitamente\n// antes de procesar contenido. Antes este caso ca\u00eda silenciosamente a '' y produc\u00eda\n// \"frontmatter inv\u00e1lido\" enga\u00f1oso en l\u00ednea 14.\nif (item?.error) {\n throw new Error(`Daemon CCM retorn\u00f3 error: ${item.error} (workflow esperaba article en .response.result o .result)`);\n}\n\nlet articulo = item?.response?.result || item?.result || '';\n\narticulo = articulo.trim();\narticulo = articulo.replace(/^```[a-zA-Z]*\\s*\\n/, '');\narticulo = articulo.replace(/\\n```\\s*$/, '');\narticulo = articulo.trim();\n\nconst metadata = $('Seleccionar Mejor Keyword').item.json;\nconst imagen_fuente = $('Combinar Datos + Imagen').item.json.imagen_fuente || 'placeholder';\n\nif (!articulo.startsWith('---')) {\n throw new Error('El articulo generado no tiene frontmatter valido');\n}\nif (articulo.length < 5000) {\n throw new Error(`Articulo muy corto: ${articulo.length} caracteres (minimo 5000)`);\n}\n\n// Inyectar imageSource en la llamada a <BlogPostLayout si el modelo no lo emitio\nif (!articulo.match(/\\bimageSource=/)) {\n articulo = articulo.replace(/<BlogPostLayout\\s*\\n/, `<BlogPostLayout\\n imageSource=\"${imagen_fuente}\"\\n`);\n}\n\nreturn {\n json: {\n ...metadata,\n contenido_articulo: articulo,\n contenido_base64: Buffer.from(articulo).toString('base64'),\n longitud_caracteres: articulo.length,\n commit_message: `blog: articulo SEO - ${metadata.keyword_seleccionada}`,\n ruta_archivo: `src/pages/blog/${metadata.nombre_archivo}`,\n imagen_fuente: imagen_fuente\n }\n};"
},
"id": "preparar-archivo",
"name": "Preparar Archivo para GitHub",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
5920,
608
]
},
{
"parameters": {
"resource": "file",
"owner": {
"__rl": true,
"value": "AddendoGrowthPartner",
"mode": "name"
},
"repository": {
"__rl": true,
"value": "don-jacinto-nahual",
"mode": "name"
},
"filePath": "={{ $json.ruta_archivo }}",
"fileContent": "={{ $json.contenido_articulo }}",
"commitMessage": "={{ $json.commit_message }}"
},
"id": "github-create-file",
"name": "GitHub - Crear Archivo Blog",
"type": "n8n-nodes-base.github",
"typeVersion": 1,
"position": [
6080,
608
],
"credentials": {
"githubApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"url": "https://api.github.com/repos/AddendoGrowthPartner/don-jacinto-nahual/contents/src/pages/blog/index.astro",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "githubApi",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Accept",
"value": "application/vnd.github+json"
}
]
},
"options": {
"timeout": 15000
}
},
"id": "github-leer-index",
"name": "GitHub - Leer Index",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
6240,
768
],
"credentials": {
"githubApi": {
"name": "<your credential>"
}
},
"onError": "continueRegularOutput"
},
{
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "try {\n const gh = $input.item.json;\n const meta = $('Preparar Archivo para GitHub').item.json;\n const articulo = meta.contenido_articulo || '';\n\n if (!gh || !gh.content || !gh.sha) {\n throw new Error('No se pudo leer index.astro de GitHub');\n }\n\n const pick = (re, fallback) => {\n const m = articulo.match(re);\n return (m && m[1]) ? m[1] : fallback;\n };\n const title = pick(/\\btitle=\"([^\"]+)\"/, meta.keyword_seleccionada);\n const description = pick(/\\bdescription=\"([^\"]+)\"/, '');\n const category = pick(/\\bcategory=\"([^\"]+)\"/, 'Orientaci\u00f3n Espiritual');\n const heroImg = pick(/\\bheroImg=\"([^\"]+)\"/, '/images/altar-espiritual.webp');\n\n const meses = ['enero','febrero','marzo','abril','mayo','junio','julio','agosto','septiembre','octubre','noviembre','diciembre'];\n const [yyyy, mm, dd] = meta.fecha.split('-');\n const fechaFormateada = `${parseInt(dd,10)} de ${meses[parseInt(mm,10)-1]}, ${yyyy}`;\n\n const currentContent = Buffer.from(gh.content, 'base64').toString('utf8');\n\n const slugCombinado = `${meta.slug}-${meta.fecha}`;\n if (currentContent.includes(`slug: '${slugCombinado}'`) || currentContent.includes(`slug: \"${slugCombinado}\"`)) {\n throw new Error(`El slug ${slugCombinado} ya est\u00e1 en el \u00edndice \u2014 no se duplica`);\n }\n\n const newEntry = ` {\n slug: ${JSON.stringify(slugCombinado)},\n title: ${JSON.stringify(title)},\n excerpt: ${JSON.stringify(description)},\n category: ${JSON.stringify(category)},\n date: ${JSON.stringify(fechaFormateada)},\n dateISO: ${JSON.stringify(meta.fecha)},\n heroImg: ${JSON.stringify(heroImg)},\n },`;\n\n const updated = currentContent.replace(\n /const posts = \\[\\s*\\n?/,\n `const posts = [\\n${newEntry}\\n`\n );\n if (updated === currentContent) {\n throw new Error('No se localiz\u00f3 el array posts en index.astro');\n }\n\n return {\n json: {\n ok: true,\n sha: gh.sha,\n updated_content_base64: Buffer.from(updated, 'utf8').toString('base64'),\n commit_message: `blog: actualizar \u00edndice \u2014 ${slugCombinado}`,\n }\n };\n} catch (e) {\n return { json: { ok: false, error: String(e.message || e) } };\n}"
},
"id": "regenerar-index",
"name": "Regenerar Index",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
6400,
768
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"method": "PUT",
"url": "https://api.github.com/repos/AddendoGrowthPartner/don-jacinto-nahual/contents/src/pages/blog/index.astro",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "githubApi",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Accept",
"value": "application/vnd.github+json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ $json.ok ? JSON.stringify({message: $json.commit_message, content: $json.updated_content_base64, sha: $json.sha, branch: 'main'}) : '{\"skip\":true}' }}",
"options": {
"timeout": 20000
}
},
"id": "github-actualizar-index",
"name": "GitHub - Actualizar Index",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
6560,
768
],
"credentials": {
"githubApi": {
"name": "<your credential>"
}
},
"onError": "continueRegularOutput"
},
{
"parameters": {
"method": "POST",
"url": "https://indexing.googleapis.com/v3/urlNotifications:publish",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "googleOAuth2Api",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"url\": \"{{ $('Preparar Archivo para GitHub').item.json.url_articulo }}\",\n \"type\": \"URL_UPDATED\"\n}",
"options": {
"timeout": 15000
}
},
"id": "google-indexing",
"name": "Google Indexing API",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
6720,
608
],
"credentials": {
"googleOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"sendTo": "admin@addendo.io",
"subject": "=\u2705 Articulo publicado \u2014 {{ $('Preparar Archivo para GitHub').item.json.keyword_seleccionada }} \u2014 Don Jacinto Nahual",
"message": "=<h2>\ud83d\udcdd Nuevo articulo publicado</h2>\n\n<p><strong>Cliente:</strong> Don Jacinto Nahual</p>\n<p><strong>Sitio:</strong> donjacintonahual.com</p>\n\n<hr>\n\n<h3>Detalles del articulo</h3>\n<ul>\n <li><strong>Keyword principal:</strong> {{ $('Preparar Archivo para GitHub').item.json.keyword_seleccionada }}</li>\n <li><strong>Volumen mensual:</strong> {{ $('Preparar Archivo para GitHub').item.json.volumen }}</li>\n <li><strong>Dificultad SEO:</strong> {{ $('Preparar Archivo para GitHub').item.json.dificultad }}</li>\n <li><strong>Slug:</strong> {{ $('Preparar Archivo para GitHub').item.json.slug }}</li>\n <li><strong>Fecha de publicacion:</strong> {{ $('Preparar Archivo para GitHub').item.json.fecha }}</li>\n</ul>\n\n<h3>Enlaces</h3>\n<ul>\n <li><strong>URL del articulo:</strong> <a href=\"{{ $('Preparar Archivo para GitHub').item.json.url_articulo }}\">{{ $('Preparar Archivo para GitHub').item.json.url_articulo }}</a></li>\n <li><strong>Repositorio:</strong> AddendoGrowthPartner/don-jacinto-nahual</li>\n <li><strong>Archivo:</strong> {{ $('Preparar Archivo para GitHub').item.json.ruta_archivo }}</li>\n</ul>\n\n<h3>Estado</h3>\n<ul>\n <li>\u2705 Articulo generado por Claude</li>\n <li>\u2705 Commit a GitHub realizado</li>\n <li>\u2705 URL enviada a Google Indexing API</li>\n</ul>\n\n<hr>\n\n<p><em>Workflow automatico de Addendo Agency OS</em></p>",
"options": {}
},
"id": "gmail-notify",
"name": "Gmail - Notificar a Jose",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.1,
"position": [
6880,
608
],
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
}
},
{
"id": "redis-listar-slugs-pub",
"name": "Redis - Listar Slugs Publicados",
"type": "n8n-nodes-base.redis",
"typeVersion": 1,
"parameters": {
"operation": "keys",
"options": {},
"keyPattern": "dj:slug_pub:*",
"getValues": false
},
"credentials": {
"redis": {
"name": "<your credential>"
}
},
"position": [
880,
304
]
},
{
"id": "redis-marcar-publicado",
"name": "Redis - Marcar Publicado",
"type": "n8n-nodes-base.redis",
"typeVersion": 1,
"parameters": {
"operation": "set",
"key": "=dj:slug_pub:{{ $('Preparar Archivo para GitHub').item.json.slug }}",
"value": "={{ $('Preparar Archivo para GitHub').item.json.fecha }}",
"keyType": "string",
"expire": true,
"ttl": 604800,
"options": {}
},
"credentials": {
"redis": {
"name": "<your credential>"
}
},
"position": [
6160,
608
]
}
],
"connections": {
"Schedule Trigger - 6am LA L/M/V": {
"main": [
[
{
"node": "Configuracion Cliente",
"type": "main",
"index": 0
}
]
]
},
"Configuracion Cliente": {
"main": [
[
{
"node": "DataForSEO - Buscar Keywords",
"type": "main",
"index": 0
}
]
]
},
"DataForSEO - Buscar Keywords": {
"main": [
[
{
"node": "GitHub - Listar Articulos",
"type": "main",
"index": 0
}
]
]
},
"GitHub - Listar Articulos": {
"main": [
[
{
"node": "Redis - Listar Slugs Publicados",
"type": "main",
"index": 0
}
]
]
},
"Seleccionar Mejor Keyword": {
"main": [
[
{
"node": "Magnific Stock - Buscar",
"type": "main",
"index": 0
}
]
]
},
"Magnific Stock - Buscar": {
"main": [
[
{
"node": "IF Stock OK",
"type": "main",
"index": 0
}
]
]
},
"IF Stock OK": {
"main": [
[
{
"node": "Set Imagen (Stock)",
"type": "main",
"index": 0
}
],
[
{
"node": "Pexels - Buscar Imagen",
"type": "main",
"index": 0
}
]
]
},
"Set Imagen (Stock)": {
"main": [
[
{
"node": "Merge Imagen",
"type": "main",
"index": 0
}
]
]
},
"Pexels - Buscar Imagen": {
"main": [
[
{
"node": "IF Pexels OK",
"type": "main",
"index": 0
}
]
]
},
"IF Pexels OK": {
"main": [
[
{
"node": "Set Imagen (Pexels)",
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.
githubApigmailOAuth2googleOAuth2ApihttpBasicAuthhttpHeaderAuthredis
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Addendo — Blog Automatico Don Jacinto Nahual. Uses httpRequest, redis, github, gmail. Scheduled trigger; 51 nodes.
Source: https://github.com/AddendoGrowthPartner/addendo-website/blob/main/workflows/blog-don-jacinto.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.
Addendo — Blog Automatico Don Jacinto Nahual. Uses httpRequest, redis, github, gmail. Scheduled trigger; 51 nodes.
This n8n workflow continuously monitors your website’s availability, sends email alerts when the server goes down, and automatically updates a status page (index.html) in your GitHub repository to ref
YOUR_ID 4. Uses gmail, googleDrive, googleSheets, httpRequest. Scheduled trigger; 53 nodes.
14310 Send Overdue Invoice Payment Reminders With Ifirma Gmail Postgrid And Slack. Uses httpRequest, stopAndError, slack, gmail. Scheduled trigger; 53 nodes.
Instead of providing a routine check, it focuses on significant movements by: Sending a Slack alert only if a query crosses a defined movement threshold. Emailing a structured report with the Top 25 i