9.9 KiB
n8n Workflows — Prisa Yachts LLC
Instancia: n8n.crewinghunters.com
Versión: 1.0 | 2026-05-04
RESUMEN DE WORKFLOWS
| Workflow | Trigger | Qué hace |
|---|---|---|
| WF1: Content Buffer Publisher | Cron 9AM / 12PM / 5PM ET diario | Lee Google Sheet → publica en Instagram y Facebook automáticamente |
| WF2: LinkedIn Reminder | Cron Lu/Mié/Vie 12PM ET | Envía email con el texto del día listo para copiar en LinkedIn |
| WF3: Weekly Analytics Report | Cron Domingo 7PM ET | Jala métricas de Meta API → guarda en Sheet → envía resumen por email |
| WF4: Content Prep Reminder | Cron Domingo 8AM ET | Detecta posts sin imagen para la semana y alerta por email |
VARIABLES DE ENTORNO (configurar en n8n antes de todo)
PRISA_META_PAGE_ACCESS_TOKEN=EAA... # Long-lived Page Access Token (60 días)
PRISA_IG_USER_ID=17841400000000000 # Instagram Business Account ID (numérico)
PRISA_FB_PAGE_ID=100000000000000 # Facebook Page ID (numérico)
PRISA_CONTENT_SHEET_ID=1BxiMVs... # ID del Google Sheet (del URL)
PRISA_CONTENT_SHEET_NAME=ContentCalendar
PRISA_NOTIFY_EMAIL=alro65@gmail.com
GOOGLE SHEETS — ESTRUCTURA
Tab 1: ContentCalendar
| Col | Header | Tipo | Valores permitidos |
|---|---|---|---|
| A | post_id |
Texto | PY-2026-001 |
| B | scheduled_date |
Fecha ISO | 2026-05-06 |
| C | scheduled_time |
Hora 24h | 11:00 |
| D | platform |
Texto fijo | instagram / facebook / both |
| E | caption_EN |
Texto largo | Caption en inglés (max 2200 chars) |
| F | caption_ES |
Texto largo | Caption en español |
| G | hashtags |
Texto | #tag1 #tag2 #tag3 |
| H | image_url |
URL | URL directa pública (ver nota crítica abajo) |
| I | pillar |
Texto | electrical / battery / education / safety / promo |
| J | status |
Texto fijo | draft / scheduled / publishing / published / failed / skipped |
| K | published_at |
Datetime | Lo escribe WF1 automáticamente |
| L | ig_media_id |
Texto | Lo escribe WF1 automáticamente |
| M | fb_post_id |
Texto | Lo escribe WF1 automáticamente |
| N | error_message |
Texto | Lo escribe WF1 si hay error |
| O | linkedin_copy |
Texto largo | Caption adaptado para LinkedIn (lo usa WF2) |
Tab 2: Analytics
Columnas A-R: report_date, post_id, platform, published_date, pillar, métricas IG (impressions, reach, likes, comments, saved, shares), métricas FB, engagement rates. WF3 appenda filas aquí cada domingo.
WF1: CONTENT BUFFER PUBLISHER
Flujo completo
[Schedule Trigger: 9AM / 12PM / 5PM ET]
→ [Google Sheets: Read ALL rows de ContentCalendar]
→ [Code: Filter — status=scheduled AND fecha=hoy AND hora±30min]
→ ¿Hay filas? NO → [NoOp Exit] FIN
→ ¿Hay filas? SÍ → [Split In Batches: 1 por vez]
→ [Google Sheets: Update status="publishing"] ← lock anti-duplicado
→ [Switch por platform:]
instagram → [Code: Build caption] → [HTTP: Create IG Container] → [Wait 10s] → [HTTP: Publish IG Container]
facebook → [Code: Build caption] → [HTTP: POST /photos a Facebook Page]
both → ambas ramas secuencial (IG primero, luego FB)
→ [SUCCESS] → [Google Sheets: Update status="published", published_at, ig_media_id, fb_post_id]
→ [FAILURE] → [Google Sheets: Update status="failed", error_message] → [Gmail: Notify alro65@gmail.com]
API Calls exactas
Instagram — Crear container:
POST https://graph.facebook.com/v21.0/{PRISA_IG_USER_ID}/media
?image_url={image_url}
&caption={caption_EN}\n\n{caption_ES}\n\n{hashtags}
&access_token={PRISA_META_PAGE_ACCESS_TOKEN}
Respuesta: { "id": "container_id" } → esperar 10s → publicar
Instagram — Publicar container:
POST https://graph.facebook.com/v21.0/{PRISA_IG_USER_ID}/media_publish
?creation_id={container_id}
&access_token={PRISA_META_PAGE_ACCESS_TOKEN}
Respuesta: { "id": "published_media_id" } → guardar en columna L
Facebook — Post con imagen:
POST https://graph.facebook.com/v21.0/{PRISA_FB_PAGE_ID}/photos
?url={image_url}
&message={caption_EN}\n\n{caption_ES}\n\n{hashtags}
&access_token={PRISA_META_PAGE_ACCESS_TOKEN}
Respuesta: { "post_id": "...", "id": "..." } → guardar en columna M
Manejo de errores
| Error | Acción |
|---|---|
| Token expirado (code 190) | Halt todo, email crítico a alro65@gmail.com |
| imagen_url inválida (code 100) | Marcar fila failed, email, continuar con siguiente |
| Container no listo (code 9007) | Retry 4x con delay 15s, luego failed |
| Rate limit (429) | Retry 3x con delay 60s, luego failed |
| Sheet no accesible | Halt todo, email crítico |
Estados del row (ciclo completo)
draft → (operador promueve) → scheduled
scheduled → (WF1 bloquea) → publishing
publishing → (éxito) → published
publishing → (error) → failed
failed → (operador resetea) → scheduled
WF2: LINKEDIN REMINDER
[Schedule Trigger: Lun/Mié/Vie 12PM ET]
→ [Google Sheets: Read ContentCalendar]
→ [Code: Filter — fecha=hoy AND status=scheduled/published]
→ ¿Hay post? NO → [Gmail: "No hay post de LinkedIn hoy"]
→ ¿Hay post? SÍ → [Code: Build email — usar linkedin_copy si existe, sino caption_EN+ES]
→ [Gmail: Enviar a alro65@gmail.com con texto listo para copiar/pegar]
Formato del email:
Asunto: [Prisa Yachts] LinkedIn post listo — 2026-05-06
LinkedIn post de hoy:
---
[texto del post]
---
Instrucciones:
1. Ir a LinkedIn
2. Crear nuevo post
3. Copiar texto arriba
4. Subir imagen desde Google Drive
5. Publicar
WF3: WEEKLY ANALYTICS REPORT
[Schedule Trigger: Domingo 7PM ET]
→ [Google Sheets: Read ContentCalendar]
→ [Code: Filter — status=published AND published_at últimos 7 días]
→ ¿Posts? NO → [Gmail: "Sin publicaciones esta semana"]
→ ¿Posts? SÍ → [Split In Batches: 1 por vez]
→ [IF ig_media_id existe] → [HTTP GET /{ig_media_id}/insights?metric=impressions,reach,likes,comments,saved,shares]
→ [IF fb_post_id existe] → [HTTP GET /{fb_post_id}/insights?metric=post_impressions,post_impressions_unique,...]
→ [Code: Calcular engagement rate = (likes+comments+saves+shares)/reach*100]
→ [Google Sheets: Append row a Analytics tab]
→ [Merge: Esperar todos los items]
→ [Code: Build reporte — ordenar por engagement desc, top post, tabla completa]
→ [Gmail: Enviar reporte a alro65@gmail.com]
Instagram insights endpoint:
GET https://graph.facebook.com/v21.0/{ig_media_id}/insights
?metric=impressions,reach,likes,comments,saved,shares
&access_token={token}
WF4: CONTENT PREP REMINDER
[Schedule Trigger: Domingo 8AM ET]
→ [Google Sheets: Read ContentCalendar]
→ [Code: Filter — scheduled_date próximos 7 días AND status=scheduled/draft]
→ ¿Hay posts? NO → [Gmail: "Sin posts programados para la semana"]
→ ¿Hay posts? SÍ → [Code: Separar los que tienen image_url vs los que no]
→ [Code: Build email]
SI hay faltantes → "ACTION REQUIRED: X posts sin imagen"
SI todos tienen → "Todo listo para la semana"
→ [Gmail: Enviar a alro65@gmail.com]
GUÍA DE IMPLEMENTACIÓN — ORDEN DE PASOS
Paso 1: Credenciales en n8n
- Google Sheets OAuth2 — autorizar con la cuenta que tiene el Sheet
- Gmail OAuth2 — autorizar con alro65@gmail.com
- Meta API — Header Auth:
Bearer {PRISA_META_PAGE_ACCESS_TOKEN}
Paso 2: Obtener los IDs de Meta
Page Access Token (long-lived):
1. developers.facebook.com → Graph API Explorer
2. Seleccionar App + Facebook Page
3. Pedir permisos: instagram_content_publish, pages_manage_posts, pages_read_engagement, instagram_manage_insights
4. GET /me/accounts → copiar access_token de tu página
5. Convertir a long-lived:
GET https://graph.facebook.com/v21.0/oauth/access_token
?grant_type=fb_exchange_token
&client_id={APP_ID}
&client_secret={APP_SECRET}
&fb_exchange_token={SHORT_TOKEN}
Instagram Business Account ID:
GET https://graph.facebook.com/v21.0/{PAGE_ID}
?fields=instagram_business_account
&access_token={LONG_TOKEN}
→ respuesta: { "instagram_business_account": { "id": "ESTE_ES_EL_IG_USER_ID" } }
Paso 3: Crear el Google Sheet
- Crear spreadsheet con 2 tabs:
ContentCalendaryAnalytics - Agregar headers exactos según tabla arriba
- Agregar fila de prueba con status=
scheduledy fecha=mañana
Paso 4: Orden de construcción (de menor a mayor riesgo)
- WF4 primero — solo lectura, envía email → probar sin riesgo
- WF2 segundo — solo lectura + email → probar sin riesgo
- WF1 tercero — publicación real → probar con 1 fila de prueba primero
- WF3 último — requiere posts publicados con IDs reales para probar
⚠️ NOTA CRÍTICA: URLs DE IMÁGENES
Meta Graph API NO acepta links de Google Drive del tipo:
❌ https://drive.google.com/file/d/{ID}/view
Formato correcto (compartir como "Cualquiera con el link"):
✅ https://drive.google.com/uc?export=download&id={FILE_ID}
Para obtener el FILE_ID: en el link de compartir de Drive, el ID es el string alfanumérico largo.
Alternativa recomendada para producción: subir fotos a Google Cloud Storage con acceso público — URLs más estables y sin límites de descarga.
TOKEN REFRESH (importante)
El Page Access Token expira en 60 días. Antes de que expire:
- Opción A: Agregar WF5 "Token Expiry Reminder" que envía email cada 30 días
- Opción B (recomendada): Usar System User Token en Meta Business Manager — no expira nunca
PREGUNTAS A RESOLVER ANTES DEL PRIMER DEPLOY
- ¿Tienes Meta Developer App creada para Prisa Yachts?
- ¿Instagram está como cuenta Business vinculada a tu Facebook Page?
- ¿Las fotos irán a Google Drive o a otro storage?
- ¿Los posts serán bilingües (EN+ES en el mismo post) o separados?