Pular para conteúdo

Tutorial: Sistema de Distribuição de Conteúdo Multicanal

Automatize a publicação e distribuição de conteúdo em múltiplas plataformas (LinkedIn, Twitter, Facebook, Instagram, Medium, WordPress) a partir de uma fonte única.

O Que Você Vai Construir

Um sistema que: 1. Recebe conteúdo de fonte única (Notion, Airtable, Google Sheets) 2. Adapta formato para cada plataforma 3. Agenda publicações otimizadas por horário 4. Publica automaticamente em múltiplos canais 5. Rastreia performance cross-platform 6. Gera relatórios de engajamento

Tempo estimado: 45 minutos Nível: Intermediário Impacto: 80% menos tempo em distribuição manual

Pré-requisitos

  • ✅ Contas nas redes sociais (LinkedIn, Twitter, etc)
  • ✅ Notion ou Airtable como fonte
  • ✅ Buffer, Hootsuite ou APIs diretas
  • ✅ PostgreSQL

Parte 1: Estrutura de Dados

CREATE TABLE content_queue (
  id SERIAL PRIMARY KEY,
  source_id VARCHAR(255),
  title VARCHAR(500),
  content TEXT,
  content_type VARCHAR(50), -- article, image, video
  tags TEXT[],
  target_platforms TEXT[],
  scheduled_for TIMESTAMP,
  status VARCHAR(50) DEFAULT 'pending',
  created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE content_posts (
  id SERIAL PRIMARY KEY,
  content_id INT REFERENCES content_queue(id),
  platform VARCHAR(50),
  platform_post_id VARCHAR(255),
  content TEXT,
  image_url VARCHAR(500),
  published_at TIMESTAMP,
  metrics JSONB,
  created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE platform_metrics (
  id SERIAL PRIMARY KEY,
  post_id INT REFERENCES content_posts(id),
  platform VARCHAR(50),
  likes INT DEFAULT 0,
  comments INT DEFAULT 0,
  shares INT DEFAULT 0,
  views INT DEFAULT 0,
  clicks INT DEFAULT 0,
  updated_at TIMESTAMP DEFAULT NOW()
);

Parte 2: Captura de Conteúdo

2.1. Webhook do Notion

// Webhook Trigger: Novo item no Notion
// Transform: "Extrair Conteúdo"
{{
  {
    "sourceId": $trigger.body.id,
    "title": $trigger.body.properties.Title?.title[0]?.plain_text || '',
    "content": $trigger.body.properties.Content?.rich_text[0]?.plain_text || '',
    "platforms": $trigger.body.properties.Platforms?.multi_select.map(p => p.name) || [],
    "scheduledFor": $trigger.body.properties.PublishDate?.date?.start || new Date().toISOString(),
    "tags": $trigger.body.properties.Tags?.multi_select.map(t => t.name) || [],
    "imageUrl": $trigger.body.properties.Image?.files[0]?.file?.url || ''
  }
}}

2.2. Salvar na Fila

// Database Insert: "Adicionar à Fila"
{
  "table": "content_queue",
  "data": {
    "source_id": "{{ $nodes['extrair'].output.sourceId }}",
    "title": "{{ $nodes['extrair'].output.title }}",
    "content": "{{ $nodes['extrair'].output.content }}",
    "target_platforms": "{{ JSON.stringify($nodes['extrair'].output.platforms) }}",
    "scheduled_for": "{{ $nodes['extrair'].output.scheduledFor }}",
    "tags": "{{ JSON.stringify($nodes['extrair'].output.tags) }}",
    "status": "pending"
  }
}

Parte 3: Adaptação de Conteúdo

3.1. Personalizar por Plataforma

// Function Node: "Adaptar Conteúdo"
{{
  (() => {
    const { title, content, tags, imageUrl } = $item;
    const platforms = JSON.parse($item.target_platforms);

    const adaptations = {};

    platforms.forEach(platform => {
      switch(platform) {
        case 'twitter':
          // Twitter: 280 chars
          adaptations.twitter = {
            text: content.substring(0, 250) + (content.length > 250 ? '... 🧵' : ''),
            hashtags: tags.slice(0, 2).map(t => `#${t.replace(/\s/g, '')}`).join(' ')
          };
          break;

        case 'linkedin':
          // LinkedIn: Mais profissional
          adaptations.linkedin = {
            text: `${title}\n\n${content}\n\n${tags.map(t => `#${t.replace(/\s/g, '')}`).join(' ')}`,
            visibility: 'PUBLIC'
          };
          break;

        case 'facebook':
          // Facebook: Mais casual
          adaptations.facebook = {
            message: `${title}\n\n${content}`,
            link: imageUrl
          };
          break;

        case 'instagram':
          // Instagram: Foco em imagem, caption curta
          adaptations.instagram = {
            caption: `${title}\n\n${content.substring(0, 200)}...\n\n${tags.slice(0, 5).map(t => `#${t.replace(/\s/g, '')}`).join(' ')}`,
            image_url: imageUrl
          };
          break;

        case 'medium':
          // Medium: Artigo completo
          adaptations.medium = {
            title: title,
            content: content,
            tags: tags.slice(0, 5),
            publishStatus: 'draft'
          };
          break;
      }
    });

    return adaptations;
  })()
}}

Parte 4: Publicação

4.1. LinkedIn

// HTTP Request: "Publicar no LinkedIn"
{
  "method": "POST",
  "url": "https://api.linkedin.com/v2/ugcPosts",
  "headers": {
    "Authorization": "Bearer {{ $credentials.linkedin.accessToken }}",
    "Content-Type": "application/json"
  },
  "body": {
    "author": "urn:li:person:{{ $credentials.linkedin.personId }}",
    "lifecycleState": "PUBLISHED",
    "specificContent": {
      "com.linkedin.ugc.ShareContent": {
        "shareCommentary": {
          "text": "{{ $nodes['adaptar'].output.linkedin.text }}"
        },
        "shareMediaCategory": "NONE"
      }
    },
    "visibility": {
      "com.linkedin.ugc.MemberNetworkVisibility": "PUBLIC"
    }
  }
}

4.2. Twitter

// HTTP Request: "Publicar no Twitter"
{
  "method": "POST",
  "url": "https://api.twitter.com/2/tweets",
  "headers": {
    "Authorization": "Bearer {{ $credentials.twitter.bearerToken }}",
    "Content-Type": "application/json"
  },
  "body": {
    "text": "{{ $nodes['adaptar'].output.twitter.text }} {{ $nodes['adaptar'].output.twitter.hashtags }}"
  }
}

4.3. Medium

// HTTP Request: "Publicar no Medium"
{
  "method": "POST",
  "url": "https://api.medium.com/v1/users/{{ $credentials.medium.userId }}/posts",
  "headers": {
    "Authorization": "Bearer {{ $credentials.medium.accessToken }}",
    "Content-Type": "application/json"
  },
  "body": {
    "title": "{{ $nodes['adaptar'].output.medium.title }}",
    "contentFormat": "markdown",
    "content": "{{ $nodes['adaptar'].output.medium.content }}",
    "tags": "{{ $nodes['adaptar'].output.medium.tags }}",
    "publishStatus": "public"
  }
}

4.4. Registrar Publicações

// Database Insert (Multiple): "Salvar Posts"
{
  "table": "content_posts",
  "data": [
    {
      "content_id": "{{ $item.id }}",
      "platform": "linkedin",
      "platform_post_id": "{{ $nodes['pub-linkedin'].output.id }}",
      "content": "{{ $nodes['adaptar'].output.linkedin.text }}",
      "published_at": "{{ new Date().toISOString() }}"
    },
    {
      "content_id": "{{ $item.id }}",
      "platform": "twitter",
      "platform_post_id": "{{ $nodes['pub-twitter'].output.data.id }}",
      "content": "{{ $nodes['adaptar'].output.twitter.text }}",
      "published_at": "{{ new Date().toISOString() }}"
    }
    // ... outros
  ]
}

Parte 5: Coleta de Métricas

5.1. Flow Agendado (a cada 6 horas)

// Database Query: "Posts para Atualizar Métricas"
SELECT id, platform, platform_post_id
FROM content_posts
WHERE published_at >= NOW() - INTERVAL '7 days'
  AND published_at <= NOW()
ORDER BY published_at DESC;

5.2. LinkedIn Metrics

// HTTP Request: "Métricas LinkedIn"
{
  "method": "GET",
  "url": "https://api.linkedin.com/v2/socialActions/{{ $item.platform_post_id }}",
  "headers": {
    "Authorization": "Bearer {{ $credentials.linkedin.accessToken }}"
  }
}

5.3. Salvar Métricas

// Database Insert: "Atualizar Métricas"
{
  "table": "platform_metrics",
  "data": {
    "post_id": "{{ $item.id }}",
    "platform": "{{ $item.platform }}",
    "likes": "{{ $nodes['metricas-linkedin'].output.likesSummary?.totalLikes || 0 }}",
    "comments": "{{ $nodes['metricas-linkedin'].output.commentsSummary?.totalComments || 0 }}",
    "shares": "{{ $nodes['metricas-linkedin'].output.sharesSummary?.totalShares || 0 }}",
    "updated_at": "{{ new Date().toISOString() }}"
  },
  "onConflict": {
    "target": ["post_id", "platform"],
    "action": "update"
  }
}

Parte 6: Relatórios

-- Performance por plataforma
SELECT
  p.platform,
  COUNT(*) as posts,
  AVG(m.likes) as avg_likes,
  AVG(m.comments) as avg_comments,
  AVG(m.shares) as avg_shares,
  SUM(m.likes + m.comments + m.shares) as total_engagement
FROM content_posts p
LEFT JOIN platform_metrics m ON m.post_id = p.id
WHERE p.published_at >= NOW() - INTERVAL '30 days'
GROUP BY p.platform
ORDER BY total_engagement DESC;

-- Melhores horários
SELECT
  EXTRACT(HOUR FROM p.published_at) as hour,
  EXTRACT(DOW FROM p.published_at) as day_of_week,
  COUNT(*) as posts,
  AVG(m.likes + m.comments + m.shares) as avg_engagement
FROM content_posts p
JOIN platform_metrics m ON m.post_id = p.id
GROUP BY hour, day_of_week
ORDER BY avg_engagement DESC
LIMIT 10;

Otimizações

Melhor Horário para Cada Plataforma

// Function Node: "Otimizar Horário"
{{
  const platform = $item.platform;
  const now = new Date();

  const optimalHours = {
    'linkedin': { days: [1,2,3,4,5], hours: [8,9,17,18] }, // Seg-Sex, manhã e fim de tarde
    'twitter': { days: [0,1,2,3,4,5,6], hours: [12,13,18,19] }, // Todos os dias, almoço e noite
    'facebook': { days: [0,1,2,3,4,5,6], hours: [13,14,19,20,21] }, // Tarde e noite
    'instagram': { days: [0,1,2,3,4,5,6], hours: [8,9,17,18,19,20] } // Manhã e noite
  };

  const optimal = optimalHours[platform] || optimalHours['twitter'];

  // Encontrar próximo horário ideal
  let scheduledDate = new Date(now);
  while (
    !optimal.days.includes(scheduledDate.getDay()) ||
    !optimal.hours.includes(scheduledDate.getHours())
  ) {
    scheduledDate.setHours(scheduledDate.getHours() + 1);
  }

  return {
    scheduledFor: scheduledDate.toISOString()
  };
}}

Teste Completo

# Criar conteúdo teste no Notion
# Ou usar API diretamente:
curl -X POST https://api.lumina.app.br/v1/webhooks/wh_content \
  -H "Content-Type: application/json" \
  -d '{
    "id": "test123",
    "properties": {
      "Title": {"title": [{"plain_text": "Como Automatizar Marketing"}]},
      "Content": {"rich_text": [{"plain_text": "Automação de marketing economiza tempo..."}]},
      "Platforms": {"multi_select": [{"name": "linkedin"}, {"name": "twitter"}]},
      "PublishDate": {"date": {"start": "2025-01-20T10:00:00Z"}},
      "Tags": {"multi_select": [{"name": "marketing"}, {"name": "automation"}]}
    }
  }'

Próximos Passos

  1. Influencer Outreach - Amplifique conteúdo
  2. Analytics Dashboard - Analise resultados
  3. Campanha Multicanal - Campanhas coordenadas

Última atualização: Janeiro 2025