diff --git a/app/models/concerns/landing_host_ai_syncable.rb b/app/models/concerns/landing_host_ai_syncable.rb
new file mode 100644
index 000000000..41d77dedd
--- /dev/null
+++ b/app/models/concerns/landing_host_ai_syncable.rb
@@ -0,0 +1,90 @@
+# frozen_string_literal: true
+
+module LandingHostAiSyncable
+ extend ActiveSupport::Concern
+
+ included do
+ after_save :sync_promotion_to_faq
+ end
+
+ def sync_promotion_to_faq
+ return unless custom_config.is_a?(Hash)
+
+ promotions = custom_config['promotions']
+
+ # Fallback to legacy format if array is empty but object exists
+ promotions = [custom_config['promotion']] if promotions.blank? && custom_config['promotion'].is_a?(Hash)
+
+ active_promos = Array(promotions).select { |p| p.is_a?(Hash) && p['active'] }
+
+ if active_promos.any?
+ create_or_update_faq_article(active_promos)
+ else
+ archive_faq_article
+ end
+ end
+
+ private
+
+ def create_or_update_faq_article(promos)
+ return unless can_sync_to_portal?
+
+ article = find_or_initialize_faq_article
+ article.title = faq_article_title
+
+ # Generating the standard AI instruction text for the multiple promotions
+ text = %(INSTRUÇÃO PARA A IA (PROMOÇÕES ATIVAS DO LINK #{hostname}):\n\n)
+ text += %(Existem promoções ativas para os leads que chegam pela landing page '#{hostname}'.\n)
+ text += %(Ofereça a promoção correspondente ao Canal/Origem pelo qual o cliente chegou.\n\n)
+
+ promos.each do |promo|
+ channel = promo['channel'].presence || 'Geral'
+ text += "--- CANAL / ORIGEM: #{channel} ---\n"
+ text += "Título da Promoção: #{promo['title']}\n" if promo['title'].present?
+ text += "Condições / Descrição: #{promo['description']}\n" if promo['description'].present?
+ text += "Cupom: #{promo['coupon_code']}\n" if promo['coupon_code'].present?
+ text += "Válida até: #{promo['valid_until']}\n" if promo['valid_until'].present?
+ text += "\n"
+ end
+
+ article.content = text
+ article.description = "FAQ Gerado automaticamente pela Landing Page: #{hostname}"
+ # Setting the author as the portal's account first user (just as a fallback) or we can use a system user
+ article.author ||= default_article_author
+ article.status = :published
+
+ article.save!
+ end
+
+ def archive_faq_article
+ return unless can_sync_to_portal?
+
+ article = find_faq_article
+ article&.update!(status: :archived)
+ end
+
+ def find_or_initialize_faq_article
+ find_faq_article || portal.articles.new(account_id: inbox.account_id)
+ end
+
+ def find_faq_article
+ portal.articles.find_by(title: faq_article_title)
+ end
+
+ def faq_article_title
+ "Promoção Automática - #{hostname.upcase}"
+ end
+
+ def portal
+ inbox.portal
+ end
+
+ def can_sync_to_portal?
+ inbox.present? && inbox.portal_id.present?
+ end
+
+ def default_article_author
+ # Assumes that the account has at least one user (owner/admin) to author the article
+ inbox.account.users.order(id: :asc).first
+ end
+end
diff --git a/app/models/landing_host.rb b/app/models/landing_host.rb
index aebf01284..b48734983 100644
--- a/app/models/landing_host.rb
+++ b/app/models/landing_host.rb
@@ -27,4 +27,7 @@
# index_landing_hosts_on_hostname (hostname) UNIQUE
#
class LandingHost < ApplicationRecord
+ belongs_to :inbox, optional: true
+
+ include LandingHostAiSyncable
end
diff --git a/progresso/automacao_mensagem_meta.md b/progresso/automacao_mensagem_meta.md
new file mode 100644
index 000000000..58ad86ddc
--- /dev/null
+++ b/progresso/automacao_mensagem_meta.md
@@ -0,0 +1,23 @@
+# Resolução: Automação por Conteúdo da Mensagem (Meta Ads)
+
+**Objetivo:** Criar uma regra de automação no Chatwoot que aplica uma etiqueta automaticamente quando uma mensagem específica (geralmente vinda de um link do Meta Ads/WhatsApp) é recebida.
+
+**Contexto e Desafios Encontrados:**
+1. **Tradução e Disponibilidade:** Em Português, a condição "Message Content" é traduzida como **"Mensagem contém"**. Porém, ela só aparece se o evento selecionado for **"Mensagem Criada"** (Message Created), e não "Conversa Criada".
+2. **Separação por Vírgulas (Bug Silencioso):** O Chatwoot usa o tipo de input `comma_separated_plain_text` para o campo "Mensagem contém". Isso significa que se a frase da campanha contiver uma vírgula (ex: *"Olá, tenho interesse"*), o sistema divide a frase em duas strings distintas e exige que a mensagem recebida seja exatamente igual a uma delas, fazendo com que a automação falhe.
+
+**Passos para a Resolução:**
+1. Criar a Automação e definir o **Evento** como **Mensagem Criada**.
+2. Na seção de Condições, escolher **Mensagem contém**.
+3. Mudar o operador de "Igual a" para **"Contém"**.
+4. No campo de valor, inserir a frase do anúncio **removendo a vírgula** ou colando apenas o trecho antes da vírgula (Ex: `Olá! Tenho interesse e queria mais informações`).
+5. Nas Ações, definir **Adicionar Rótulo** e escolher a tag desejada da campanha.
+
+**Principais Códigos/Arquivos Analisados (Backend/Frontend):**
+- `app/javascript/dashboard/routes/dashboard/settings/automation/constants.js`: Onde a condição `content` é mapeada no evento de `message_created`.
+- `app/javascript/dashboard/composables/useEditableAutomation.js`: Onde o input `comma_separated_plain_text` é convertido para envio (responsável pelo problema da quebra no texto por vírgulas).
+- `app/services/automation_rules/conditions_filter_service.rb` e `app/services/filter_service.rb`: Serviços que processam e validam as condições da automação (a busca é feita no campo banco de dados através da sintaxe `LOWER(messages.processed_message_content)`).
+
+**Como validar ou reverter:**
+- **Validar:** Simular o envio de uma nova mensagem no WhatsApp usando o mesmo formato definido na campanha (antes ou após a vírgula). O painel do Chatwoot deve aplicar o rótulo da campanha de forma simultânea à recepção da mensagem.
+- **Reverter:** Pausar a regra no painel em `Configurações > Automação` alternando a chave de 'Ativo', ou excluí-la permanentemente.
diff --git a/spec/models/landing_host_spec.rb b/spec/models/landing_host_spec.rb
index f8c5ec3ff..e787bfd2c 100644
--- a/spec/models/landing_host_spec.rb
+++ b/spec/models/landing_host_spec.rb
@@ -1,5 +1,56 @@
require 'rails_helper'
RSpec.describe LandingHost, type: :model do
- pending "add some examples to (or delete) #{__FILE__}"
+ let(:account) { create(:account) }
+ let!(:user) { create(:user, account: account) }
+ let(:portal) { create(:portal, account: account) }
+ let(:inbox) { create(:inbox, account: account, portal: portal) }
+
+ describe 'LandingHostAiSyncable' do
+ let(:landing_host) do
+ build(:landing_host,
+ inbox: inbox,
+ hostname: 'promo.example.com',
+ custom_config: {
+ promotions: [
+ {
+ active: true,
+ channel: 'Instagram',
+ title: 'Black Friday 50% Off',
+ description: 'Valid for all suites.',
+ coupon_code: 'BLACK50',
+ valid_until: '2024-11-30'
+ }
+ ]
+ })
+ end
+
+ it 'creates a new FAQ article when promotion is active' do
+ expect do
+ landing_host.save!
+ end.to change(Article, :count).by(1)
+
+ title = "Promoção Automática - #{landing_host.hostname.upcase}"
+ article = portal.articles.find_by(title: title)
+ expect(article).to be_present
+ expect(article.title).to include("Promoção Automática - #{landing_host.hostname.upcase}")
+ expect(article.content).to include('Black Friday 50% Off')
+ expect(article.content).to include('BLACK50')
+ expect(article.content).to include('Instagram')
+ expect(article.status).to eq('published')
+ end
+
+ it 'archives an existing FAQ article when promotion is deactivated' do
+ landing_host.save! # Automatically creates the article
+ title = "Promoção Automática - #{landing_host.hostname.upcase}"
+ article = portal.articles.find_by(title: title)
+ expect(article.status).to eq('published')
+
+ # Deactivate promotion
+ landing_host.custom_config['promotions'].first['active'] = false
+ landing_host.save!
+
+ expect(article.reload.status).to eq('archived')
+ end
+ end
end