Projeto PulseSaaSStack Laravel · Livewire · AlpineVersão 1.0
Landing Factory ↗
CL Soluções · PRD · SRS · Arquitetura · Regras de negócio

PulseSaaS

CRM + automação de marketing whitelabel multi-tenant: inbox unificada (WhatsApp), funil Kanban, builder visual de automações, agentes de IA, WABA/Evolution, afiliados e billing. Documentação técnica, planejamento e material comercial consolidados.

PRDSRSArquiteturaRegras de negócio
01

Sistema & estado (As-Is / To-Be)

Este documento apresenta uma visão clara, estruturada e visual do estado atual da plataforma PulseSaaS, detalhando o que já está consolidado (As-Is) e o que está em andamento/planejado no backlog (To-Be), com base nas discussões de brainstorm e nos últimos handoffs de desenvolvimento.


1. O Que Temos Hoje (Estado Atual - As-Is)

O sistema passou por uma profunda reestruturação de identidade visual e backend de automações. A estrutura de rotas está 100% íntegra (zero links quebrados ou telas inativas) e a base de design foi unificada sob Tokens Semânticos.

Módulos do Sistema e Status

Abaixo está o inventário de telas e funcionalidades atualmente operacionais no sistema:

Módulo Rota Descrição / Estado Atual
CRM / Dashboard dashboard Painel geral de performance, agora totalmente integrado aos design tokens.
Atendimento (Inbox) inbox.index Inbox redesenhado (estilo WhatsApp Web), navegação fluida em Alpine.js/AJAX, player de áudio integrado e barra de automação rápida.
Funil / Kanban kanban.index Movimentação de negócios (Deals) por etapas do pipeline.
Pedidos (Orders) orders.index (+ show) Listagem, detalhe, processamento de pagamentos e envios.
Automações (Builder) automations.index + /builder Novo Builder Visual (Canvas) para drag-and-drop de fluxos. CRUD de automações completo (criar do zero, ativar/pausar, duplicar/clonar com remapeamento de UUIDs, deletar).
Agentes de IA ai-agents.index Configuração de agentes OpenAI/Anthropic e execução direta nos fluxos.
Integrações integrations.index Configurações de provedores (WABA Meta Cloud, Evolution API, gateways de pagamento).
Parceiros (Afiliados) partners.index Gestão de afiliados pelo produtor. Respeita estritamente o isolamento de leads.
Branding (Personalizar) tenant.branding.edit Customização de logotipos e paletas de cores do Tenant.
Financeiro (Billing) billing.index Gestão de kits de comissão, faturamento de faturas e planos.
Admin da Plataforma platform.dashboard Painel master do administrador para monitorar produtores e consumo do SaaS.

Conquistas Técnicas Recentes (Entregues)

[!NOTE] Os épicos de design, usabilidade básica e o núcleo do motor de automação já foram finalizados e estão ativos localmente no branch feat/white-label-self-serve-login-fix.

  • Épico A (Identidade Visual & Tema): Fim de cores hardcoded (emerald-500, sky-300, etc.). Substituição por tokens baseados em CSS vars RGB (--brand-rgb, --brand-soft-rgb, --brand-strong-rgb). A interface agora se adapta perfeitamente à cor primária do tenant, suportando tema claro e escuro. Um teste automático (DesignTokenGuardTest) impede regressão de cores hardcoded.
  • Épico B (Sidebar): Recomposta para respeitar os tokens e o tema claro real. Dropdown de avatar limpo (Alpine.js).
  • Épico C + E (CRUD de Automações & Emojis): CRUD completo funcional na lista de automações. Eliminação total de emojis decorativos (ex.: no step-chain da lista), substituídos por ícones SVG semânticos.
  • Épico G1 (Ordem de Envio de Mídia): Para garantir que mídias cheguem na ordem exata enviada (ex.: imagem antes de áudio), o provedor WABA agora realiza upload síncrono para o /media da Meta e envia pelo media id, em vez de enviar links assíncronos.
  • Motor M1 & M2 (Drip de Automações): Drip automático por etapa implementado. O motor reconhece fluxos e entra em status waiting_reply após disparar uma etapa de envio, aguardando resposta do lead. A retomada possui um debounce de segurança de 10s (AUTOMATION_REPLY_DEBOUNCE_SECONDS) para evitar fragmentação de mensagens.

2. Fluxos Visuais do Sistema

A. Fluxo de Entrada, Atribuição e Conversão de Leads

O fluxo abaixo descreve a jornada lógica do lead e a política estrita de privacidade entre Produtores e Afiliados:

flowchart TD
    subgraph Captura["1. Captura e Entrada (Porta Única)"]
        A1[Meta Ads / CAPI] -->|Webhook / API| B[Criar Registro: Lead]
        A2[WhatsApp / Inbox] -->|Integração Canal| B
        A3[Link de Cadastro Público] -->|Signup do Afiliado| B
        A4[Cadastro Manual CRM] --> B
    end

    subgraph Atribuicao["2. Triagem e Escopo de Privacidade"]
        B --> C{Possui identificador de Afiliado?}
        C -->|Sim| D[Atribuir affiliate_id = X]
        C -->|Não| E[Atribuir affiliate_id = null \n Dono: Produtor]
        
        D --> F1[Visível APENAS para o Afiliado X]
        E --> F2[Visível APENAS para o Produtor]
    end

    subgraph Qualificacao["3. Qualificação Comercial"]
        F1 --> G{Lead Qualificado?}
        F2 --> G
        G -->|Não| H[Marcar como Perdido / Descartar]
        G -->|Sim| I[Converter Lead em Contato]
        I --> J[Criar Oportunidade Deal]
    end

    subgraph Kanban["4. Pipeline Comercial e Conversão"]
        J --> K[Entrar no Kanban / Pipeline]
        K --> L[Interações: Tarefas e Atividades]
        L --> M{Venda Concluída?}
        M -->|Não| L
        M -->|Sim| N[Disparar Meta CAPI Evento de Conversão \n Se configurado com token]
        N --> O[Atualizar Métricas no Dashboard]
    end
    
    style F1 fill:#f9f,stroke:#333,stroke-width:2px
    style F2 fill:#bbf,stroke:#333,stroke-width:2px
    style N fill:#bfb,stroke:#333,stroke-width:2px

B. Ciclo de Execução do Motor de Automação (Drip Engine)

Este é o fluxo interno do motor de automação que processa o envio de etapas e aguarda respostas do lead antes de prosseguir de forma autônoma:

stateDiagram-v2
    [*] --> Iniciado : Gatilho Ativado (Manual / Inbound)
    Iniciado --> ExecutandoEtapa : Processa nós da Etapa
    
    state ExecutandoEtapa {
        [*] --> EnviarMensagem : Dispara Mensagem/Mídia
        EnviarMensagem --> VerificarProximo : Existe próximo nó?
        VerificarProximo --> EnviarMensagem : Próximo é envio/mídia \n (Envia em sequência)
        VerificarProximo --> [*] : Fim da etapa ou nó de controle
    }
    
    ExecutandoEtapa --> ApenasManual : Modo de Automação = Manual
    ApenasManual --> Agradecimento : Operador clica em "Executar Próxima" no Chat
    Agradecimento --> ExecutandoEtapa : Executa Próxima Etapa
    
    ExecutandoEtapa --> AguardandoResposta : Modo de Automação = Automático E \n próximo nó requer resposta
    
    state AguardandoResposta {
        [*] --> waiting_reply : Define status na conversa
        waiting_reply --> LeadResponde : Lead envia mensagem inbound
    }
    
    LeadResponde --> Debounce10s : Job ResumeAutomationAfterReply enfileirado
    
    state Debounce10s {
        [*] --> AguardarSilencio : Aguarda 10 segundos
        AguardarSilencio --> UltimaMensagem : É a última mensagem do lead?
        UltimaMensagem --> RetomarFluxo : Sim (Estabilizou)
        UltimaMensagem --> AguardarSilencio : Não (Lead continua digitando)
    }
    
    RetomarFluxo --> ExecutandoEtapa : Continua para a próxima etapa do Grafo
    ExecutandoEtapa --> [*] : Fim do Grafo (completed)

C. Arquitetura de Design Tokens e White-Label Dinâmico

Como o PulseSaaS processa cores dinâmicas por tenant sem quebrar a compilação do Tailwind:

sequenceDiagram
    autonumber
    actor Cliente as Lead / Operador
    participant Browser as Navegador (CSS / DOM)
    participant Tailwind as Tailwind CSS (Runtime)
    participant Laravel as Laravel Backend
    participant DB as Banco de Dados (Branding)

    Cliente->>Laravel: Acessa subdomínio (ex: tenant.pulsecrm.test)
    Laravel->>DB: Busca registro do Tenant & brand_theme
    DB-->>Laravel: Retorna Hex da Cor Primária (ex: #DB2777)
    
    Note over Laravel: BrandingResolver converte Hex para RGB <br/> e calcula variantes Soft e Strong
    
    Laravel->>Browser: Renderiza HTML com CSS Variables injetadas no header
    Note over Browser: :root { <br/> --brand-rgb: 219 39 119; <br/> --brand-soft-rgb: 235 136 180; <br/> }
    
    Tailwind->>Browser: Mapeia classes (ex: bg-brand/10) para usar as CSS vars
    Note over Browser: bg-brand/10 -> rgb(var(--brand-rgb) / 0.1)
    
    Browser-->>Cliente: Exibe tela estilizada na cor exata do Tenant!

3. O Que Está Sendo Desenvolvido (Fase Atual - To-Be)

Abaixo estão listadas as tarefas priorizadas que estão ativas na fila ou prontas para validação pelo usuário:

Fila de Testes do Usuário (Validações WhatsApp Real)

Antes de avançar para os próximos grandes blocos, estas tarefas precisam ser validadas em ambiente real pelo operador:

  • T1: Validar se a mensagem de texto da automação chega corretamente no WhatsApp (integração do inbox).
  • T2: Testar o gravador de voz no Builder (gravação direta pelo microfone).
  • T3: Confirmar se o envio de áudio chega como nota de voz activa e tocável (formato .ogg/opus com voice:true).
  • G1: Confirmar se a ordem de envio de mídias simultâneas é preservada no WhatsApp real (upload via media id).

Próximos Passos Priorizados do Backlog

flowchart LR
    A[B1-B4: Polish do Builder] --> B[M3 Pleno: Dashboard de Status]
    B --> C[G2: Ordem em Sequência Evolution]
    C --> D[D1: Upload de Favicon/Logo na tela de Branding]
  1. Polish do Builder (Frente B):
    • B1 (Edges Horizontais): Alterar as conexões de arestas (handles) para saíre/entrarem pelas laterais (esquerda/direita), já que o layout visual do fluxo é horizontal.
    • B2 (Cores nos Nós): Permitir a personalização de cores individuais de cada nó diretamente no menu do botão direito no Canvas.
    • B3 & B4 (Usabilidade): Suporte a configuração de etapas em abas no Builder ou visão unificada no canvas, com agrupamento visual de cards em Etapas.
  2. Gerenciamento de Automações Automáticas (M3 Pleno):
    • Integrar painéis de controle mais robustos no Builder e no chat para gerenciar o andamento das automações automáticas ativas no lead.
  3. Ordem de Envio no Evolution Provider (G2):
    • Garantir a confiabilidade e ordem sequencial de disparos de mensagens e mídias usando o provider Evolution API (no mesmo padrão já construído para a WABA Meta Cloud).
  4. Branding Avançado (D1 Gaps):
    • Implementar upload direto de Favicon e logotipos específicos para temas claro e escuro na tela de configurações de Branding.

[!WARNING] Atenção: Mantenha sempre a suíte de testes ativa ao implementar novas telas. O guard de design tokens (DesignTokenGuardTest) lançará falhas se forem utilizadas classes Tailwind com cores estáticas (ex: emerald-*, sky-*) na estrutura central do sistema.

02

Base de conhecimento do projeto

purpose: memoria interna de contexto; otimizada para consulta rapida por agente; nao orientada a leitura humana status: active last_manual_update: 2026-05-12

[identity] official_name_defined=false placeholder_name=Pulse CRM SaaS placeholder_name_note=nome meramente ilustrativo current_public_repo_name=PulseSaaS product_type=saas product_core=crm+inbox+kanban+tags+automations+ads_tracking+conversion_events+billing+payment_gateway_foundation+ai_agents value_prop=fechar ciclo marketing->atendimento->venda->evento_de_conversao->billing_operacional->cobranca->assistencia_ia

[business_model] model=platform_admin->producer->affiliate platform_admin_scope=gerencia_produtores+assinaturas+planos+limites+suspensao+reativacao producer_scope=gerencia_workspace+afiliados+modulos+canais+funis+kits+desempenho affiliate_scope=acesso_restrito_por_produtor monetization=producer_subscription+affiliate_kits+optional_percentage_over_affiliate_revenue

[product_modules.target] auth=true workspaces=true crm=true contacts=true leads=true opportunities=true tasks=true activities=true kanban=true tags=true inbox_omnichannel=true automations=true ads_tracking=true conversion_events=true billing=true partners=true ai_agents=true light_erp=true

[product_rules.fixed] tags_are_operational_core=true kanban_supports_manual_and_automatic_moves=true tags_can_trigger_automations=true stage_changes_can_trigger_automations=true crm_events_can_trigger_conversion_events=true data_isolation_required=true workspace_isolation_required=true producer_affiliate_scope_isolation_required=true responsive_ui_required=true mobile_first_required=true full_width_no_artificial_max_required=true padding_grows_with_breakpoint=true premium_visual_required=true auditability_required=true

[product_rules.leads_visibility] model=full_isolation_between_producer_and_affiliates rule_producer_sees_own_leads_only=true rule_producer_does_not_see_affiliate_leads=true rule_producer_does_not_read_affiliate_conversations=true rule_producer_does_not_distribute_leads=true rule_affiliate_sees_only_own_leads=true rule_affiliates_do_not_see_each_other=true rule_lead_source_for_producer=manual_or_public_capture_link_or_webhook_or_inbox_message rule_lead_source_for_affiliate=manual_or_their_own_capture_link_or_their_inbox_message exception_producer_aggregated_dashboard=true exception_producer_sees_affiliate_status=true exception_producer_sees_affiliate_orders_for_billing=true exception_producer_does_not_see_affiliate_conversation_content=true exception_producer_manages_affiliate_permissions=true implementation_status=needs_code_change_in_scopeVisibleToUser_and_new_capture_link implementation_note=currently_owner_sees_everything_including_affiliate_leads_must_filter

[product_rules.public_capture_link] producer_can_generate_public_capture_link=planned affiliate_can_generate_own_capture_link=planned captured_lead_is_assigned_to_link_owner=planned no_lead_distribution_from_producer_to_affiliate=true

[current_stack] backend=Laravel 13 php=8.3+ php_version_to_select_on_panel=8.3 auth=Jetstream frontend_runtime=Livewire api_auth=Sanctum roles_permissions=Spatie Permission activity_log=Spatie Activitylog build=Vite css=Tailwind CSS database=MariaDB queue_cache_plan=Redis webserver_plan=Nginx process_supervisor_plan=Supervisor hosting_plan=Hostinger KVM 8 + AlmaLinux 9 version_control=GitHub

[database.current_confirmed] engine=MariaDB version=10.11.15-MariaDB host=localhost via UNIX socket protocol=10 user=root@localhost ssl_in_use=false server_charset=cp1252 latin1 decision_primary_db=MariaDB warning_charset_should_move=utf8mb4 warning_root_user_not_for_prod=true warning_remote_db_access_requires_protection=true

[database.notes] current_prod_target_charset=utf8mb4 current_prod_target_collation=utf8mb4_unicode_ci_or_similar must_review_indexes=true must_review_foreign_keys=true must_review_unique_constraints=true must_review_team_scoping=true must_review_sensitive_logs=true

[implementation.current] implemented_auth=true implemented_workspaces_teams=true implemented_crm_base=true implemented_contacts=true implemented_deals=true implemented_tasks=true implemented_recent_activity=true implemented_leads=true implemented_organizations=true implemented_kanban=true implemented_tags=true implemented_inbox_foundation=true implemented_native_automations=true implemented_tracking_ads_foundation=true implemented_conversion_events_foundation=true implemented_billing_foundation=true implemented_payment_gateway_foundation=true implemented_signed_payment_webhooks=true implemented_payment_outbound_adapter=true implemented_payment_gateway_http_client=true implemented_ai_agents_foundation=true implemented_hardening_foundation=true implemented_docs=true implemented_exec_presentation_doc=true implemented_dev_checklist_doc=true implemented_vps_precautions_doc=true implemented_tenancy_white_label=true implemented_subdomain_resolution=true implemented_branding_runtime=true implemented_affiliate_signup_links=true implemented_affiliate_approval_workflow=true implemented_integration_hub_pluga_style=true implemented_waba_cloud_real=true implemented_evolution_api_real=true implemented_automation_templates=true implemented_automation_steps_multi=true implemented_wait_seconds_with_queue=true implemented_ai_agents_real_provider=true implemented_openai_provider=true implemented_anthropic_provider=true implemented_orders_pay_after=true implemented_shipments_webhook_carrier=true implemented_inbox_cockpit_ui=true implemented_inbox_ai_inline_suggestion=true implemented_ui_polish_humanlabels=true

[implementation.current_code_shape] producer_current_technical_mapping=producer_model_linked_to_team affiliate_fully_modeled=true platform_admin_module_fully_modeled=true billing_real=foundation_with_manual_gateway_contract omnichannel_inbox_real=waba_cloud_and_evolution_implemented_with_signed_webhooks tags_complete=true dynamic_kanban_complete=true native_automations_complete=multi_step_with_templates_and_real_provider_dispatch meta_ads_tracking_complete=foundation_without_real_api_sync conversion_api_complete=foundation_simulated_send_without_real_meta_token billing_foundation_complete=true payment_gateway_complete=generic_http_client_without_provider_specific_mapping signed_payment_webhooks_complete=true payment_outbound_adapter_complete=simulated_contract_without_real_provider_api payment_gateway_http_client_complete=generic_http_client_without_provider_specific_mapping ai_agents_complete=real_provider_with_openai_anthropic hardening_complete=initial_pilot_ready light_erp_complete=orders_and_shipments_implemented integration_hub_complete=catalog_plus_tenant_integrations_with_encrypted_credentials channel_provider_registry_complete=waba_and_evolution_registered ai_provider_registry_complete=openai_and_anthropic_registered carrier_provider_registry_complete=melhor_envio_and_generic_shipping tenant_scope_global_complete=true mail_from_per_tenant_runtime=true public_signup_link_for_affiliates=true inbox_cockpit_with_linked_orders=true ai_inline_suggestion_endpoint=true voice_deep_link_whatsapp=true

[codebase.current_routes_features] web_root=true dashboard=true contact_create=true deal_create=true deal_advance=true task_create=true task_toggle=true lead_create=true lead_convert=true organization_create=true activity_create=true kanban_index=true pipeline_create=true pipeline_stage_create=true deal_kanban_move=true label_create=true label_toggle=true label_filter_dashboard=true label_filter_kanban=true inbox_index=true inbox_create=true conversation_create=true message_create=true conversation_label_filter=true automations_index=true automation_create=true automation_label_trigger=true automation_stage_trigger=true automation_queue_job=true automation_logs=true tracking_index=true tracking_meta_account_create=true tracking_campaign_create=true tracking_ad_set_create=true tracking_ad_create=true lead_attribution_capture=true conversions_index=true conversion_integration_create=true conversion_rule_create=true conversion_event_queue=true conversion_event_deduplication=true conversion_event_logs=true billing_index=true billing_plan_create=true billing_plan_limits=true affiliate_kit_create=true affiliate_commission_calculate=true payment_gateway_index=true payment_gateway_create=true billing_invoice_issue=true payment_webhook_idempotent=true payment_webhook_public_signed=true payment_gateway_encrypted_secrets=true payment_gateway_dispatch=true payment_gateway_request_logs=true payment_gateway_http_dispatch=true ai_agents_index=true ai_agent_create=true ai_agent_preview_run=true ai_agent_audit_logs=true security_headers=true hardening_indexes=true pilot_checklist=true api_user_route=true

[seed_state] demo_user_email=[email protected] demo_user_password=password demo_team_name=Pulse CRM

[design_constraints] must_be_responsive=true must_be_operational=true must_be_clear=true must_be_fast_to_scan=true must_avoid_visual_noise=true desktop_priority=true mobile_support_required=true design_system_state=custom_light_premium_direction

[infra_constraints] developing_on_vps=true vps_provider=Hostinger vps_type=KVM 8 os=AlmaLinux 9 github_primary_repo=true github_owner=gabriellimanagy github_repo=PulseSaaS github_repo_visibility=private github_repo_url=https://github.com/gabriellimanagy/PulseSaaS github_current_branch_seen=fix github_branches_seen=4 recommended_branching=main+develop_or_staging protect_main_branch=true separate_envs_asap=true backup_required=true monitoring_required=true

[domains.current] primary_domain=clsolucoes.com production_domain=pulsesaas.clsolucoes.com staging_domain=pulsehomolog.clsolucoes.com test_domain=pulsetest.clsolucoes.com subdomains_created_in_panel=true server_ip_seen=147.93.15.29

[security.non_negotiables] no_env_in_repo=true no_private_keys_in_repo=true no_prod_tokens_in_repo=true no_real_customer_dumps_in_repo=true csrf_protection=true authz_by_policy_required=true input_validation_required=true rate_limiting_required=true credential_rotation_required=true integration_secret_storage=env_only_until_secret_manager ssh_key_auth_required=true disable_root_ssh_recommended=true firewall_required=true ssl_for_app_required=true webhook_auth_required=true job_idempotency_required=true

[ops.non_negotiables] redis_required_before_heavy_automation=true queue_workers_must_be_supervised=true retries_required=true dead_letter_or_failure_strategy_required=true integration_logs_required=true backup_restore_test_required=true error_monitoring_required=true resource_monitoring_required=true

[launch_risks.top] 1=latin1_charset_for_conversation_data 2=root_db_user_in_production 3=no_ssl_for_future_remote_connections 4=dev_prod_environment_mixing 5=no_tested_backups 6=tenant_scope_leakage 7=unauthenticated_webhooks 8=non_idempotent_automations 9=unmonitored_queues 10=secret_leak_in_repo

[docs.index] readme=README.md technical_doc=docs/DOCUMENTACAO_TECNICA.md executive_presentation=docs/APRESENTACAO_SOCIOS.md dev_checklist=docs/CHECKLIST_DESENVOLVIMENTO_SAAS.md delivered_vs_requested=docs/CHECKLIST_PEDIDO_VS_ENTREGAS.md technical_backlog=docs/CHECKLIST_TAREFAS_TECNICO.md pilot_operation=docs/CHECKLIST_OPERACAO_PILOTO.md leads_flow=docs/FLUXOGRAMA_ENTRADA_E_TRATAMENTO_DE_LEADS.md whitelabel_plan=docs/archived/PLANO_REARQUITETURA_WHITELABEL.md krayin_benchmark=docs/archived/COMPARATIVO_KRAYIN_X_PULSESAAS_IMPLANTACAO.md evolution_setup=docs/setup/EVOLUTION_QUICKSTART.md

[development_phases.master] 1=fundacao_tecnica 2=hierarquia_admin_produtor_afiliado 3=crm_principal 4=kanban_dinamico 5=etiquetas_motor_operacional 6=inbox_omnichannel 7=automacoes_nativas 8=tracking_ads_atribuicao 9=eventos_de_conversao 10=billing_planos_parceiros 11=agentes_de_ia 12=hardening_final

[next_memory_updates_when] official_product_name_defined=true db_schema_stabilized=true producer_affiliate_tables_finalized=true charset_and_collation_finalized=true deploy_architecture_finalized=true billing_flow_finalized=true first_integration_contracts_finalized=true

[sync_rule] if_conflict_update_order=kb_internal->documentacao_tecnica->readme_if_needed

03

Documentação técnica

Atualizacao de implementacao em 2026-04-17

A base atual foi consolidada durante a conclusao da Etapa 1.

Implementado nesta rodada:

  • repositório Laravel 13 criado e estruturado localmente
  • Jetstream + Livewire + Teams
  • Sanctum
  • Spatie Permission com teams
  • Spatie Activitylog
  • estrutura por dominio inicial em app/Domain/CRM
  • controller fino do CRM com Actions, Requests e Policies
  • models Contact, Deal e Task
  • dashboard inicial do CRM
  • BelongsToTeam e SyncPermissionsTeam
  • seed inicial de workspace demo
  • testes do CRM adicionados e validados

Validacoes executadas:

  • php artisan migrate:fresh --seed
  • php artisan test
  • npm run build

Atualizacao de implementacao em 2026-04-17 - Sprint 2

A Etapa 2 foi concluida sobre a fundacao criada no Sprint 1.

Implementado nesta rodada:

  • PlatformAdmin para a camada dona do SaaS
  • Producer ligado explicitamente ao Team
  • ProducerSubscription e ProducerSubscriptionItem
  • Affiliate e pivot affiliate_users
  • current_affiliate_id em users
  • affiliate_id em contacts, deals e tasks
  • painel administrativo da plataforma em platform.dashboard
  • painel de gestao de afiliados em partners.index
  • dashboard do CRM adaptado para contexto de produtor e de afiliado
  • login com redirecionamento por perfil
  • seed com admin, produtor e afiliado
  • testes cobrindo plataforma, criacao de afiliado e isolamento interno

Atualizacao de planejamento em 2026-04-17 - Benchmark Krayin

Foi consolidado um benchmark tecnico e de produto com base no Krayin CRM para orientar a implantacao da Etapa 3 - CRM principal.

Artefatos criados nesta rodada:

  • docs/PESQUISA_KRAYIN_CRM.md
  • docs/COMPARATIVO_KRAYIN_X_PULSESAAS_IMPLANTACAO.md
  • scrum-steps/SPRINT_3_ETAPA_3_SCRUM.md
  • scrum-steps/SPRINT_3_STATUS.md

Direcionamento principal resultante:

  • implantar Lead, Organization e Activity como pilares da proxima etapa
  • enriquecer Contact e Deal sem quebrar o isolamento por afiliado
  • preparar a base para filtros, busca, webhooks e automacoes futuras
  • evitar misturar agora inbox, IMAP, AI e kanban multipipeline completo

Atualizacao de implementacao em 2026-04-17 - Sprint 3

A Etapa 3 - CRM principal foi concluida sobre a hierarquia criada no Sprint 2.

Implementado nesta rodada:

  • model Lead com origem, status, valor estimado, previsao de fechamento e conversao
  • model Organization para contexto empresarial
  • model Activity para timeline funcional do CRM
  • enriquecimento de Contact com empresa, lead de origem, cargo e origem
  • enriquecimento de Deal com empresa, contato, lead, resumo, ultima atividade e mudanca de etapa
  • dashboard do CRM refeito com filtros, metricas, formulários ricos e timeline operacional
  • automacao inicial de timeline em criacao, conversao, tarefa e avancos de oportunidade
  • seed demo expandido para exibir fluxo real de lead -> contato -> deal
  • testes do CRM principal cobrindo criacao, conversao e atividades

Validacoes executadas:

  • php artisan migrate:fresh --seed
  • php artisan test
  • npm run build

Atualizacao de implementacao em 2026-04-17 - Sprint 4

A Etapa 4 - Kanban dinamico foi concluida sobre o CRM principal entregue no Sprint 3.

Implementado nesta rodada:

  • models DealPipeline, DealPipelineStage e DealStageMovement
  • Deal enriquecido com pipeline_id, pipeline_stage_id e kanban_position
  • criacao de pipelines configuraveis com finalidade operacional
  • criacao de etapas configuraveis com cor, probabilidade e terminalidade
  • acao manual de movimentacao de cards entre colunas
  • historico de movimentacao por oportunidade
  • tela dedicada kanban.index com filtro por pipeline, afiliado e busca
  • seed demo com pipelines de vendas, afiliado e cobranca
  • testes cobrindo criacao de pipeline, etapa e movimentacao de deal

Validacoes executadas:

  • php artisan migrate:fresh --seed
  • php artisan test
  • npm run build

Atualizacao de implementacao em 2026-05-02 - Sprint 5

A Etapa 5 - Etiquetas como motor operacional foi concluida sobre o CRM principal e o kanban dinamico.

Implementado nesta rodada:

  • requests StoreLabelRequest e ToggleLabelRequest
  • LabelPolicy e registro da policy no AppServiceProvider
  • rotas labels.store e labels.toggle
  • criacao de etiquetas globais do workspace ou vinculadas ao afiliado
  • aplicacao e remocao manual de etiquetas em Lead, Deal e Activity
  • filtros por etiqueta no dashboard do CRM
  • filtros por etiqueta no kanban
  • exibicao de etiquetas nos cards do CRM e do kanban
  • painel operacional de etiquetas com contadores por uso
  • historico visivel de aplicacao e remocao a partir de crm_label_events
  • seed demo com etiquetas globais e por afiliado
  • testes cobrindo criacao, aplicacao, historico e filtros

Validacoes executadas nesta rodada:

  • php artisan test --filter=CrmDashboardTest
  • php artisan test --filter=KanbanBoardTest
  • php artisan migrate:fresh --seed
  • php artisan test
  • npm run build

Atualizacao de implementacao em 2026-05-02 - Sprint 6

A Etapa 6 - Inbox omnichannel foi implementada como fundacao operacional, sem dependencias externas reais.

Implementado nesta rodada:

  • migration 2026_05_02_010000_create_inbox_tables.php
  • models Inbox, Conversation e Message
  • policies InboxPolicy, ConversationPolicy e MessagePolicy
  • actions EnsureInbox, BuildInboxData, CreateInbox, CreateConversation e CreateMessage
  • requests StoreInboxRequest, StoreConversationRequest e StoreMessageRequest
  • controller InboxController
  • rota GET /inbox
  • rotas para criar caixa, conversa e mensagem
  • navegacao principal com item Inbox
  • tela inbox.index com lista de conversas, painel de mensagens e contexto do CRM
  • vinculo de conversas com Lead, Deal, Affiliate e etiquetas
  • etiquetas aplicaveis a conversas usando crm_labelables
  • seed demo com inbox principal, inbox de afiliado, conversas e mensagens
  • testes cobrindo renderizacao, criacao de conversa, mensagem, filtros por etiqueta e isolamento por afiliado

Validacoes executadas nesta rodada:

  • php artisan migrate:fresh --seed
  • php artisan test
  • npm run build

Atualizacao de implementacao em 2026-05-03 - Sprint 7

A Etapa 7 - Automacoes nativas foi implementada como nucleo operacional inicial para transformar eventos do CRM, kanban e inbox em acoes auditaveis.

Implementado nesta rodada:

  • migration 2026_05_03_010000_create_automation_tables.php
  • models Automation e AutomationLog
  • dominio app/Domain/Automation com actions, job, request, controller e models
  • policy AutomationPolicy registrada no AppServiceProvider
  • permissao automations.manage adicionada ao mapa de workspace
  • rota GET /automations
  • rota POST /automations
  • navegacao principal com item Automações
  • tela automations.index com cadastro de regra, lista de regras e logs recentes
  • gatilhos iniciais label_attached, deal_stage_changed, conversation_waiting e webhook_received
  • acoes iniciais create_task, apply_label, send_message e move_deal_stage
  • job ProcessAutomationEvent para processamento em fila
  • despacho de eventos ao aplicar etiqueta e ao mover oportunidade no kanban
  • seed demo de automacao por etiqueta de alta prioridade
  • testes cobrindo dashboard, criacao de automacao e execucao de gatilho por etiqueta com log

Validacoes executadas nesta rodada:

  • vendor/bin/pint --dirty
  • php artisan migrate:fresh --seed
  • php artisan test com 62 passed e 7 skipped
  • npm run build

Atualizacao de implementacao em 2026-05-03 - Sprint 8

A Etapa 8 - Tracking ADS e atribuicao foi implementada como fundacao para mapear trafego pago, UTMs e origem comercial dos leads.

Implementado nesta rodada:

  • migration 2026_05_03_020000_create_tracking_tables.php
  • models MetaAdAccount, MetaCampaign, MetaAdSet, MetaAd e LeadAttribution
  • dominio app/Domain/Tracking com actions, requests, controller e models
  • policy MetaAdAccountPolicy registrada no AppServiceProvider
  • permissao tracking.manage adicionada ao mapa de workspace
  • rota GET /tracking
  • rotas para criar conta Meta, campanha, conjunto e anuncio
  • navegacao principal com item Tracking
  • tela tracking.index com metricas, campanhas, origem por UTM e atribuicoes recentes
  • campos de tracking no cadastro de lead para utm_source, utm_medium, utm_campaign, utm_content, utm_term, fbclid, landing page, referrer e anuncio vinculado
  • criacao automatica de lead_attributions ao cadastrar lead com dados de tracking
  • seed demo com conta Meta, campanha, conjunto, anuncio e atribuicao do lead principal
  • testes cobrindo dashboard, criacao da estrutura Meta e captura de atribuicao no lead

Validacoes executadas nesta rodada:

  • vendor/bin/pint --dirty
  • php artisan migrate:fresh --seed
  • php artisan test com 65 passed e 7 skipped
  • npm run build

Atualizacao de implementacao em 2026-05-03 - Sprint 9

A Etapa 9 - Eventos de conversao foi implementada como fundacao auditavel para gerar e processar eventos internos de conversao.

Implementado nesta rodada:

  • migration 2026_05_03_030000_create_conversion_tables.php
  • models ConversionIntegration, ConversionEventRule, ConversionEvent e ConversionEventLog
  • dominio app/Domain/Conversion com actions, job, requests, controller e models
  • policy ConversionIntegrationPolicy registrada no AppServiceProvider
  • permissao conversions.manage adicionada ao mapa de workspace
  • rota GET /conversions
  • rotas para criar integracao, regra e webhook interno autenticado
  • navegacao principal com item Conversões
  • tela conversions.index com metricas, regras, eventos recentes, logs e formularios
  • job SendConversionEvent com fila, retries e backoff
  • deduplicacao por event_id unico
  • logs de sucesso e falha em conversion_event_logs
  • gatilhos reais por etiqueta aplicada, mudanca de etapa e lead convertido
  • gatilho por webhook autenticado no painel
  • envio simulado para Meta CAPI enquanto tokens reais nao existem
  • seed demo com integracao, regra e evento enviado
  • testes cobrindo dashboard, criacao de regra/integracao e evento por etiqueta com deduplicacao

Validacoes executadas nesta rodada:

  • vendor/bin/pint --dirty
  • php artisan migrate:fresh --seed
  • php artisan test com 68 passed e 7 skipped
  • npm run build

Atualizacao de implementacao em 2026-05-03 - Sprint 10

A Etapa 10 - Billing, planos e parceiros foi implementada como fundacao comercial para planos SaaS, kits de afiliados e comissoes operacionais.

Implementado nesta rodada:

  • migration 2026_05_03_040000_create_billing_tables.php
  • models BillingPlan, BillingPlanLimit, AffiliateKit e AffiliateCommission
  • dominio app/Domain/Billing com actions, requests, controller e models
  • policy BillingPlanPolicy registrada no AppServiceProvider
  • permissao billing.manage adicionada ao mapa de workspace
  • rota GET /billing
  • rotas para criar plano, kit e comissao
  • navegacao principal com item Billing
  • tela billing.index com metricas, catalogo de planos, kits, comissoes e formularios
  • catalogo formal de planos com limites por recurso
  • kits internos do produtor para afiliados com limites e features
  • calculo operacional de comissao por percentual ou valor fixo do afiliado
  • seed demo com planos Growth, Scale, Partner Pro, kit de afiliado e comissao calculada
  • testes cobrindo dashboard, criacao de plano, kit e comissao

Validacoes executadas nesta rodada:

  • vendor/bin/pint --dirty
  • php artisan test tests/Feature/BillingTest.php
  • php artisan migrate:fresh --seed
  • php artisan test com 71 passed e 7 skipped
  • npm run build

Atualizacao de implementacao em 2026-05-03 - Sprint 11

A Etapa 11 - Agentes de IA foi implementada como fundacao operacional simulada, preparada para provedores externos em sprint futura.

Implementado nesta rodada:

  • migration 2026_05_03_050000_create_ai_agent_tables.php
  • models AiAgent e AiAgentRun
  • dominio app/Domain/AI com actions, requests, controller e models
  • policy AiAgentPolicy registrada no AppServiceProvider
  • permissao ai.manage adicionada ao mapa de workspace
  • rota GET /ai-agents
  • rotas para criar agente e registrar previa simulada
  • navegacao principal com item IA
  • tela ai-agents.index com metricas, agentes, execucoes recentes e formularios
  • agentes com prompt, guardrails, contexto e handoff humano
  • associacao opcional por afiliado, pipeline, etapa e etiqueta
  • propositos operacionais follow_up, billing, routing, post_sale e tracking
  • execucao simulada auditavel enquanto nao ha chave/provedor real
  • seed demo com agente de follow-up e execucao aguardando revisao humana
  • testes cobrindo dashboard, criacao de agente e auditoria de execucao

Validacoes executadas nesta rodada:

  • vendor/bin/pint --dirty
  • php artisan test tests/Feature/AiAgentTest.php
  • php artisan migrate:fresh --seed
  • php artisan test com 74 passed e 7 skipped
  • npm run build

Atualizacao de implementacao em 2026-05-03 - Sprint 12

A Etapa 12 - Hardening final, performance, UX e seguranca foi concluida como primeira camada de preparo para operacao piloto.

Implementado nesta rodada:

  • middleware AddSecurityHeaders
  • headers X-Frame-Options, X-Content-Type-Options, Referrer-Policy e Permissions-Policy
  • HSTS automatico para requisicoes HTTPS
  • migration 2026_05_03_060000_add_hardening_indexes.php
  • indices complementares em leads, deals, conversations, automation_logs, conversion_events e ai_agent_runs
  • validacao de pipeline_stage_id dos agentes restrita ao workspace atual
  • bloqueio de execucao de agente restrito a afiliado contra deal fora do mesmo escopo
  • testes tests/Feature/HardeningTest.php
  • checklist de operacao piloto em docs/CHECKLIST_OPERACAO_PILOTO.md
  • documentacao e status Scrum atualizados

Validacoes executadas nesta rodada:

  • vendor/bin/pint --dirty
  • php artisan test tests/Feature/HardeningTest.php tests/Feature/AiAgentTest.php
  • php artisan migrate:fresh --seed
  • php artisan test com 76 passed e 7 skipped
  • npm run build

Atualizacao de implementacao em 2026-05-03 - Sprint 13

A Sprint 13 - Integracao financeira foi implementada como fundacao para gateway de pagamento, emissao de faturas e processamento idempotente de webhooks financeiros.

Implementado nesta rodada:

  • migration 2026_05_03_070000_create_payment_integration_tables.php
  • models PaymentGateway, BillingInvoice e PaymentWebhookEvent
  • actions CreatePaymentGateway, IssueBillingInvoice e ProcessPaymentWebhook
  • requests para gateway, fatura e evento financeiro
  • rotas:
    • POST /billing/gateways
    • POST /billing/invoices
    • POST /billing/payment-webhooks
  • painel Billing expandido com gateways, faturas e eventos financeiros
  • processamento idempotente por external_event_id
  • atualizacao de fatura para paid, overdue ou cancelled conforme evento
  • seed demo com gateway manual sandbox, fatura paga e webhook processado
  • teste cobrindo cadastro de gateway, emissao de fatura e replay de webhook

Validacoes executadas nesta rodada:

  • vendor/bin/pint --dirty
  • php artisan test tests/Feature/BillingTest.php
  • php artisan migrate:fresh --seed
  • php artisan test com 77 passed e 7 skipped
  • npm run build

Atualizacao de implementacao em 2026-05-03 - Sprint 14

A Sprint 14 - Webhooks financeiros assinados evoluiu a fundacao financeira para receber eventos externos reais com assinatura HMAC.

Implementado nesta rodada:

  • migration 2026_05_03_080000_harden_payment_gateway_credentials.php
  • campos api_base_url, secret_key e webhook_secret em payment_gateways
  • casts criptografados para secret_key e webhook_secret
  • endpoint publico POST /webhooks/payments/{gateway}
  • controller PublicPaymentWebhookController
  • validacao do header X-Pulse-Signature usando HMAC SHA-256 do corpo bruto
  • excecao de CSRF para webhooks/payments/*
  • formulario de gateway com URL base, chave secreta e segredo webhook
  • seed demo com segredo criptografado
  • teste cobrindo assinatura invalida, assinatura valida e baixa automatica da fatura

Validacoes executadas nesta rodada:

  • vendor/bin/pint --dirty
  • php artisan test tests/Feature/BillingTest.php
  • php artisan migrate:fresh --seed
  • php artisan test com 78 passed e 7 skipped
  • npm run build

Atualizacao de implementacao em 2026-05-03 - Sprint 15

A Sprint 15 - Adapter outbound de cobranca adicionou o contrato interno para enviar faturas ao gateway, ainda em modo simulado, mas com auditoria e idempotencia prontas para substituir por client HTTP real.

Implementado nesta rodada:

  • migration 2026_05_03_090000_create_payment_gateway_requests.php
  • model PaymentGatewayRequest
  • action DispatchInvoiceToGateway
  • request DispatchBillingInvoiceRequest
  • rota autenticada POST /billing/invoices/dispatch
  • formulario no Billing para envio manual de fatura ao gateway
  • painel Billing com requests outbound recentes
  • seed demo com envio outbound registrado
  • teste de idempotencia para evitar duplicar criacao de cobranca

Validacoes executadas nesta rodada:

  • vendor/bin/pint --dirty
  • php artisan test tests/Feature/BillingTest.php
  • php artisan migrate:fresh --seed
  • php artisan test com 79 passed e 7 skipped
  • npm run build

Atualizacao de implementacao em 2026-05-03 - Sprint 16

A Sprint 16 - Client HTTP de gateway financeiro evoluiu o adapter outbound para realizar chamadas HTTP reais quando o gateway nao e manual, mantendo a simulacao local para homologacao.

Implementado nesta rodada:

  • client PaymentGatewayHttpClient
  • envio de faturas para api_base_url/invoices via Http::post
  • headers de idempotencia e autenticacao do gateway
  • captura de resposta JSON/corpo bruto
  • mapeamento generico de identificador externo de fatura
  • persistencia de falhas HTTP/conexao em payment_gateway_requests
  • exibicao de erro do request outbound no painel Billing
  • teste com Http::fake validando endpoint, headers, payload e atualizacao da fatura

Validacoes executadas nesta rodada:

  • vendor/bin/pint --dirty
  • php artisan test tests/Feature/BillingTest.php
  • php artisan migrate:fresh --seed
  • php artisan test com 80 passed e 7 skipped
  • npm run build

1. Objetivo deste documento

Este documento detalha a base tecnica atual e a estruturacao planejada do Pulse CRM SaaS.

Ele complementa o README.md e serve como referencia para:

  • arquitetura
  • tecnologias e bibliotecas
  • organizacao do projeto
  • rotas
  • banco de dados
  • seguranca
  • multiempresa
  • convencoes de desenvolvimento
  • roadmap tecnico

2. Escopo atual da base

A base atual ja entrega um primeiro nucleo SaaS operacional em Laravel com:

  • landing page
  • autenticacao
  • multiempresa via teams
  • controle de permissao por workspace
  • dashboard comercial
  • contatos
  • oportunidades
  • tarefas
  • trilha de atividade

Esta base ainda nao contem todos os modulos descritos no README, mas foi desenhada para suportar a expansao do produto para:

  • inbox omnichannel
  • produtor e afiliado
  • etiquetas
  • automacoes
  • IA
  • tracking ADS
  • eventos de conversao
  • ERP operacional

3. Stack atual

Backend

  • PHP ^8.3
  • Laravel Framework ^13.0
  • Laravel Jetstream ^5.5
  • Laravel Sanctum ^4.0
  • Laravel Tinker ^3.0
  • Livewire ^3.6.4
  • Spatie Activitylog ^4.12
  • Spatie Permission ^7.3

Frontend

  • Blade
  • Tailwind CSS ^4.0.0
  • Vite ^8.0.0
  • @tailwindcss/vite ^4.0.0

Desenvolvimento e qualidade

  • PHPUnit ^12.5.12
  • Laravel Pint ^1.27
  • Laravel Pail ^1.2.5
  • Mockery ^1.6
  • NunoMaduro Collision ^8.6
  • FakerPHP Faker ^1.23
  • Concurrently ^9.0.1

4. Bibliotecas principais e papel de cada uma

Laravel Framework

Base do sistema.

Responsavel por:

  • roteamento
  • controllers
  • migrations
  • Eloquent ORM
  • request validation
  • jobs, events, queue e infraestrutura base

Jetstream

Camada de recursos prontos de autenticacao e contas.

Responsavel por:

  • login
  • registro
  • reset de senha
  • gerenciamento de perfil
  • times
  • convites
  • controle de time atual

Livewire

Base para componentes reativos server-driven.

No estado atual, o projeto utiliza principalmente Blade, mas Livewire ja esta instalado para expansao futura de:

  • inbox em tempo real
  • kanban interativo
  • builder de automacao
  • configurador de IA

Sanctum

Base para autenticacao de API e protecao de sessao.

Importante para futuras integracoes com:

  • webhooks
  • APIs publicas
  • apps mobile
  • agentes externos

Spatie Permission

Base para RBAC e permissao com escopo por time.

Uso atual:

  • roles e permissions vinculadas ao workspace
  • preparacao para perfis como produtor, afiliado, atendente, closer

Spatie Activitylog

Base para trilha de auditoria.

Uso atual:

  • registrar criacao e alteracao de contatos
  • registrar criacao e avancos de oportunidades
  • registrar criacao e conclusao de tarefas
  • preparar o sistema para logs de automacao, integracoes e IA

5. Estrutura atual de pastas

Visao resumida:

  • app/
  • bootstrap/
  • config/
  • database/
  • public/
  • resources/
  • routes/
  • storage/
  • tests/
  • vendor/

app/

Contem a logica principal da aplicacao.

Pastas relevantes:

  • Domain/CRM
  • Actions/Fortify
  • Actions/Jetstream
  • Http/Controllers
  • Http/Middleware
  • Models
  • Policies
  • Providers
  • View/Components

database/

Contem:

  • migrations
  • seeders
  • factories
  • database.sqlite

resources/

Contem:

  • views Blade
  • css
  • js

routes/

Contem:

  • web.php
  • api.php
  • console.php

6. Arquitetura atual

Modelo arquitetural

O projeto segue o modelo classico do Laravel com inicio de separacao por dominio:

  • rotas
  • controllers
  • models
  • views Blade
  • middleware
  • service providers
  • Actions e Requests por caso de uso
  • dominio inicial em app/Domain/CRM

Multiempresa

O isolamento por conta e feito com teams do Jetstream.

Cada usuario possui:

  • current_team_id
  • times owned
  • times membership

Cada registro de negocio principal e associado a um team_id.

Tabelas com escopo por workspace atualmente:

  • contacts
  • deals
  • tasks
  • roles
  • model_has_roles
  • model_has_permissions
  • activity_log por propriedade de time

Escopo por team

A trait BelongsToTeam.php define:

  • relacao com team
  • relacao com owner
  • scope forTeam()

O middleware SyncPermissionsTeam.php sincroniza o team_id atual para a camada do Spatie Permission.

Logica atual de produtor x afiliado

Depois do Sprint 2, essa logica deixou de ser apenas conceitual e passou a existir na base atual.

O que ja existe implementado
  • platform_admins
  • producers
  • producer_subscriptions
  • producer_subscription_items
  • affiliates
  • affiliate_users
  • users.current_affiliate_id
  • contacts.affiliate_id
  • deals.affiliate_id
  • tasks.affiliate_id

Na pratica, o comportamento atual agora e:

  • o administrador da plataforma gerencia produtores e limites
  • o produtor esta materializado como entidade ligada ao workspace
  • o afiliado existe como subconta operacional do produtor
  • usuarios de afiliado entram como membros do workspace e ficam vinculados ao afiliado
  • o CRM base respeita o escopo do afiliado quando o usuario esta nessa camada
O que ainda ficou para evolucao futura
  • comissionamento detalhado por regra comercial
  • limitacao por canais reais e pipelines reais
  • planos internos mais sofisticados
  • billing real com gateway externo
  • telas de relatorio especificas por afiliado

6.1 Modelo recomendado para produtor x afiliado

Antes da camada produtor x afiliado, existe uma camada superior que pertence a operacao dona do SaaS.

6.1.1 Administrador da plataforma

Esta camada representa a empresa proprietaria do software.

Ela fica acima do produtor e administra a base inteira do SaaS.

Responsabilidades:

  • cadastrar produtores
  • aprovar ou rejeitar produtores
  • ativar, suspender e cancelar contas
  • gerenciar assinaturas dos produtores
  • controlar planos e limites
  • acompanhar MRR, churn e inadimplencia
  • medir quantidade de afiliados por produtor
  • aplicar regras comerciais da plataforma

Como essa gestao deve funcionar:

  1. a plataforma cria ou aprova o produtor
  2. vincula um plano ao produtor
  3. libera limites de canais, afiliados, IA e automacoes
  4. acompanha uso e faturamento
  5. pode suspender, bloquear ou reativar a conta

Painel esperado do administrador:

  • lista de produtores
  • status da assinatura
  • plano atual
  • quantidade de afiliados
  • quantidade de canais
  • uso de IA
  • uso de automacoes
  • faturamento e inadimplencia

6.1.2 Produtor

Para o produto ficar alinhado com a visao do README, a logica ideal e:

Produtor

Responsavel pelo workspace principal.

Deve possuir:

  • ownership da conta
  • configuracao de canais
  • configuracao de automacoes
  • criacao e gestao de afiliados
  • definicao de planos internos
  • visao de cobranca, tracking e parceiros

Como o produtor gerencia seus afiliados:

  • cria afiliados dentro do workspace
  • define plano interno do afiliado
  • libera ou bloqueia modulos
  • define canais disponiveis
  • define funis e kanbans permitidos
  • controla acesso a tracking
  • controla acesso a IA
  • acompanha desempenho comercial e uso por afiliado

Afiliado

Subconta ou unidade comercial vinculada a um produtor.

Deve possuir:

  • vinculo obrigatorio com um produtor
  • permissao restrita ao escopo liberado
  • acesso apenas aos canais e funis permitidos
  • leitura separada dos seus leads, conversas e resultados
  • possibilidade de kit/plano proprio

Relacao tecnica esperada

O relacionamento recomendado e:

  • 1 produtor possui muitos afiliados
  • 1 afiliado pertence a 1 produtor
  • 1 afiliado pode ter 1 ou mais usuarios operadores
  • 1 afiliado pode ter acesso a 1 ou mais canais
  • 1 afiliado pode ser limitado a pipelines especificos

6.2 Como isso aparece no banco

Hoje a base ja contem uma primeira camada funcional com:

  • platform_admins
  • producers
  • producer_subscriptions
  • producer_subscription_items
  • affiliates
  • affiliate_users

Continuam recomendadas, para etapas futuras, tabelas especializadas como:

  • platform_admins
  • producer_subscriptions
  • producer_subscription_items
  • producers
  • affiliates
  • affiliate_users
  • affiliate_channels
  • affiliate_pipeline_permissions
  • affiliate_plan_assignments
  • affiliate_commissions

Camada plataforma -> produtor

producer_subscriptions

Campos sugeridos:

  • id
  • producer_id
  • external_subscription_id
  • plan_code
  • status
  • started_at
  • expires_at
  • trial_ends_at
  • cancelled_at
  • price
  • billing_cycle
  • created_at
  • updated_at

Objetivo:

  • controlar a assinatura do produtor perante a plataforma
producer_subscription_items

Campos sugeridos:

  • id
  • producer_subscription_id
  • resource_type
  • limit_value
  • consumed_value
  • created_at
  • updated_at

Objetivo:

  • controlar cotas de canais, afiliados, IA, automacoes e outros limites

Exemplo conceitual de tabelas

producers

Campos sugeridos:

  • id
  • team_id
  • user_id
  • display_name
  • status
  • created_at
  • updated_at

Objetivo:

  • representar o cliente principal do SaaS dentro do workspace
  • conectar o team da conta ao dominio de negocio do produtor
affiliates

Campos sugeridos:

  • id
  • team_id
  • producer_id
  • name
  • slug
  • email
  • status
  • plan_code
  • commission_type
  • commission_value
  • created_at
  • updated_at

Objetivo:

  • representar cada parceiro ou suboperacao criada pelo produtor
  • permitir controle proprio de acesso, desempenho e monetizacao
affiliate_users

Campos sugeridos:

  • id
  • affiliate_id
  • user_id
  • role
  • created_at
  • updated_at

6.3 Como isso deve aparecer na regra de permissao

Hoje a permissao esta centrada em team.

No futuro, sera necessario compor dois niveis:

Nivel 0 - Plataforma

Quem administra o SaaS inteiro.

Exemplos:

  • super admin
  • financeiro da plataforma
  • suporte da plataforma
  • operador de billing

Nivel 1 - Workspace do produtor

Quem pertence a conta SaaS.

Base atual:

  • teams
  • team_user
  • roles
  • permissions

Nivel 2 - Escopo interno do afiliado

O que aquele usuario pode fazer dentro da estrutura do produtor.

Exemplos:

  • ver apenas leads do proprio afiliado
  • acessar apenas inbox especifica
  • atuar apenas em certos kanbans
  • nao acessar parceiros
  • nao acessar billing
  • nao acessar configuracoes globais

6.4 Como a gestao deve funcionar na pratica

Fluxo do administrador da plataforma

  1. a plataforma cadastra ou aprova um produtor
  2. define ou altera o plano contratado
  3. controla limites de uso
  4. acompanha status financeiro
  5. suspende ou reativa quando necessario

Fluxo do produtor

  1. produtor entra no seu workspace
  2. acessa o modulo Parceiros ou Afiliados
  3. cria um novo afiliado
  4. define plano interno e permissoes
  5. escolhe canais, funis e acessos
  6. acompanha o desempenho desse afiliado
  7. pode editar, suspender ou remover o afiliado

Fluxo do afiliado

  1. afiliado acessa apenas o escopo liberado
  2. opera seus canais, leads e funis permitidos
  3. acompanha apenas seus indicadores
  4. nao enxerga os dados globais do produtor nem dados de outros afiliados

6.5 Como isso deve aparecer na interface

A documentacao funcional e a apresentacao comercial falam de produtor e afiliado.

No sistema final, isso deve ficar visivel em:

  • painel administrativo da plataforma
  • dashboard do produtor
  • dashboard do afiliado
  • tela de parceiros
  • tela de usuarios
  • tela de planos internos
  • filtros de relatorio
  • escopo de inbox e kanban

6.6 Resumo executivo desta parte

Para nao gerar ambiguidade:

  • a camada administrador da plataforma ja esta implementada como modulo inicial
  • a base atual suporta produtor como entidade propria conectada ao workspace
  • a base atual suporta afiliado como entidade propria com escopo no CRM
  • a documentacao funcional descreve o alvo do produto
  • a documentacao tecnica separa o que ja entrou em codigo do que ainda e expansao futura

7. Modelos atuais

User

Arquivo: app/Models/User.php

Traits utilizadas:

  • HasApiTokens
  • HasFactory
  • HasProfilePhoto
  • HasTeams
  • HasRoles
  • Notifiable
  • TwoFactorAuthenticatable

Principais campos:

  • id
  • name
  • email
  • password
  • current_team_id
  • profile_photo_path
  • email_verified_at

Team

Arquivo: app/Models/Team.php

Representa o workspace SaaS.

Campos de negocio:

  • name
  • slug
  • billing_email
  • plan
  • seat_limit
  • personal_team

Relacionamentos:

  • contacts()
  • deals()
  • tasks()

Contact

Arquivo: app/Models/Contact.php

Representa lead ou cliente.

Campos principais:

  • team_id
  • owner_id
  • name
  • company
  • email
  • phone
  • status
  • value
  • last_contact_at
  • notes

Deal

Arquivo: app/Models/Deal.php

Representa oportunidade comercial.

Campos principais:

  • team_id
  • owner_id
  • title
  • company
  • contact_name
  • stage
  • value
  • probability
  • close_date

Task

Arquivo: app/Models/Task.php

Representa tarefa operacional.

Campos principais:

  • team_id
  • owner_id
  • title
  • status
  • priority
  • due_date

8. Controller atual

CrmDashboardController

Arquivo: app/Http/Controllers/CrmDashboardController.php

Responsabilidades atuais:

  • montar dashboard comercial
  • listar contatos do workspace
  • listar deals do workspace
  • listar tarefas do workspace
  • ler atividade recente
  • criar contato
  • criar oportunidade
  • avancar oportunidade
  • criar tarefa
  • concluir/reabrir tarefa

Acoes

  • __invoke()
  • storeContact()
  • storeDeal()
  • advanceDeal()
  • storeTask()
  • toggleTask()

Observacao

Hoje ele concentra a primeira camada do CRM.

Na evolucao do produto, o ideal e separar responsabilidades em:

  • controllers por contexto
  • actions/services por caso de uso
  • jobs para integracoes
  • policies por modulo

9. Rotas atuais

Web

Arquivo: routes/web.php

Rotas de negocio implementadas:

  • GET /
  • GET /dashboard
  • POST /contacts
  • POST /deals
  • PATCH /deals/{deal}/advance
  • POST /tasks
  • PATCH /tasks/{task}/toggle

Rotas de autenticacao e conta vem do Fortify e Jetstream.

API

Arquivo: routes/api.php

Rota atual:

  • GET /api/user

Tabela resumida das rotas de negocio

Metodo URI Nome Controller Objetivo
GET /dashboard dashboard CrmDashboardController dashboard do CRM
POST /contacts contacts.store CrmDashboardController@storeContact criar contato
POST /deals deals.store CrmDashboardController@storeDeal criar oportunidade
PATCH /deals/{deal}/advance deals.advance CrmDashboardController@advanceDeal avancar etapa da oportunidade
POST /tasks tasks.store CrmDashboardController@storeTask criar tarefa
PATCH /tasks/{task}/toggle tasks.toggle CrmDashboardController@toggleTask concluir ou reabrir tarefa

Middleware relevantes

Grupo autenticado do dashboard:

  • auth:sanctum
  • config('jetstream.auth_session')
  • verified
  • permissions.team

10. Frontend atual

Tecnologia

O frontend atual utiliza:

  • Blade
  • Tailwind CSS
  • Vite

Views principais

CSS

Arquivo principal:

Padrao visual atual:

  • fundo claro
  • cards arredondados
  • dashboard comercial
  • uso de tipografia Fraunces e Manrope
  • layout responsivo por grid

11. Banco de dados atual

Engine atual

  • SQLite

Arquivo:

  • database/database.sqlite

Tabelas atuais

  • users
  • password_reset_tokens
  • sessions
  • cache
  • cache_locks
  • jobs
  • job_batches
  • failed_jobs
  • personal_access_tokens
  • teams
  • team_user
  • team_invitations
  • permissions
  • roles
  • model_has_permissions
  • model_has_roles
  • role_has_permissions
  • activity_log
  • contacts
  • deals
  • tasks

12. Esquema do banco de dados

users

Origem:

Campos principais:

  • id
  • name
  • email
  • email_verified_at
  • password
  • remember_token
  • current_team_id
  • profile_photo_path
  • created_at
  • updated_at

teams

Origem:

Campos:

  • id
  • user_id
  • name
  • slug
  • billing_email
  • plan
  • seat_limit
  • personal_team
  • created_at
  • updated_at

team_user

Origem:

  • 2026_04_15_201946_create_team_user_table.php

Papel:

  • relaciona usuarios membros a workspaces
  • guarda role do membro no time

team_invitations

Origem:

  • 2026_04_15_201947_create_team_invitations_table.php

Papel:

  • convites pendentes para membros de workspace

contacts

Origem:

Campos:

  • id
  • team_id
  • owner_id
  • name
  • company
  • email
  • phone
  • status
  • value
  • last_contact_at
  • notes
  • created_at
  • updated_at

Indices:

  • team_id + status

deals

Origem:

Campos:

  • id
  • team_id
  • owner_id
  • title
  • company
  • contact_name
  • stage
  • value
  • probability
  • close_date
  • created_at
  • updated_at

Indices:

  • team_id + stage

tasks

Origem:

Campos:

  • id
  • team_id
  • owner_id
  • title
  • status
  • priority
  • due_date
  • created_at
  • updated_at

Indices:

  • team_id + status

permissions

Origem:

Papel:

  • catalogo de permissoes do sistema

roles

Papel:

  • perfis por team

Campos relevantes:

  • id
  • team_id
  • name
  • guard_name

model_has_roles

Papel:

  • vincular usuario a role dentro do team_id

model_has_permissions

Papel:

  • vincular permissoes diretas a modelos dentro do team_id

role_has_permissions

Papel:

  • relacao entre perfis e permissoes

activity_log

Origem:

Campos:

  • id
  • log_name
  • description
  • subject_type
  • subject_id
  • event
  • causer_type
  • causer_id
  • attribute_changes
  • properties
  • created_at
  • updated_at

Tabelas de infraestrutura Laravel

  • cache
  • cache_locks
  • jobs
  • job_batches
  • failed_jobs
  • personal_access_tokens
  • sessions
  • password_reset_tokens

13. Relacionamentos atuais

Relacoes principais

  • User possui muitos Team como owner
  • User pode pertencer a muitos Team via team_user
  • Team possui muitos Contact
  • Team possui muitos Deal
  • Team possui muitos Task
  • Contact pertence a Team
  • Contact pertence a User como owner
  • Deal pertence a Team
  • Deal pertence a User como owner
  • Task pertence a Team
  • Task pertence a User como owner

14. Seeder atual

Arquivo:

O seed atual cria:

  • 1 usuario demo
  • 1 workspace Pulse CRM
  • role owner
  • permissions iniciais do CRM
  • 2 contatos
  • 3 deals
  • 2 tarefas
  • 1 log inicial de atividade

Credenciais demo:

15. Seguranca atual

Ja implementado

  • autenticacao Jetstream/Fortify
  • sessao protegida por Sanctum
  • suporte a 2FA
  • multiempresa por team
  • permissao por team
  • trilha de auditoria com activity log

Recomendacoes para evolucao

  • criptografia de tokens de integracao
  • policies especificas por modulo
  • segregacao de canais por perfil
  • assinatura de webhooks
  • rate limiting por integracao
  • rotacao de segredos

16. Scripts de execucao

Composer

Scripts definidos em composer.json:

  • composer setup
  • composer dev
  • composer test

NPM

Scripts definidos em package.json:

  • npm run dev
  • npm run build

Fluxo local recomendado

composer install
npm install
php artisan migrate:fresh --seed
npm run build
php artisan serve

17. Testes

A base inclui suite de testes padrão do Jetstream/Fortify.

Escopo coberto hoje:

  • autenticacao
  • registro
  • reset de senha
  • times
  • perfil
  • 2FA

Ainda nao existem testes de feature especificos para:

  • contatos
  • deals
  • tarefas
  • dashboard
  • RBAC customizado do CRM

18. Limites atuais da base

O sistema atual e um MVP tecnico de fundacao.

Ainda nao existem tabelas dedicadas para:

  • afiliados
  • produtores
  • inbox
  • mensagens
  • conversas
  • etiquetas
  • pipelines configuraveis
  • colunas kanban dinamicas
  • automacoes
  • webhooks
  • integracoes Meta
  • eventos de conversao
  • pedidos
  • cobranca
  • pay after
  • parceiros
  • assinaturas internas
  • agentes de IA

19. Estrutura recomendada de evolucao do banco

Para atender ao README completo, a proxima fase deve introduzir tabelas como:

  • producers
  • affiliates
  • channels
  • inboxes
  • conversations
  • messages
  • labels
  • labelables
  • pipelines
  • pipeline_stages
  • lead_stage_history
  • automations
  • automation_triggers
  • automation_actions
  • meta_ad_accounts
  • meta_pixels
  • ad_campaigns
  • adsets
  • ads
  • lead_attributions
  • conversion_events
  • conversion_event_logs
  • orders
  • order_shipments
  • collections
  • partner_plans
  • subscriptions
  • subscription_commissions
  • ai_agents
  • ai_agent_assignments
  • webhook_endpoints
  • webhook_logs

20. Roadmap tecnico recomendado

Fase A - consolidacao do dominio

  • extrair services do CrmDashboardController
  • criar requests dedicados
  • criar testes de CRM
  • padronizar policies

Fase B - inbox e etiquetas

  • modelos de conversa
  • mensagens
  • caixas de entrada
  • etiquetas
  • vinculo entre etiqueta e automacao

Fase C - kanban dinamico

  • pipelines configuraveis
  • colunas customizadas
  • historico de movimentacao
  • automacao por etapa

Fase D - tracking e conversao

  • contas Meta
  • pixels
  • atribuicao
  • fila de eventos
  • retry e auditoria

Fase E - IA e ERP operacional

  • agentes
  • rastreio
  • cobranca
  • pos-venda
  • recompra

21. Regras tecnicas recomendadas para as proximas fases

Separacao por contexto

Organizar por dominios, por exemplo:

  • Domain/CRM
  • Domain/Inbox
  • Domain/Automation
  • Domain/Tracking
  • Domain/Billing
  • Domain/Partners

Padrao de aplicacao

Usar:

  • Controllers finos
  • Services/Actions por caso de uso
  • Form Requests
  • Policies
  • Jobs para integracoes
  • Events e listeners para gatilhos

Integracoes externas

Toda integracao deve possuir:

  • tabela de configuracao
  • tabela de logs
  • retries
  • job assincrono
  • estados de processamento

22. Arquivos mais importantes hoje

Produto e visao

Backend

Rotas

Banco

Frontend

23. Conclusao

O projeto ja possui uma base tecnica forte para um SaaS multiempresa em Laravel.

Hoje ele entrega a fundacao do CRM e da governanca da conta.

O README define a visao expandida do produto.

Este documento define como essa visao conversa com a implementacao atual e quais sao os proximos passos tecnicos para transformar a base em:

  • CRM operacional
  • inbox omnichannel
  • sistema de etiquetas e automacao
  • tracking ADS
  • eventos de conversao
  • IA por funil
  • ERP leve de cobranca, rastreio e pos-venda
  • plataforma de produtor e afiliado
04

Fluxo de entrada & tratamento de leads

Documento de referencia para a operacao comercial do PulseSaaS.

Objetivo:

  • definir de onde os leads vao entrar
  • esclarecer como o sistema deve tratar cada lead
  • alinhar o papel de administrador, produtor e afiliado
  • orientar a implantacao futura de tracking, webhooks, automacoes e eventos de conversao

1. Resposta curta

Sim, os leads tendem a vir de plataformas de marketing e canais operacionais, como:

  • Meta Ads
  • Google Ads
  • landing pages
  • formularios externos
  • WhatsApp
  • Instagram
  • Facebook
  • importacao CSV
  • webhooks de ferramentas terceiras
  • integracoes futuras com inbox e automacoes

Mas hoje, no estado atual da aplicacao, o lead entra principalmente por:

  • cadastro manual no CRM
  • conversao e enriquecimento interno do proprio funil

Ou seja:

  • o modelo de processo ja pode ser definido agora
  • a entrada automatica por marketing sera consolidada nas etapas de Inbox, Tracking ADS, Eventos de Conversao e Automacoes

2. Origem esperada dos leads

2.1 Fontes principais

  • campanhas pagas
  • trafego organico
  • formularios de captacao
  • canais de mensagem
  • importacao de listas
  • parceiros e afiliados
  • integrações externas via webhook

2.2 Campos minimos que o sistema deve tentar capturar

  • nome
  • email
  • telefone
  • empresa
  • origem
  • campanha
  • utm_source
  • utm_medium
  • utm_campaign
  • fbclid quando existir
  • afiliado responsavel, quando aplicavel
  • produtor dono do workspace
  • data e hora da entrada

3. Papel de cada perfil

Administrador da plataforma

Responsavel por:

  • acompanhar produtores
  • acompanhar afiliados totais da base
  • acompanhar consumo de plano e receita
  • monitorar se integracoes e captura de leads estao funcionando
  • futuramente ver falhas de webhook, falhas de tracking e volume por produtor

O administrador nao deveria operar lead por lead no dia a dia.

Produtor

Responsavel por:

  • configurar funis e regras comerciais
  • definir quais afiliados podem operar e quais modulos cada um acessa
  • receber e atender os proprios leads (pelos canais do produtor)
  • acompanhar agregados dos afiliados (quantidade, conversao, faturamento) sem ler conversas
  • validar origem de campanhas e performance por canal

Afiliado

Responsavel por:

  • receber apenas leads do seu escopo
  • tratar o lead comercialmente
  • mover o lead no funil
  • registrar atividades
  • converter lead em contato e oportunidade
  • acompanhar sua propria performance

4. Processo funcional esperado

Etapa 1. Captura

O lead entra por um canal de marketing ou operacao.

Exemplos:

  • anuncio Meta Ads
  • formulario de landing page
  • webhook de ferramenta externa
  • inbox com origem em WhatsApp ou Instagram
  • importacao manual

Etapa 2. Recepcao no PulseSaaS

O sistema recebe o lead e grava:

  • dados de identificacao
  • origem
  • owner do workspace
  • afiliado, quando houver atribuicao
  • contexto da campanha
  • trilha inicial de auditoria

Etapa 3. Triagem

O lead entra com status inicial, por exemplo:

  • new
  • contacted
  • qualified
  • lost

Nessa fase o produtor ou a automacao pode:

  • manter no pool geral
  • atribuir ao afiliado correto
  • aplicar regra por origem
  • mover para pipeline especifico

Etapa 4. Atendimento

O responsavel faz o primeiro contato e registra:

  • atividade
  • observacoes
  • proximo passo
  • tarefa
  • mudanca de status

Etapa 5. Qualificacao

Se o lead for valido:

  • associa empresa
  • associa contato
  • cria oportunidade
  • posiciona no pipeline / kanban

Se nao for valido:

  • marca como perdido
  • registra motivo

Etapa 6. Conversao comercial

O lead convertido vira:

  • contact
  • deal

E passa a ser acompanhado no funil comercial.

Etapa 7. Pos-conversao e retroalimentacao

Quando a venda avanca ou fecha, o sistema deve poder:

  • gerar eventos internos
  • alimentar automacoes
  • disparar conversao para plataformas de ads
  • atualizar metricas de produtor e afiliado

5. Fluxograma macro

flowchart TD
    A[Plataformas de marketing e canais] --> B[Entrada do lead no PulseSaaS]
    B --> C[Registro da origem e contexto]
    C --> D{Qual canal recebeu?}

    D -->|Canal do produtor| E[Lead do produtor: affiliate_id null]
    D -->|Canal do afiliado X| F[Lead do afiliado X: affiliate_id X]

    E --> G[Triagem pelo produtor no funil dele]
    F --> G2[Triagem pelo afiliado X no funil dele]

    G --> H{Lead qualificado?}
    G2 --> H

    G --> H{Lead qualificado?}
    H -->|Nao| I[Marcar como perdido ou descartar]
    H -->|Sim| J[Registrar atividade e tarefa]

    J --> K[Associar empresa]
    K --> L[Converter para contato]
    L --> M[Converter para oportunidade]
    M --> N[Entrar no pipeline / kanban]
    N --> O[Movimentacao comercial]
    O --> P{Venda fechou?}

    P -->|Nao| Q[Follow-up, automacao e novas atividades]
    Q --> N
    P -->|Sim| R[Conversao e metricas]
    R --> S[Evento de conversao / tracking / relatorio]

6. Fluxograma por responsabilidade

flowchart LR
    A[Administrador] --> A1[Monitora produtores]
    A --> A2[Monitora afiliados]
    A --> A3[Monitora receita e integracoes]

    B[Produtor] --> B1[Configura permissoes dos afiliados]
    B --> B2[Recebe leads no seu proprio link/inbox]
    B --> B3[Opera seu proprio funil]
    B --> B4[Acompanha agregados de afiliados sem ler conversas]

    C[Afiliado] --> C1[Recebe leads no seu proprio link/inbox]
    C --> C2[Atende e qualifica]
    C --> C3[Move no kanban]
    C --> C4[Converte em deal]
    C --> C5[Privacidade: produtor nao le suas conversas]

7. Como isso deve funcionar na pratica

Modelo de propriedade dos leads (decisao de produto)

Cada um tem seus proprios leads. Produtor NAO distribui para afiliados.

  • Produtor capta leads pelos canais dele (link publico do produtor, formulario do produtor, inbox WhatsApp do produtor) e atende no funil do produtor.
  • Cada afiliado capta leads pelos canais dele (link publico do afiliado, inbox dele) e atende no proprio funil.
  • Produtor NAO ve os leads dos afiliados nem as conversas individuais.
  • Produtor ve apenas dados agregados (quantidade de leads, conversoes, faturamento por afiliado) para gestao e comissao.
  • Afiliados nao veem leads de outros afiliados.

Cenario A. Lead chega no canal do produtor

  1. Lead acessa o link publico de captura do produtor OU envia mensagem no WhatsApp do produtor.
  2. Sistema cria Lead com affiliate_id = null (lead do produtor).
  3. Produtor ve o lead no funil dele.
  4. Produtor atende e converte. Nada e enviado para afiliados.

Cenario B. Lead chega no canal do afiliado

  1. Lead acessa o link publico de captura do afiliado X OU envia mensagem no WhatsApp do afiliado X.
  2. Sistema cria Lead com affiliate_id = X.
  3. Apenas o afiliado X ve o lead.
  4. Produtor ve apenas o agregado (afiliado X tem +1 lead) sem ler conteudo.

Cenario C. Lead chega via webhook generico sem identificacao de afiliado

  1. Lead chega via webhook generico configurado pelo produtor.
  2. Sistema atribui ao produtor por padrao (affiliate_id = null).
  3. Webhook pode incluir parametro affiliate_slug para atribuir a um afiliado especifico, se o produtor configurar.

8. O que ja existe hoje

Ja existe hoje:

  • modelagem de Lead
  • conversao de Lead -> Contact + Deal
  • affiliate_id para escopo
  • dashboard por produtor e afiliado
  • Activity para timeline
  • Task para follow-up
  • Kanban para deals

Ainda nao existe de forma completa:

  • entrada automatica por Meta Ads
  • entrada automatica por Google Ads
  • webhooks autenticados de captura
  • inbox omnichannel consolidando mensagens em lead
  • atribuicao completa com utm_* e fbclid
  • evento de conversao devolvido para plataformas de marketing
  • automacoes por origem, etiqueta, webhook e etapa

9. Ordem recomendada de implantacao

Fase 1. Captura estruturada

  • criar endpoints de captura
  • criar webhooks autenticados
  • salvar origem, campanha e identificadores

Fase 2. Atribuicao

  • definir regra de atribuicao
  • suporte a pool geral
  • suporte a atribuicao por afiliado
  • fallback quando nao houver afiliado definido

Fase 3. Tracking

  • salvar utm_source
  • salvar utm_medium
  • salvar utm_campaign
  • salvar utm_content
  • salvar utm_term
  • salvar fbclid

Fase 4. Operacao automatizada

  • criar tarefas automaticas
  • mover pipeline automaticamente
  • disparar automacoes por origem e etapa

Fase 5. Conversao externa

  • marcar venda
  • disparar evento de conversao
  • retroalimentar a plataforma de ads

10. Desenho recomendado para o PulseSaaS

O desenho mais seguro para o produto e este:

  1. Toda entrada vira Lead.
  2. Todo Lead nasce com origem e dono de workspace.
  3. Se existir contexto de parceiro, salvar affiliate_id.
  4. O produtor pode ver tudo do workspace.
  5. O afiliado ve apenas o que for dele.
  6. O lead qualificado vira Contact e Deal.
  7. O Deal entra no Kanban.
  8. A venda fechada alimenta metricas + tracking + conversao.

Esse fluxo evita bagunca e impede que canais diferentes criem registros desconectados.


11. Recomendacao objetiva

Para o projeto seguir bem, o ideal e tratar a entrada de lead assim:

  • marketing / canal / webhook / inbox sempre entram como Lead
  • Lead e a porta unica de entrada comercial
  • Contact e Deal nascem da qualificacao
  • Kanban opera o deal, nao o lead cru

Isso deixa a jornada limpa:

captura -> triagem -> qualificacao -> contato -> oportunidade -> pipeline -> conversao


12. Proximo passo tecnico sugerido

Depois deste alinhamento, o proximo passo mais coerente e implementar:

  • tabela e campos de tracking no Lead
  • endpoint de captura de leads
  • webhook autenticado para entrada externa
  • regra inicial de atribuicao entre produtor e afiliado

Assim o produto sai do CRM manual e comeca a virar uma maquina real de captura e operacao.

05

Plano: CRM, funil, automação & integrações

Diagnóstico do estado real do código + sugestões priorizadas. Data: 26/06/2026. Base: varredura factual de app/Domain/CRM, app/Domain/Automation, app/Domain/Integration. Tudo aqui tem referência de arquivo:linha pra ser acionável.


1. Diagnóstico honesto (o que já existe vs o que falta)

A premissa de que "o Kanban não tem drag-and-drop" estava errada. O que realmente está faltando é outra coisa — gestão (editar/excluir), paridade entre as duas engines de automação, e a camada de integração via fluxo (outbound/ERP). Quadro real:

Área EXISTE PELA METADE NÃO EXISTE
Kanban/Funil Drag-and-drop (SortableJS) + fallback mobile; múltiplos pipelines por team/afiliado; histórico de movimentação (DealStageMovement); seed automático de 4 etapas Criar pipeline e etapa pela UI (só criar) Editar/excluir/reordenar etapa e pipeline; reordenar colunas por drag
Etiquetas Model polimórfico (lead/deal/conversa/atividade); criar + aplicar/remover; cor; log de eventos CRUD parcial (só criar e aplicar) Editar, excluir, desativar, ordenar (sem campo position)
Etiqueta ↔ Funil Ponte configurável via automação (label_attached → mover; deal_stage_changed → etiquetar) Acoplamento nativo automático (só via regra opt-in)
Automação (Flow Builder grafo) send_message, send_template, send_media, wait real, apply_label, ai_response, branch if/else, lookup_contact, capture_variable, trigger, stage, end; fila + gates manual/auto + retomada por resposta Branch só lê variables (não lê etiqueta/campo direto) Nós de mover etapa, criar tarefa e webhook no grafo
Automação (engine linear legada) Tem move_pipeline_stage, create_task webhook é stub vazio
Integração via fluxo Webhooks inbound robustos (Meta, pagamento, shipping/ERP-aviso, canal genérico) Catálogo lista generic_webhook/generic_shipping mas sem provider (decorativo) Nó HTTP outbound real; conector ERP genérico (buscar/enviar dados); gatilho webhook_received ligado às automações
API pública API REST v1 (tudo comentado em routes/api.php:9-28)

Causa-raiz de metade dos buracos: existem duas engines de automação coexistindo (RunAutomationGraph.php = grafo novo; RunAutomation.php = linear legada). A legada tem ações que a nova não tem (move_pipeline_stage, create_task, webhook), mas só o grafo está exposto no builder visual. Unificar é o maior destravamento de valor por esforço.


2. Tema A — Funil/Kanban virar CRM de verdade

O que já serve: mover card arrastando, múltiplos funis, histórico de quem moveu o quê e quando. Isso já é um Kanban utilizável.

O que falta pra ser um funil de CRM "completo":

# Item Por que importa Esforço
A1 Editar / excluir / reordenar etapa (hoje só store) Funil real muda: renomear "Proposta" → "Negociação", remover etapa morta, reordenar. Sem isso o funil fica engessado no seed Baixo (faltam rotas update/destroy + UI)
A2 Editar / excluir pipeline Mesmo motivo, no nível do funil Baixo
A3 Reordenar colunas por drag (já tem position na etapa) UX esperada de Kanban; o campo já existe, falta o handler de drag de coluna Baixo-médio
A4 Limpar o legado DealStage/coluna stage convivendo com o modelo novo Dois conceitos de "etapa" no código = bug latente e confusão Médio (migração de dados)
A5 WIP/contadores e valor por etapa (soma de R$ por coluna) Vira ferramenta de gestão, não só lista Baixo (agregação na BuildKanbanBoardData)
A6 Filtros no board (por afiliado já existe; faltam: etiqueta, responsável, período) Funil grande fica inútil sem filtro Médio

Recomendação: A1+A2 primeiro (destravam o uso real e são baratos). A5 é alto valor percebido por baixo custo.


3. Tema B — Etiquetas

O que já serve: criar, aplicar/remover, cor, log. Cobre o caso básico.

O que falta:

# Item Por que importa Esforço
B1 Editar / excluir / desativar etiqueta (campo is_active existe, sem UI) Etiqueta errada hoje é pra sempre; bagunça acumula Baixo
B2 Ordenar etiquetas (não há campo position) Etiqueta é motor de funil e automação — a ordem importa pra leitura Baixo (migration + UI)
B3 Tela dedicada de gestão de etiquetas (hoje espalhada no dashboard) Centraliza a curadoria Baixo-médio
B4 Etiqueta com "ação ao aplicar" (ex.: ao aplicar "Cliente VIP", mover de funil) Transforma etiqueta no gatilho nº 1 do CRM — ver Tema C Médio

4. Tema C — Acoplar Etiqueta ↔ Funil (o "motor" do CRM brasileiro)

Hoje a ponte existe só via automação configurada manualmente, e o nó de mover etapa nem está no Flow Builder novo. Esse é o ponto que o usuário levantou: "movimentar o lead pelo funil baseado na etiqueta e no flow de automação".

Estado: gatilhos label_attached e deal_stage_changed já disparam (ToggleLabelOnRecord.php:56, MoveDealToStage.php:61). A ação move_deal_stage existe só na engine linear (RunAutomation.php:277). Logo: tecnicamente possível, mas não montável no builder visual.

Plano:

# Item Resultado
C1 Portar o nó move_pipeline_stage para o grafo (adicionar a ALLOWED_KINDS em SaveAutomationGraph.php:26 + handler em RunAutomationGraph.php) Usuário monta no Flow Builder: "etiqueta X aplicada → move pra etapa Y"
C2 Branch ler etiqueta e campo do contato (hoje branch só lê state->variables) "Se tem etiqueta VIP → caminho A; senão → caminho B" sem gambiarra
C3 Atalho nativo na etapa: "ao entrar, aplicar etiqueta Z" (config na própria DealPipelineStage) Funil e etiqueta sincronizados sem precisar montar automação

C1 é o item de maior impacto do documento inteiro: é o que o usuário pediu, e é portar código que já existe, não criar do zero.


5. Tema D — Unificar as duas engines de automação

O Flow Builder (grafo) é o futuro, mas a engine linear tem 3 ações que faltam nele. Portar essas 3 e aposentar a linear elimina a dívida e destrava recursos.

# Nó a portar pro grafo Origem (já implementado) Esforço
D1 move_pipeline_stage (= C1) RunAutomation.php:277-300 Baixo
D2 create_task RunAutomation.php Baixo
D3 webhook outbound real (hoje stub) RunAutomation.php:302-309 (vazio) Médio — ver Tema E
D4 Ligar gatilhos mortos: lead_created, deal_won, no_response_for_days (declarados em EvaluateAutomations.php:97-99 mas sem fonte que dispare) Adicionar ProcessAutomationEvent::dispatch nos pontos certos Baixo cada

no_response_for_days depende de cron rodando — hoje o servidor não tem scheduler (ver memória project_no_scheduler_cron). Bloqueio de infra, não de código.


6. Tema E — Integração via fluxo (ERP, enviar/buscar dados, webhooks)

Aqui está o maior gap e o maior potencial. O usuário perguntou: "conectar nosso sistema num ERP terceiro e buscar dados de lá? E o contrário, mandar nossos dados pra outro sistema? Webhooks?"

Estado atual:

  • Inbound (terceiro → nós): ✅ robusto. Rotas em routes/web.php:27-53. Mas o gatilho webhook_received não chega às automações (só alimenta o subsistema de Conversão, ConversionController.php:51).
  • Outbound (nós → terceiro): ❌ stub vazio. RunAutomation::dispatchWebhook() não faz HTTP.
  • Buscar dados de fora (ERP → preencher lead): ❌ não existe nó de "GET em API externa".
  • Encaixe pronto: Contact.custom_fields (JSON, Contact.php:37) já existe pra receber dados de ERP — falta só algo que escreva nele.

Plano em 3 níveis (do mais barato/útil ao mais ambicioso):

Nível 1 — Mandar nossos dados pra fora (outbound) — prioridade alta

# Item Resultado
E1 webhook outbound real no grafo: POST com payload (lead/deal/variáveis) pra URL configurável, com headers/secret HMAC e retry "Lead chegou na etapa Fechado → POST pro ERP do cliente". Resolve metade do pedido com 1 nó
E2 Templates de payload (mapear quais campos vão) + log de entrega Cliente confia que o dado chegou

Nível 2 — Receber dados de fora e agir (inbound → automação) — prioridade alta

# Item Resultado
E3 Ligar webhook_received às automações: webhook inbound genérico vira gatilho de fluxo "ERP avisou que pedido faturou → dispara fluxo de pós-venda"
E4 Escrever em custom_fields via webhook/automação ERP manda CPF/status/saldo → grava no contato → usável no funil e na IA

Nível 3 — Buscar dados ativos de API externa (pull) — prioridade média

# Item Resultado
E5 http_request (GET/POST) com mapeamento de resposta → variáveis/custom_fields "Antes de cobrar, consulta saldo no ERP e decide o caminho". É o conector ERP genérico de verdade
E6 Conectores prontos (Asaas, Bling, Tiny, Omie) por cima do nó genérico Plug-and-play pros ERPs/gateways brasileiros mais comuns

Nível 4 — API pública (pra terceiros integrarem com a gente) — prioridade média/baixa

# Item Resultado
E7 Reativar API REST v1 (hoje 100% comentada em routes/api.php:9-28) com auth por token Terceiros leem/escrevem leads, deals, mensagens programaticamente. Vira plataforma, não só app

Recomendação: E1 + E3 primeiro. Juntos entregam o "manda e recebe dados de qualquer sistema via webhook" — que é 80% do que o mercado chama de "integração com ERP" — com esforço médio. E5/E6/E7 são a evolução pra plataforma aberta.


7. Roadmap sugerido (ordem por valor/esforço)

Sprint 1 — Tornar o CRM gerenciável (baixo esforço, destrava uso real)

  • A1, A2 (editar/excluir etapa e pipeline)
  • B1, B2 (editar/excluir/ordenar etiqueta)
  • A5 (valor por etapa no board)

Sprint 2 — Unificar automação + acoplar etiqueta↔funil (o pedido central)

  • C1/D1 (nó mover-etapa no Flow Builder)
  • D2 (nó criar-tarefa no grafo)
  • C2 (branch lê etiqueta/campo)
  • D4 (ligar gatilhos lead_created, deal_won)

Sprint 3 — Integração via fluxo (o grande diferencial)

  • E1 (webhook outbound real)
  • E3 (webhook_received vira gatilho)
  • E4 (gravar custom_fields via inbound)

Sprint 4 — Conector ativo + plataforma aberta

  • E5 (nó http_request genérico)
  • E6 (conectores Bling/Tiny/Omie/Asaas)
  • E7 (API REST v1)

8. Decisões que precisam do Caio

  1. Aposentar a engine linear legada (RunAutomation.php) depois de portar os 3 nós, ou manter as duas? (Recomendo aposentar — dívida técnica e fonte de bug.)
  2. Limpar o legado DealStage/coluna stage (A4) — exige migração de dados; quando?
  3. no_response_for_days e qualquer fluxo por tempo dependem de cron 24/7 — hoje o servidor não tem scheduler (memória project_no_scheduler_cron). É bloqueio de infra: sem isso, "esperar 3 dias e cobrar" não roda. Priorizar a infra antes de prometer esses fluxos.
  4. Conectores ERP: focar nos brasileiros (Bling, Tiny, Omie) ou só entregar o webhook genérico e deixar o cliente montar?
  5. Abrir API pública (E7)? É o que transforma o produto em plataforma, mas adiciona superfície de segurança e suporte.

9. Resumo de uma linha

O CRM já é utilizável (Kanban com drag, etiquetas, histórico, automação visual com wait real e branch). O que falta é gestão (editar/excluir), paridade entre as duas engines (portar mover-etapa/tarefa/webhook pro grafo) e a camada de integração via fluxo (outbound real + inbound como gatilho + conector ERP) — sendo que boa parte já existe em código e só precisa ser portada/ligada, não criada do zero.

06

Backlog: builder & atendimento

Documento autoritativo de tasks pendentes desta frente (criado 2026-06-10). Consolida o spec do redesign do atendimento (WhatsApp CRM) + o épico do Builder de Automações (CRUD/versionamento/simulador/IA/consolidação/atributos/botões/captura).

Como ler: cada task tem ID, estado (⬜ aberta · 🔵 andamento · ✅ feita), tamanho (P ~2h · M ~1d · G ~2-3d · GG sprint+) e EXISTE/NOVO (o que já está no código vs. precisa nascer). Métrica do projeto: "dá pra demonstrar vendendo?", não "teste verde". Regras invioláveis: production-grade (sem hack), privacidade do afiliado, handoff/WIP ao vivo.


🧭 Estado atual (fundação que JÁ existe — NÃO refazer)

  • Modelo de grafo: Automation, AutomationStep (KINDs: trigger, branch, lookup_contact, capture_variable, send_message, send_template, send_media, wait_seconds, apply_label, webhook, ai_response, end), AutomationEdge (handles default/truthy/falsy/found/not_found), ConversationAutomationState (running/paused/awaiting_operator/completed/failed).
  • Builder visual React Flow em /automations/{id}/builder (canvas drag-drop, paleta, painel de propriedades, save com validação anti-ciclo DFS). AutomationBuilderController (show/graph/save), SaveAutomationGraph.
  • Motor RunAutomationGraph (branch, lookup, capture, wait, send_media, send_template, apply_label, end; modos automatic/manual; fireManualStep já percorre config['items']).
  • Painel de automação no chat (AUTOMAÇÃO→ETAPAS-abas→ITENS read-only) + automationSelector / projectStages / nodeItems (feito 2026-06-10).
  • CRUD parcial: AutomationController tem index, store, instantiateTemplate.
  • Campos da automations: team_id, affiliate_id, created_by, template_id, name, slug, status, mode, trigger_type/config, action_type/config, last_run_at. automation_logs existe.

🔀 DECISÃO DE ARQUITETURA PENDENTE (bloqueia o épico — decidir com Caio)

Hoje existem dois editores: (a) canvas React Flow (grafo) e (b) editor linear inline no chat. O spec do builder (ETAPAS como abas, edição inline no nome da aba, sem modal/redirect) descreve um TERCEIRO paradigma: um builder de ETAPAS-em-abas com CRUD inline. Opções:

  • A. Novo builder dedicado de abas (mais fiel ao spec; convive com o canvas pra fluxos ramificados).
  • B. Evoluir o canvas React Flow (potente, mas "abas/inline" não é o forte de um canvas).
  • C. Evoluir o editor inline do chat em uma tela cheia de builder.

Recomendação inicial: A — builder de abas dedicado, reusando SaveAutomationGraph por baixo (etapa = nó com config['items']; grafo continua a verdade interna). Confirmar antes de codar.

🧱 DECISÃO DE MODELO: ETAPA = grupo de itens

Hoje 1 nó = 1 mensagem. "Etapa = grupo de itens/ações" pode ser:

  • Minimal (recomendado p/ começar): etapa = 1 nó com config['items'] (array de itens). Motor já envia items em sequência. Zero migration. Limite: itens só lineares dentro da etapa.
  • Normalizado: tabelas automation_stages + automation_step_items. Mais limpo, mais caro, migration + refactor de projeção/save. Migrar pra cá quando o minimal apertar.

FRENTE A — Redesign do Atendimento (pendências do spec WhatsApp)

RA-01 — Editor de ITENS por etapa (inline) — ⬜ G · NOVO

Estender funnelEditor (app.js) + UpdateFunnelRequest + SaveLinearFunnel + _funnel-editor pra montar VÁRIOS itens dentro de uma etapa nomeada (add/remover/reordenar texto/mídia/áudio/botões), gravando config['items']. Destrava "Saudação = 2 msgs + imagem + áudio" do spec. Aceitação: operador cria etapa com N itens; "Executar Etapa" dispara todos em sequência.

RA-02 — Polish de contraste da lista de conversas — ✅ FEITO 2026-06-16

Não-lida: fundo tingido reforçado (bg-brand/10, sem sobrepor a ativa) + hora verde (text-success) — corrigido bug text-brand-soft0 (classe inexistente; a hora ficava sem cor). Conversa selecionada: barra lateral + fundo mais fortes (active 20%/14%; hover não apaga o destaque). Hover geral mais visível. Nome/prévia/hora já estavam em primary/secondary/muted.

RA-03 — Sweep emoji → SVG (regra absoluta) — ✅ JÁ ESTAVA FEITO (verificado 2026-06-16)

Varredura confirmou: composer/header/ticks/selos já são SVG (sweep de 06-10 + ticks trocados no P3 de 06-16). Únicos emojis restantes = conteúdo legítimo do SELETOR de emoji (usuário insere na mensagem), não decoração de UI. Guards DesignToken/HumanLabel verdes.

RA-04 — Tela de customização de tema COMPLETA — ⬜ G · NOVO

Estender tenant/branding pra sobrescrever TODA a paleta (--primary, --message-out, --surface, --background, --wa-*…), não só primary/secondary. Padrão = verde WhatsApp; override opcional (white-label). Futuro: trava por assinatura. Por ora liberado. (Decisão Caio 06-10.)

RA-05 — SLA com dados reais — ⬜ M · NOVO

$sla (tempo sem resposta, tempo médio) hoje não é populado → seção some. Computar em BuildInboxData (ou action dedicada) + alertas (conversa parada, lead esquecido, SLA vencido).

RA-06 — Auditoria com dados reais — ⬜ M · NOVO

$auditLog não é populado. Registrar ações (operador/data/hora/ação) e expor na aba Auditoria. Considerar reusar automation_logs + um log de ações do operador.

RA-07 — Busca avançada — ⬜ M · NOVO

Busca por nome/telefone/email/tag/pedido/empresa/protocolo (hoje só name/email/phone/subject).

RA-08 — Transferência de atendimento — ⬜ P · EXISTE-base

Atribuição já existe; falta UX explícita de "transferir para operador X" + registro.

RA-09 — Fixar/silenciar/ciclo de vida — ⬜ M · NOVO (migration)

muted_until e lifecycle_stage não existem (ver T-018 do checklist técnico). Fixar já existe.

RA-10 — Busca global / histórico de automações por contato — ⬜ M · NOVO

Buscar em conversas/contatos/pedidos/tags/empresas; listar automações já executadas pro contato.


FRENTE B — Builder de Automações (épico CRUD completo)

Spec do Caio 2026-06-10: AUTOMAÇÃO → ETAPAS → ITENS → AÇÕES, montar/editar/testar/publicar sem sair do builder, edição inline, versionamento, simulador, IA, atributos.

B1 — CRUD de Automações

BA-01 — Campos novos: descrição, categoria, canal, versão — ⬜ P · NOVO (migration)

Migration add em automations: description, category, channel, version (status já existe).

BA-02 — CRUD completo (duplicar/arquivar/excluir/publicar/despublicar) — ⬜ M · NOVO

Endpoints + ações: duplicate (clona steps+edges+config), archive (status), delete (soft?), publish/unpublish (status active↔draft). Create/edit/instantiate já existem.

BA-03 — Exportar / Importar automação (JSON) — ⬜ M · NOVO

Export = dump do grafo (nodes/edges/config/mode/fields) em JSON; Import = valida + recria via SaveAutomationGraph. Cuidado: ids de integração/etiqueta por tenant (remapear ou avisar).

BA-04 — Edição inline do nome (sem página nova) — ⬜ P · NOVO

Clique no nome da automação → input inline → PATCH. Mesmo padrão já usado no contato do chat.

B2 — CRUD de Etapas

BA-05 — Etapa como agrupador (criar/renomear/duplicar/reordenar/mover/excluir) — ⬜ G · NOVO

Depende da decisão de modelo (minimal config['items'] vs normalizado). Renome inline na aba.

B3 — CRUD de Itens & Ações

BA-06 — Itens: editar/duplicar/mover/excluir inline — ⬜ G · NOVO

Tipos: Mensagem✅, Imagem/Áudio/Vídeo/Documento✅(send_media), Condição✅(branch), Espera✅(wait), Tag✅(apply_label), Webhook✅(kind), IA✅(ai_response) — Botão❌ e Nota❌ são novos.

BA-07 — Construtor de Botões interativos — ⬜ GG · NOVO

Botão: texto/destino/tipo/ação. Tipos: ir-p/etapa, ir-p/automação, aplicar-tag, chamar-operador, encerrar-conversa, abrir-URL, executar-webhook. Exige: modelo de botão no config, suporte a interactive message na WABA (/messages type=interactive buttons) e no Evolution, e roteamento no motor (resposta do botão → handle/edge correspondente). Item de maior complexidade.

BA-08 — Bloco Nota (interno, não enviado) — ⬜ P · NOVO

Item que documenta o fluxo sem enviar nada ao cliente.

B4 — Captura & Atributos

BA-09 — Captura de resposta enriquecida — ⬜ M · NOVO (estende capture_variable)

Pergunta + atributo de destino + validação + obrigatório. Hoje capture_variable só guarda em variável.

BA-10 — Gerenciador de Atributos — ⬜ G · NOVO (domínio novo + migration)

Atributos do contato com tipos: texto/número/boolean/lista/data/telefone/email/moeda. CRUD + busca. Liga com Contact.custom_fields (já existe JSON) ou tabela dedicada de definição de atributos.

B5 — IA & Consolidação

BA-11 — Bloco "Interpretar Resposta" (IA classificadora) — ⬜ G · NOVO

Novo KIND interpret_response: classifica intenção (ex.: interesse/desinteresse/suporte) e alimenta condições (branch). Usa o domínio AI (RunAiAgent). Resultado vira variável p/ o branch.

BA-12 — Consolidação de mensagens (anti-fragmentação) — ⬜ G · NOVO

Bloco/config "aguardar estabilização": ao receber inbound, segura X segundos; cada nova mensagem reinicia o contador; só processa após silêncio. Toca IngestIncomingMessage + job de debounce (precisa queue worker — infra). Processa as mensagens juntas.

B6 — Teste & Versões

BA-13 — Modo de teste / simulador — ⬜ GG · NOVO

Dry-run do motor sem enviar nem publicar: simula cliente/respostas, mostra atributos capturados e caminho percorrido. Requer um modo "sandbox" no RunAutomationGraph (sem dispatch real).

BA-14 — Controle de versões — ⬜ G · NOVO (migration)

Tabela automation_versions (snapshot do grafo por alteração: v1.0, v1.1…). Restaurar versão.


📌 Ordem sugerida de execução

  1. Decisões (com Caio): paradigma do builder (A/B/C) + modelo de etapa (minimal/normalizado).
  2. RA-01 (editor de itens por etapa) — destrava o conceito ETAPAS→ITENS na prática.
  3. RA-02 + RA-03 (polish + emoji→SVG) — fecha o visual do atendimento.
  4. BA-01 + BA-04 + BA-02 (campos + nome inline + CRUD básico) — gestão sem telas externas.
  5. BA-05/06/08 (etapas + itens + nota) — o coração do builder.
  6. BA-09 + BA-10 (captura + atributos).
  7. BA-11 + BA-12 (IA + consolidação).
  8. BA-07 (botões interativos — alto custo, depende de provider).
  9. BA-13 + BA-14 (simulador + versões).
  10. RA-04..RA-10 (tema completo + lacunas) intercaladas conforme prioridade de venda.

⚠️ Tensão registrada

Este épico é muita largura — contraria o pivô project_sellable_whitelabel_pivot ("fechar UMA jornada vendável, parar de adicionar largura"). É decisão do Caio retomar o builder; seguir a direção dele, mas priorizar o que aproxima de "dá pra vender" (RA-01..03 e BA básico) antes do que é avançado (simulador, botões interativos, versões).

07

Estudo: saldo pré-pago WhatsApp

Data: 2026-06-17 · Autor: sessão Claude/Caio · Status: estudo (pré-implementação)

Origem: ao conectar a Silviane, a Meta retornou "essa permissão só pode ser atribuída ao parceiro responsável pela cobrança". Para sermos o responsável pela cobrança precisamos compartilhar uma linha de crédito com a WABA do cliente — e, para isso fazer sentido como negócio, cada usuário precisa ter um saldo pré-pago que debitamos por uso. Este documento estuda viabilidade (técnica, financeira, legal/fiscal) e propõe um plano de execução.


1. O problema, em uma frase

Hoje cada cliente conecta o número e a própria conta da Meta dele paga as mensagens (cartão na BM do cliente). Para nós controlarmos o envio, vendermos com margem e oferecermos pré-pago, nós precisamos pagar a Meta (via nossa linha de crédito) e cobrar o cliente via um saldo que debita conforme o uso. É o modelo de BSP/Tech Provider revendendo mensageria.


2. Como a Meta cobra hoje (fundamentação)

  • Modelo por mensagem desde 01/07/2025 (acabou o "por conversa"). Cobra-se cada template entregue, por categoria e país do destinatário.
  • Categorias e preço (Brasil, ordem de grandeza 2025/26): Marketing ≈ US$ 0,0625/msg; Utilidade (utility) bem mais barato; Autenticação intermediário. Varia ~13× por país.
  • Grátis: toda mensagem recebida; mensagens dentro da janela de 24h (atendimento) e utility dentro da janela; conversas iniciadas pelo cliente. Só template outbound é cobrado.
  • Dado de custo disponível por mensagem: o webhook de status traz pricing / pricing_analytics (categoria, modelo, valor/tier por mensagem entregue). Há ainda account_update com VOLUME_BASED_PRICING_TIER_UPDATE (descontos por volume). → dá para debitar o saldo com o custo REAL, não estimado. Esse é o pilar técnico que torna o pré-pago preciso.

Fontes: Meta for Developers (pricing, credit lines), CleverTap/hello-charles (mudança jul/2025).


3. O que "ser o responsável pela cobrança" exige (linha de crédito)

No modelo Tech Provider, o parceiro estende uma linha de crédito à WABA do cliente. Quem tem a linha compartilhada vira o billed party (quem a Meta cobra). Sem isso, o número usa o cartão do cliente — e algumas permissões (a que a Silviane reclamou) só existem para o dono da linha.

Pré-requisitos (lado Meta, BM da PLATAFORMA/produtor):

  1. Business Verification concluída no BM que vai deter a linha.
  2. Método de pagamento válido + linha de crédito ativa nesse BM (cartão/cobrança Meta).
  3. App com Advanced Access de whatsapp_business_messaging (já temos — números virgens enviam).
  4. API de compartilhamento de linha de crédito (Business Management API):
    • GET /{business_id}/extendedcredits → pega o allocation_config_id da linha.
    • POST /{credit_line_id}/whatsapp_credit_sharing_and_attach?waba_id=...&waba_currency=... → compartilha a linha com a WABA do cliente e devolve um allocation_config_id.
    • No Embedded Signup, dá para automatizar isso logo após o subscribed_apps/register.
  5. Revogar ao desconectar: DELETE do attach (devolve a WABA ao cartão dela).

Implicação de negócio: a Meta cobra a nós (fatura única do BM da plataforma). Nós rateamos esse custo por tenant e debitamos do saldo de cada um. O lucro é o markup sobre o custo Meta (ex.: custo US$0,0625 → cobra R$ X com margem).

⚠️ Decisão 🔑 Caio: qual BM detém a linha — o BM da plataforma (centraliza tudo, mais simples) ou um BM por produtor? Recomendado: BM da plataforma detém a linha; produtores e afiliados são camadas de saldo por cima.


4. Arquitetura proposta do saldo (carteira + razão/ledger)

Modelo contábil de carteira (wallet) + livro-razão (ledger) append-only — padrão de qualquer sistema de créditos confiável (cada centavo rastreável).

wallet (1 por tenant/usuário cobrável)
  - balance_cents (saldo atual, cacheado do ledger)
  - currency (BRL)
  - low_balance_threshold_cents (alerta)
  - auto_recharge (opcional: recarrega X quando cai abaixo de Y)

wallet_ledger (append-only, NUNCA edita/deleta — é a verdade contábil)
  - wallet_id
  - type: credit (recarga) | debit (uso) | adjustment (ajuste manual) | refund | bonus
  - amount_cents (+ entra, - sai)
  - balance_after_cents (saldo após o lançamento)
  - source: pix | card | whatsapp_message | manual | promo
  - reference (id da mensagem / id do pagamento / nota)
  - meta_cost_cents (custo real Meta, quando uso) + markup_cents (nossa margem)
  - description, created_at

Fluxos:

  • Recarga: cliente paga (PIX/cartão) → webhook do gateway confirma → credit no ledger → saldo sobe. Reaproveita o PaymentGateway + ProcessPaymentWebhook que já existem.
  • Débito por uso: webhook de status da Meta entrega o pricing da mensagem → calculamos custo Meta + markupdebit no ledger → saldo cai. Idempotente por wamid (não debita 2×).
  • Bloqueio: antes de enviar template, checa balance >= custo estimado; saldo zero/negativo → bloqueia envio com mensagem clara ("Saldo insuficiente, recarregue"). Recebidas e janela de 24h nunca bloqueiam (são grátis).
  • Extrato: lista o ledger paginado, com filtro por tipo/período, e exportável (CSV/PDF).

5. Viabilidade legal/fiscal (Brasil) — o ponto mais importante

A pergunta central: esse saldo me torna uma Instituição de Pagamento (regulada pelo BACEN, Lei 12.865/2013)? Resposta — provavelmente NÃO, SE desenhado certo.

  • Vira moeda eletrônica / conta pré-paga (escopo BACEN) quando o saldo é resgatável em dinheiro, transferível a terceiros, ou gasta com terceiros. Aí precisa virar IP autorizada.
  • Crédito pré-pago do PRÓPRIO serviço (como crédito de celular pré-pago, créditos da Uber, fichas de um app) é adiantamento de serviço, fora do escopo de IP. É o desenho que queremos.

Regras de desenho para ficar fora de IP/BACEN (🔑 confirmar com contador/advogado):

  1. Saldo gasta no nosso serviço (envio de mensagens). Nunca vira dinheiro, nunca saca.
  2. Não transferível entre usuários.
  3. Não reembolsável em dinheiro (ou reembolso só por exceção/CDC, nunca como saque rotineiro).
  4. Prazo/validade e regras claras nos Termos de Uso.

Fiscal:

  • O que vendemos é serviço (intermediação/envio de mensagens) → NFS-e + ISS (município). Possível enquadrar como serviço de tecnologia/comunicação — 🔑 contador define o código de serviço e a alíquota.
  • Fato gerador: emitir NFS-e na recarga (mais simples) ou no consumo (mais preciso). Reco: NFS-e na recarga = recebimento de adiantamento; reconhecimento de receita conforme consumo (contábil). Contador decide.
  • Markup é receita; o custo Meta é custo de mercadoria/serviço (a fatura em USD da Meta vira despesa, com IOF/câmbio — atenção ao fechamento em dólar).
  • Imposto sobre a fatura da Meta: importação de serviço (a Meta cobra em USD) pode ter PIS/COFINS-importação, ISS-importação, IOF🔑 contador.

LGPD: dados de saldo/extrato/pagamento são dados financeiros → base legal (execução de contrato), retenção, e cuidado especial (não logar dados de cartão; tokenizar no gateway).

Antifraude/PLD: mesmo fora de IP, recarga com cartão pede antifraude (chargeback) e, em volume, políticas de PLD básicas.

Conclusão de viabilidade: Viável como "créditos de serviço pré-pago", sem virar fintech regulada, DESDE QUE o saldo seja não-sacável/não-transferível e os termos + a contabilidade reflitam isso. Precisa de validação de um contador e de um advogado antes do go-live (🔑).


6. O que JÁ existe vs o que falta desenvolver

Reaproveitável (já no código):

  • App\Domain\BillingPaymentGateway (genérico), ProcessPaymentWebhook, PublicPaymentWebhookController, BillingInvoice, PaymentGatewayRequest. → base para recarga e comprovantes.
  • WabaCloudProvider + webhooks de status (MetaWebhookRouterController, PublicChannelWebhookController) + ApplyMessageStatusUpdate. → onde plugar a leitura do pricing para o débito.
  • Módulo de permissões F5 e escopo afiliado↔produtor → para quem vê qual saldo.

A construir (novo):

  1. Carteira + ledger (migrations, models Wallet/WalletLedgerEntry, action de débito/crédito idempotente).
  2. Integração de linha de crédito Meta (compartilhar/revogar no Embedded Signup + no connect/disconnect).
  3. Captura de custo por mensagem (ler pricing no webhook de status → débito com markup).
  4. Recarga (tela + PIX/cartão via gateway; idealmente PIX por custo/instantâneo).
  5. Gate de envio por saldo (bloqueio quando insuficiente; só para template cobrado).
  6. Página de Saldo + Extrato (saldo atual, projeção de duração, histórico, exportar, recarga).
  7. Alertas (saldo baixo, recarga automática opcional) — ⚠️ depende de cron para varreduras periódicas (🔑 mesma pendência de infra do schedule:run).
  8. Conciliação (fatura mensal da Meta em USD × soma dos débitos; relatório de margem).
  9. Fiscal (emissão de NFS-e na recarga via gateway/contador; armazenar documento).

7. Plano de execução faseado

Filosofia: entregar vendável cedo. Dá para faturar com pré-pago manual antes de automatizar a linha de crédito e a NFS-e.

Fase Entrega Depende de Sem cron?
F0 — Decisões 🔑 BM dono da linha; markup; política de reembolso; contador valida fiscal + advogado valida termos Caio + contador/advogado
F1 — Carteira + Ledger wallets, wallet_ledger; crédito/débito idempotente; saldo no painel (read-only) F0
F2 — Recarga tela de recarga + PIX/cartão (gateway existente) + webhook credita F1
F3 — Débito por uso ler pricing no webhook de status → debitar custo+markup por wamid F1
F4 — Gate de envio bloquear template quando saldo insuficiente (recebidas/janela nunca) F3
F5 — Página Saldo + Extrato saldo, projeção, histórico filtrável, exportar CSV/PDF F1-F3
F6 — Linha de crédito Meta compartilhar/revogar credit line no Embedded Signup + connect F0 (BM verificado)
F7 — Alertas + recarga auto saldo baixo, auto-recharge F2 ❌ cron 🔑
F8 — Conciliação + NFS-e fatura Meta × débitos; emissão de nota na recarga F0 (contador) parcial

Caminho mínimo para começar a cobrar (MVP vendável): F0 → F1 → F2 → F3 → F4 → F5. Dá um pré-pago real, com recarga, débito preciso e bloqueio — sem depender de cron. F6 (automatizar a linha de crédito) pode começar manual no painel da Meta enquanto não está pronto. F7/F8 são maturidade.


8. Riscos e decisões 🔑 do Caio

  • 🔑 Fiscal/jurídico: validar com contador (NFS-e, ISS, importação da fatura Meta em USD, IOF) e advogado (termos de uso que mantêm o saldo fora de "moeda eletrônica"). Bloqueante para go-live, não para desenvolver.
  • 🔑 BM dono da linha de crédito e quem assume o risco de inadimplência (cliente gasta o crédito Meta e dá chargeback na recarga).
  • 🔑 Markup e moeda: preço por categoria/país com margem; proteção contra variação do dólar (a Meta cobra em USD, o cliente paga em BRL).
  • 🔑 Política de reembolso: não-reembolsável (mais seguro juridicamente) vs reembolsável (mais amigável, porém aproxima de "conta de pagamento").
  • Cron schedule:run (já pendente): necessário para F7 (alertas/auto-recharge) e conciliação agendada.
  • Câmbio/fechamento: débito por mensagem usa USD da Meta → converter com uma taxa do dia (definir fonte/markup de câmbio).

8b. Sem contador/advogado (realidade 2026-06-17) — caminho de menor risco

Caio sinalizou que não temos contador nem advogado. Isso muda a recomendação, mas não inviabiliza. Pontos honestos:

  • Contabilidade já é praticamente obrigatória pra qualquer CNPJ que fatura cliente (emitir nota, apurar imposto). Se já cobramos assinatura, já existe algum enquadramento; se não, contabilidade online (ex.: Contabilizei/Agilize) resolve barato — independe deste recurso.

  • O risco mora no "saldo guardado" (pré-pago), não na cobrança em si. A forma de menor risco jurídico de monetizar mensageria sem advogado AGORA é:

    ➜ PÓS-PAGO MEDIDO (recomendado p/ começar): em vez de carteira/saldo, medimos o uso e faturamos por consumo (igual conta de AWS/nuvem). Não há valor armazenado → a discussão de "moeda eletrônica / Instituição de Pagamento" praticamente desaparece (é só cobrança de serviço consumido). Reaproveita 100% a fatura + gateway que já existem. Contra: risco de inadimplência (cliente usa, não paga) — mitigado porque controlamos o envio e cortamos na hora.

  • Pré-pago (saldo) continua possível, mas exige o desenho ultra-conservador (não reembolsável, não transferível, só serviço próprio + Termos de Uso) e uma revisão jurídica pontual antes de escalar — não precisa de advogado fixo, basta uma consulta única.

  • O que dá pra construir JÁ, sem profissional: toda a engenharia (medição de uso por mensagem via webhook pricing, fatura/cobrança, página de consumo/extrato, gate de envio). O que precisa de humano: o enquadramento fiscal da nota e (se pré-pago) os termos — resolvível quando começar a faturar de verdade, com contabilidade online + 1 consulta jurídica.

Comparativo rápido:

Modelo Risco jurídico Reuso do código Risco de calote Esforço
Pós-pago medido baixo (cobra serviço consumido) alto (fatura existente) médio (corta envio) menor
Pré-pago (saldo) médio (precisa ToS + revisão) médio baixo (paga antes) maior
Pass-through (cliente paga a Meta) nenhum n/a nenhum mínimo, mas sem margem

Recomendação: começar pós-pago medido (engenharia idêntica à do pré-pago no "medir uso", só muda recarga→fatura). Migrar pra pré-pago depois, quando houver a revisão jurídica. Assim você monetiza já, com o menor risco possível sem advogado.

8c. Estruturação fiscal — CNPJ que nunca emitiu nota (decisão: PRÉ-PAGO)

Caio escolheu pré-pago e pediu pra estruturar a parte fiscal (temos CNPJ, nunca emitimos nota). Roteiro prático (confirmar específicos com contabilidade — barato):

O que estamos vendendo (pra fins de nota): serviço (acesso/uso da plataforma + envio de mensagens). Logo: NFS-e (nota de serviço, municipal, imposto ISS) — não é produto (sem NF-e/ICMS). A recarga de saldo = recebimento antecipado de serviço.

Passos pra habilitar emissão (uma vez):

  1. Contabilidade online (Contabilizei/Agilize/etc., ~R$100-300/mês — necessário de qualquer forma pra um CNPJ que fatura). Ela faz/confirma:
    • Regime tributário (provável Simples Nacional; ver se a atividade/CNAE cobre software/TI).
    • Inscrição Municipal (pré-requisito pra emitir NFS-e na cidade).
    • Código de serviço (LC 116) + alíquota ISS (2–5%). Provável enquadramento: licenciamento de software / serviços de TI / processamento de dados — a contabilidade define.
  2. Emissor de NFS-e: em vez de emitir manual no site da prefeitura, usar uma API fiscal que abstrai as ~5.500 prefeituras e o novo padrão NFS-e Nacional: ex. eNotas, PlugNotas, NFE.io, Focus NFe. Integra no fluxo de recarga → cada recarga emite nota automática.

Quando emitir: na recarga (recebimento) — mais simples e amarrado ao pagamento. O consumo do saldo é baixa contábil, não gera nota nova.

Sobre a fatura da Meta (custo): a Meta cobra em USD (importação de serviço) → atenção a IOF/câmbio e possíveis tributos de importação. Isso é despesa nossa; a contabilidade lança. Não afeta a nota que EMITIMOS ao cliente (essa é sobre o nosso serviço em BRL).

Melhor meio (recomendado): contabilidade online (resolve regime + inscrição municipal + código de serviço) + API de NFS-e (eNotas/PlugNotas) plugada na recarga → emissão automática, sem mexer em prefeitura na mão. Custo total baixo, e a parte técnica (chamar a API ao confirmar o pagamento) eu construo na F2/F8.

Jurídico (pré-pago): o desenho conservador (não reembolsável em dinheiro, não transferível, só serviço próprio) + Termos de Uso mantém o saldo fora de "moeda eletrônica". Sem advogado fixo — basta uma revisão pontual dos Termos antes de escalar. Eu já construo o saldo com essas travas.

O que NÃO bloqueia o desenvolvimento: F1 (carteira+ledger) é estrutura interna, não move dinheiro nem emite nota — pode ser construída já. Fiscal/jurídico só entram na F2 (recarga real) e no go-live.

9. Resumo executivo

  • É viável técnica, financeira e legalmente — sem virar fintech regulada — desde que o saldo seja crédito pré-pago de serviço próprio (não sacável, não transferível) e a parte fiscal/jurídica seja validada por profissional.
  • A Meta dá o custo real por mensagem (webhook pricing), então o débito é preciso, não estimado — diferencial de confiança.
  • Reaproveitamos boa parte do módulo de Billing/gateways; o novo é carteira+ledger, débito por uso, página de saldo/extrato e a integração da linha de crédito.
  • MVP vendável (F0–F5) não depende de cron nem de automação da linha de crédito; dá para começar a cobrar cedo e amadurecer depois.
08

Plano: fluidez & profissionalismo

Data: 26/06/2026 · Status: 📋 DIAGNÓSTICO + PLANO (nada alterado no código) Escopo: camada de interação e acabamento visual (microinterações, estados, consistência, performance percebida, acessibilidade, mobile). Complementa, não repete:

  • REVIEW_UX_LINGUAGEM_AMIGAVEL_2026-06-12.md cuida de copy/idioma/jargão.
  • PLANO_CRM_FUNIL_AUTOMACAO_INTEGRACOES_2026-06-26.md cuida de gaps funcionais (editar/excluir, engines, integração).
  • Este doc cuida do "como parece e como reage" — a sensação de produto pago vs. CRUD de framework. Toda afirmação tem referência arquivo:linha (varredura factual de resources/views, resources/css/app.css, resources/js/app.js, resources/js/flow-builder).

1. Princípios — o que "fluido e profissional" significa aqui

  1. Resposta imediata, verdade depois (optimistic-first). Toda ação do usuário deve ter feedback em <100ms — a UI reage antes do servidor confirmar e reconcilia depois. O inbox já faz isso no envio de mensagem (app.js:1040-1065); essa é a régua para o resto (kanban, etiquetas, toggles de automação).
  2. Sem flash branco: a página nunca "pisca". Recarregar a página inteira para mudar um chip ou aplicar um filtro é o maior delator de "sistema de framework". Onde já existe Alpine/fetch, terminar o trabalho; onde há <a href>/form submit puro, migrar para partial/AJAX.
  3. Um só sistema visual. Um token, um botão, um card, um modal. Hoje convivem 4 sistemas de botão e 2 de KPI card — isso lê como "remendos". Profissional = previsível.
  4. Todo estado tem desenho. Loading, vazio, erro e sucesso não são "o que sobra" — são telas de primeira classe, escritas e desenhadas. Uma tela sem skeleton e sem empty state parece quebrada mesmo funcionando.
  5. Calmo e acessível. Transições curtas (150-250ms) com propósito, respeitando prefers-reduced-motion, foco visível no teclado e contraste em ambos os temas. Movimento que não comunica nada é ruído.

2. Diagnóstico por área

2.1 Inbox / Chat — a joia, com arestas

A tela mais madura (optimistic send, status ticks, empty states existem), mas o acabamento trai:

  • Scroll sem suavidade e "puxões". scrollToEnd() faz scrollTop = scrollHeight instantâneo (app.js:1235-1238); toda mensagem nova/enviada salta. Pior: força scroll ao fim em toda inbound (app.js:992) mesmo se o operador subiu para ler histórico — não há check "está no fim?" nem pílula "novas mensagens abaixo".
  • Bolhas surgem sem animação de entrada (app.js:426 monta className estático) — optimistic e realtime aparecem com corte seco.
  • Falha de envio sem retry. Bolha falha marca ícone de erro (app.js:1227-1233) mas não oferece botão "tentar de novo"; em mídia, o anexo é perdido (só o texto volta ao composer, app.js:1115).
  • Botão enviar não mostra "enviando". Só alterna :disabled (conversation.blade.php:669-671), ícone de avião parado — ambíguo.
  • requestRefresh()window.location.reload() quando chega mensagem de conversa ainda não renderizada (app.js:752-755) — flash de página inteira, péssimo no mobile.
  • Ações só-hover, inúteis no touch: pin (index.blade.php:331, opacity-0 → hover), responder/favoritar bolha (conversation.blade.php:249).
  • "Online" é fake (heurística de last_message_at <5min, conversation.blade.php:16,56-57) e não há indicador de "digitando" (typing) em lugar nenhum.
  • Cores hardcoded vs. tokens: botão enviar bg-[var(--primary,#25D366)] (conversation.blade.php:670); avatar bg-{{ $avatarColor }}-600 (classe Tailwind dinâmica que o JIT pode não gerar, index.blade.php:280); slate cru espalhado (index.blade.php:116,126, conversation.blade.php:716,760,813,851) ao lado de tokens --wa-* — mesma tela, dois cinzas conforme o caminho de render (app.js:540,1219 vs conversation.blade.php:329).
  • Falhas silenciosas de realtime: sem window.Echo é só console.warn (app.js:614-616,961-963) — operador para de receber ao vivo sem aviso. Som de notificação via AudioContext (app.js:690-711) é bloqueado até 1º gesto no mobile e a falha é só logada.

2.2 Kanban — drag bom, periferia recarrega tudo

SortableJS com ghost/chosen/optimistic move está sólido. Os furos:

  • Rollback de drag = reload de página inteira (app.js:209 submitFallbackMove()form.submit()); idem mobile "Mover card" (app.js:240) e quando não há coluna correspondente (app.js:230). Falha deveria animar de volta + toast, nunca recarregar.
  • Filtro do quadro é GET que recarrega tudo (index.blade.php:54-84), perde scroll.
  • Sem skeleton do quadro (render 100% server-side, index.blade.php:106-177); pós-filtro = nav em branco.
  • Sem empty-state de quadro vazio (pipeline sem etapas renderiza flex vazio; só empty de coluna existe, index.blade.php:169).
  • Scroll horizontal de colunas sem scroll-snap no mobile (w-[88vw] sem snap, index.blade.php:106-107).
  • confirm() nativo para excluir funil/etapa (index.blade.php:408,441) e todo CRUD do slide-over recarrega (index.blade.php:286,325,396,424,441).

2.3 Dashboard — quebra no dark mode e dois de tudo

  • Gráficos SVG com cores light hardcoded (grid rgba(15,23,42,0.08), eixo, #f8fafc, traço pipeline, barra — dashboard.blade.php:289,293,305-307): invisíveis no dark mode. Maior problema de profissionalismo da tela.
  • Empty-chart com border-slate-200/text-slate-500 (dashboard.blade.php:354) ilegível no dark.
  • Dois sistemas de KPI card coexistindo: <x-kpi-card> (5 cards) vs dashboard-kpi-card (6 cards) com grids e visuais diferentes e métricas duplicadas (dashboard.blade.php:166-185 vs 422-435).
  • Delta de KPI nunca passado (:delta omitido, dashboard.blade.php:177-183) → a tendência "↑ vs período" (kpi-card.blade.php:44-49) nunca renderiza; KPIs sempre estáticos. E text-{{ $deltaColor }}-300 (kpi-card.blade.php:45) é classe dinâmica que o JIT pode não gerar.
  • Tabs sem transição (x-show sem x-transition, dashboard.blade.php:147,438,513,611).
  • Filtro e todos os forms "Cadastros" recarregam a página (dashboard.blade.php:458,554,640+).
  • Listas "Próximas ações"/"Atividade" sem hover apesar de parecerem clicáveis (dashboard.blade.php:216,242).
  • producer-panel.blade.php: usa tokens dark-only (text-white, bg-white/5, l.19-35) — não adapta a tema; barra de progresso inline sem transição e sem clamp de largura >100% (l.76).

2.4 Automações — toggles e ações recarregam

  • Builder Blade monta <div id="flow-builder"> vazio e carrega o JSX via @vite (builder.blade.php:3-14) — flash em branco até o React hidratar (sem skeleton).
  • Lista: "Nova automação"/"Criar do zero" (index.blade.php:31-39,75-78), toggles Ativar/Desativar (index.blade.php:114-140) e clone/delete (index.blade.php:159-180) são form submits puros — sem spinner, sem optimistic, recarregam para trocar um chip. Delete usa confirm() nativo (l.170).
  • Hover sem transition em ações (index.blade.php:120,133,143,152) enquanto o dropdown (l.157) usa x-transition — inconsistente.
  • Busca client-side sem empty "nenhum resultado" (index.blade.php:89).
  • React (bom, com 1 furo): save tem spinner/disabled/status (FlowBuilder.jsx:557-602,700-714), mas o status "Alterações salvas" nunca auto-some (FlowBuilder.jsx:587). Drag-to-canvas sem highlight da zona de drop (FlowBuilder.jsx:326-346); nós entram sem animação. nodeEditors.jsx:97-199 (upload de mídia) é o melhor padrão de feedback do sistema — copiar para o resto.

2.5 Branding — preview ótimo, salvar sem feedback

  • Preview ao vivo rico (sidebar+chat, tenant/branding.blade.php:201-228,266-304) e aviso de contraste WCAG (l.126-128) são exemplares.
  • "Salvar marca" sem loading/disabled/spinner e sem confirmação inline (depende do banner global, l.260-262).
  • Erros de validação em bloco estático no topo, sem scroll-into-view nem highlight por campo (l.70-78).
  • Botões do preview não têm hover (l.296-301) — não refletem interatividade real.

2.6 Auth — a "esquizofrenia" de marca

  • Login é polido, dark, white-label (auth/login.blade.php: hover/active/:focus-visible em l.80-82, prefers-reduced-motion em l.102-105). Register e forgot-password são Jetstream stock, light, com indigo-500/gray-600 hardcoded (register.blade.php:40-41,50; forgot-password.blade.php:12,40-41) via guest.blade.php:39,44. O cliente white-label sai de uma tela linda para uma genérica de Laravel — quebra de marca grave no primeiro contato.
  • Login "Entrar" sem loading/disabled → duplo-clique pode postar 2x (login.blade.php:217-220). Mesmo problema em todo botão Jetstream.

2.7 Sistêmico (atravessa tudo)

  • 4 sistemas de botão: <x-button> Jetstream cinza bg-gray-800 uppercase (38 usos, button.blade.php:1) · .pulse-btn-primary (31) · .pulse-btn-secondary (22) · .lp-btn (6, landing/login). O botão "primário" do framework nem usa a cor da marca.
  • Sem prefers-reduced-motion global — só o login trata (login.blade.php:102-105); o resto ignora.
  • Sem sistema de skeleton/shimmer — só 2 @keyframes no projeto inteiro (app.css:1630 accordion, app.css:1973 bounce dos typing-dots).
  • Inputs removem o foco (focus:outline-none focus:ring-0, app.css:315-323) e só trocam cor da borda — navegação por teclado some.
  • Navegação 100% full-reload (<a href> em sidebar.blade.php/bottom-nav.blade.php, sem wire:navigate/Turbo) — sem indicador de progresso de página (layouts/app.blade.php:95-113).
  • Toast sem empilhamento: um único <x-banner> global; 2º evento banner-message sobrescreve o 1º e reseta o timer (banner.blade.php:18-19) — notificações simultâneas se perdem.
  • confirm() nativo para destrutivos (kanban, automações) ao lado de modais bonitos no flow-builder — inconsistente.

3. Ações priorizadas

Esforço: P = até ~½ dia · M = 1-3 dias · G = >3 dias. Impacto: 🔴 alto · 🟠 médio · 🟡 baixo.

(a) Microinterações & feedback

Ação Impacto Esforço Onde
Spinner + disabled em todo botão de submit (helper Alpine @submit); resolve duplo-clique 🔴 P login.blade.php:217, tenant/branding.blade.php:260, automations/index.blade.php:31,114, components/button.blade.php
Optimistic toggle de Ativar/Desativar automação (sem reload, com rollback) 🔴 M automations/index.blade.php:114-140
Retry em mensagem falha + preservar anexo de mídia 🔴 M app.js:1115,1227-1233
Botão enviar com troca ícone→spinner enquanto envia 🟠 P conversation.blade.php:669-671
Animação de entrada das bolhas (fade+slide curto) 🟠 P app.js:426 (+ keyframe em app.css)
Highlight da zona de drop no flow-builder; auto-dismiss do status "salvo" 🟠 P FlowBuilder.jsx:326-346,587
Trocar confirm() nativo pelo <x-confirmation-modal> existente 🟠 M kanban/index.blade.php:408,441, automations/index.blade.php:170
Toast com fila/empilhamento (não sobrescrever) 🟡 M components/banner.blade.php:5-21

(b) Consistência visual

Ação Impacto Esforço Onde
Unificar botões num só componente com variantes (primary/secondary/ghost/danger) usando tokens de marca; aposentar bg-gray-800 Jetstream 🔴 M components/button.blade.php + 38 usos <x-button>
Unificar KPI cards num só componente e passar :delta real 🔴 M dashboard.blade.php:166-185,422-435, kpi-card.blade.php
Trocar slate/gray cru por tokens --wa-*/--surface/--text-* no inbox 🟠 M inbox/index.blade.php, conversation.blade.php, app.js (nós injetados)
Eliminar classes Tailwind dinâmicas (bg-{$color}-600, text-{$deltaColor}-300) — usar mapa de classes fixas ou style com token 🟠 P inbox/index.blade.php:280, kpi-card.blade.php:45
Botão enviar usar bg-brand (sem fallback #25D366) 🟡 P conversation.blade.php:670

(c) Performance percebida

Ação Impacto Esforço Onde
Filtros AJAX/partial (kanban + dashboard) sem reload, preservando scroll 🔴 M kanban/index.blade.php:54, dashboard.blade.php:458
Eliminar window.location.reload() do inbox; renderizar a conversa nova via fetch 🔴 M app.js:752-755
Rollback de drag anima de volta + toast em vez de form.submit() 🔴 M app.js:209,230,240
Indicador de progresso de navegação no topo (mesmo sem SPA) 🟠 P layouts/app.blade.php
CRUD do slide-over do kanban e "Cadastros" do dashboard via fetch 🟠 G kanban/index.blade.php:286+, dashboard.blade.php:640+
Scroll suave + check "está no fim?" + pílula "novas mensagens" 🟠 M app.js:992,1235-1238

(d) Estados vazios / erro / sucesso

Ação Impacto Esforço Onde
Sistema de skeleton reutilizável (componente + keyframe shimmer) 🔴 M app.css + inbox, kanban, dashboard
Empty "nenhum resultado" na busca de automações 🟠 P automations/index.blade.php:89
Empty-state de quadro/pipeline vazio 🟠 P kanban/index.blade.php:107
Empty-chart com tokens (legível no dark) 🟠 P dashboard.blade.php:354
Skeleton no shell do builder antes do React hidratar 🟡 P automations/builder.blade.php:3-14
Banner de "conexão ao vivo caiu" quando Echo falha 🟠 M app.js:614-616,961-963

(e) Acessibilidade

Ação Impacto Esforço Onde
prefers-reduced-motion global (cortar transições/animações) 🔴 P app.css (regra @media)
Restaurar foco visível (:focus-visible ring de marca) em inputs/links/cards 🔴 P app.css:315-323, inbox/index.blade.php:277
Corrigir gráficos invisíveis no dark (tokens em vez de cores light) 🔴 M dashboard.blade.php:289-307
Contraste do producer-panel em tema claro 🟠 M producer-panel.blade.php:19-35
aria-live já existe no toast — estender a status dinâmicos (envio/erro) 🟡 P banner.blade.php:31-32

(f) Mobile

Ação Impacto Esforço Onde
Ações só-hover → tap-to-reveal/sempre-visível no touch (pin, responder, favoritar) 🔴 M inbox/index.blade.php:331, conversation.blade.php:249
Som de notificação: desbloquear AudioContext no 1º gesto + fallback visual 🟠 P app.js:690-711
scroll-snap-x nas colunas do kanban 🟡 P kanban/index.blade.php:106-107
Painel de detalhes da conversa como overlay full-screen no mobile 🟠 M conversation.blade.php:695 + app.css:1759
Abreviar valores monetários grandes em cards estreitos 🟡 P dashboard.blade.php:405, producer-panel.blade.php:76

4. Quick wins (≤1 dia) vs Investimentos maiores

✅ Quick wins (alto retorno, baixo custo)

  1. prefers-reduced-motion global — 1 bloco @media no app.css. Acessibilidade instantânea no sistema todo.
  2. Foco visível (:focus-visible) — remover outline-none/ring-0 e padronizar ring de marca. Navegação por teclado volta a existir.
  3. Gráficos do dashboard com tokens — troca de ~6 cores hardcoded; conserta dark mode quebrado.
  4. Spinner + disabled nos botões de submit — helper Alpine genérico aplicado nos CTAs; mata duplo-clique e dá "vida" a auth/branding/automações.
  5. confirm() → modal existente — reusar <x-confirmation-modal> que já existe.
  6. Auto-dismiss do "Alterações salvas" e empties faltantes (busca de automação, quadro vazio, empty-chart tokenizado).
  7. Animação de entrada das bolhas + scroll suave no inbox.

🏗️ Investimentos maiores (planejar)

  1. Unificar o sistema de botões (1 componente, variantes de marca) e migrar register/forgot para o layout dark white-label — fim da "esquizofrenia" Jetstream.
  2. Unificar KPI cards + ligar deltas reais.
  3. Filtros e CRUD via AJAX/partial (kanban, dashboard) — fim do flash de reload. (Considerar um único mecanismo: Alpine+fetch padronizado, ou avaliar wire:navigate/Turbo para o app inteiro.)
  4. Sistema de skeleton reutilizável aplicado às telas server-rendered.
  5. Presença/typing reais no inbox (canal de presença) + banner de queda de realtime + retry de mídia.
  6. Tokenização completa do inbox (eliminar slate cru e nós JS com classes hardcoded fora de sincronia).

5. Definition of Polished — checklist de toda tela nova

Nenhuma tela entra em produção sem passar nesta régua:

Estados

  • Loading: skeleton/placeholder enquanto carrega (nunca tela em branco ou conteúdo velho esmaecido).
  • Vazio: empty-state com 1 frase de contexto + 1 CTA do próximo passo.
  • Erro: mensagem humana + ação de recuperação (retry/reconectar); nunca erro cru de API.
  • Sucesso: confirmação visível (toast/inline) que auto-some.

Feedback & interação

  • Toda ação responde em <100ms (optimistic onde fizer sentido, com rollback).
  • Botão de submit tem disabled+spinner e bloqueia duplo-clique.
  • Nada de window.location.reload() nem full-reload para mudar um chip/filtro/toggle.
  • Destrutivos usam o modal de confirmação do design system (nunca confirm() nativo).
  • Hover, active e :focus-visible definidos em todo elemento clicável.

Consistência

  • Usa os componentes únicos (botão, card, modal, input) e os tokens --brand/--surface/--text-*/--dashboard-*zero cor hardcoded, zero classe Tailwind dinâmica (bg-{$x}).
  • Funciona em tema claro e escuro (conferir contraste de gráficos/ilustrações).
  • Respeita a marca do tenant (white-label) — nada de cor de framework (indigo/gray-800).

Acessibilidade

  • Foco visível e ordem de tabulação coerente.
  • prefers-reduced-motion respeitado.
  • Contraste AA em texto e estados; aria-live em feedback dinâmico.

Mobile

  • Nenhuma ação essencial depende só de hover (tap-to-reveal/sempre-visível).
  • Toque ≥44px, scroll-snap onde há carrossel/colunas, sem overflow de números/labels.
  • Transições 150-250ms, com propósito.

6. Fontes

Varredura factual de resources/views/** (inbox, kanban, dashboard, automations, branding, auth, layouts, components), resources/css/app.css (tokens, keyframes, breakpoints wa-*), resources/js/app.js (optimistic send, drag, realtime, scroll) e resources/js/flow-builder/* (FlowBuilder.jsx, nodeEditors.jsx). Referências arquivo:linha ao longo do §2/§3.

09

Rearquitetura whitelabel

Data de referencia: 2026-05-11 Status: em execucao - Sprint A1

Motivacao

A base atual entrega 70% do modelo de dados pedido para o CRM Digital, mas tres falhas estruturais bloqueiam a evolucao para o produto desejado:

  1. Multi-tenancy real nao existe. O sistema usa Jetstream Teams em DB unico com escopo por team_id, sem dominio, branding ou ciclo de vida proprio por produtor.
  2. White-label nao existe. Nao ha logo, paleta, dominio, e-mails transacionais nem politica por produtor.
  3. Automacoes acopladas a integracoes. O motor atual mistura "como conectar" com "o que automatizar", impossibilitando catalogo de receitas prontas estilo Pluga.

Esta rearquitetura corrige os tres pontos sem jogar fora o que ja funciona.

Decisoes arquiteturais

Decisao Escolha Motivacao
Isolamento de tenants DB unico + tenant_id Simplicidade operacional, escala ate ~500 tenants, backup unificado
Dominio white-label Subdominio na plataforma (wildcard SSL) Zero ops por novo tenant; CNAME custom fica para fase futura
Voice Fora do MVP Decisao de produto adiada; revisitar apos primeiras vendas
Stack Manter Laravel 13 + Livewire Reaproveita 70% do codigo; refactor estrutural, nao rewrite
Cadastro publico Fechado Usuarios nascem por convite dentro de um tenant
Hierarquia PlatformAdmin -> Tenant(=Producer) -> Affiliate -> User Modelagem ja existente; renomear semantica e fechar onboarding

Mapeamento atual -> novo

Atual Novo papel Acao
teams (Jetstream) Tenant (semanticamente) Manter tabela teams para nao quebrar Jetstream/Spatie; adicionar colunas white-label
team_id em todos os modelos tenant_id semantico Sem renomear coluna; tratar como tenant via TenantContext
producers Identidade comercial do tenant Mantido
affiliates + affiliate_users Escopo logico dentro do tenant Mantido
Fortify register Onboarding por convite Remover rota publica
Automation (regra unica trigger+action) Automation + AutomationStep + AutomationTemplate Refactor estrutural
Inbox sem provider ChannelHub + providers Adicionar abstracao + WABA/Evolution
Sem catalogo de integracoes integration_types + tenant_integrations Construir Pluga-core

Fases e sprints

Fase A - Refundacao Tenancy (bloqueia tudo)

Sprint A1 - Tenancy infrastructure
  • Migration adicionando colunas white-label em teams: subdomain, brand_logo_path, brand_primary_color, brand_secondary_color, support_email, support_url, terms_url, mail_from_name, mail_from_address, feature_flags, subscription_status.
  • App\Support\Tenant\TenantContext resolver singleton.
  • App\Support\Tenant\TenantScoped trait (global scope + auto-fill).
  • App\Http\Middleware\ResolveTenantFromSubdomain.
  • Atualizar seeders para popular subdomain no tenant demo.
  • Testes feature: 404 sem subdomain, isolamento entre tenants, auto-fill em create.
Sprint A2 - Onboarding dual (produtor manual + afiliado self-service)

O cadastro de novos usuarios tem dois fluxos distintos, pensados para volumes opostos:

Produtor (baixo volume, alto controle). Plataforma decide quem vira produtor. Provisionamento manual pelo platform admin (ou via venda automatizada na Fase D). Cria Tenant + Producer + User owner + envia link de definicao de senha. Justifica-se manual porque o produtor representa uma marca contratante e exige curadoria.

Afiliado (alto volume, self-service). Produtores com 1k+ afiliados nao podem depender de convite individual. Cada produtor gera um ou mais AffiliateSignupLink (link publico com token unico) e divulga onde quiser (grupo de WhatsApp, comunidade, painel proprio). Afiliado acessa https://{subdomain}.pulsesaas.clsolucoes.com.br/r/{token}, preenche cadastro, conta criada dentro do tenant do produtor com o role e permissoes definidos no link.

Caracteristicas do link:

  • kit_id opcional (define teto de permissoes via kit do produtor)
  • default_role (operador, afiliado_manager, etc)
  • max_uses opcional (limite de cadastros pelo link)
  • expires_at opcional (janela de campanha)
  • requires_approval (afiliado entra como pendente, produtor aprova)
  • requires_email_verification (e-mail real obrigatorio)
  • allow_paid_signup (cadastro paga acesso via gateway antes de ativar - Fase D)

Entregas da sprint:

  • Desativar rota publica register do Fortify (signup global fechado).
  • Migration affiliate_signup_links + model + Eloquent.
  • Action GenerateAffiliateSignupLink chamavel pelo produtor.
  • UI no painel do produtor: criar/listar/desativar links, copiar URL, ver metricas (uses_count vs max_uses).
  • Rota publica GET /r/{token} no subdominio do tenant, com branding do produtor.
  • Controller PublicAffiliateSignupController (form + submit), valida token ativo/dentro do limite/dentro da janela.
  • Action RegisterAffiliateViaLink: cria User + AffiliateUser + vincula Affiliate ao Producer + atribui role + incrementa contador.
  • Provisionamento de produtor via platform admin: action ProvisionProducer + UI na area da plataforma.
  • Wildcard DNS configurado (*.pulsesaas.clsolucoes.com.br em homolog) com wildcard SSL via Let's Encrypt DNS-01.
  • Login customizado por subdominio (branding aplicado).
Sprint A3 - RBAC limpo
  • Roles renomeados/normalizados: platform_admin, producer_owner, producer_staff, affiliate_owner, affiliate_operator.
  • Producer controla permissoes granulares do afiliado via UI.
  • Kits da Fase B definem teto de permissoes liberadas.
Sprint A4 - Branding runtime
  • Aplicar brand_logo_path e brand_primary_color em layouts via View::composer.
  • E-mails transacionais (mail_from_*) por tenant via runtime config.
  • Pagina de login branded por subdominio.

Fase B - Integration Hub (Pluga-core)

Sprint B1 - Catalogo + conexoes
  • Tabelas integration_types (catalogo global) e tenant_integrations (instancias).
  • Credenciais cifradas (Crypt).
  • UI "Minhas Integracoes" com health check.
  • Seeders do catalogo inicial (10 tipos).
Sprint B2 - WABA Cloud API real
  • Provider WabaCloudProvider (envio, recepcao, webhook assinado).
  • OAuth/registro de phone_number_id.
  • Substituir foundation atual de inbox.
Sprint B3 - Evolution API (QR nao-oficial)
  • Provider EvolutionApiProvider (pareamento por QR, recepcao por webhook).
  • UI de pareamento dentro da inbox.
Sprint B4 - Automation refactor
  • Introduzir automation_steps + automation_templates.
  • Migrar regras atuais para steps.
  • Builder visual basico (Livewire + Alpine).

Fase C - Operacao completa

  • Sprint C1: templates prontos (funil inicial, cobranca pay-after, follow-up, pos-venda).
  • Sprint C2: Meta Ads OAuth + Marketing API + CAPI real.
  • Sprint C3: AI agents reais (OpenAI/Anthropic + fila autonoma + BYOK).
  • Sprint C4: dominio Order/Shipment (pay-after operacional do tenant).

Fase D - Polimento

  • Sprint D1: webchat widget JS + Instagram DM.
  • Sprint D2: webhooks genericos por tenant (entrada e saida) com HMAC.
  • Sprint D3: relatorios financeiros (comissao plataforma sobre afiliados).
  • Sprint D4: hardening final (pentest, observabilidade, backup tested).

Camada Pluga-style (resumo do schema-alvo)

integration_types          (catalogo global gerenciado por vocês)
  key, name, icon
  capabilities (json)
  credentials_schema (json)
  webhook_supported, is_active

tenant_integrations        (instancias conectadas por tenant)
  tenant_id, integration_type_id
  label, status, last_check_at, last_error
  credentials_encrypted, metadata

automation_templates       (receitas prontas)
  key, name, category
  required_integrations (json)
  blueprint (json: triggers + steps)

automations                (instancia do tenant - tabela ja existe)
  tenant_id, template_id (nullable)
  + AutomationStep ordenado, cada step apontando para tenant_integration_id quando precisar

Pay-after operacional (Fase C4)

Pay-after no contexto desse projeto significa o cliente final do produtor recebendo o produto e pagando depois (modelo COD comum no Brasil). Nao tem relacao com a cobranca da assinatura do SaaS contra o produtor.

Deal (fechou venda)
  └── 1:N Orders
         status: etiquetado | postado | em_transito | entregue | devolvido
         tracking_code, carrier
         pay_after_amount, paid_amount, paid_at
         └── 1:N Shipments (eventos do correio via webhook)

Kanban dedicado "Pay-After" consome Order.status, alimentado por webhooks de Correios / Melhor Envio / Frenet.

Riscos e mitigacoes

Risco Mitigacao
Vazamento entre tenants TenantScoped global em todos os Models; testes feature de isolamento por sprint
Quebra de Jetstream/Spatie ao renomear Nao renomear tabela teams; tratar como Tenant semanticamente
SSL wildcard Let's Encrypt wildcard via DNS-01 desde A2; certbot-dns no painel
Token de integracao expirado Health check job + status e last_error em tenant_integrations
Onboarding manual Provisionamento automatizado: assinatura paga -> cria Tenant + Producer + convida owner

Convencoes de codigo (novas)

  • Todo Model de dominio que carrega dados de tenant DEVE usar App\Support\Tenant\TenantScoped.
  • app('tenant') retorna o Tenant resolvido, ou null em rotas de plataforma.
  • Jobs em fila DEVEM serializar tenant_id no payload e re-resolver o contexto na execucao.
  • Webhooks publicos resolvem tenant via path/header, NUNCA via subdomain (request publica nao tem auth).

Sincronia com docs existentes

  • docs/DOCUMENTACAO_TECNICA.md: sera atualizada apos cada sprint da Fase A.
  • docs/CHECKLIST_DESENVOLVIMENTO_SAAS.md: sera complementada com as novas fases (nao substituida).
  • docs/VALIDACAO_CHECKLIST_ATUAL.md: continua sendo o ground truth do que ja foi entregue.
  • scrum-steps/: cada sprint nova ganha seu SPRINT_X_SCRUM.md e SPRINT_X_STATUS.md.
10

Comparativo: Krayin × PulseSaaS

Data de referencia

2026-04-17

Objetivo

Transformar a pesquisa sobre o Krayin CRM em direcionamento pratico para a implantacao das proximas etapas do PulseSaaS, principalmente a Etapa 3 - CRM principal.

Este documento nao tem o objetivo de copiar o Krayin.

O objetivo e usar o Krayin como benchmark para:

  • validar prioridades de CRM
  • acelerar decisoes de produto
  • reduzir improviso arquitetural
  • preparar backlog e ordem de implantacao

Resumo executivo

O Krayin CRM confirma que existe valor real em um CRM Laravel com:

  • arquitetura modular
  • foco em leads, contatos, atividades, produtos, email, workflows e webhooks
  • separacao entre uso operacional e configuracao administrativa
  • estrategia de extensoes e white-label

Para o PulseSaaS, a leitura recomendada e:

  • adotar a disciplina modular do Krayin
  • adaptar a modelagem de CRM ao nosso contexto de administrador -> produtor -> afiliado
  • evitar tentar replicar agora todos os modulos pesados de uma vez

Decisao de produto

O que vamos adotar agora

  • estrutura por dominio em app/Domain
  • CRM mais completo com leads, contatos, oportunidades, tarefas e atividades
  • filtros, busca e visibilidade clara por nivel de acesso
  • historico operacional unificado por entidade
  • preparo estrutural para webhooks, automacoes e importacao

O que vamos adaptar ao nosso contexto

  • separacao Persons + Organizations
  • visibilidade em camadas no estilo Global / Group / Individual
  • modulo de produtos ligado ao comercial
  • dashboard operacional voltado a produtor e afiliado

O que nao vamos copiar agora

  • modulo de mail completo com IMAP
  • omnichannel completo nesta etapa
  • camada de AI / Magic AI
  • marketplace de extensoes
  • tentativa de transformar a Etapa 3 em ERP

Comparativo direto

Tema Krayin CRM PulseSaaS hoje Direcao de implantacao
Arquitetura modular por pacotes e modulos modular por dominio ja iniciada manter e aprofundar
Hierarquia usuarios, roles e visibilidade administrador, produtor e afiliado prontos integrar CRM a essa hierarquia
Leads modulo forte e nativo ainda nao existe como entidade propria implementar no Sprint 3
Contatos persons e organizations Contact existe, mas ainda basico ampliar e separar melhor pessoas e empresas
Oportunidades pipeline comercial estruturado Deal existe, mas ainda enxuto enriquecer regras e historico
Atividades camada operacional relevante activity log existe, mas nao como timeline funcional completa criar modulo de atividades
Produtos modulo comercial proprio inexistente deixar preparado para etapa futura
Email modulo de mails e IMAP inexistente adiar
Webhooks nativo inexistente preparar base, implementar depois
Importacao nativa inexistente preparar base, implementar depois
Automacao workflows nativos inexistente preparar modelagem para Sprint futuro

Leitura do gap atual do PulseSaaS

A base atual ja esta melhor preparada do que um CRM greenfield comum porque temos:

  • autenticacao pronta
  • multiworkspace por Team
  • hierarquia administrador -> produtor -> afiliado
  • Contact, Deal e Task
  • dashboard e sementes de demonstracao
  • policies e isolamento por afiliado

Mesmo assim, ainda existe um gap claro em relacao ao que o Krayin entrega como CRM operacional:

  • falta Lead como entidade propria
  • falta separar melhor pessoa e organizacao
  • falta uma timeline de atividades visivel no produto
  • faltam filtros e busca de uso diario
  • falta historico comercial mais legivel
  • falta preparar a base para importacao, webhook e automacao

Decisao arquitetural para a Etapa 3

A Etapa 3 - CRM principal deve ser o momento de consolidar quatro pilares:

  1. Lead como entrada comercial principal
  2. Contact e Organization como base relacional
  3. Deal como oportunidade comercial rica
  4. Activity como memoria operacional

Escopo recomendado de implantacao

Bloco 1 - Leads

Implementar:

  • model Lead
  • relacionamento com Team
  • relacionamento opcional com Affiliate
  • relacionamento com Producer
  • origem do lead
  • status inicial
  • responsavel comercial
  • valor estimado
  • previsao de fechamento
  • conversao de lead para contato / oportunidade

Campos recomendados:

  • name
  • email
  • phone
  • company_name
  • source
  • status
  • assigned_user_id
  • estimated_value
  • expected_close_at
  • notes

Bloco 2 - Organizations e enriquecimento de contatos

Implementar:

  • model Organization
  • vinculo de Contact com Organization
  • dono comercial
  • cargo
  • canais de contato principais
  • filtro por empresa

Essa decisao aproxima o produto do que o Krayin faz com Persons e Organizations, mas sem engessar o nosso modelo.

Bloco 3 - Opportunities / Deals ricos

Evoluir Deal com:

  • motivo da oportunidade
  • origem
  • responsavel
  • probabilidade
  • stage history
  • resumo comercial
  • relacionamento opcional com Lead
  • relacionamento com Organization

Observacao:

o kanban multipipeline continua pertencendo formalmente a Etapa 4, mas o Deal ja deve nascer preparado para isso.

Bloco 4 - Activities

Criar um modulo funcional de atividades, separado do Spatie Activitylog.

O Activitylog continua importante para auditoria.

Mas o produto precisa de uma timeline de negocio para:

  • ligacao
  • tarefa
  • nota
  • reuniao
  • follow-up
  • mudanca de status
  • conversao

Campos recomendados:

  • subject_type
  • subject_id
  • team_id
  • affiliate_id
  • user_id
  • type
  • description
  • scheduled_at
  • completed_at
  • metadata

Bloco 5 - Filtros, busca e operacao diaria

Implementar filtros consistentes para:

  • responsavel
  • afiliado
  • status
  • origem
  • periodo
  • empresa
  • prioridade

Implementar busca por:

  • nome
  • email
  • telefone
  • empresa

Mapeamento para a estrutura atual

Arquivos e pastas que devem receber a proxima rodada:

  • app/Domain/CRM/Models
  • app/Domain/CRM/Actions
  • app/Domain/CRM/Http/Controllers
  • app/Domain/CRM/Http/Requests
  • app/Policies
  • database/migrations
  • database/seeders
  • resources/views
  • routes/web.php

Novos artefatos sugeridos:

  • app/Domain/CRM/Models/Lead.php
  • app/Domain/CRM/Models/Organization.php
  • app/Domain/CRM/Models/Activity.php
  • app/Domain/CRM/Actions/CreateLead.php
  • app/Domain/CRM/Actions/ConvertLead.php
  • app/Domain/CRM/Actions/LogActivity.php
  • app/Policies/LeadPolicy.php
  • app/Policies/OrganizationPolicy.php
  • app/Policies/ActivityPolicy.php

Regras de visibilidade que devemos absorver do benchmark

O Krayin reforca a importancia de visibilidade por nivel.

No PulseSaaS, o equivalente recomendado e:

  • administrador da plataforma: enxerga a operacao macro da plataforma
  • produtor: enxerga tudo do proprio workspace
  • afiliado: enxerga apenas os registros vinculados ao proprio escopo

Para registros comerciais, a regra recomendada e:

  • Lead, Contact, Deal e Activity sempre com team_id
  • affiliate_id opcional, mas obrigatorio quando o registro pertencer ao afiliado
  • assigned_user_id para a operacao diaria

O que o benchmark do Krayin muda na nossa implantacao

Decisao 1

Nao tratar Contact como modulo isolado demais.

Precisamos de relacao clara entre:

  • lead
  • contato
  • organizacao
  • oportunidade
  • atividade

Decisao 2

Nao usar apenas o Spatie Activitylog como historico visivel do CRM.

Ele continua como auditoria tecnica, mas o produto precisa de uma timeline de negocio.

Decisao 3

Nao misturar agora kanban, mail, IMAP, AI e inbox na mesma etapa.

Isso aumentaria o risco e atrasaria a entrega de um CRM operacional coerente.

Decisao 4

Ja preparar a modelagem para automacoes futuras, mesmo sem entrega completa nesta rodada.

Por isso vale incluir desde ja:

  • campos de origem
  • eventos de conversao
  • historico de mudanca de stage
  • estrutura de filtros previsivel

Sprint 3 recomendado

Prioridade 1

Lead + Organization + Activity

Prioridade 2

enriquecimento de Contact e Deal

Prioridade 3

filtros, busca e dashboard operacional

Prioridade 4

seed, testes e homologacao

Backlog sugerido para execucao

PBI 1 - Leads como entrada comercial

Entregar:

  • migration
  • model
  • policy
  • CRUD inicial
  • tela responsiva
  • relacao com produtor e afiliado

PBI 2 - Organizations e consolidacao relacional

Entregar:

  • model Organization
  • relacao com Contact e Lead
  • filtros por empresa
  • visualizacao basica

PBI 3 - Timeline de atividades

Entregar:

  • model Activity
  • timeline em lead, contato e deal
  • criar nota, agendamento e conclusao

PBI 4 - Deals com memoria comercial

Entregar:

  • aprimoramento de Deal
  • stage history
  • relacao com lead e organizacao

PBI 5 - Busca, filtros e dashboard

Entregar:

  • filtros reutilizaveis
  • busca textual
  • contadores no dashboard
  • visao adaptada para produtor e afiliado

PBI 6 - Qualidade e demonstracao

Entregar:

  • seed demo coerente
  • testes de acesso e isolamento
  • validacao na homolog
  • documentacao final

Criterios de aceite da implantacao

  • produtor consegue cadastrar e acompanhar leads
  • afiliado ve apenas o proprio escopo
  • oportunidades ficam ligadas a contatos e empresas
  • timeline registra interacoes reais do negocio
  • filtros aceleram a operacao diaria
  • dashboard reflete dados por perfil
  • testes cobrem os fluxos principais

O que fica explicitamente para depois

  • Etapa 4: kanban multipipeline completo
  • Etapa 5: etiquetas como motor operacional
  • Etapa 6: inbox omnichannel
  • Etapa 7: automacoes nativas
  • Etapa 8: tracking ADS e atribuicao
  • Etapa 11: agentes de IA

Recomendacao final

O benchmark do Krayin CRM reforca que o caminho mais seguro para o PulseSaaS e:

  • consolidar primeiro um CRM central muito bem amarrado
  • conectar esse CRM a hierarquia produtor -> afiliado
  • preparar o terreno para automacoes, webhooks e inbox sem tentar entregar tudo ao mesmo tempo

Essa leitura deixa a equipe pronta para implantar a Etapa 3 com menos risco, menos retrabalho e mais coerencia de produto.

11

Inbox unificada

Data: 2026-05-27 Estado: Caio quer que /inbox deixe de obrigar a escolher UMA caixa de entrada por vez. Pedido: uma aba única "Todas as conversas" que junta tudo de todos os números/integrações que o usuário tem acesso, com filtros aplicáveis em cima (por número, por agente atribuído, por status, etc).


⚠️ Regras absolutas (manter)

Vide handoffs anteriores. Resumo do que NÃO pode quebrar:

  • Privacidade afiliado é INVIOLÁVEL (memo project_affiliate_privacy_model.md)
  • Production-grade desde o início (memo feedback_no_temporary_fixes.md)
  • Janela 24h WABA enforced na validação de envio (vide HANDOFF de E2.8)

🏷️ Modelo de propriedade Inbox/Integration/Conversation

Entidades-chave e dono:

Entidade team_id affiliate_id Significado
TenantIntegration obrigatório nullable NULL = integração do produtor (owner); setado = integração de um afiliado específico
Inbox obrigatório nullable Herda affiliate_id da integração via IngestIncomingMessage. Inbox "manual" pode ser criada com qualquer affiliate_id
Conversation obrigatório nullable Herda affiliate_id da inbox em que está
Message obrigatório nullable Herda affiliate_id da conversation

Propagação automática (já implementada):

ConnectIntegration → seta affiliate_id no TenantIntegration baseado no user.affiliateScopeId()
   ↓
IngestIncomingMessage → cria Inbox + Conversation + Message herdando affiliate_id da integration
   ↓
Toda mensagem dali pra frente carrega o mesmo affiliate_id pelo grafo

Quem vê o quê (regra BelongsToTeam::scopeVisibleToUser):

User
Produtor (owner) sem affiliateScopeId Registros com affiliate_id IS NULL (apenas próprios)
Afiliado X (tem current_affiliate_id = X) Registros com affiliate_id = X (apenas próprios)
Operador funcionário do produtor Mesma visão do produtor
Operador funcionário de afiliado X Mesma visão do afiliado X

Único caminho administrativo que cruza a fronteira: CalculateAffiliateCommission::resolveDeal() faz lookup unscoped por team_id quando produtor configura comissão. Documentado inline. NÃO inventar outros caminhos.

Implicação direta pra inbox unificada:

  • Os chips de "Número WhatsApp" no filtro mostram só inboxes visíveis ao user logado, automaticamente, via Inbox::visibleToUser($user). Não tem o que codar a mais — a arquitetura já garante.
  • Produtor com 4 inboxes próprias + 2 inboxes de afiliados no mesmo tenant só vê os 4 chips (próprios).
  • Afiliado com 1 inbox vê 1 chip — modo "todas" e modo "única" colapsam visualmente pra mesma coisa.
  • Nunca vaza dado: mesmo passando inbox_ids[]=2 (de outro afiliado) na URL, o whereIn é cumulativo ao scope visibleToUser. Resultado: lista vazia, sem erro, sem leak.

Implicação pra automações (vide handoff Flow Builder):

Cada Automation também tem affiliate_id. Automation do produtor (affiliate_id=NULL) só dispara em conversations com affiliate_id=NULL. Automation do afiliado X só dispara em conversations com affiliate_id=X. Isso é matemática do BelongsToTeam + filtro no EvaluateAutomations — segue valendo no Flow Builder.


🧭 Estado atual

UI hoje

  • Dropdown único <select name="inbox_id"> força a escolher UMA inbox (resources/views/inbox/index.blade.php:70)
  • BuildInboxData::__invoke() recebe inbox_id no filter, default = primeira inbox visível ao user, e SEMPRE filtra where('inbox_id', $selectedInbox->id) na query de conversations
  • selectedInbox é referenciado em 15+ lugares na view ($selectedInbox->name, ->id, ->team_id, ->tenantIntegration, etc) — refactor exige cuidado
  • Echo binding (real-time): channelName = 'conversation.${conversationId}', mas o canal de inbox-level (broadcast quando chega mensagem nova em qualquer conversa da inbox) está em inboxId: {{ $selectedInbox->id }} — em modo "todos", precisa subscrever em N canais

Filtros já existentes no BuildInboxData

  • inbox_id (obrigatório implícito; default = primeira)
  • affiliate_id (opcional, mas user com affiliateScopeId já tem auto-aplicado)
  • status (open/waiting/closed)
  • label_id
  • search (LIKE em contact_name, contact_email, contact_phone, subject)
  • conversation_id (seleção atual na thread central)

Campos disponíveis em Conversation (sem alterar schema)

  • team_id, affiliate_id, inbox_id
  • assigned_user_id ✅ (não está sendo usado como filtro hoje)
  • status, priority, source
  • last_message_at, last_inbound_at
  • Relações: inbox, affiliate, lead, deal, labels, assignee, messages

View — onde tem dependência rígida do selectedInbox

Linhas que vão precisar de refactor pra suportar modo "all" (sem inbox única selecionada):

  • L30: chip do nome da inbox no header → vira chip "Todas" ou múltiplas pills
  • L70: dropdown <select> → vira multi-select de inboxes (chips) + dropdown de agente
  • L106-107: Alpine teamId: {{ $selectedInbox->team_id }}, inboxId: {{ $selectedInbox->id }} no inboxRealtime → precisa array de inboxIds
  • L123: link de conversation_id passa inbox_id no querystring → manter, mas opcional
  • L163: header da thread central mostra $selectedInbox->name → mostrar $selectedConversation->inbox->name em vez disso
  • L247: botão "Templates" depende de $selectedInbox->tenantIntegration → mover pra contexto da conversation selecionada
  • L522: form de criar conversation manual usa <input hidden name="inbox_id" value="{{ $selectedInbox->id }}"> → exigir seleção explícita de inbox no form

🎯 Estado desejado

Comportamento

Modo "Todas" (default):

  • Lista de conversations agrega TODAS as inboxes que o user tem visibilidade
  • Sidebar de filtros (lateral esquerda da lista):
    • Multi-select "Número WhatsApp" (chips clicáveis por inbox/integração)
    • Dropdown "Atribuído a" (lista de operadores do team)
    • Dropdown "Status" (aberta / aguardando / fechada / todas)
    • Dropdown "Etiqueta" (já existe, manter)
    • Toggle "Apenas não atribuídas" (assigned_user_id IS NULL)
  • Cada card da conversation ganha badge de origem mostrando ícone + nome curto da inbox (ex: "📱 WABA CL" ou "✉️ Suporte")
  • URL persiste filtros em query string: /inbox?inbox_ids[]=3&inbox_ids[]=5&assignee=2&status=open
  • Botão "Limpar filtros" no topo da sidebar

Modo "Única inbox" (opt-in):

  • Usuário pode clicar num chip de inbox específico e expandir só ela (mesma UX atual, mas como filtro, não como obrigação)
  • Quando 1 chip ativo: badges no card somem (redundante) e a thread/painel direito ganha contexto da inbox específica (botão Templates volta, etc)

Resolução de problemas atuais

Hoje Depois
Operador precisa trocar de inbox pra ver mensagens de outro número Vê tudo na mesma lista
Atribuição de conversa não é filtro de primeira classe Vira chip principal
Operador novo se perde no dropdown de inboxes Padrão é "tudo" — não precisa decidir

🏗️ Plano de implementação

Schema

Nada muda. Todos os campos já existem (assigned_user_id, inbox_id, etc).

Backend — BuildInboxData

// Hoje (simplificado)
$selectedInbox = ...;
$conversations = Conversation::query()
    ->visibleToUser($user)
    ->where('inbox_id', $selectedInbox->id)
    ->when($filters['status'], fn ($q, $s) => $q->where('status', $s))
    ->latest('last_message_at')
    ->take(20)
    ->get();

// Depois
$inboxIds = $filters['inbox_ids'] ?? [];  // array, vazio = todas visíveis
$assigneeId = (int) ($filters['assignee_id'] ?? 0);

$conversations = Conversation::query()
    ->visibleToUser($user)
    ->with(['inbox', 'affiliate', 'lead', 'deal', 'labels', 'assignee'])
    ->when($inboxIds, fn ($q, $ids) => $q->whereIn('inbox_id', $ids))
    ->when($assigneeId, fn ($q, $id) => $q->where('assigned_user_id', $id))
    ->when($filters['status'] ?? null, fn ($q, $s) => $q->where('status', $s))
    ->when($filters['label_id'] ?? null, fn ($q, $l) => $q->whereHas('labels', fn ($lq) => $lq->whereKey((int) $l)))
    ->when($filters['unassigned_only'] ?? false, fn ($q) => $q->whereNull('assigned_user_id'))
    ->when($filters['search'] ?? null, function ($q, $s) {
        $q->where(function ($b) use ($s) {
            $b->where('contact_name', 'like', "%{$s}%")
              ->orWhere('contact_phone', 'like', "%{$s}%")
              ->orWhere('contact_email', 'like', "%{$s}%")
              ->orWhere('subject', 'like', "%{$s}%");
        });
    })
    ->latest('last_message_at')
    ->take(50)
    ->get();

// `$selectedInbox` vira opcional/computado:
$selectedInbox = count($inboxIds) === 1
    ? $inboxes->firstWhere('id', $inboxIds[0])
    : null;  // modo "todas"

// `$availableAssignees` novo:
$availableAssignees = User::query()
    ->whereHas('teams', fn ($q) => $q->where('teams.id', $user->current_team_id))
    ->orderBy('name')
    ->get(['id', 'name']);

View — inbox/index.blade.php

Sidebar de filtros (lateral esquerda da lista de conversations):

<aside class="pulse-filter-panel">
    <h4>Filtros</h4>

    <div class="filter-group">
        <p class="filter-label">Número WhatsApp</p>
        <div class="filter-chips">
            @foreach ($inboxes as $inbox)
                @php $active = in_array($inbox->id, $filterState['inbox_ids']); @endphp
                <a href="{{ /* toggle url */ }}"
                   class="filter-chip {{ $active ? 'active' : '' }}">
                    {{ $inbox->name }}
                </a>
            @endforeach
        </div>
    </div>

    <div class="filter-group">
        <label for="assignee">Atribuído a</label>
        <select name="assignee_id" onchange="this.form.submit()">
            <option value="">Qualquer agente</option>
            @foreach ($availableAssignees as $u)
                <option value="{{ $u->id }}" @selected($filterState['assignee_id'] == $u->id)>{{ $u->name }}</option>
            @endforeach
        </select>
    </div>

    <div class="filter-group">
        <label class="flex items-center gap-2">
            <input type="checkbox" name="unassigned_only" @checked($filterState['unassigned_only'])>
            Apenas não atribuídas
        </label>
    </div>

    <!-- ... status, label, search ... -->

    <a href="{{ route('inbox.index') }}" class="btn-clear-filters">Limpar filtros</a>
</aside>

Conversation card — badge de origem:

<a href="..." class="conv-item ...">
    <div class="flex items-start justify-between gap-3">
        <div class="min-w-0">
            <p class="text-sm font-medium">{{ $conversation->contact_name }}</p>
            <p class="text-xs text-stone-400">{{ ... preview ... }}</p>
        </div>
        <div class="flex flex-col items-end gap-1">
            <span class="pulse-chip">{{ $statusLabels[$conversation->status] ?? $conversation->status }}</span>
        </div>
    </div>

    <div class="mt-2 flex flex-wrap gap-1.5 text-xs">
        {{-- Badge da inbox quando estiver no modo "todas" --}}
        @if (count($filterState['inbox_ids']) !== 1)
            <span class="pulse-chip border-violet-300/20 bg-violet-300/10 text-violet-100">
                <x-icon name="message-square" class="size-3" />
                {{ $conversation->inbox->name }}
            </span>
        @endif

        {{-- Badge do agente quando não filtrado por um específico --}}
        @if ($conversation->assigned_user_id && !$filterState['assignee_id'])
            <span class="pulse-chip">
                <x-icon name="user" class="size-3" />
                {{ $conversation->assignee->name }}
            </span>
        @endif

        @foreach ($conversation->labels as $label)
            <span class="pulse-chip {{ $label->color_classes['badge'] ?? '' }}">{{ $label->name }}</span>
        @endforeach
    </div>
</a>

Header da thread central — mostrar contexto da conversation, não da inbox selecionada:

@if ($selectedConversation)
    <div>
        <p class="text-xs text-stone-400">
            {{ $selectedConversation->inbox->name }}
            @if ($selectedConversation->inbox->channel === 'waba_cloud')
                · WhatsApp Business
            @endif
        </p>
        <h3>{{ $selectedConversation->contact_name }}</h3>
    </div>
@endif

Echo (real-time)

Hoje: subscreve apenas em conversation.{id} da conversa selecionada.

Depois: continua igual no nível de conversation. Mas pra a LISTA de conversations atualizar quando chega nova mensagem em qualquer conversa, precisa de canal team.{teamId}.inbox.refresh ou similar:

  • Backend dispara evento InboxListShouldRefresh quando qualquer Message inbound chega
  • Frontend escuta esse canal e dispara um refetch da lista (Alpine fetch GET /inbox.json?...filters... ou Livewire-style)
  • Alternativa pragmática: polling a cada 30s na lista quando modo "todas" está ativo

Persistência de filtros

URL:

/inbox
/inbox?inbox_ids[]=3&inbox_ids[]=5
/inbox?assignee_id=2&status=open
/inbox?unassigned_only=1

Form de filtros envia via GET com checkboxes/selects. Botão "Aplicar" submete; cada chip clica = link com query atualizada.


📋 Tasks ordenadas

Sub-sessão única (~2.5h)

  1. Refactor BuildInboxData (~30min)

    • Aceitar inbox_ids[] array em vez de inbox_id singular
    • Adicionar filtros assignee_id, unassigned_only
    • Carregar availableAssignees pra UI
    • selectedInbox vira opcional (null quando >1 inbox)
    • Retornar filterState enriquecido
  2. Refactor InboxController request parsing (~10min)

    • Aceitar inbox_ids como array OU CSV string ?inbox_ids=3,5
  3. Refactor view inbox/index.blade.php (~50min)

    • Sidebar de filtros nova (chips de inboxes, dropdown de assignee, toggle unassigned)
    • Card mostra badge de origem quando modo "todas"
    • Header da thread central usa $selectedConversation->inbox em vez de $selectedInbox
    • Botão "Templates" só aparece quando 1 inbox WABA ativa nos filtros
    • Form de criar conversa exige select de inbox (não pega mais do contexto)
  4. Validação de modo seletivo dos features que dependem de inbox única (~15min)

    • Botão "Templates" — só aparece em modo single-inbox WABA
    • Compose adaptativo 24h (windowOpen indicator) — só calcula quando há conversa selecionada (já é o caso)
    • Modal "Nova conversa" — exige selecionar uma inbox
  5. Tests (~30min)

    • InboxUnifiedListTest: usuário com 3 inboxes vê conversas de todas as 3 na lista quando sem filtro
    • InboxFilterByInboxIdsTest: passa 2 dos 3 inbox_ids → lista filtrada corretamente
    • InboxFilterByAssigneeTest: filtro por assignee retorna só conversas atribuídas a ele
    • InboxAffiliateScopeStillEnforcedTest: produtor não vê inbox de afiliado mesmo com modo "todas"
    • InboxBadgeRenderedInUnifiedModeTest: badge da inbox aparece nas cards quando >1 inbox no filtro
  6. Polish UX (~15min)

    • Hover tooltips nos chips ("Filtrar por essa caixa")
    • Badge "X filtros ativos" no topo da lista
    • Atalho Esc pra limpar filtros
  7. Vite build + smoke test manual (~10min)

    • npm run build
    • Login, navega em /inbox, confere que todas as conversas aparecem
    • Aplica cada filtro
    • Confere que privacidade afiliado segue intacta

📦 Arquivos previstos

Modificados:

  • app/Domain/Inbox/Actions/BuildInboxData.php — lógica principal
  • app/Domain/Inbox/Http/Controllers/InboxController.php — request → filters
  • resources/views/inbox/index.blade.php — sidebar de filtros, badges nos cards, header da thread
  • resources/js/app.jsinboxRealtime aceita teamId sem inboxId (pra modo unified)

Novos:

  • tests/Feature/InboxUnifiedListTest.php
  • (opcional) resources/views/inbox/partials/filter-sidebar.blade.php — componente isolado da sidebar

Schema: nenhuma migration.


🧠 Armadilhas e decisões já tomadas

  1. Privacidade afiliado: Conversation::scopeVisibleToUser continua sendo a fonte da verdade. O filtro adicional whereIn('inbox_id', $inboxIds) é cumulativo, não substitui. Afiliado nunca vê inbox que não é dele independente do que passar em inbox_ids[].

  2. selectedInbox vira opcional: muitas partes da view assumem que $selectedInbox sempre existe. Em modo "todas", não existe. Cada lugar precisa ser auditado pra cair em fallback gracioso (mostrar conteúdo da conversation selecionada em vez da inbox). Use ?-> defensivo.

  3. Botão "Templates" no header: atualmente está no contexto da inbox. Movido pra contexto da conversation — só aparece quando há conversation selecionada cuja inbox é WABA.

  4. Modal "Nova conversa manual": hoje pega inbox_id do $selectedInbox. Em modo "todas", precisa exigir que o user escolha em qual inbox a conversa será criada. Adicionar <select> obrigatório no modal.

  5. Echo real-time da LISTA: o canal de conversa individual continua funcionando. O ponto cego é "lista atualiza quando chega msg em conversa NÃO selecionada" — hoje já é assim em modo single-inbox (depende do canal de inbox), então não regrediu. Mas pra modo "todas" o ideal é um canal de team-level. Implementação dessa parte é opcional pro MVP — polling de 30s resolve enquanto.

  6. Limite de 50 conversas na lista: atualmente é 20 (hardcoded). Em modo "todas" com 3 inboxes ativas, 20 fica curto. Subi pra 50. Se precisar de mais, pagination ou infinite scroll na próxima iteração.

  7. Salva filtros do usuário? Por enquanto, não. Filtros vivem só na URL. Persistência por user (ex: "deixar último filtro aplicado da próxima vez") pode entrar como follow-up se Caio pedir.

  8. Performance: whereIn('inbox_id', $ids) é índice-friendly (já existe índice). Junto com latest('last_message_at') deve performar bem até ~10k conversas/team. Se passar disso, indexar last_message_at composto com team_id.


📋 Tasks ativas (state final)

(completed - sessão anterior)
#1-#3 - Etapa 2 templates (E2.6, E2.7, E2.8)
#4    - Teste E2E WABA corporativo

(pending - próximas sessões)
- Inbox unificada (este handoff) — ~2.5h, próxima sessão recomendada
- Flow Builder drag-drop (vide HANDOFF_2026-05-27_AUTOMATION_FLOW_BUILDER.md) — 12-15h, dividir em 3-4 sub-sessões

(esperando externos)
- App Review da Meta (3-7 dias úteis)
- Wildcard SSL, Queue worker, Webhook router multi-tenant

🔮 Sugestão de abertura na próxima sessão

"Hoje vou implementar inbox unificada conforme o handoff 2026-05-27.
Sub-sessão única, ~2.5h. Começo refatorando BuildInboxData,
depois a view, depois testes. Sem mexer em Flow Builder ainda."

Esse trabalho é independente do Flow Builder e do App Review. Pode rodar em qualquer ordem. Recomendação: fazer ANTES do Flow Builder, porque o builder vai ter referências a inboxes/integrações e a UX já vai estar coerente.

12

Automation flow builder

Data: 2026-05-27 Estado: Engine de Automation linear existe e funciona. Falta o tudo o que faz parecer chatbot moderno: trigger de inbound, captura de variável, branch, lookup de contato, UI visual drag-drop. Caio quer UI estilo Typebot/ManyChat — drag-and-drop com nós conectados, não lista linear.


⚠️ Regras absolutas (LEIA primeiro)

Vide handoffs anteriores. Resumo:

  1. Production-grade desde o início. Memo ~/.claude/projects/-home-pulsehomolog-clsolucoes-com/memory/feedback_no_temporary_fixes.md.
  2. Privacidade do afiliado é INVIOLÁVEL. Memo project_affiliate_privacy_model.md. Toda Automation, AutomationStep e qualquer state novo TEM que respeitar scope via BelongsToTeam::scopeVisibleToUser.
  3. Manejo de tokens fica com o usuário. "Confia em mim" — Caio cola tokens no chat.

🏷️ Modelo de propriedade — quem vê/dispara o quê

Toda entidade do domínio carrega team_id (obrigatório) + affiliate_id (nullable):

Entidade affiliate_id NULL affiliate_id setado
TenantIntegration Integração do produtor Integração de um afiliado
Inbox Inbox do produtor Inbox de um afiliado
Conversation Conversa do produtor Conversa de um afiliado
Automation Fluxo do produtor Fluxo de um afiliado
ConversationAutomationState (novo) Estado de execução do fluxo do produtor Idem do afiliado

Propagação automática:

Quando uma message inbound chega:
  IngestIncomingMessage:
    1. Carrega TenantIntegration (tem affiliate_id)
    2. Cria/encontra Inbox herdando affiliate_id da integration
    3. Cria/encontra Conversation herdando affiliate_id da inbox
    4. Cria Message herdando affiliate_id da conversation
    → tudo isolado por scope automaticamente

Quem dispara qual fluxo:

  • Producer's Automation (affiliate_id=NULL) dispara em Conversations com affiliate_id=NULL
  • Affiliate X's Automation (affiliate_id=X) dispara em Conversations com affiliate_id=X
  • Nunca cruzam. Mesmo no mesmo team_id, fluxo do produtor jamais executa em conversa de afiliado.

Como isso é enforced no EvaluateAutomations:

Automation::query()
    ->where('team_id', $subject->team_id)
    ->where('status', 'active')
    ->where('trigger_type', $eventType)
    ->when($affiliateId, function ($query, int $affiliateId) {
        $query->where(function ($builder) use ($affiliateId) {
            $builder->whereNull('affiliate_id')
                ->orWhere('affiliate_id', $affiliateId);
        });
    }, function ($query) {
        $query->whereNull('affiliate_id');
    })

MAS — atenção crítica pro Flow Builder: o orWhereNull('affiliate_id') no ramo do afiliado significa que automation do produtor PODE disparar em conversa de afiliado (sentido inverso da privacidade). Isso é correto pro caso "produtor configura mensagem de boas-vindas master que vale pra todos os canais do team incluindo dos afiliados" — mas tem que validar com Caio se esse é o comportamento desejado, ou se quer total isolamento (sem o orWhereNull).

Recomendação: adicionar campo is_global (boolean) na Automation. Se TRUE e produtor, dispara em conversas de qualquer afiliado também. Se FALSE (default), só dispara em conversas com mesmo affiliate_id exato. Discutir com Caio antes de implementar.

Cenários reais (do tenant team_id=1 atual, em 2026-05-28):

Inbox affiliate Quem vê esta inbox Quem dispara fluxo aqui
#1 WhatsApp Principal NULL Produtor + operadores dele Fluxos do produtor
#3 WhatsApp Homolog NULL Produtor + operadores dele Fluxos do produtor
#4 teste main sandbox NULL Produtor + operadores dele Fluxos do produtor
#5 WABA Corporativo CL NULL Produtor + operadores dele Fluxos do produtor
#2 WhatsApp Afiliado Sul 1 Afiliado #1 + operadores dele Fluxos do afiliado #1 (+ do produtor se is_global=true)
#6 Inbox do afiliado 3 Afiliado #3 + operadores dele Fluxos do afiliado #3 (+ do produtor se is_global=true)

🧭 Baseline — O que já existe (NÃO refazer)

Domínio app/Domain/Automation/

Models:

  • Automation — fillable: team_id, affiliate_id, created_by, template_id, name, slug, status, trigger_type, trigger_config (JSON), action_type, action_config, last_run_at
  • AutomationStep — fillable: automation_id, position, kind, label, config (JSON), tenant_integration_id, retry_max, retry_backoff_seconds
  • AutomationTemplate — catálogo de templates pré-prontos (key, name, category, is_active). 4 templates seeded.
  • AutomationLog — histórico de execução (automation_id, subject info, status, message, payload)

Actions:

  • EvaluateAutomations($eventType, $subject, $payload) — busca todas Automations ativas do team que matcham trigger + payload, e chama RunAutomation em cada
  • RunAutomation($automation, $subject, $eventType, $payload, $fromPosition = 0) — pega steps a partir de $fromPosition, executa um por um. Suporta KIND_WAIT_SECONDS agendando ContinueAutomation::dispatch com delay
  • CreateAutomation, BuildAutomationData, InstantiateAutomationFromTemplate

Jobs:

  • ProcessAutomationEvent — fila pra disparar evento
  • ContinueAutomation(automationId, subjectClass, subjectId, fromPosition, eventType, payload) — pra retomar depois de delay

Step kinds atuais (constantes em AutomationStep)

KIND_APPLY_LABEL = 'apply_label'
KIND_CREATE_TASK = 'create_task'
KIND_MOVE_PIPELINE_STAGE = 'move_pipeline_stage'
KIND_SEND_MESSAGE = 'send_message'
KIND_WEBHOOK = 'webhook'
KIND_WAIT_SECONDS = 'wait_seconds'
KIND_AI_RESPONSE = 'ai_response'

Trigger types atuais (em EvaluateAutomations::matches)

label_attached         — match por label_id
deal_stage_changed     — match por pipeline_stage_id
conversation_waiting   — sem filtro
webhook_received       — match por event_key
order_delivered        — sem filtro
order_status_changed   — match por target_status
deal_won               — sem filtro
lead_created           — sem filtro
no_response_for_days   — sem filtro

UI atual

  • /automationsAutomationController (single-action invoke) → automations.index.blade.php (182 linhas)
  • Tem listagem + form de criar + botão de instanciar template
  • NÃO tem drag-and-drop, nem editor visual de steps por automation

NÃO PLUGADO ainda

  • IngestIncomingMessage não chama EvaluateAutomations — automações nunca disparam em mensagem real
  • CreateMessage (outbound) tampouco
  • O único caminho que dispara é via outros eventos (CRM principalmente)

🎯 Objetivo desta próxima sessão

Construir um Flow Builder visual drag-and-drop estilo Typebot/ManyChat, com:

  1. Canvas onde o usuário arrasta nós (steps), conecta arestas (próximo passo / branch)
  2. Trigger configurável incluindo primeira mensagem inbound do cliente
  3. Steps novos: BRANCH (if/else), LOOKUP_CONTACT, CAPTURE_VARIABLE, SEND_TEMPLATE
  4. Estado da conversa por automação (variáveis capturadas, posição atual)
  5. Engine reformulado pra suportar grafo (não mais lista linear)
  6. Execução real disparada no IngestIncomingMessage

Tempo estimado total: 12-15h. Dividir em 3-4 sub-sessões.


🏗️ Arquitetura proposta

Schema novo

-- Adicionar na automation_steps
ALTER TABLE automation_steps
  ADD COLUMN node_id VARCHAR(36) UNIQUE NOT NULL,  -- UUID do nó no canvas
  ADD COLUMN node_type VARCHAR(40) NOT NULL,       -- 'trigger', 'send_message', 'branch', 'capture', etc
  ADD COLUMN position_x INT NOT NULL DEFAULT 0,    -- coordenadas no canvas
  ADD COLUMN position_y INT NOT NULL DEFAULT 0;
-- (manter campo `position` legado pra automações lineares, mas marcar deprecated)

-- Nova tabela: arestas do grafo
CREATE TABLE automation_edges (
  id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
  automation_id BIGINT UNSIGNED NOT NULL,
  from_node_id VARCHAR(36) NOT NULL,    -- node_id de origem
  to_node_id VARCHAR(36) NOT NULL,      -- node_id de destino
  source_handle VARCHAR(40) NULL,       -- 'default', 'truthy', 'falsy', 'option_1', etc (pra branches)
  label VARCHAR(60) NULL,               -- texto opcional na aresta
  INDEX (automation_id),
  FOREIGN KEY (automation_id) REFERENCES automations(id) ON DELETE CASCADE
);

-- Nova tabela: estado de execução por conversa
CREATE TABLE conversation_automation_states (
  id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
  team_id BIGINT UNSIGNED NOT NULL,
  affiliate_id BIGINT UNSIGNED NULL,
  conversation_id BIGINT UNSIGNED NOT NULL,
  automation_id BIGINT UNSIGNED NOT NULL,
  current_node_id VARCHAR(36) NULL,         -- onde parou (NULL = finalizou)
  variables JSON NOT NULL,                  -- {nome: 'Caio', email: '[email protected]', ...}
  status VARCHAR(20) NOT NULL DEFAULT 'running', -- running | paused | completed | failed
  started_at TIMESTAMP NOT NULL,
  last_activity_at TIMESTAMP NOT NULL,
  completed_at TIMESTAMP NULL,
  INDEX (team_id, conversation_id),
  INDEX (status),
  UNIQUE KEY (conversation_id, automation_id),
  FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE,
  FOREIGN KEY (automation_id) REFERENCES automations(id) ON DELETE CASCADE
);

Step kinds novos

// Em AutomationStep
public const KIND_TRIGGER = 'trigger';              // nó inicial (não executa, marca o ponto de entrada)
public const KIND_BRANCH = 'branch';                // if/else baseado em variável ou expressão
public const KIND_LOOKUP_CONTACT = 'lookup_contact'; // busca Contact por phone/email, joga em variables
public const KIND_CAPTURE_VARIABLE = 'capture_variable'; // espera próxima inbound do user, salva em variável
public const KIND_SEND_TEMPLATE = 'send_template';  // envia template WhatsApp (já temos sendTemplate no provider)
public const KIND_END = 'end';                       // marca fim explícito do fluxo

Trigger types novos

// Em EvaluateAutomations::matches()
'conversation_first_inbound' => $this->matchesScopedConfig($config, $payload),
'conversation_keyword' => $this->matchesScopedConfig($config, $payload),
'conversation_outside_window' => $this->matchesScopedConfig($config, $payload),

Escopo / filtro de qual automação dispara em qual chat

Problema: sem filtros, todas as automações com mesmo trigger_type disparam em paralelo. Caos pro usuário.

Solução: trigger_config aceita campos de escopo opcionais. Engine aplica TODOS os filtros antes de disparar:

{
  "inbox_ids": [5],                    // só estas inboxes (null/vazio = qualquer)
  "tenant_integration_ids": [3],       // só estas integrações
  "keyword_any": ["oi", "olá"],        // matchea se body contém qualquer palavra
  "keyword_all": [],                   // matchea só se contém TODAS
  "labels_any": [],                    // labels na conversation
  "labels_all": [],
  "contact_phone_prefix": null,        // ex: "5511" pra DDD 11
  "time_window": {                     // janela de horário (timezone do team)
    "start": "09:00",
    "end": "18:00",
    "weekdays": [1,2,3,4,5]
  }
}

Adicionar method em EvaluateAutomations:

protected function matchesScopedConfig(array $config, array $payload): bool
{
    // inbox/integration filter
    if (!empty($config['inbox_ids']) && !in_array($payload['inbox_id'] ?? null, $config['inbox_ids'])) {
        return false;
    }

    if (!empty($config['tenant_integration_ids']) && !in_array($payload['tenant_integration_id'] ?? null, $config['tenant_integration_ids'])) {
        return false;
    }

    // keyword
    $body = strtolower($payload['body'] ?? '');
    if (!empty($config['keyword_any'])) {
        $matchAny = collect($config['keyword_any'])
            ->contains(fn ($kw) => str_contains($body, strtolower($kw)));
        if (!$matchAny) return false;
    }

    if (!empty($config['keyword_all'])) {
        $matchAll = collect($config['keyword_all'])
            ->every(fn ($kw) => str_contains($body, strtolower($kw)));
        if (!$matchAll) return false;
    }

    // labels (carrega via subject->labels())
    // ... (similar)

    // phone prefix
    if (!empty($config['contact_phone_prefix']) &&
        !str_starts_with($payload['contact_phone'] ?? '', $config['contact_phone_prefix'])) {
        return false;
    }

    // time window
    if (!empty($config['time_window'])) {
        $tz = $payload['team_timezone'] ?? 'America/Sao_Paulo';
        $now = now($tz);
        $w = $config['time_window'];
        if (!empty($w['weekdays']) && !in_array($now->dayOfWeek, $w['weekdays'])) return false;
        // ... start/end comparison
    }

    return true;
}

Múltiplas automações matcham — quem ganha?

Adicionar dois campos novos em Automation:

ALTER TABLE automations
  ADD COLUMN priority INT NOT NULL DEFAULT 100,    -- menor = roda primeiro
  ADD COLUMN is_exclusive BOOLEAN NOT NULL DEFAULT TRUE;  -- TRUE = só esta roda, ignora demais

EvaluateAutomations ordena por priority asc. Pra o primeiro match que casa, se is_exclusive=true, para o loop. Senão, continua e dispara as outras também.

Exemplo de uso:

  • Automação "Fora do horário comercial" — priority=10, exclusive=true, time_window=fora 9-18 → ganha sempre fora do horário e bloqueia outras
  • Automação "Boas-vindas geral" — priority=100, exclusive=true → roda dentro do horário se nenhuma de prioridade menor matchou
  • Automação "Log de toda inbound em webhook externo" — priority=999, exclusive=false → SEMPRE roda em paralelo (não bloqueia nada)

Plugagem no IngestIncomingMessage

// app/Domain/Integration/Actions/IngestIncomingMessage.php
// após criar Message e atualizar Conversation:

if ($isFirstInboundOnConversation) {
    app(EvaluateAutomations::class)('conversation_first_inbound', $conversation, [
        'message_id' => $message->id,
        'body' => $incoming->body,
    ]);
}

app(EvaluateAutomations::class)('conversation_keyword', $conversation, [
    'message_id' => $message->id,
    'body' => $incoming->body,
]);

// Plus: continuar automações pausadas em CAPTURE_VARIABLE
app(ResumeWaitingAutomations::class)($conversation, $message);

Frontend — React Flow

Biblioteca: React Flow (@xyflow/react)

  • Drag-drop nativo
  • Conexões automáticas entre handles
  • MIT licensed
  • Bundle ~50kb gzipped

Estrutura:

  • Página /automations/{automation}/builder com canvas full-screen
  • Sidebar esquerda: paleta de nós draggable (trigger, send message, branch, capture, etc)
  • Canvas central: nós + edges editáveis
  • Sidebar direita: painel de propriedades do nó selecionado (config JSON via form)
  • Botão "Salvar" → POST /automations/{id}/builder com {nodes: [...], edges: [...]} → backend persiste em automation_steps + automation_edges

Alpine.js vs React: o resto do projeto usa Alpine. React Flow exige React. Decisão: adicionar React isoladamente APENAS pra essa página, via Vite chunk separado. Não substitui o resto do stack — coexiste.

Engine reformulado

RunAutomation vira grafo-aware:

public function __invoke(
    ConversationAutomationState $state,
    array $payload = [],
): void {
    while ($state->current_node_id !== null) {
        $node = AutomationStep::where('automation_id', $state->automation_id)
            ->where('node_id', $state->current_node_id)
            ->firstOrFail();

        $result = $this->executeNode($node, $state, $payload);

        if ($result === 'paused') {
            // ex: CAPTURE_VARIABLE espera próxima inbound — salva e sai
            return;
        }

        $nextNodeId = $this->resolveNext($node, $result, $state);

        $state->forceFill([
            'current_node_id' => $nextNodeId,
            'last_activity_at' => now(),
        ])->save();

        if ($nextNodeId === null) {
            $state->complete();
            return;
        }
    }
}

private function resolveNext(AutomationStep $node, mixed $result, ConversationAutomationState $state): ?string
{
    $sourceHandle = match ($node->kind) {
        AutomationStep::KIND_BRANCH => $result === true ? 'truthy' : 'falsy',
        AutomationStep::KIND_LOOKUP_CONTACT => $result !== null ? 'found' : 'not_found',
        default => 'default',
    };

    $edge = AutomationEdge::where('automation_id', $node->automation_id)
        ->where('from_node_id', $node->node_id)
        ->where('source_handle', $sourceHandle)
        ->first();

    return $edge?->to_node_id;
}

CAPTURE_VARIABLE — fluxo pausa esperando próxima inbound

// Quando o engine encontra um CAPTURE_VARIABLE:
private function executeCapture(AutomationStep $node, ConversationAutomationState $state): string
{
    // Envia prompt opcional pro user ("Por favor, me diga seu nome:")
    if (!empty($node->config['prompt'])) {
        $this->sendMessage($state->conversation, $node->config['prompt']);
    }

    $state->forceFill(['status' => 'paused'])->save();
    return 'paused';
}

// Quando uma inbound chega numa conversa que TEM um state paused:
class ResumeWaitingAutomations
{
    public function __invoke(Conversation $conversation, Message $message): void
    {
        $states = ConversationAutomationState::where('conversation_id', $conversation->id)
            ->where('status', 'paused')
            ->get();

        foreach ($states as $state) {
            $node = AutomationStep::where('automation_id', $state->automation_id)
                ->where('node_id', $state->current_node_id)
                ->first();

            if ($node?->kind !== AutomationStep::KIND_CAPTURE_VARIABLE) continue;

            // Salva a resposta na variável configurada
            $varName = $node->config['variable_name'] ?? 'value';
            $variables = $state->variables;
            $variables[$varName] = $message->body;

            $state->forceFill([
                'variables' => $variables,
                'status' => 'running',
            ])->save();

            // Retoma execução do próximo nó
            app(RunAutomation::class)($state, ['triggered_by_message' => $message->id]);
        }
    }
}

📋 Tasks ordenadas pra próxima sessão

Sub-sessão 1 — Backend foundation (~3h)

  1. Migration add_node_fields_to_automation_steps (node_id, node_type, position_x, position_y)
  2. Migration create_automation_edges_table
  3. Migration create_conversation_automation_states_table
  4. Model AutomationEdge + relação Automation::edges()
  5. Model ConversationAutomationState + relações + scope visibleToUser
  6. Constantes novas em AutomationStep::KIND_* (5 novas)
  7. Testes feature: criar automation via factory com 3 nós + 2 edges; persist e recupera grafo correto

Sub-sessão 2 — Engine grafo-aware (~3h)

  1. Refactor RunAutomation::__invoke() pra trabalhar com state + grafo (manter retrocompatibilidade pra steps lineares legados)
  2. Action nova ResumeWaitingAutomations
  3. Implementar executeBranch, executeLookupContact, executeCaptureVariable, executeSendTemplate, executeEnd
  4. Plugar EvaluateAutomations('conversation_first_inbound', ...) e ResumeWaitingAutomations no IngestIncomingMessage
  5. Trigger types novos em EvaluateAutomations::matches
  6. Testes: fluxo lookup → branch → 2 caminhos; fluxo capture → pausa → retoma na próxima inbound

Sub-sessão 3 — Frontend React Flow (~5h)

  1. Adicionar @xyflow/react ao package.json
  2. Configurar Vite pra build de chunk React isolado
  3. Criar componente <FlowBuilder> em resources/js/flow-builder/
  4. Nó customizado pra cada step kind (cores, ícones, handles)
  5. Painel lateral de propriedades (form que muta config do nó)
  6. Paleta de nós draggable (lateral esquerda)
  7. View Blade /automations/{id}/builder que monta o React app
  8. Endpoint POST /automations/{id}/builder que recebe {nodes, edges} e persiste
  9. Endpoint GET /automations/{id}/builder.json que retorna estado pra carregar

Sub-sessão 4 — Templates + UX polish (~2h)

  1. Atualizar AutomationTemplate seeder com pelo menos 1 template novo de fluxo de chat (ex: "Boas-vindas com captura de nome")
  2. Botão "Editar visualmente" nos cards de automation existente
  3. Validação no save: garantir 1 e só 1 nó de trigger, sem ciclos, sem nós órfãos
  4. Indicador visual de "execuções ativas" (quantas conversas estão neste fluxo agora)

📦 Arquivos novos previstos

app/Domain/Automation/Models/
  AutomationEdge.php
  ConversationAutomationState.php

app/Domain/Automation/Actions/
  ResumeWaitingAutomations.php
  ExecuteAutomationNode.php (substitui executeStep mais complexo)

app/Domain/Automation/Http/Controllers/
  AutomationBuilderController.php (GET .json, POST save)

database/migrations/
  2026_05_XX_add_node_fields_to_automation_steps.php
  2026_05_XX_create_automation_edges_table.php
  2026_05_XX_create_conversation_automation_states_table.php

resources/js/flow-builder/
  index.tsx
  nodes/TriggerNode.tsx
  nodes/SendMessageNode.tsx
  nodes/BranchNode.tsx
  nodes/CaptureNode.tsx
  nodes/LookupContactNode.tsx
  nodes/SendTemplateNode.tsx
  panels/PropertyPanel.tsx
  panels/NodePalette.tsx
  api.ts (fetch helpers)

resources/views/automations/builder.blade.php (monta o React)

tests/Feature/
  AutomationFlowBuilderTest.php
  AutomationGraphEngineTest.php
  AutomationFirstInboundTriggerTest.php
  AutomationCaptureVariableTest.php

🧠 Armadilhas e decisões já tomadas

  1. React isolado, não migra resto do app. Adicionar React Flow só pra essa página. Resto continua Alpine+Blade. Vite suporta chunks separados; configure-o assim pra não inchar bundle global.

  2. node_id é UUID gerado no frontend. Não use auto-increment do step.id como referência de edge, porque o front precisa criar edges ANTES de salvar (sem ter id).

  3. Manter retrocompatibilidade com steps lineares. As 4 AutomationTemplates legadas usam position. Cria um adapter que migra "lista linear" pra "grafo trivial" (cada step vira nó, cada par adjacente vira edge com handle 'default').

  4. ConversationAutomationState é unique por (conversation_id, automation_id). Não pode rodar 2x a mesma automação na mesma conversa simultaneamente.

  5. Privacidade afiliado é INVIOLÁVEL. ConversationAutomationState herda affiliate_id da conversation. scopeVisibleToUser aplicado igual aos outros.

  6. Variáveis não persistem entre conversas por padrão. São locais ao state. Pra "memória do contato", grava no Contact/Lead model, não no state.

  7. Lookup de contato: match prioritário por contact_phone (E.164) → contact_email → cria novo Lead se nenhum match. Use LookupOrCreateContact action pra evitar duplicação de lógica.

  8. Branch expression: começa simples — variable == "valor", variable contains "texto", lookup_result.is_returning_customer == true. Sem expression language complexa (não vire DSL desnecessariamente).

  9. Trigger conversation_first_inbound é o MAIS importante. É o que destrava chat-bot pra Caio. Priorize esse trigger sobre os outros novos.

  10. Loop / recursão no grafo: proibir ciclos no save. Validar via DFS antes de aceitar a malha. Caso contrário, fluxo pode rodar pra sempre.


🔮 Sugestão de abertura da próxima sessão

"Vamos começar Sub-sessão 1 do flow builder. Primeira coisa:
verificar Integration #3 (token ainda válido?), depois rodar
as 3 migrations + criar os models + escrever os testes feature
de persistência do grafo. Sem mexer no engine ainda."

A engine vem na Sub-sessão 2. Frontend na 3. Não tentem fazer tudo em uma sessão só — vai estourar contexto.


📋 Tasks ativas (state final desta sessão)

#1-#3 completed - Etapa 2 templates (E2.6, E2.7, E2.8)
#4 completed - Teste E2E WABA corporativo
(novas) pending - Sub-sessão 1, 2, 3, 4 do Flow Builder

(externas) - App Review da Meta (Caio finalizando agora)
(infra)    - Wildcard SSL, Queue worker, Webhook router
(futuro)   - Mídia outbound (áudio/imagem), read receipts, typing indicator

Última nota: Caio enviou o pedido de App Review nesta sessão (+descrições inglesa, PT-BR, business one-liner, reviewer instructions). Aguarda 3-7 dias úteis. Flow Builder pode começar antes do review terminar — não depende dele.

13

WhatsApp / Evolution — setup

Subir Evolution em ~10 min, parear via QR, conectar ao PulseSaaS.

Pré-requisitos

  • Servidor com Docker + Docker Compose instalados
  • Servidor acessível por HTTPS (ou pelo menos pelo IP/porta de fora)
  • Domínio apontando pro servidor (opcional, mas recomendado pra produção)

1. Gerar a API key global

No servidor, rode:

openssl rand -hex 32

Copia o resultado. Esse será o valor de AUTHENTICATION_API_KEY no docker-compose.


2. Subir os containers

# Cria uma pasta dedicada
mkdir -p ~/evolution && cd ~/evolution

# Baixa o docker-compose.yml (ou copia o arquivo docs/setup/docker-compose.evolution.yml deste projeto)
# Edita o arquivo e substitui TROQUE_POR_CHAVE_LONGA_E_ALEATORIA pela chave que você gerou

# Sobe
docker compose -f docker-compose.evolution.yml up -d

# Verifica que está rodando
docker ps | grep evolution

Espera ~30s pra Postgres inicializar.


3. Testar que está vivo

curl -i http://localhost:8080/
# Deve responder com algum JSON da Evolution

Se você usou domínio, ex evolution.seudominio.com, configure o nginx/Apache reverse proxy:

server {
    listen 443 ssl http2;
    server_name evolution.seudominio.com;

    ssl_certificate /etc/letsencrypt/live/evolution.seudominio.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/evolution.seudominio.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # Habilita websocket caso queira streaming
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

E rode certbot --nginx -d evolution.seudominio.com pra SSL.


4. Criar uma instância na Evolution

# Substitua URL e CHAVE pela sua
curl -X POST "https://evolution.seudominio.com/instance/create" \
  -H "apikey: SUA_CHAVE_GLOBAL" \
  -H "Content-Type: application/json" \
  -d '{
    "instanceName": "pulse-prod-1",
    "qrcode": true,
    "integration": "WHATSAPP-BAILEYS"
  }'

Resposta vai ter instance.instanceName = pulse-prod-1. Esse é o nome que você vai colocar no PulseSaaS.


5. Cadastrar a conexão no PulseSaaS

  1. Login no PulseSaaS
  2. Menu → 🔌 Integrações
  3. Categoria 💬 Canais de atendimento → card "Evolution API (QR não-oficial)" → expande
  4. Preenche:
    • Rótulo: "WhatsApp principal"
    • Base URL da Evolution: https://evolution.seudominio.com
    • API Key: sua AUTHENTICATION_API_KEY (a mesma do passo 1)
    • Nome da Instância: pulse-prod-1
  5. Conectar agora

Vai aparecer o card da conexão em "Minhas conexões" no topo da página.


6. Configurar o webhook (a Evolution avisar o PulseSaaS de mensagens novas)

Volta no terminal e roda (substitui {ID} pelo ID da conexão que apareceu no card do PulseSaaS — número inteiro):

curl -X POST "https://evolution.seudominio.com/webhook/set/pulse-prod-1" \
  -H "apikey: SUA_CHAVE_GLOBAL" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://SEU_PULSESAAS/webhooks/integration/{ID}",
    "webhook_by_events": false,
    "webhook_base64": false,
    "events": [
      "MESSAGES_UPSERT",
      "CONNECTION_UPDATE"
    ]
  }'

Pronto. Próxima mensagem que chegar no WhatsApp pareado vai aparecer automaticamente na inbox do PulseSaaS.


7. Parear via QR no PulseSaaS

  1. No card da conexão recém-criada (em "Minhas conexões" no PulseSaaS)
  2. Clica 📱 Parear via QR
  3. QR aparece + atualiza automático
  4. No celular: WhatsApp → ⋮ → Aparelhos conectadosConectar um aparelho → escaneia
  5. Badge fica verde "✅ Aparelho conectado"

Troubleshooting comum

Sintoma Causa provável Fix
Conexão criada mas QR não aparece Base URL com http:// em ambiente HTTPS Use HTTPS ou configure CORS_ORIGIN
"Erro 401" no PulseSaaS API Key errada Confere se a key no PulseSaaS é igual à AUTHENTICATION_API_KEY no compose
QR aparece mas escanea e cai Webhook não configurado Roda o curl do passo 6
Mensagem chega mas não aparece no PulseSaaS URL do webhook errada ou ID errado Confere o ID da integração no PulseSaaS e a URL no webhook/set
Container reiniciando direto Postgres ainda subindo docker logs pulse-evolution-db e espera ele inicializar

Backup

Os dados ficam nos volumes Docker evolution-db-data e evolution-redis-data. Pra backup:

docker exec pulse-evolution-db pg_dump -U evolution evolution > evolution-backup-$(date +%F).sql

Riscos importantes

Evolution é não-oficial: usa engenharia reversa do protocolo WhatsApp. Meta pode banir o número:

  • Se enviar muitas mensagens em pouco tempo (>20/min sem contexto)
  • Se receber muitas denúncias dos destinatários
  • Se Meta atualizar protocolo e Evolution demorar pra acompanhar

Pra produção séria, use WABA Cloud API (oficial). Evolution é ótimo pra MVP, testes e volumes baixos.

14

Checklist de desenvolvimento SaaS

Documento de acompanhamento do desenvolvimento do produto em formato de checklist.

Objetivo:

  • centralizar as etapas macro do projeto
  • permitir acompanhamento visual do que ja foi concluido
  • guardar o prompt recomendado de cada fase
  • manter uma ordem clara de evolucao para chegar em um SaaS funcional, responsivo, bonito e seguro

Aviso

O nome do produto ainda nao foi definido.

Qualquer nome citado em outros documentos, como Pulse CRM SaaS, deve ser entendido apenas como nome meramente ilustrativo.

Como usar este arquivo

  1. Execute uma etapa por vez ou por bloco.
  2. Quando concluir uma etapa, troque [ ] por [x].
  3. Use o prompt sugerido da etapa correspondente para orientar a implementacao.
  4. Ao final de cada etapa, valide:
  • regras de negocio
  • responsividade
  • seguranca
  • isolamento multiempresa
  • testes
  • documentacao

Ordem recomendada

  • Etapa 1 - Fundacao tecnica e organizacao da arquitetura
  • Etapa 2 - Hierarquia administrador, produtor e afiliado
  • Etapa 3 - CRM principal
  • Etapa 4 - Kanban dinamico
  • Etapa 5 - Etiquetas como motor operacional
  • Etapa 6 - Inbox omnichannel
  • Etapa 7 - Automacoes nativas
  • Etapa 8 - Tracking ADS e atribuicao
  • Etapa 9 - Eventos de conversao
  • Etapa 10 - Billing, planos e parceiros
  • Etapa 11 - Agentes de IA
  • Etapa 12 - Hardening final, performance, UX e seguranca

Etapa 1 - Fundacao tecnica e organizacao da arquitetura

Checklist

  • Revisar a arquitetura atual do projeto Laravel
  • Organizar a estrutura por dominio
  • Padronizar controllers, models, requests, services e policies
  • Garantir que a base continue preparada para SaaS multiempresa
  • Preservar Jetstream, Livewire, Sanctum e Spatie
  • Validar que a UI base continue responsiva e consistente
  • Atualizar documentacao tecnica apos a refatoracao

Prompt recomendado

Quero que você consolide a base atual do projeto Laravel SaaS para servir como fundação de produto.
Objetivos:
- revisar a arquitetura atual
- organizar estrutura por domínio sem quebrar o que já existe
- manter Laravel 13 + Jetstream + Livewire + Sanctum + Spatie Permission + Spatie Activitylog
- melhorar organização de controllers, models, requests, services e policies
- preparar a base para multiempresa, produtor, afiliado, inbox, CRM, automações, IA e billing
- preservar layout responsivo e visual premium já iniciado
- documentar as decisões técnicas no final

Entregue:
- refatoração da base
- estrutura de pastas sugerida e implementada
- ajustes necessários em rotas, providers e models
- validação com testes e build, se possível

Regras:
- não me devolva só plano, implemente o máximo possível
- preserve a base existente e evolua em cima dela
- mantenha padrão visual premium, responsivo e limpo
- aplique boas práticas de segurança
- documente o que foi feito
- valide com testes/build quando possível
- se precisar fazer escolhas arquiteturais, escolha a opção mais escalável para um SaaS real

Etapa 2 - Hierarquia administrador, produtor e afiliado

Checklist

  • Criar modelagem de administrador da plataforma
  • Criar modelagem de produtor
  • Criar modelagem de afiliado
  • Definir relacoes entre plataforma, produtor e afiliado
  • Implementar permissoes por nivel
  • Garantir isolamento por workspace e por afiliado
  • Criar telas iniciais de gestao dessa hierarquia
  • Criar seeders e documentacao funcional

Prompt recomendado

Quero que você implemente a camada de negócio completa de administrador da plataforma, produtor e afiliado no projeto atual.
Objetivos:
- administrador da plataforma gerencia produtores e assinaturas
- produtor gerencia afiliados, permissões e recursos liberados
- afiliado acessa apenas o escopo permitido
- criar modelagem de banco, migrations, models, policies, seeders e telas iniciais
- garantir isolamento por workspace e por afiliado
- manter segurança e clareza de permissão

Entregue:
- banco de dados completo para essa hierarquia
- regras de acesso
- CRUDs iniciais
- documentação técnica do fluxo

Regras:
- não me devolva só plano, implemente o máximo possível
- preserve a base existente e evolua em cima dela
- mantenha padrão visual premium, responsivo e limpo
- aplique boas práticas de segurança
- documente o que foi feito
- valide com testes/build quando possível
- se precisar fazer escolhas arquiteturais, escolha a opção mais escalável para um SaaS real

Etapa 3 - CRM principal

Referencias de implantacao

  • docs/COMPARATIVO_KRAYIN_X_PULSESAAS_IMPLANTACAO.md
  • scrum-steps/SPRINT_3_ETAPA_3_SCRUM.md
  • scrum-steps/SPRINT_3_STATUS.md

Checklist

  • Estruturar contatos
  • Estruturar leads
  • Estruturar oportunidades
  • Estruturar tarefas
  • Estruturar historico e atividades
  • Implementar filtros e busca
  • Separar claramente escopo de produtor e afiliado
  • Garantir dashboard responsivo e operacional

Prompt recomendado

Quero que você transforme a base atual em um CRM mais completo.
Objetivos:
- contatos
- leads
- oportunidades
- tarefas
- atividades
- funil comercial
- histórico por lead
- filtros e busca
- visual responsivo e premium
- separar bem o que pertence ao produtor e ao afiliado

Entregue:
- modelagem e migrations necessárias
- rotas e controllers
- telas responsivas
- componentes reutilizáveis
- validações
- auditoria com activity log

Regras:
- não me devolva só plano, implemente o máximo possível
- preserve a base existente e evolua em cima dela
- mantenha padrão visual premium, responsivo e limpo
- aplique boas práticas de segurança
- documente o que foi feito
- valide com testes/build quando possível
- se precisar fazer escolhas arquiteturais, escolha a opção mais escalável para um SaaS real

Etapa 4 - Kanban dinamico

Referencias de implantacao

  • scrum-steps/SPRINT_4_ETAPA_4_SCRUM.md
  • scrum-steps/SPRINT_4_STATUS.md

Checklist

  • Criar multiplos pipelines
  • Criar multiplas etapas por pipeline
  • Permitir movimentacao manual de cards
  • Registrar historico de movimentacao
  • Preparar funis de vendas, ligacao, cobranca, rastreio e pos-venda
  • Garantir experiencia forte em desktop e mobile
  • Preparar base para movimentacao automatica

Prompt recomendado

Quero que você implemente um sistema de kanban configurável para o SaaS.
Objetivos:
- múltiplos pipelines
- múltiplas etapas por pipeline
- cards movidos manualmente
- histórico de movimentação
- kanban responsivo com boa experiência desktop e mobile
- suporte a funil inicial, funil de ligação, cobrança, rastreio e pós-venda
- base preparada para movimentação automática por etiqueta, webhook e automação

Entregue:
- banco de dados para pipelines, etapas e histórico
- interface visual forte e elegante
- regras de permissão por produtor e afiliado
- documentação do fluxo

Regras:
- não me devolva só plano, implemente o máximo possível
- preserve a base existente e evolua em cima dela
- mantenha padrão visual premium, responsivo e limpo
- aplique boas práticas de segurança
- documente o que foi feito
- valide com testes/build quando possível
- se precisar fazer escolhas arquiteturais, escolha a opção mais escalável para um SaaS real

Etapa 5 - Etiquetas como motor operacional

Referencias de implantacao

  • scrum-steps/SPRINT_5_ETAPA_5_SCRUM.md
  • scrum-steps/SPRINT_5_STATUS.md

Status atual

Etapa concluida em 2026-05-02, com fundacao, interface, filtros, historico visivel e preparacao para automacoes futuras.

Checklist

  • Criar modelagem base de etiquetas por workspace
  • Preparar vinculo de etiquetas a leads, deals e atividades
  • Expor aplicacao manual de etiquetas na interface
  • Criar filtros por etiqueta no CRM e no kanban
  • Registrar historico visivel de aplicacao e remocao
  • Preparar uso de etiquetas em automacoes futuras

Prompt recomendado

Quero que você implemente o sistema de etiquetas do produto como peça central da operação.
Objetivos:
- etiquetas globais por workspace
- associação de etiquetas a leads, conversas e pedidos
- regras para usar etiquetas em automações e kanban
- filtros por etiqueta
- histórico de aplicação e remoção
- visual simples e rápido para operação

Entregue:
- migrations, models e relacionamentos
- UI para criar, editar e aplicar etiquetas
- base para automações futuras
- auditoria das alterações

Regras:
- não me devolva só plano, implemente o máximo possível
- preserve a base existente e evolua em cima dela
- mantenha padrão visual premium, responsivo e limpo
- aplique boas práticas de segurança
- documente o que foi feito
- valide com testes/build quando possível
- se precisar fazer escolhas arquiteturais, escolha a opção mais escalável para um SaaS real

Etapa 6 - Inbox omnichannel

Referencias de implantacao

  • scrum-steps/SPRINT_6_ETAPA_6_SCRUM.md
  • scrum-steps/SPRINT_6_STATUS.md

Checklist

  • Modelar caixas de entrada
  • Modelar conversas
  • Modelar mensagens
  • Preparar arquitetura para WhatsApp oficial
  • Preparar arquitetura para conexao via QR code
  • Preparar arquitetura para Instagram e chat do site
  • Integrar conversas com lead, etiquetas, CRM e automacoes
  • Garantir layout responsivo de atendimento

Prompt recomendado

Quero que você implemente a fundação do módulo de inbox omnichannel.
Objetivos:
- modelar caixas de entrada
- modelar conversas
- modelar mensagens
- preparar integração com WhatsApp oficial, QR code, Instagram e chat de site
- criar interface de inbox profissional e responsiva
- separar visualmente canais, conversas e contexto do lead
- garantir que cada conversa possa alimentar CRM, etiquetas e automações

Entregue:
- modelagem de banco
- rotas e telas
- interface operacional forte
- arquitetura pronta para plugar integrações reais depois

Regras:
- não me devolva só plano, implemente o máximo possível
- preserve a base existente e evolua em cima dela
- mantenha padrão visual premium, responsivo e limpo
- aplique boas práticas de segurança
- documente o que foi feito
- valide com testes/build quando possível
- se precisar fazer escolhas arquiteturais, escolha a opção mais escalável para um SaaS real

Etapa 7 - Automacoes nativas

Referencias de implantacao

  • scrum-steps/SPRINT_7_ETAPA_7_SCRUM.md
  • scrum-steps/SPRINT_7_STATUS.md

Status atual

Etapa concluida em 2026-05-03, com engine inicial, gatilhos operacionais, acoes nativas, processamento em fila e logs de execucao.

Checklist

  • Criar engine basica de automacao
  • Criar gatilhos por etiqueta
  • Criar gatilhos por etapa de kanban
  • Criar gatilhos por tempo sem resposta
  • Criar gatilhos por webhook
  • Criar acoes para mensagem, etiqueta, tarefa e movimentacao
  • Processar automacoes em fila
  • Registrar logs de execucao

Prompt recomendado

Quero que você implemente o núcleo de automações nativas do SaaS.
Objetivos:
- builder inicial baseado em gatilho e ação
- gatilhos por etiqueta, etapa, tempo sem resposta, webhook e evento de pagamento
- ações como enviar mensagem, aplicar etiqueta, mover kanban, criar tarefa, acionar IA e disparar evento de conversão
- fila para processar automações com segurança
- logs de execução
- base visual simples, clara e didática

Entregue:
- estrutura de banco
- engine inicial de execução
- jobs e filas
- interface para configurar automações
- documentação técnica do fluxo

Regras:
- não me devolva só plano, implemente o máximo possível
- preserve a base existente e evolua em cima dela
- mantenha padrão visual premium, responsivo e limpo
- aplique boas práticas de segurança
- documente o que foi feito
- valide com testes/build quando possível
- se precisar fazer escolhas arquiteturais, escolha a opção mais escalável para um SaaS real

Etapa 8 - Tracking ADS e atribuicao

Referencias de implantacao

  • scrum-steps/SPRINT_8_ETAPA_8_SCRUM.md
  • scrum-steps/SPRINT_8_STATUS.md

Status atual

Etapa concluida em 2026-05-03, com fundacao de contas Meta, campanhas, conjuntos, anuncios, captura de UTMs/fbclid e dashboard inicial de atribuicao.

Checklist

  • Estruturar conexao com Meta Ads
  • Armazenar campanhas, conjuntos e anuncios
  • Registrar UTMs e fbclid
  • Vincular origem de campanha ao lead
  • Criar dashboards de atribuicao
  • Preparar base para envio de eventos de conversao

Prompt recomendado

Quero que você implemente o módulo de tracking ADS e atribuição comercial.
Objetivos:
- conectar contas da Meta de forma arquitetural
- armazenar campanhas, adsets e ads
- registrar utms, fbclid e origem do lead
- vincular lead à campanha
- mostrar dashboards de performance por origem
- preparar base para eventos de conversão

Entregue:
- modelagem de banco
- serviços e estrutura de integração
- telas de tracking e atribuição
- relatórios iniciais por campanha, origem e fechamento

Regras:
- não me devolva só plano, implemente o máximo possível
- preserve a base existente e evolua em cima dela
- mantenha padrão visual premium, responsivo e limpo
- aplique boas práticas de segurança
- documente o que foi feito
- valide com testes/build quando possível
- se precisar fazer escolhas arquiteturais, escolha a opção mais escalável para um SaaS real

Etapa 9 - Eventos de conversao

Referencias de implantacao

  • scrum-steps/SPRINT_9_ETAPA_9_SCRUM.md
  • scrum-steps/SPRINT_9_STATUS.md

Status atual

Etapa concluida em 2026-05-03, com eventos internos, regras por gatilho, fila, retries, deduplicacao, logs e painel inicial.

Checklist

  • Criar eventos internos de conversao
  • Implementar fila de envio
  • Implementar retries
  • Implementar deduplicacao
  • Criar logs de sucesso e falha
  • Permitir gatilhos por etiqueta, etapa e webhook
  • Criar painel de integracoes de conversao
  • Garantir trilha auditavel

Prompt recomendado

Quero que você implemente o módulo completo de eventos de conversão para Meta.
Objetivos:
- eventos internos de conversão
- fila de envio
- retries
- deduplicação
- logs de sucesso e falha
- gatilhos por etiqueta, mudança de etapa e webhook
- painel de integrações de conversão
- estrutura segura e auditável

Entregue:
- migrations
- models
- jobs
- services
- UI de configuração
- logs
- documentação do workflow ponta a ponta

Regras:
- não me devolva só plano, implemente o máximo possível
- preserve a base existente e evolua em cima dela
- mantenha padrão visual premium, responsivo e limpo
- aplique boas práticas de segurança
- documente o que foi feito
- valide com testes/build quando possível
- se precisar fazer escolhas arquiteturais, escolha a opção mais escalável para um SaaS real

Etapa 10 - Billing, planos e parceiros

Referencias de implantacao

  • scrum-steps/SPRINT_10_ETAPA_10_SCRUM.md
  • scrum-steps/SPRINT_10_STATUS.md

Status atual

Etapa concluida em 2026-05-03, com catalogo formal de planos, limites por plano, kits comerciais de afiliado, calculo operacional de comissoes e painel inicial de billing.

Checklist

  • Criar modulo administrativo da plataforma
  • Gerenciar assinatura de produtores
  • Criar planos do SaaS
  • Definir limites por plano
  • Criar kits internos do produtor para afiliados
  • Preparar calculo de comissoes e percentual
  • Criar paines administrativos da plataforma e do produtor

Prompt recomendado

Quero que você implemente o módulo de billing e parceiros do SaaS.
Objetivos:
- administrador da plataforma gerencia produtores e suas assinaturas
- produtor gerencia afiliados e kits internos
- controlar planos como Standard, Premium e Platinum
- limitar canais, IA, automações e afiliados por plano
- preparar cálculo de comissão sobre afiliados
- criar telas administrativas claras e premium

Entregue:
- banco de dados
- regras de assinatura e limites
- painel administrativo da plataforma
- painel de parceiros do produtor
- documentação das regras comerciais

Regras:
- não me devolva só plano, implemente o máximo possível
- preserve a base existente e evolua em cima dela
- mantenha padrão visual premium, responsivo e limpo
- aplique boas práticas de segurança
- documente o que foi feito
- valide com testes/build quando possível
- se precisar fazer escolhas arquiteturais, escolha a opção mais escalável para um SaaS real

Etapa 11 - Agentes de IA

Referencias de implantacao

  • scrum-steps/SPRINT_11_ETAPA_11_SCRUM.md
  • scrum-steps/SPRINT_11_STATUS.md

Status atual

Etapa concluida em 2026-05-03, com fundacao de agentes operacionais, prompts personalizados, vinculo a pipeline/etapa/etiqueta, execucao simulada e trilha auditavel.

Checklist

  • Criar cadastro de agentes de IA
  • Permitir prompts personalizados
  • Associar agentes a pipelines, etapas ou contextos
  • Permitir atuacao em follow-up, cobranca, rastreio e pos-venda
  • Registrar historico das acoes do agente
  • Garantir configuracao clara, segura e auditavel

Prompt recomendado

Quero que você implemente a base do módulo de agentes de IA operacionais.
Objetivos:
- criar agentes com prompt personalizado
- vincular agentes a kanbans, etapas ou contextos específicos
- permitir atuação em follow-up, cobrança, rastreio e pós-venda
- registrar histórico das ações do agente
- manter arquitetura segura, auditável e preparada para provedores de IA
- criar telas bonitas e claras para configuração

Entregue:
- modelagem de banco
- fluxo de execução
- UI de criação de agentes
- associação de agentes a cenários
- logs e trilha de auditoria

Regras:
- não me devolva só plano, implemente o máximo possível
- preserve a base existente e evolua em cima dela
- mantenha padrão visual premium, responsivo e limpo
- aplique boas práticas de segurança
- documente o que foi feito
- valide com testes/build quando possível
- se precisar fazer escolhas arquiteturais, escolha a opção mais escalável para um SaaS real

Etapa 12 - Hardening final, performance, UX e seguranca

Referencias de implantacao

  • scrum-steps/SPRINT_12_ETAPA_12_SCRUM.md
  • scrum-steps/SPRINT_12_STATUS.md
  • docs/CHECKLIST_OPERACAO_PILOTO.md

Status atual

Etapa concluida em 2026-05-03, com headers de seguranca, indices complementares de performance, validacao de escopo mais rígida para IA, testes de hardening e checklist de operacao piloto.

Checklist

  • Revisar seguranca de autenticacao e autorizacao
  • Revisar isolamento de dados por workspace e afiliado
  • Revisar queries, indices e performance
  • Revisar responsividade completa
  • Melhorar estados vazios, erros e feedbacks
  • Revisar acessibilidade
  • Criar testes para fluxos criticos
  • Atualizar documentacao final
  • Preparar checklist de producao

Prompt recomendado

Quero que você faça a etapa final de hardening e polimento do SaaS.
Objetivos:
- revisar segurança, permissões e isolamento de dados
- melhorar validações
- revisar queries e performance
- melhorar responsividade completa
- elevar o nível visual para algo premium e consistente
- revisar logs, erros e estados vazios
- revisar acessibilidade e UX operacional
- criar testes para fluxos críticos
- atualizar documentação final do sistema

Entregue:
- refino visual
- hardening de segurança
- testes
- documentação final
- checklist de produção

Regras:
- não me devolva só plano, implemente o máximo possível
- preserve a base existente e evolua em cima dela
- mantenha padrão visual premium, responsivo e limpo
- aplique boas práticas de segurança
- documente o que foi feito
- valide com testes/build quando possível
- se precisar fazer escolhas arquiteturais, escolha a opção mais escalável para um SaaS real

Checklist final de controle

  • Arquitetura base consolidada
  • Modelo administrador, produtor e afiliado concluido
  • CRM principal funcional
  • Kanban funcional e responsivo
  • Etiquetas operacionais implementadas
  • Inbox omnichannel estruturado
  • Automacoes nativas funcionando
  • Tracking ADS implementado
  • Eventos de conversao implementados
  • Billing e parceiros implementados
  • Agentes de IA implementados
  • Seguranca, performance e UX revisados
  • Documentacao atualizada
  • Sistema pronto para operacao piloto
15

Checklist de produção real

Criado 2026-06-11 a pedido do Caio: "tem que ser tudo real, produção". Mapa honesto do que JÁ é real, do que está SIMULADO e do que falta pra vender. Marque [x] ao concluir. Itens 🔑 = só o Caio pode fazer (credenciais/contas).


1. 🟢 EMBEDDED SIGNUP DA META (código PRONTO — falta o app na Meta)

O produtor conecta o número oficial num popup, sem tocar na Meta. O código desta parte foi entregue em 2026-06-11 (action + webhook roteador /webhooks/meta + botão na tela de Integrações + testes). O que falta é 100% do lado da Meta:

1a. Criar e aprovar o app central (uma vez — é o que demora)

  • 🔑 Meta Business Manager da CL Soluções com Business Verification aprovada (CNPJ, site, e-mail corporativo). Prazo típico: 2 dias a 2 semanas.
  • 🔑 Criar Meta App (tipo Business) em developers.facebook.com.
  • 🔑 Adicionar os produtos WhatsApp e Facebook Login for Business ao app.
  • 🔑 Em Facebook Login for Business → Configurations → criar a configuração de WhatsApp Embedded Signup → copiar o Configuration ID.
  • 🔑 App Review / Advanced Access para whatsapp_business_messaging, whatsapp_business_management e business_management. Prazo típico: alguns dias a ~2 semanas; exige a Business Verification acima e demonstração em vídeo do fluxo. Sem isso o signup só funciona pra admins/testers do próprio app.
  • 🔑 (Recomendado) Solicitar status de Tech Provider no painel WhatsApp — destrava onboarding de clientes em escala.
  • 🔑 App em Live mode (não Development).

1b. Configurar o webhook do app (uma vez)

  • 🔑 Meta App → WhatsApp → Configuration → Callback URL: https://SEU-DOMINIO/webhooks/meta (rota já existe e responde o handshake).
  • 🔑 Verify Token: o mesmo valor que for pro META_WEBHOOK_VERIFY_TOKEN.
  • 🔑 Assinar os campos messages e message_template_status_update.

1c. Ligar no Pulse — ✅ FEITO 2026-06-11

  • .env completo: app Pulse Saas (1441737097662937) + secret + config_id (1364254832435215) + verify token. Worker reiniciado.
  • Webhook do app MIGRADO pra /webhooks/meta via API (autorizado pelo Caio; confirmado ativo na Meta com os 10 campos preservados).
  • Domínios do SDK JS: pulsehomolog + pulse-crm.pulsehomolog adicionados (novos tenants → adicionar o subdomínio; revisar no domínio de produção).
  • 🔑 VALIDAR COM TRÁFEGO REAL: (a) mandar "oi" do celular pro número atual (prova o roteador novo); (b) Integrações → "Conectar WhatsApp oficial" → popup → conectar um número de teste → mensagem → automação responde.

1d. Pós-conexão (expectativas de aprovação — ver resposta abaixo)

  • Display name do número: review automático da Meta (minutos a ~1 dia; o envio já funciona enquanto isso).
  • 🔑 Orientar o produtor a fazer a Business Verification dele quando quiser escalar volume (sem ela: limite inicial de conversas/dia).

2. ✅ OPERADORES — FEITO 2026-06-11

  • Tela Equipe (/workspace/members, item na sidebar, só o dono vê): criar membro com nome, e-mail, SENHA e papel; trocar papel; remover.
  • Papel REAL atribuído na criação (WorkspaceAccess): Gestor = tudo exceto Financeiro/Equipe; Operador = atendimento + CRM.
  • Financeiro RESTRITO (policy + sidebar): operador/gestor não veem.
  • Proteções: dono imutável; membro de outro workspace inacessível; remoção zera papéis (sem permissão fantasma).
  • (fase 2) granularidade por módulo nas demais telas (hoje gestor/operador diferem no Financeiro/Equipe; módulos de CRM são acessíveis a ambos).

3. 🔴 E-MAIL REAL (MAIL_MAILER=log hoje — nada sai)

  • 🔑 Escolher provedor (Resend/SES/SMTP do domínio) e colocar credenciais.
  • MAIL_MAILER/MAIL_FROM_* de produção + queue:restart.
  • Testar: reset de senha, convite de equipe, verificação de e-mail.
  • White-label de e-mail (remetente por tenant) — fase 2.

4. 🟡 EVOLUTION API (QR) — caminho não-oficial

  • 🔑 Servidor Evolution central de produção (URL + API key) — hoje não há env.
  • Revalidar o fluxo QR na tela de Integrações (handoff de maio registrou bloqueio; houve upgrade depois — testar conectar um chip de verdade).
  • Definir oferta: oficial (embedded signup) vs QR (chip) — preço/risco.

5. 🔴 BILLING / COBRANÇA (simulado hoje)

  • PaymentGatewayHttpClient está em simulateInvoiceCreation (adapter outbound simulado): integrar gateway REAL (Asaas/Stripe — catálogo já tem os tipos) pra gerar cobrança de verdade do produtor.
  • Webhook de pagamento (/webhooks/payments/{gateway}) com credencial real.
  • Régua: trial 14d → cobrar → suspender (status já existe no modelo).

6. 🟡 IA & CONVERSÕES (reais COM credencial; simulam sem)

  • IA: sem chave OpenAI/Anthropic conectada o agente SIMULA — garantir aviso claro na UI (já existe no nó do builder) e doc de onboarding do produtor.
  • Meta CAPI: sem access_token o envio é simulado COM aviso — validar 1 evento real no Events Manager do produtor piloto.

7. 🟡 INFRA DE PRODUÇÃO

  • 🔑 Domínio definitivo de produção (hoje staging pulsehomolog.clsolucoes.com) + Cloudflare (wildcard de tenant já validado no staging).
  • Deploy SEMPRE com: npm run build + php artisan migrate --force + php artisan queue:restart (lição de 2026-06-11: worker antigo = falso bug).
  • Storage de mídia no domínio do tenant (hoje cross-host no domínio base) + contrato anti-SSRF do envio WABA (F3/L5 do backlog).
  • Backup do banco + storage (rotina e teste de restore).
  • Sentry/monitoramento de erros em produção.
  • HTTPS em tudo (já ok no staging via Cloudflare).

8. 🟢 JÁ É REAL (sem pendência além de teste contínuo)

  • Envio/recebimento WABA Cloud (Graph API real; mídia por upload).
  • Motor de automações (etapas = fluxos; manual/automático; debounce).
  • Tempo real no chat (Reverb/websocket; broadcast do motor).
  • White-label: subdomínio por tenant + branding (cores/logo/temas).
  • Criação de produtor pelo painel Plataforma (workspace + plano + limites).
  • Privacidade do afiliado (modelo afiliado-cliente).

Ordem de ataque sugerida (pra vender mais rápido)

  1. 1a–1c Embedded Signup (inicia HOJE — a fila de aprovação da Meta é o gargalo; tudo do nosso lado já está pronto).
  2. 2 Operadores (épico curto de código — destrava equipe do produtor).
  3. 3 E-mail real (pequeno; pré-requisito de convites/reset).
  4. 5 Billing real (pra cobrar os produtores).
  5. 4 Evolution QR (plano B de canal) e 7 Infra em paralelo.
16

Apresentação a investidor

Documento executivo. Linguagem de negócio, sem termos técnicos. Versão 1.0 — 25/06/2026


1. Resumo em uma frase

O PulseSaaS é a "loja de marca própria" do atendimento por WhatsApp: qualquer empresa que vende pelo WhatsApp contrata a plataforma, coloca a própria marca, e ainda pode revender o sistema para uma rede de afiliados — gerando três fontes de receita recorrente para nós ao mesmo tempo.

Pense em "Shopify do WhatsApp com programa de afiliados embutido".


2. O problema

Hoje, no Brasil, milhões de reais por dia em vendas passam pelo WhatsApp — infoprodutos, e-commerce com pagamento na entrega, tráfego pago. Mas quem vende assim opera no improviso:

  • Atendimento bagunçado: celular pessoal, vários números, mensagens perdidas, nenhum histórico do cliente.
  • Nenhum controle de equipe: o dono não sabe quem respondeu o quê, quanto cada vendedor faturou, nem onde o cliente travou no funil.
  • Cobrança e follow-up manuais: alguém precisa lembrar de cobrar, de mandar o lembrete de 48h, de reativar quem sumiu. Na prática, não manda — e perde a venda.
  • Quem tem afiliados não tem ferramenta: o produtor que monta uma rede de revendedores não tem como dar a cada afiliado um sistema próprio, com privacidade, sob a mesma marca.

As ferramentas que existem (CRMs tradicionais, plataformas de automação) são caras, complexas e não foram feitas para o jeito brasileiro de vender por WhatsApp com afiliados e pagamento na entrega.


3. A solução

Uma plataforma única, vendida como marca própria de cada cliente, que junta num só lugar:

O que faz O que o cliente ganha
Central de atendimento estilo WhatsApp Web Toda a operação num lugar só: conversas, histórico, etiquetas, dados do cliente
CRM com funil visual Enxerga onde cada venda está e o que falta para fechar
Automações prontas (1 clique) A máquina cobra, faz follow-up e reativa cliente sozinha, na hora certa
Inteligência Artificial (GPT / Claude) Sugere respostas e atende em escala, sempre com aprovação humana
Cobrança na entrega (pay-after) Acompanha o pedido até a entrega e dispara a cobrança automática
Rede de afiliados O cliente revende o sistema para milhares de parceiros sob a marca dele
WhatsApp oficial da Meta Número verificado, selo verde, sem risco de banimento

Em resumo: o cliente liga o WhatsApp dele e a plataforma transforma conversa solta em operação de vendas organizada — com a cara da empresa dele.


4. Por que agora (timing)

  • WhatsApp virou o balcão de vendas do Brasil. É onde a compra acontece, não só onde o cliente tira dúvida.
  • A Meta abriu a API oficial e hoje informa o custo real de cada mensagem — isso permite cobrar o cliente com precisão e ter margem garantida (impossível há poucos anos).
  • A economia de afiliados / criadores explodiu. Há um exército de produtores montando redes de revenda que precisam de infraestrutura própria.
  • IA ficou barata e boa o suficiente para atender e cobrar em escala, com humano no controle.

A janela é agora: a tecnologia amadureceu e o comportamento de compra já migrou para o WhatsApp.


5. Como ganhamos dinheiro — três camadas de receita

Este é o coração do modelo. A mesma base de clientes gera receita por três caminhos diferentes, e os três são recorrentes:

Camada 1 — Assinatura mensal (SaaS clássico)

O cliente (chamado de produtor) paga um plano fixo por mês:

Plano Preço/mês Para quem
Starter R$ 297 Quem está começando
Growth R$ 497 Operação em crescimento (10 afiliados, 5 canais, IA)
Scale R$ 997 Operação grande (50 afiliados, 20 canais, mais IA e automações)

Receita previsível, recorrente, com baixo custo para servir. É a base do valuation de qualquer SaaS.

Camada 2 — Revenda para afiliados (margem sobre a rede)

O produtor revende o sistema para os afiliados dele (kit a partir de R$ 97/mês por afiliado). A plataforma fica com uma comissão sobre cada afiliado ativo.

Aqui está a alavanca: um único produtor com 50 afiliados ativos move R$ 4.850/mês em planos de afiliado — e nós participamos disso sem custo de aquisição, porque foi o produtor quem trouxe a rede. Cada cliente que entra pode trazer dezenas de subcontas.

Camada 3 — Margem por mensagem (a de maior potencial)

A plataforma paga a Meta pelas mensagens e revende ao cliente com markup, debitando de um saldo pré-pago (modelo "carteira", igual crédito de celular).

  • A Meta informa o custo real de cada mensagem → nossa margem é precisa e garantida.
  • Mensagens recebidas e respostas em até 24h são grátis (custo zero da Meta) — só mensagem de campanha é cobrada.
  • Escala automaticamente com o sucesso do cliente: quanto mais ele vende, mais mensagens dispara, mais nós faturamos. Receita que cresce sem precisar vender nada novo.

→ Esta camada está construída e pronta para ser ligada — falta apenas a definição final da tabela de preços de venda e a liberação fiscal (ver seção 9).

A soma das três: receita recorrente previsível (camada 1) + crescimento viral via redes de afiliados (camada 2) + receita por uso que escala com o cliente (camada 3). É o que torna o modelo difícil de copiar e atraente de financiar.


6. Exemplo ilustrativo de unit economics

Cenário de um único produtor no plano Growth, com uma rede de 50 afiliados ativos. Valores ilustrativos para mostrar o empilhamento das camadas.

Fonte Cálculo Receita/mês para a plataforma
Assinatura do produtor Plano Growth R$ 497
Margem sobre afiliados 50 afiliados × parte da comissão centenas a milhares de R$
Margem por mensagem markup sobre o volume disparado escala com o faturamento do cliente

Leitura para o investidor: o valor por cliente (ARPU) não é fixo — ele cresce conforme o cliente cresce. Um produtor de sucesso vale muito mais no mês 12 do que no mês 1, sem que a gente precise vender de novo. Isso é expansão de receita líquida (net revenue retention) embutida no modelo — a métrica que mais valoriza um SaaS.


7. Diferenciais competitivos (o "fosso")

  1. White-label de verdade, em 3 níveis. Não é só trocar logo: é domínio próprio, marca própria e uma hierarquia plataforma → produtor → afiliado, cada um com seu espaço isolado. Concorrentes genéricos não têm essa cadeia.
  2. Programa de afiliados nativo. O produtor monta um exército de revendedores sob a marca dele — e cada afiliado é uma nova subconta pagante. Crescimento que se auto-alimenta.
  3. Privacidade entre afiliados como produto. O afiliado é tratado como cliente do produtor, não funcionário: o produtor vê o resultado (faturamento, conversão) mas não lê as conversas do afiliado. Isso gera confiança e destrava redes grandes.
  4. Cobrança por uso com custo real da Meta. Margem precisa, não estimada. Diferencial de confiança e de previsibilidade financeira.
  5. WhatsApp oficial sem o cliente encostar na Meta. A plataforma faz a parte difícil (provedor de tecnologia oficial); o cliente conecta em poucos cliques.
  6. Feito para o jeito brasileiro de vender: pagamento na entrega, tráfego pago, IA em português, integração com transportadoras.

O fosso real: a combinação white-label + rede de afiliados + cobrança por uso cria custo de troca altíssimo. Quando um produtor já tem a operação dele e 200 afiliados rodando sob a marca dele na nossa plataforma, migrar é praticamente impossível.


8. Estágio atual — o que está pronto

A plataforma não é uma ideia no papel: é um produto construído e demonstrável de ponta a ponta, fruto de mais de 20 ciclos de desenvolvimento, com mais de 440 verificações automáticas de qualidade rodando a cada mudança (garante que nada quebra conforme evoluímos).

Já funcionando:

  • ✅ Plataforma multimarca: cada cliente com domínio e identidade próprios.
  • ✅ Hierarquia plataforma → produtor → afiliado, com cadastro de afiliados em escala por link público.
  • ✅ WhatsApp oficial e não-oficial conectando e trocando mensagens.
  • ✅ CRM, funis visuais, central de atendimento redesenhada estilo WhatsApp.
  • ✅ Automações com 4 modelos prontos (funil, cobrança pay-after, reativação, pós-venda).
  • ✅ Pedidos com cobrança na entrega e atualização automática de transportadora.
  • ✅ Agentes de Inteligência Artificial integrados ao atendimento.
  • ✅ Motor das três camadas de cobrança codificado (assinatura, afiliados, carteira de mensagens).

Estágio: MVP avançado / pré-piloto. A casa está construída; falta ligar a água e a luz (decisões comerciais e configuração de servidor) para receber o primeiro cliente pagante.


9. O que falta para faturar (e por que não é risco tecnológico)

São pendências comerciais e operacionais, não de engenharia — o que reduz o risco de execução:

Pendência Natureza Esforço
Definir a tabela de preços de venda das mensagens Decisão comercial do dono Dias
Ligar a cobrança por uso (hoje desligada por padrão) Apertar um botão após definir preço Imediato
Liberar a parte fiscal (nota fiscal / impostos) Contabilidade online Semanas
Ativar a linha de crédito junto à Meta Aprovação da Meta na conta da empresa Externo (depende da Meta)
Escolher gateway de pagamento (recomendado: Asaas) Decisão + integração ~1 semana
Configurar servidor de produção (domínios, filas 24/7, backup) Configuração de infraestrutura ~1 semana
Concluir a marca própria em todas as telas (hoje plena no chat) Acabamento visual Poucas semanas

Mensagem-chave: o produto está pronto para ligar. O caminho até o primeiro faturamento é curto e previsível, e a maioria dos itens são decisões e configuração — não desenvolvimento de risco.


10. Mercado-alvo

  • Produtor (cliente pagante principal): quem vende por WhatsApp e quer operar com marca própria e rede de afiliados — infoprodutores, operações de tráfego pago e e-commerce com pagamento na entrega. Paga a assinatura mensal.
  • Afiliado: revendedor do produtor; opera sob a marca dele, paga kit/plano, gera comissão. Cada afiliado é uma nova subconta pagante que entrou sem custo de aquisição para nós.
  • Operador/atendente: usa a central de atendimento no dia a dia.

→ O modelo é bottom-up e viral por natureza: conquistar um produtor pode significar conquistar dezenas ou centenas de subcontas junto.


11. Riscos e mitigação (transparência)

Risco Mitigação
Dependência da Meta/WhatsApp Oferecemos também canal não-oficial; o produto vale pelo CRM + automação + afiliados, não só pelo canal
Aprovação da linha de crédito pela Meta Camadas 1 e 2 (assinatura + afiliados) não dependem disso e já podem faturar
Definição fiscal da cobrança por uso Existe caminho alternativo de menor risco (faturar por consumo medido, como conta de nuvem) já estudado
Execução até o primeiro cliente Pendências são de configuração/decisão, não de tecnologia — risco baixo
Concorrência de players genéricos Fosso = white-label + afiliados + custo de troca alto

12. Para onde vai o investimento

O aporte acelera a passagem de "pronto para ligar" → "faturando e crescendo". As frentes:

  1. Lançamento comercial: infraestrutura de produção, gateway de pagamento, liberação fiscal, primeiro produtor-piloto.
  2. Acabamento do produto: marca própria em todas as telas, central de atendimento polida, construtor visual de automações.
  3. Aquisição e expansão: marketing para conquistar os primeiros produtores-âncora (cada um traz a própria rede de afiliados).
  4. Ativação da camada de maior margem: ligar a cobrança por mensagem com a tabela de preços definida.

Valores e participação a detalhar conforme o estágio da conversa com o investidor.


13. Por que vale a pena

  • Produto construído e demonstrável — risco tecnológico já vencido.
  • Três fontes de receita recorrente sobre a mesma base de clientes.
  • Crescimento viral embutido — cada produtor traz uma rede de afiliados pagantes.
  • Receita que cresce com o sucesso do cliente (margem por mensagem), sem custo de venda adicional.
  • Custo de troca altíssimo — quando a operação e a rede estão dentro, o cliente não sai.
  • Caminho curto e previsível até o primeiro faturamento — pendências são comerciais, não técnicas.

O produto está pronto. Falta combustível para transformar uma plataforma construída em um negócio faturando.


Documento preparado para apresentação a investidores. Os números de planos refletem a configuração atual do sistema; a tabela final de preços por mensagem está em definição.

17

Apresentação a sócios

Versão executiva. Sem jargão técnico. Última atualização: 2026-05-12.


🎯 Onde estamos

Em 22 sprints de desenvolvimento, entregamos a infraestrutura de um CRM multi-tenant white-label estilo Pluga, com 194 testes automatizados verdes e zero regressão acumulada.

O sistema já permite que um produtor:

  1. Tenha seu próprio domínio (empresa.pulsesaas.com.br)
  2. Cadastre afiliados em escala via link público (suporta 10 mil afiliados)
  3. Conecte WhatsApp oficial (WABA) e WhatsApp não-oficial (QR code)
  4. Crie automações em 1 clique a partir de modelos prontos (Funil 7 etapas, Cobrança Pay-After, Pós-venda, Reativação)
  5. Acompanhe pedidos pay-after com webhook automático das transportadoras (Melhor Envio)
  6. Use agentes de IA reais (OpenAI / Claude) integrados ao chat
  7. Veja tudo em um cockpit unificado no chat com etiquetas, pedidos, IA e botão de ligar pelo WhatsApp

Mas o atendimento (chat) ainda não está como queremos. Esse é o foco agora.


🎯 Próxima missão: o chat

Queremos um chat parecido com o WhatsApp:

  • Limpo e prático, sem poluição visual
  • Etiquetas visíveis e fáceis de aplicar
  • Informações do contato sempre visíveis (telefone, e-mail, variáveis customizadas)
  • Edição de qualquer dado direto na conversa, sem sair do chat
  • Histórico de pedidos do cliente ao lado da conversa
  • IA sugerindo resposta com um clique
  • Botão de ligação rápida

Esse é o coração da operação. O operador passa o dia aqui.


✅ O que está pronto (resumo)

1. Infraestrutura multi-tenant ✅

  • Cada produtor vira um "tenant" com domínio próprio e marca própria
  • Cadastro de afiliados por link público — escala para 10k+ sem trabalho manual
  • Sistema de aprovação opcional de afiliados
  • Permissões por afiliado (quem pode mexer em quê)
  • Modelo de isolamento de leads (decisão 2026-05-12): cada um (produtor + cada afiliado) tem seus próprios leads. Produtor NÃO distribui leads. Produtor vê apenas dashboard agregado dos afiliados (quantidade, conversão, faturamento) — sem ler conversas. Implementação técnica em ajuste no backlog (bloco 🔒 do checklist técnico).

2. Canais de atendimento ✅

  • WhatsApp oficial (WABA Cloud API)
  • WhatsApp não-oficial via QR code (Evolution API)
  • Recebimento automático de mensagens via webhook
  • Envio integrado

3. CRM básico ✅

  • Funis (kanbans) dinâmicos para vendas
  • Funil pay-after (cobrança pós-entrega) com etapas dedicadas
  • Contatos, leads, oportunidades, atividades
  • Etiquetas como motor de automação

4. Automações estilo Pluga ✅

  • 4 modelos prontos para usar em 1 clique:
    • Funil inicial com 7 mensagens
    • Cobrança pay-after com lembretes em 48h e 72h
    • Follow-up para quem parou de responder
    • Pós-venda e recompra
  • Cada passo pode ser: enviar mensagem, aplicar etiqueta, aguardar tempo, chamar IA, mover etapa do funil
  • Sistema espera o tempo certo (48h, 7 dias) e continua sozinho

5. Pedidos pay-after ✅

  • Quadro visual de pedidos por etapa (etiquetado → postado → em trânsito → entregue)
  • Recebe atualização automática dos Correios / Melhor Envio
  • Quando pedido é entregue, cobrança automática dispara
  • Controle de quanto cliente já pagou e quanto falta

6. Inteligência Artificial ✅

  • Agentes de IA com prompt customizado por propósito (cobrança, follow-up, pós-venda)
  • Conectam OpenAI (GPT) ou Anthropic (Claude)
  • IA pode atuar em automações ou ser consultada pelo operador no chat
  • Sempre mantém humano no loop (operador aprova antes de enviar)

7. Tracking de campanhas ✅ (modelagem)

  • Captura UTM, fbclid de cada lead
  • Modelagem de campanhas, conjuntos e anúncios Meta
  • Falta: conexão real com a Meta (OAuth) e envio de eventos de conversão

🚧 O que está em construção / planejado

Prioridade Item Por quê
🔥 AGORA Chat estilo WhatsApp (redesign completo da tela de conversas) É o coração do produto. Operador passa o dia aqui.
🔥 ALTO Campos customizados no contato (CPF, endereço, observações) Pediram pra editar info na hora
🔥 ALTO Notas internas do operador na conversa Marcar contexto sem o cliente ver
🟡 MÉDIO Notificação em tempo real (sem F5) Operador hoje precisa atualizar a página
🟡 MÉDIO Cobrança real (Asaas/Stripe) — vocês cobrando o produtor Hoje é manual; precisa de gateway real
🟡 MÉDIO Meta Ads OAuth + envio de conversões reais Quem faz tráfego pago precisa
🟢 BAIXO Drag-and-drop nos kanbans Funciona com select, mas drag é mais natural
🟢 BAIXO Builder visual de automação Hoje instancia template e edita config; visual seria polish
⏸️ ESPERAR Instagram DM, webchat no site Baixa adoção no nosso público-alvo
⏸️ ESPERAR Voice via Twilio (API) Custo alto; já temos botão "ligar pelo WhatsApp"
⏸️ ESPERAR Reports avançados, dashboard financeiro Depende de ter dados reais primeiro

⚠️ Bloqueios para o primeiro cliente real (infra)

Tem coisas que não dependem de código, dependem da estrutura do servidor antes do lançamento:

Item Status Por que importa
Domínio curinga (*.pulsesaas.com.br) configurado no DNS 🔴 Falta Sem isso, cada produtor não consegue ter o subdomínio próprio
Certificado SSL para subdomínios 2 níveis 🔴 Falta Hoje em homolog estamos usando Cloudflare for SaaS (plano free, limite 100 hostnames) para emitir cert por tenant. Para produção escalar além disso, vamos precisar upgrade: ou pagar Cloudflare Pro/Business (US$20-200/mês), ou Advanced Certificate Manager (US$10/mês por zona, cert wildcard direto), ou plano dedicado SaaS pago. Decisão pendente quando aproximarmos de 80 produtores.
Servidor de filas (Redis + worker rodando 24/7) 🔴 Falta Sem isso, automações de "aguardar 48h" não funcionam
Banco de dados em UTF-8 (hoje está em latin1) 🔴 Falta Emoji e acentos quebram em chat real
Backup automatizado + teste de restauração 🔴 Falta Se cair, perde tudo
Monitoramento de erros (Sentry) 🔴 Falta Sem isso, descobrimos problema só quando cliente reclama

Estimativa para resolver tudo isso: 1 sprint de infraestrutura. Não é desenvolvimento; é configuração de servidor.


💭 Decisões que precisamos tomar juntos

Estas decisões definem o que priorizar nas próximas 4-6 semanas:

1. Lançamos para produtores que fazem Meta Ads desde o MVP?

  • Se SIM: prioridade alta para integração real com Facebook Ads + envio de conversões. ~2 sprints.
  • Se NÃO: focamos no chat e pay-after primeiro, Meta Ads vira fase 2.

2. Qual gateway de pagamento para nossa cobrança?

  • Asaas: brasileiro, fácil, Pix/boleto/cartão. Recomendação: ✅
  • Stripe: global, melhor para cartão internacional. Útil se vender para fora.
  • Mercado Pago: brasileiro, popular mas API mais limitada.
  • Decisão impacta ~1 sprint de integração.

3. VPS atual (Hostinger KVM8) ou migrar para cloud (Hetzner/DO)?

  • Atual: custa pouco, mas escala limitada.
  • Cloud: mais caro, mas elástico, snapshots automáticos.
  • Sugiro manter atual até passar de 50 produtores.

4. Desktop ou mobile-first? ✅ DECIDIDO (2026-05-12): Mobile-first + responsivo + largura total

  • Layout mobile-first em todas as telas (320px → 4K)
  • Sem max-w-* artificiais; padding lateral cresce com a tela
  • Sidebar lateral: colapsada em laptop, expandida em monitor grande, drawer em mobile
  • Chat: 4 colunas em desktop (inbox + conversas + chat + contato) → tabs em mobile
  • Touch targets ≥ 44px; inputs com font-size: 16px mínimo

5. Lançamos com 1 produtor piloto SEM Meta Ads real?

  • Se SIM: entramos em piloto em 2-3 sprints (chat + infra + cobrança).
  • Se NÃO: esperamos Meta Ads real ficar pronto antes.

🗓️ Roteiro sugerido das próximas 4-6 semanas

Cada sprint = ~1 semana de trabalho focado.

Semana 1-2: Chat WhatsApp-style (PRIORIDADE MÁXIMA)

  • Redesenho completo da tela de conversas
  • Campos customizados editáveis inline
  • Notas internas do operador
  • Histórico do pedido visível ao lado
  • IA sugerindo resposta com 1 clique (já existe, falta polish)

Semana 3: Infraestrutura para o lançamento

  • DNS curinga + SSL
  • Redis + worker de filas
  • Migration UTF-8
  • Monitoramento Sentry
  • Backup automatizado

Semana 4: Cobrança real (Asaas ou Stripe)

  • Integração com gateway escolhido
  • Assinatura recorrente do produtor
  • Suspensão automática por inadimplência
  • % sobre afiliados calculada automaticamente

Semana 5: Tracking ADS real (se decidir que é prioridade)

  • OAuth com Meta
  • Sincronização de campanhas
  • Envio de eventos de conversão

Semana 6: Polish final + reports

  • Dashboard com métricas chave
  • Notificação real-time no chat
  • Aprovação visual de sugestões de IA
  • Página de status pública

🎁 Diferenciais que entregamos além do pedido

Coisas que vocês não pediram explicitamente, mas que ganhamos por construir bem:

  • Marca personalizada por produtor (logo, cor, e-mail) sem custo extra
  • Cifragem de credenciais das integrações no banco
  • Idempotência de webhooks (mensagem duplicada da Meta não vira mensagem dupla no painel)
  • Isolamento total entre produtores (impossível um ver dado do outro mesmo que tentasse)
  • Log de auditoria em afiliados, conversas e automações
  • Health check de cada integração (avisa quando token expira)

💰 O que isso significa para o negócio

Hoje, o sistema já permite:

✅ Vender uma assinatura mensal para um produtor (R$ 109/297/397) com:

  • Marca dele
  • Domínio dele (produtor.pulsesaas.com.br)
  • Limite de afiliados (definido pelo plano)
  • WhatsApp dele conectado
  • Templates prontos rodando

✅ Cobrar % sobre o que ele cobra de afiliados (modelagem pronta, falta gateway real)

✅ Escalar para 10k afiliados em 1 produtor sem trabalho manual (signup por link)

Falta para vender de verdade:

❌ Pagamento da assinatura recorrente (gateway real) ❌ Infraestrutura de produção configurada ❌ Chat polido como o operador espera ❌ (Opcional) Meta Ads se foco em produtores de tráfego pago


📞 Próximo passo

Decidir junto com os sócios:

  1. As 5 decisões listadas acima
  2. Confirmar que o foco em chat WhatsApp-style é prioridade 1
  3. Validar a sequência das 6 semanas

Depois disso, podemos abrir o backlog técnico (documento separado) e executar uma tarefa por vez.

18

Termos — saldo pré-pago (rascunho)

⚠️ AVISO HONESTO: isto é um rascunho de partida escrito por engenheiro, não é parecer jurídico. Cobre o essencial pra manter o saldo fora de "moeda eletrônica" (e longe de regulação do BACEN) e te proteger nos pontos mais comuns. Antes de operar em volume, peça a um advogado uma revisão pontual (não precisa advogado fixo). Os campos [ENTRE COLCHETES] são decisões suas. Ver docs/planejamento/ESTUDO_SALDO_PRE_PAGO_WHATSAPP_2026-06-17.md.


Termos de Uso — Créditos Pré-pagos de Mensagens

1. O que são os créditos. Os "Créditos" são um adiantamento de pagamento destinado exclusivamente ao uso dos serviços de envio de mensagens da [NOME DA PLATAFORMA] ("Plataforma"). Os Créditos não são moeda, não constituem depósito, conta de pagamento ou saldo bancário, não rendem juros e não são resgatáveis em dinheiro. Servem unicamente para consumir os serviços da Plataforma.

2. Aquisição (recarga). O Cliente adquire Créditos mediante pagamento antecipado (PIX, cartão ou outros meios disponíveis). A cada recarga será emitida a nota fiscal correspondente ao serviço contratado. O valor pago é receita da Plataforma pelo serviço, não custódia de valores do Cliente.

3. Uso e débito. O envio de mensagens consome Créditos conforme a tabela de tarifas vigente, que varia por categoria de mensagem (marketing, utilidade, autenticação) e pode variar por país de destino. Mensagens recebidas e mensagens dentro da janela de atendimento de 24h não são cobradas. A tabela de tarifas pode ser alterada mediante aviso prévio de [30] dias; mensagens já enviadas não são afetadas.

4. Não reembolso em dinheiro. Salvo o direito de arrependimento previsto em lei (item 8), os Créditos não são reembolsáveis em dinheiro, não são sacáveis e não são conversíveis em qualquer valor monetário.

5. Intransferibilidade. Os Créditos são vinculados à conta do Cliente e não podem ser transferidos, vendidos ou cedidos a terceiros.

6. Validade. Os Créditos têm validade de [12] meses a contar da recarga. Créditos não utilizados nesse prazo expiram. A Plataforma poderá notificar o Cliente antes do vencimento.

7. Saldo insuficiente. Quando o saldo for insuficiente, o envio de mensagens cobradas será suspenso até nova recarga. Mensagens recebidas e o atendimento continuam normalmente.

8. Direito de arrependimento (CDC). Nos termos do art. 49 do Código de Defesa do Consumidor, o Cliente pessoa física pode solicitar o cancelamento da recarga em até 7 (sete) dias da compra, com devolução do valor dos Créditos não utilizados dessa recarga. Créditos já consumidos não são reembolsáveis.

9. Encerramento da conta. Em caso de encerramento [pela Plataforma por violação destes termos], os Créditos não utilizados [poderão ser perdidos / serão reembolsados proporcionalmente] — [DECISÃO SUA, recomendo reembolso proporcional do não-usado para evitar disputa de consumidor].

10. Custos de terceiros. O envio depende do WhatsApp/Meta, que cobra por mensagem segundo regras próprias. Os Créditos cobrem esse custo acrescido do serviço da Plataforma. Indisponibilidades do WhatsApp/Meta fogem ao controle da Plataforma.

11. Privacidade (LGPD). Os dados de saldo, extrato e pagamento são tratados conforme a Política de Privacidade, com base legal na execução do contrato.

12. Alterações e foro. Estes termos podem ser alterados mediante aviso. Fica eleito o foro de [CIDADE/UF].


Ressalvas — onde tomar cuidado (leia com atenção)

  1. A linha que te mantém fora do BACEN: os Créditos nunca podem virar dinheiro (saque), ser transferidos a terceiros, ou ser gastos fora do seu serviço. Se você um dia permitir "transferir saldo entre clientes" ou "sacar saldo", vira moeda eletrônica e cai na regulação de Instituição de Pagamento (Lei 12.865/2013). Não cruze essa linha sem advogado.

  2. Direito de arrependimento (7 dias) é lei — não dá pra anular. Por isso o item 8. "Não reembolsável" só vale depois dos 7 dias e só para o que já foi consumido. Ignorar isso é processo de consumidor na certa.

  3. Expiração de créditos é tema sensível (lembra das brigas sobre crédito de celular pré-pago). Reduz risco: validade longa (12 meses), aviso antes de expirar, e idealmente deixar o cliente reativar recarregando. Validade curta + sem aviso = risco de PROCON/CDC.

  4. Nunca trate a recarga como "custódia" do dinheiro do cliente. Contabilmente é receita antecipada de serviço (sua). Se você falar/escriturar como "guardo o dinheiro do cliente", começa a parecer conta de pagamento. (Isso é também por que precisa do contador — escriturar certo.)

  5. Nota fiscal na recarga é obrigatória. Sem emitir nota você não pode cobrar de empresa (que precisa da nota) e há risco fiscal. É o motivo nº 1 de precisar do contador.

  6. Câmbio/dólar: a Meta cobra em USD; você vende em BRL. Deixe explícito que tarifas podem mudar (item 3) pra não ficar preso a um preço quando o dólar subir.

  7. Chargeback de cartão: cliente recarrega no cartão, gasta os créditos, e contesta a compra. Mitigue: priorize PIX (sem chargeback), e/ou antifraude no cartão, e/ou liberar crédito só após confirmação.

  8. Markup transparente: você pode ter margem, mas evite prometer "preço da Meta" se cobra mais — diga "tarifa da Plataforma" (item 3 já faz isso).

  9. Reembolso no encerramento (item 9): recomendo reembolsar o saldo não usado se VOCÊ encerrar a conta — segurar dinheiro de serviço não prestado é o que mais gera disputa de consumidor. Se o CLIENTE viola os termos, aí pode reter (com cautela).

  10. Antes de escalar: uma revisão única por advogado destes termos + da política de privacidade. O grosso já está aqui; é revisar, não escrever do zero.