Módulo RRSS
Gestión completa del ciclo de contenido para redes sociales: estrategia, banco de ideas, posts con copy por plataforma, revisión, aprobación, programación, publicación OAuth y métricas.
El backend está completamente implementado incluyendo el flujo OAuth para conectar Facebook e Instagram. Modo manual disponible hoy — modo automático disponible cuando Meta apruebe la app. Los agentes n8n son la siguiente fase.
Cómo funciona el sistema — visión completa
Este diagrama explica el ciclo de vida completo de un post: desde que se configura la empresa hasta que el contenido aparece publicado en redes sociales.
Hay dos modos según si la empresa ya conectó sus redes:
Tablas y estados
| Tabla | Propósito |
|---|---|
company_social_channels | Canales configurados por empresa |
social_strategies | Estrategia RRSS — 1 registro por empresa |
social_ideas | Banco de ideas de contenido |
social_posts | Posts en todas sus etapas |
social_metrics | Métricas de rendimiento por plataforma y período |
| Valor | Significado |
|---|---|
idea | Recién creada, pendiente de revisión |
aprobado | Aprobada — lista para convertirse en post |
descartado | Descartada, no se usará |
| Valor | Significado |
|---|---|
borrador | Generado, sin revisar |
en_revision | Enviado a revisión por el equipo |
aprobado | Aprobado — listo para programar |
programado | Tiene fecha asignada |
publicado | Ya fue publicado |
generated_by — presente en todos los modelos.
manual = creado desde el panel, n8n = generado por un agente.
Estrategia
auth:sanctumUna empresa tiene exactamente una estrategia RRSS. El PUT es upsert — crea o reemplaza.
Devuelve la estrategia de la empresa activa. data: null si no existe.
curl -s "https://api.growth54.com/api/rrss/estrategia" -H "Authorization: Bearer <token>" | jq
Crea o actualiza la estrategia. Todos los campos son opcionales.
curl -s -X PUT "https://api.growth54.com/api/rrss/estrategia" \
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{
"objetivo": "Generar visibilidad y captar leads",
"audiencia": "Dueños de PYME, 28-45 años, LATAM",
"tono": "Profesional pero cercano",
"frecuencia": "6 posts semanales",
"cta_principal": "Agendar llamada de diagnóstico gratuita",
"embudo": "RRSS → Website → Lead Magnet → CRM",
"plataformas": ["Facebook","Instagram","LinkedIn"],
"pilares": ["Educación","Casos de éxito","Detrás de cámaras"],
"kpis": ["Alcance orgánico","Engagement","Leads generados"]
}' | jq
Ideas
auth:sanctumBanco de ideas de contenido. Pueden crearse manualmente o generarse por el agente Content AI.
Lista ideas. Filtros opcionales: ?status=aprobado · ?platform=Instagram
curl -s "https://api.growth54.com/api/rrss/ideas?status=aprobado" -H "Authorization: Bearer <token>" | jq
Crea una idea manualmente. topic es obligatorio.
curl -s -X POST "https://api.growth54.com/api/rrss/ideas" \
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{
"topic": "5 errores que cometen las PYMEs en Instagram",
"platform": "Instagram",
"keyword": "marketing PYME",
"fecha_propuesta": "2026-05-15"
}' | jq
Cambia el estado. Valores: idea · aprobado · descartado
curl -s -X PUT "https://api.growth54.com/api/rrss/ideas/1/status" \
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{"status": "aprobado"}' | jq
Elimina una idea. Solo puede eliminar ideas de su empresa activa.
Posts
auth:sanctumPosts en todas las etapas del ciclo de vida: borrador → revisión → aprobado → programado → publicado.
Lista posts. Filtros opcionales: ?status=borrador · ?platform=LinkedIn
curl -s "https://api.growth54.com/api/rrss/posts" -H "Authorization: Bearer <token>" | jq
Crea un post manualmente. tema es obligatorio.
New Acepta fecha_programada y status opcionales, para crear el post ya con fecha o directamente programado (lo usa el composer del calendario).
Si se omiten, el comportamiento es el de siempre: borrador sin fecha.
— status admite borrador o programado.
— Con programado la fecha es obligatoria y debe ser futura, o responde 422.
— fecha_programada se interpreta en la zona horaria de la empresa, igual que en /schedule, y se guarda en UTC.
curl -s -X POST "https://api.growth54.com/api/rrss/posts" \
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{
"tema": "5 errores que cometen las PYMEs en Instagram",
"platform": "Instagram",
"copy_instagram": "¿Tu negocio está en Instagram pero no consigue resultados?\n...",
"copy_facebook": null,
"copy_linkedin": null,
"cta": "Escríbenos para una auditoría gratuita",
"hooks": ["¿Tu negocio está en IG pero no consigue resultados?"],
"fecha_programada": "2026-08-05T14:30:00",
"status": "programado"
}' | jq
Edita un post existente. Todos los campos son opcionales; solo se escribe lo que mandes.
New Ahora acepta también fecha_programada y status, con las mismas reglas que POST: así el panel guarda contenido, fecha y estado en una sola llamada.
— Mandar fecha_programada: null borra la fecha; omitir la clave la deja como estaba.
— Campos editables: tema, platform, copy_instagram, copy_facebook, copy_linkedin, hook, cta, imagen_url.
curl -s -X PUT "https://api.growth54.com/api/rrss/posts/1" \
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{
"tema": "5 errores que cometen las PYMEs en Instagram",
"copy_instagram": "Texto corregido a mano...",
"fecha_programada": "2026-08-05T14:30:00",
"status": "programado"
}' | jq
Cambia el estado del post.
— Al aprobar → guarda approved_by_user_id automáticamente.
— Al publicar → guarda fecha_publicada con la fecha actual.
curl -s -X PUT "https://api.growth54.com/api/rrss/posts/1/status" \
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{"status": "aprobado"}' | jq
Programa el post para una fecha futura. Cambia status a programado. La fecha debe ser futura. New La hora se interpreta en la zona horaria de la empresa (companies.timezone): p. ej. "2026-05-20T10:00:00" con empresa en Nicaragua = 10:00 hora de Nicaragua. Internamente se guarda en UTC. Si la empresa no tiene zona configurada, se asume UTC. Verifica que la empresa tenga su zona horaria establecida en Editar datos de la empresa → País / Zona horaria; sin ella, las horas se programarán en UTC.
curl -s -X PUT "https://api.growth54.com/api/rrss/posts/1/schedule" \
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{"fecha_programada": "2026-05-20T10:00:00"}' | jq
Métricas
auth:sanctumConsulta de métricas agrupadas por plataforma con totales y promedios.
Resumen por plataforma. Filtros: ?platform=Instagram · ?from=2026-05-01 · ?to=2026-05-31
curl -s "https://api.growth54.com/api/rrss/metricas?from=2026-05-01&to=2026-05-31" \
-H "Authorization: Bearer <token>" | jq
// Respuesta:
{
"data": [{
"platform": "Instagram",
"alcance_total": 12430,
"impresiones_total": 38000,
"engagement_total": 584,
"engagement_rate_avg": 4.7,
"clics_total": 384,
"leads_total": 17
}]
}
Top 10 posts publicados ordenados por engagement.
curl -s "https://api.growth54.com/api/rrss/metricas/top-posts" -H "Authorization: Bearer <token>" | jq
Triggers — disparar agentes
auth:sanctumEl panel llama estos endpoints y el backend dispara el webhook del agente correspondiente. Si el webhook no está configurado en Panel › Agentes → HTTP 422.
Dispara el agente Strategist AI. Key: rrss_strategist
curl -s -X POST "https://api.growth54.com/api/rrss/run-strategist" -H "Authorization: Bearer <token>" | jq
Dispara el agente Content AI. Key: rrss_content_ai. El campo mode controla si genera ideas o un post específico.
// Generar ideas:
curl -s -X POST "https://api.growth54.com/api/rrss/run-content-ai" \
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{"mode": "ideas", "idea_id": null}' | jq
// Generar post desde una idea aprobada:
curl -s -X POST "https://api.growth54.com/api/rrss/run-content-ai" \
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{"mode": "post", "idea_id": 5}' | jq
La pantalla RRSS › Ideas tiene ahora dos botones separados, y ambos disparan el mismo webhook (rrss_content_ai). Lo único que cambia es el campo mode. El agente de n8n debe ramificar según mode (un nodo Switch al inicio):
- Botón "Generar Ideas" → envía
mode: "ideas"→ el agente genera ideas nuevas (sin correr el Strategist completo) y las guarda conPOST /api/agent/rrss/ideas. Ver Agente 2A. - Botón "Generar Post" → envía
mode: "post"(opcionalmenteidea_id) → el agente genera el post/borrador y lo guarda conPOST /api/agent/rrss/posts. Ver Agente 2B.
Payload exacto que recibe tu webhook (lo arma el backend en RrssAgentTriggerController):
{
"company_id": 24,
"trigger": { "source": "ui", "user_id": 1 },
"mode": "ideas", // "ideas" (Generar Ideas) o "post" (Generar Post)
"idea_id": null // sólo relevante en mode "post": la idea aprobada a desarrollar
}
Importante: si tu agente ignora mode y siempre genera posts, el botón "Generar Ideas" saldrá mal (era el bug anterior). El branch por mode es obligatorio.
Dispara el agente Analytics AI. Key: rrss_analytics. No requiere body.
curl -s -X POST "https://api.growth54.com/api/rrss/run-analytics" -H "Authorization: Bearer <token>" | jq
Tokens de Acceso Social
auth:sanctumCredenciales OAuth por plataforma (Facebook, Instagram, LinkedIn). El agente Publisher las usa para publicar. El token de acceso se almacena encriptado.
Lista los tokens configurados para la empresa activa. El campo access_token no se devuelve por seguridad.
curl -s "https://api.growth54.com/api/rrss/tokens" -H "Authorization: Bearer <token>" | jq
Crea o actualiza el token de una plataforma (upsert por platform). Si access_token se omite en una actualización, el token existente no cambia.
curl -s -X PUT "https://api.growth54.com/api/rrss/tokens" \
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{
"platform": "Instagram",
"access_token": "EAAxxxx...",
"page_id": "123456789",
"instagram_account_id": "17841400000000000",
"label": "System User — Cliente XYZ",
"expires_at": "2027-01-01"
}' | jq
Elimina un token. Solo puede eliminar tokens de su empresa activa.
Conectar Facebook + Instagram — OAuth
2026-06-07
El flujo OAuth conecta la cuenta de Facebook de la empresa con Growth54. Después de conectar, el token queda encriptado en company_social_tokens y el sistema puede publicar en nombre de la empresa.
https://apigrowth.mdarthurdigital.com/api/oauth/meta/callback.
El panel llama este endpoint con el company_id. Laravel construye la URL de autorización de Meta y redirige al usuario al login de Facebook.
El estado OAuth incluye el company_id encriptado en Base64 para recuperarlo en el callback.
// El frontend abre esta URL en el mismo tab o popup:
window.location.href = "https://api.growth54.com/api/oauth/meta/redirect?company_id=3"
// Parámetros enviados a Meta:
scope = pages_manage_posts, pages_read_engagement,
instagram_basic, instagram_content_publish
response_type = code
state = base64({ company_id: 3, csrf: "aleatorio" })
Meta redirige aquí con el code de autorización. Laravel intercambia el code por un token de corta duración, lo convierte a token de 60 días, obtiene las páginas de Facebook y la cuenta de Instagram vinculada, y guarda todo en company_social_tokens.
Al terminar redirige al panel.
| Qué guarda | Dónde |
|---|---|
| Token de página de Facebook (60 días) | company_social_tokens platform=facebook |
| Token de Instagram Business (si está vinculado) | company_social_tokens platform=instagram |
| Page ID, Instagram Account ID | misma tabla, campos page_id y instagram_account_id |
?error=... y redirige al panel con ?oauth=error. El panel debe mostrar un toast de error.
Elimina los tokens de Facebook e Instagram de la empresa. El botón "Desconectar" del panel llama este endpoint.
curl -s -X POST "https://api.growth54.com/api/oauth/meta/disconnect" \
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{"company_id": 3}' | jq
// Respuesta:
{ "ok": true, "deleted": 2 }
META_APP_ID= ← App ID de Meta for Developers
META_APP_SECRET= ← App Secret de Meta for Developers
META_REDIRECT_URI= ← debe coincidir EXACTAMENTE con la URL registrada en Meta
# Local (con ngrok):
META_REDIRECT_URI=https://TU-NGROK.ngrok-free.app/api/oauth/meta/callback
# Producción:
META_REDIRECT_URI=https://apigrowth.mdarthurdigital.com/api/oauth/meta/callback
FRONTEND_URL= ← a donde redirige el callback (ej: http://localhost:5173)
SocialAccountsPage.jsx — tarjeta por plataforma con el estado de conexiónwindow.location.href = /api/oauth/meta/redirect?company_id=X?oauth=success → mostrar toast verde "¡Facebook e Instagram conectados!"?oauth=error → mostrar toast rojo con el mensajePOST /api/oauth/meta/disconnect → recarga estadopages_manage_posts e instagram_content_publish a Meta (proceso de revisión de 1–3 semanas con screencast y Privacy Policy).
Conectar LinkedIn — OAuth
pendiente de implementarLinkedIn permite publicar en páginas de empresa. El token dura 60 días y el refresh token 1 año, lo que hace la reconexión prácticamente automática. A diferencia de Meta, LinkedIn tiene un proceso de aprobación más simple — los permisos básicos se aprueban solos.
company_social_tokens con platform=linkedin). Falta crear la app en LinkedIn Developer Portal y construir el LinkedInOAuthService.
Solo se hace una vez. Growth54 tendrá UNA app y todos los clientes se conectan a través de ella.
App name: Growth54
LinkedIn Page: [seleccionar la página de empresa de Growth54]
App Logo: subir logo de Growth54
Privacy policy URL: https://growth54.com/privacidad
# Producción:
https://apigrowth.mdarthurdigital.com/api/oauth/linkedin/callback
# Local (con ngrok, agregar temporalmente para pruebas):
https://TU-NGROK.ngrok-free.app/api/oauth/linkedin/callback
.env:
LINKEDIN_CLIENT_ID=86xxxxxxxxxxxxxxxx
LINKEDIN_CLIENT_SECRET=xxxxxxxxxxxxxxxx
LINKEDIN_REDIRECT_URI=https://apigrowth.mdarthurdigital.com/api/oauth/linkedin/callback
En la pestaña "Products" de la app, solicitar estos tres productos en orden:
| Producto | Para qué sirve | Aprobación |
|---|---|---|
| Sign In with LinkedIn using OpenID Connect | Obtener perfil básico del usuario (nombre, email). Base del OAuth. | Automática ✓ |
| Share on LinkedIn | Publicar posts en el perfil personal del usuario. | Automática ✓ |
| Marketing Developer Platform | Publicar en páginas de empresa (w_organization_social). El más importante para Growth54. |
Requiere solicitud — LinkedIn la aprueba en 1–7 días |
w_member_social (publicar en perfil personal). Sirve para probar el flujo OAuth completo sin esperar.
| Scope | Qué permite | Requiere |
|---|---|---|
openid | Identificar al usuario | Sign In with LinkedIn |
profile | Nombre, foto del usuario | Sign In with LinkedIn |
email | Email del usuario | Sign In with LinkedIn |
w_member_social | Publicar en perfil personal | Share on LinkedIn |
w_organization_social | Publicar en página de empresa | Marketing Developer Platform |
r_organization_social | Leer métricas de página de empresa | Marketing Developer Platform |
| Meta (Facebook/Instagram) | ||
|---|---|---|
| Duración del token | 60 días (no renovable automático) | 60 días + refresh token de 1 año |
| Renovar token | El usuario debe reconectar | Se renueva automático con el refresh token |
| Publicar en empresa | Token de página (independiente) | Scope w_organization_social + URN de la organización |
| App Review | Proceso largo (1–3 semanas) | Marketing Developer Platform (1–7 días) |
| Límite de posts | Sin límite específico | 150 posts por día por organización |
company_social_tokens ya soporta LinkedIn (platform=linkedin). Solo falta el service y las rutas.
app/Services/LinkedIn/LinkedInOAuthService.php con:
getAuthorizationUrl(company_id)— URL de login LinkedIn con statehandleCallback(code, state)— intercambia code → token → guarda refresh token → obtiene URN de organizaciónrefreshToken(company_id)— usa el refresh token para renovar antes de que expire
OAuthController.php:
linkedinRedirect()linkedinCallback()linkedinDisconnect()
routes/api.php:
GET /api/oauth/linkedin/redirect (auth:sanctum)
GET /api/oauth/linkedin/callback (público)
POST /api/oauth/linkedin/disconnect (auth:sanctum)
company_social_tokens:
platform: 'linkedin'
access_token: (encriptado)
page_id: URN de la organización → "urn:li:organization:XXXXXXX"
label: nombre de la página de empresa
expires_at: now + 60 días
El refresh token se guarda por separado (columna a agregar: refresh_token).
// Endpoint de LinkedIn para publicar en página de empresa:
POST https://api.linkedin.com/v2/ugcPosts
// Body:
{
"author": "urn:li:organization:XXXXXXX", ← el page_id guardado en company_social_tokens
"lifecycleState": "PUBLISHED",
"specificContent": {
"com.linkedin.ugc.ShareContent": {
"shareCommentary": { "text": "el copy generado por el agente" },
"shareMediaCategory": "NONE"
}
},
"visibility": { "com.linkedin.ugc.MemberNetworkVisibility": "PUBLIC" }
}
// Header:
Authorization: Bearer <access_token de company_social_tokens>
POST /v2/assets?action=registerUpload y luego incluir el asset URN en el shareMediaCategory: "IMAGE". El agente Publisher lo maneja.
| # | Tarea | Dónde | Estado |
|---|---|---|---|
| 1 | Crear app en developer.linkedin.com | developer.linkedin.com | ⬜ Pendiente |
| 2 | Solicitar "Sign In with LinkedIn" y "Share on LinkedIn" | Tab Products de la app | ⬜ Pendiente |
| 3 | Solicitar "Marketing Developer Platform" | Tab Products de la app | ⬜ Pendiente (1–7 días) |
| 4 | Agregar Redirect URI de producción | Tab Auth de la app | ⬜ Pendiente |
| 5 | Llenar LINKEDIN_CLIENT_ID y LINKEDIN_CLIENT_SECRET en .env | .env backend | ⬜ Pendiente |
| 6 | Crear LinkedInOAuthService + métodos en OAuthController | Backend Laravel | ✅ Implementado 2026-06-07 |
| 7 | Agregar rutas /api/oauth/linkedin/* | routes/api.php | ✅ Implementado 2026-06-07 |
| 8 | Botón "Conectar LinkedIn" en CanalesPage.jsx | Frontend | ⬜ Pendiente |
| 9 | Agregar columna refresh_token en company_social_tokens | Nueva migración | ✅ Migración aplicada 2026-06-07 |
Endpoints n8n — lectura
middleware: n8nSolo lectura para los workflows de n8n. Header requerido: Authorization: Bearer <N8N_TOKEN>
Base: /api/n8n/companies/{id}/rrss/
Canales activos con handle, objetivo, frecuencia, formatos, rol en el embudo y la configuración de imagen que eligió el cliente para esa red (image_config).
{ "data": [{
"id": 12, "platform": "Facebook", "handle": "...", "objective": "...",
"content_formats": ["post","carrusel"],
"image_config": {
"img_estilo": "realista", // realista | humanos | 3d | animado | minimalista | artistico
"img_formato": "horizontal", // cuadrada (1:1) | vertical (4:5) | horizontal (16:9)
"img_texto_modo": "titulo_subtitulo" // sin_texto | solo_titulo | titulo_subtitulo | titulo_sub_detalles | libre
}
}]}
- Lee
image_configde cada plataforma por separado — no es igual para Facebook que para Instagram. Cada red trae su propia config. - img_formato define la proporción:
cuadrada= 1024×1024 (feed Instagram) ·vertical= 1024×1536 (stories/reels) ·horizontal= 1536×1024 (Facebook / LinkedIn / banner). - img_estilo = el look de la imagen (fotográfico, animado, 3D, etc.).
- img_texto_modo = si la imagen lleva título/subtítulo encima. Si lleva texto, debe ir en el mismo idioma del post y bien escrito.
- Nunca inventar logos ni marcas/logotipos de terceros.
- Si
image_configvienenull, usar por defecto: Facebook/LinkedIn → horizontal, Instagram → cuadrada, estilo realista, sin texto.
Estrategia vigente. Devuelve null si no existe.
Ideas con status aprobado, listas para convertirse en posts.
Posts con status aprobado o programado.
Posts programado cuya fecha_programada ya pasó. Cada post incluye ya el token de Meta de la empresa (page_id, instagram_account_id, access_token) — el Distribution AI publica con eso, no hay que pedirlo aparte.
New Aquí también aparecen los posts creados a mano desde el calendario del panel, no solo los del Content AI. Salen con la misma forma (status: programado, fecha en UTC, platform, copy de esa red e imagen_url), así que el Distribution AI no necesita ningún cambio: los recoge y publica igual.
{ "data": [{
"id": 123, "platform": "instagram", "copy_instagram": "...", "imagen_url": "...",
"page_id": "1713965015486703", "instagram_account_id": "17841...", "access_token": "EAAxxxxx..."
}]}
Resuelve la empresa por page_id de Meta (Community AI). Cuando Meta envía un webhook trae el page_id pero no el company_id; esto lo traduce. Devuelve 404 si el page_id no está registrado (el agente NO atiende — sin fallback a una empresa fija). Funciona para cualquier empresa con su página conectada.
{ "company_id": 24, "fb_access_token": "EAAxxxxx...", "ig_user_id": "17841402206774619" }
Endpoints agent — escritura
middleware: agentPara que los agentes guarden datos generados. Header: Authorization: Bearer <AGENT_TOKEN>
Los registros creados llevan generated_by = 'n8n' automáticamente.
Guarda o actualiza la estrategia. Incluir company_id.
{ "company_id": 3, "objetivo": "...", "audiencia": "...", "tono": "...",
"frecuencia": "...", "cta_principal": "...", "embudo": "...",
"plataformas": ["Instagram","LinkedIn"],
"pilares": ["Educación","Casos de éxito"],
"kpis": ["Alcance","Engagement"] }
Guarda ideas en bulk. Respuesta: {"ok": true, "created": N}
{ "company_id": 3, "ideas": [
{ "topic": "5 errores que cometen las PYMEs", "platform": "Instagram", "keyword": "marketing PYME" },
{ "topic": "Cómo automatizar con IA", "platform": "LinkedIn" }
]}
Guarda posts como borradores en bulk. La imagen generada va en imagen_url (URL pública), y opcionalmente el prompt usado en imagen_prompt.
{ "company_id": 3, "posts": [{
"tema": "5 errores que cometen las PYMEs",
"platform": "Instagram", "social_idea_id": 5,
"copy_instagram": "¿Tu negocio está en Instagram pero no consigue resultados?\n...",
"copy_facebook": "Si tu página de Facebook publica pero no genera conversaciones...",
"copy_linkedin": "En 2026, producir contenido sin estrategia es el error más común...",
"hooks": ["¿Tu negocio está en Instagram pero no consigue resultados?"],
"cta": "Comenta o envíame un DM para una auditoría gratuita.",
"imagen_url": "https://.../imagen-optimizada.jpg",
"imagen_prompt": "Minimalist flat design 5 warning signs PYME"
}]}
- Genera la imagen según el
image_configde la plataforma del post (ver GET .../rrss/canales arriba): estilo, formato y texto que eligió el cliente. - Guarda su URL pública en
imagen_url(el Distribution AI la usa para publicar en Meta; Instagram exige imagen). - Peso ligero: preferimos imágenes livianas (JPEG comprimido). Nuestro backend ofrece
POST /api/agent/optimize-image(recibe PNG base64, devuelve JPEG comprimido) si quieres optimizar antes de hostearla.
Marca un post como publicado. Guarda fecha_publicada con la fecha actual. Usar por el agente Publisher tras publicar en la red social.
curl -s -X POST "https://api.growth54.com/api/agent/rrss/posts/7/mark-published" \
-H "Authorization: Bearer <AGENT_TOKEN>" | jq
Elimina un post. Útil para limpiar borradores duplicados generados en pruebas. Respuesta: {"ok": true}
curl -s -X DELETE "https://api.growth54.com/api/agent/rrss/posts/7" \
-H "Authorization: Bearer <AGENT_TOKEN>" | jq
Guarda métricas. social_post_id es opcional.
{ "company_id": 3, "metricas": [{
"platform": "Instagram", "period_start": "2026-05-01", "period_end": "2026-05-31",
"social_post_id": 12, "alcance": 3420, "impresiones": 10500,
"engagement_count": 212, "engagement_rate": 6.2, "clics": 89, "leads": 4
}]}
WhatsApp Engine multi-empresa. Resuelve la empresa por el phone_number_id que manda Meta y devuelve toda la config del bot (token, prompt, catálogo, FAQs, mensajes, palabras de lead, correos del vendedor). 404 si el número no está registrado → el agente NO atiende (sin fallback). La config vive en wa_configs (1 fila por empresa); no existe ni se necesita wa_empresas. Las conversaciones se guardan luego con POST /api/agent/whatsapp/inbound.
{ "company_id": 20, "nombre": "Cratingexpress",
"phone_number_id": "1083260611538246", "wa_access_token": "EAAxxxxx...",
"system_prompt": "...", "catalogo": "...", "faqs": "...",
"msg_bienvenida": "...", "msg_followup_24h": "...", "msg_followup_72h": "...",
"palabras_lead_caliente": ["cotizar","precio"], "emails_vendedor": ["ventas@empresa.com"] }
Agentes n8n — Configuración inicial
Todos los workflows comienzan con un nodo Set llamado Config que define las variables globales.
BASE_URL = https://api.growth54.com ← producción
https://xxx.ngrok-free.app ← local con ngrok
AGENT_TOKEN = (AGENT_API_TOKEN del .env del backend)
N8N_TOKEN = (N8N_API_TOKEN del .env del backend)
COMPANY_ID = {{ $json.company_id }} ← viene del trigger del panel
Lectura →
Authorization: Bearer <N8N_TOKEN>Escritura →
Authorization: Bearer <AGENT_TOKEN>
| Agente | Key | Configurar en |
|---|---|---|
| Strategist AI | rrss_strategist | Panel › Configuración › Agentes |
| Content AI | rrss_content_ai | Panel › Configuración › Agentes |
| Analytics AI | rrss_analytics | Panel › Configuración › Agentes |
| Distribution AI | rrss_distribution_ai | Panel › Configuración › Agentes |
| Community AI | rrss_community_ai | Panel › Configuración › Agentes |
| Sales AI | rrss_sales_ai | Panel › Configuración › Agentes |
| Prospección AI New | lead_prospecting | Panel › Configuración › Agentes |
| Nurture & Outreach AI New | blog_distribution | Panel › Configuración › Agentes |
Agente 1 — Strategist AI
rrss_strategistLee el perfil y canales de la empresa, genera una estrategia RRSS completa y la guarda. El equipo puede editarla desde el panel.
company_id del panelGET /api/n8n/companies/{id} — nombre, industria, país, descripciónGET /api/n8n/companies/{id}/rrss/canales — canales activos con handles y objetivosGET /api/n8n/companies/{id}/rrss/estrategia — estrategia actual (puede ser null)POST /api/agent/rrss/estrategia — guarda resultado, generated_by queda en n8nEres un estratega de redes sociales experto en marketing para PYMEs latinoamericanas.
Empresa: {nombre} — {industria} — {país}
Descripción: {descripción}
Canales activos: {plataforma, handle, objetivo, frecuencia, funnel_role}
Estrategia actual: {estrategia existente o "No hay estrategia aún"}
Genera una estrategia RRSS completa y accionable.
Responde SOLO en JSON:
{
"objetivo": "objetivo principal de RRSS",
"audiencia": "descripción del buyer persona",
"tono": "tono de comunicación recomendado",
"frecuencia": "frecuencia de publicación",
"cta_principal": "llamada a la acción principal",
"embudo": "ruta desde RRSS hasta conversión",
"plataformas": ["plataforma1", "plataforma2"],
"pilares": ["pilar 1", "pilar 2", "pilar 3"],
"kpis": ["KPI 1", "KPI 2", "KPI 3"]
}
Agente 2A — Content AI, modo Ideas
mode: ideasDisparado con "mode": "ideas". Genera 10 ideas basadas en la estrategia y los canales. Llegan con status idea — el equipo las aprueba o descarta.
company_id y mode = "ideas"GET /api/n8n/companies/{id} — perfil empresaGET /api/n8n/companies/{id}/rrss/canales — canales activosGET /api/n8n/companies/{id}/rrss/estrategia — pilares, audiencia, tonoPOST /api/agent/rrss/ideas — guarda en bulk → {"ok": true, "created": 10}Eres un experto en contenido para redes sociales de PYMEs.
Empresa: {nombre} — {industria} — {país}
Objetivo: {objetivo} | Audiencia: {audiencia} | Tono: {tono}
Pilares: {pilares} | CTA: {cta_principal}
Canales: {plataformas con handle y frecuencia}
Genera 10 ideas variadas distribuidas entre canales activos.
Cubre todos los pilares. Sé específico. Fechas: próximos 30 días.
Responde SOLO en JSON:
{
"ideas": [
{
"topic": "título claro del contenido",
"platform": "Instagram | Facebook | LinkedIn",
"keyword": "palabra clave central",
"fecha_propuesta": "YYYY-MM-DD"
}
]
}
Agente 2B — Content AI, modo Post
mode: postDisparado con "mode": "post", "idea_id": N. Toma una idea aprobada y genera el post completo: copy por plataforma, hooks y CTA. Llega como borrador para revisión del equipo.
company_id, mode = "post", idea_idGET /api/n8n/companies/{id} — perfil empresaGET /api/n8n/companies/{id}/rrss/estrategia — tono y CTAGET /api/n8n/companies/{id}/rrss/ideas — buscar la idea por idea_idPOST /api/agent/rrss/posts — guarda como borrador con social_idea_id enlazadoEres un copywriter experto en RRSS para PYMEs de LATAM.
Empresa: {nombre} — {industria}
Tono: {tono} | CTA: {cta_principal}
Idea a desarrollar:
- Tema: {topic} | Plataforma: {platform} | Keyword: {keyword}
Genera copy adaptado para cada plataforma. Si no aplica, devuelve null.
Responde SOLO en JSON:
{
"copy_instagram": "texto con emojis, saltos de línea y hashtags al final",
"copy_facebook": "texto conversacional más extenso, puede incluir pregunta",
"copy_linkedin": "tono profesional, párrafos cortos, dato de valor",
"hooks": [
"primera línea alternativa 1",
"primera línea alternativa 2"
],
"cta": "llamada a la acción específica para este post",
"imagen_prompt": "descripción en inglés para DALL-E o Midjourney"
}
Agente 3 — Analytics AI
rrss_analyticsLee métricas reales de las plataformas y las carga al backend. La fuente de datos depende de lo que cada empresa tenga disponible.
company_idGET /api/n8n/companies/{id}/rrss/canales — canales activos (handles y plataformas)GET /api/n8n/companies/{id}/rrss/posts — posts publicados (para linkear métricas)POST /api/agent/rrss/metricas — guardar métricas del períodoEl cliente llena una hoja con métricas del mes. n8n lee la sheet y llama
/api/agent/rrss/metricas. Sin OAuth por empresa.Meta Graph API para Instagram/Facebook, LinkedIn Marketing API. Cada empresa necesita tokens OAuth. Más preciso, más configuración inicial.
El cliente exporta CSV de cada plataforma. n8n lo procesa con un nodo Code y envía al backend.
Agente 4 — Prospección AI New · 19-sep
lead_prospectingSale a buscar negocios nuevos por rubro y ciudad y los agrega al CRM como leads reales — generación de leads pasiva, el cliente no hace nada, los leads le aparecen solos en el tablero. Multiempresa desde el diseño: el rubro y la ciudad salen del perfil real de cada empresa, no de una lista fija.
GET /api/n8n/companies?activas=1industry/base_city/geography ya incluidos en esa misma respuesta como criterio de búsqueda — no hace falta pedirlos aparte/contact → scraping de /about)POST /api/agent/crm/leads con origen:"prospeccion" — no a una hoja de cálculo externa. Ese origen es justamente el que distingue "salimos a buscarlo" de agente (llegó solo)origen:"manual" como parche. Antes de que prospeccion
estuviera disponible en el ENUM, algunos leads se guardaban así con la fuente real escondida en
notas — ya no hace falta, el valor real está soportado y probado
(tests/Feature/Api/Integrations/AgentCrmOrigenTest.php).
POST /api/rrss/run-prospeccion (sin parámetros) llama
al webhook registrado en la key lead_prospecting, igual que ya hacían Strategist/Content/
Analytics AI. El botón "Ejecutar" ya está habilitado en el panel (paso 7 del Dashboard de Agentes).
Agente 5 — Nurture & Outreach AI New · 19-sep
blog_distributionDos funciones separadas, activables por el cliente de forma independiente (por el tema legal pendiente del correo en frío a listas raspadas): nutrir leads calificados (cada artículo nuevo del blog arma un correo para cada lead calificado del CRM que no lo haya recibido todavía) y contactar leads en frío (primer correo de presentación a los leads que trajo Prospección AI, nunca contactados). Ninguna manda nada sola — todo queda como borrador esperando aprobación humana.
GET /api/n8n/companies?activas=1&con_cms=wordpress (modo calificado) o simplemente las empresas activas (modo frío)GET /api/n8n/companies/{id}/rrss/posts o el WordPress real de la empresa para el último artículo publicadoPOST /api/agent/email/drafts — arma el borrador, no lo manda. Ver /docs/api — Correo, bandeja de borradores para el contrato completo, los campos y las respuestasPOST /api/agent/email/send:
cambiar ese nodo por POST /api/agent/email/drafts. El resto (leer empresa, CMS, board
del CRM) queda igual. El envío real solo ocurre cuando un humano aprueba desde el panel.
POST /api/rrss/run-nurture requiere modo: "blog" (nutrir
calificados) | "outreach" (contactar en frío). El botón "Ejecutar" del paso 8 del
Dashboard de Agentes dispara siempre con modo:"blog" — para el modo "outreach"
no hay botón propio todavía en el panel, solo la ruta.
Orden de construcción recomendado
| # | Agente | Por qué primero |
|---|---|---|
| 1 | Content AI — Ideas | Valor inmediato. El cliente ve ideas de contenido en segundos. |
| 2 | Content AI — Post | Amplía el anterior con la misma key. Posts listos para revisar. |
| 3 | Strategist AI | Más elaborado. Requiere canales bien configurados primero. |
| 4 | Analytics AI (Sheets) | Mínimo viable sin OAuth. El cliente llena una hoja. |
| 5 | Analytics AI (APIs) | Iteración avanzada. Automatización total de métricas. |
- No enviar
company_iden el body de escritura → 422 - Enviar
statuscon valor fuera del enum → 422 - Enviar
idea_idque no pertenece a la empresa → 422 fecha_programadaen el pasado → rechazada por /schedule- Omitir el header
Authorization→ 401