275 lines
9.9 KiB
Markdown
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?
|