Files

275 lines
9.9 KiB
Markdown

# 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)
```env
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
1. **Google Sheets OAuth2** — autorizar con la cuenta que tiene el Sheet
2. **Gmail OAuth2** — autorizar con alro65@gmail.com
3. **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
1. Crear spreadsheet con 2 tabs: `ContentCalendar` y `Analytics`
2. Agregar headers exactos según tabla arriba
3. Agregar fila de prueba con status=`scheduled` y fecha=mañana
### Paso 4: Orden de construcción (de menor a mayor riesgo)
1. **WF4 primero** — solo lectura, envía email → probar sin riesgo
2. **WF2 segundo** — solo lectura + email → probar sin riesgo
3. **WF1 tercero** — publicación real → probar con 1 fila de prueba primero
4. **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
1. ¿Tienes Meta Developer App creada para Prisa Yachts?
2. ¿Instagram está como cuenta Business vinculada a tu Facebook Page?
3. ¿Las fotos irán a Google Drive o a otro storage?
4. ¿Los posts serán bilingües (EN+ES en el mismo post) o separados?