Arquitetura e integrações
Referência técnica baseada no código local revisado em 07/10/2026. Os caminhos citados são relativos à raiz que contém pepper-api, pepper-front e pepper-docs. A análise mapeia a arquitetura geral e aprofunda os contratos de CRM; não equivale a uma auditoria completa de todos os domínios ou da infraestrutura implantada.
Os três projetos
| Projeto | Stack encontrada | Responsabilidade |
|---|---|---|
pepper-api | Node 20, Express 4, Mongoose 8, MongoDB, Agenda, Firebase Admin | Regras de negócio, autenticação, permissões, persistência, integrações e jobs |
pepper-front | Angular 18, Ionic 7, Capacitor 6, RxJS, AngularFire | Aplicação web e mobile, formulários, quadro, agenda, relatórios e áreas públicas |
pepper-docs | Docusaurus 3, React 19, TypeScript, MDX e Mermaid | Documentação operacional e técnica |
O frontend tem áreas por perfil (colaborador, administrador e cliente), área pública, catálogo de produtos, clientes, pedidos, assinaturas, fornecedores, cupons, contratos, feiras, eventos e equipe. O backend agrupa recursos em domínios com arquivos de modelo, controller, rotas, validações, permissões e políticas. Os módulos de feira, avaliação e presença suportam fluxos como ATLA Expo/Fitec; eles compartilham clientes, produtos e perfis com o restante da plataforma.
Fontes: pepper-front/src/app/app-routing.module.ts, home/home-routing.module.ts, app.module.ts; pepper-api/src/routes.js, src/models/index.js e os package.json dos três projetos.
O backend inicializa Firebase Admin, logging e GlitchTip, expõe /health, /api-docs e /api-docs.json e monta os domínios sob /api/v1. As chamadas do CRM usam API REST; a presença de AngularFire na aplicação não significa que o kanban grava oportunidades diretamente no Firestore.
Fluxo de uma requisição
CrmServiceobtém o identificador empresarial deBusinessService.id.- O interceptador acrescenta
x-access-tokenerequesting, conforme token e perfil guardados no navegador, e monta a URL da API configurada. src/routes.jsinstala parser JSON e middleware de transação antes das rotas de domínio.- As rotas internas passam por validação, JWT e permissão do recurso.
- O controller consulta ou grava modelos, normalmente com
req.session. - A resposta segue o formato de mensagem, dados e, nas listas, metadados de paginação.
O cabeçalho requesting representa o perfil ativo, não o ID da empresa. O sistema verifica se o perfil pertence ao usuário e se possui a permissão exigida. As políticas complementam essa decisão com atributos do recurso. Filtros de empresa também precisam ser aplicados no servidor; não basta confiar no identificador enviado pelo frontend.
A camada transacional exige MongoDB compatível com transações, como um replica set. Os controllers frequentemente enviam a resposta antes de chamar next() para o commit. Há riscos registrados na análise sobre essa ordem e sobre requisições negadas que não chegam ao encerramento da sessão.
Modelo comercial
| Entidade | Dados e vínculos principais |
|---|---|
| ProfileBusiness | Contexto empresarial; configurações de formulário, roteamento, webhook e cadências |
| Pipeline | Empresa, padrão e estágios; cada etapa tem chave estável, ordem, probabilidade, prazo, roteiro e marcas de resultado |
| Lead | Empresa, responsável, contato, BANT, origem inicial/último toque, consentimento, score, situação e cliente convertido |
| Customer | Cadastro da carteira; pode ser reaproveitado pela conversão do lead |
| Deal | Empresa, pipeline, estágio, lead, cliente, responsável, valor, previsão, resultado, motivo e histórico de etapas |
| Activity | Empresa e vínculos opcionais com negócio/lead/cliente; tipo, responsável, prazo, conclusão e resultado |
| Cadence | Empresa, passos, trigger, evento, estado ativo e limite diário |
| CadenceEnrollment | Vínculos de inscrição, passo atual, próximo horário, estado e histórico dos toques |
| Segment | Base alvo, regras, modo de combinação, dinâmica ou snapshot de membros e último tamanho |
| Order / Subscription | Registro de venda/pagamento e base de cálculo para RFM e recuperação |
O campo business do CRM referencia o contexto ProfileBusiness. Não troque por um ID genérico do cadastro Business sem conferir a relação.
O quadro pede resumo agregado e páginas de negócios por estágio, com result=open. Ganho e perda são endpoints próprios. stageHistory registra entradas, saídas e duração das passagens. Renomear o estágio preservando sua chave mantém a relação; remover uma chave usada no histórico pode prejudicar a interpretação futura.
Fontes: modelos em pepper-api/src/domains/{Pipeline,Lead,Deal,Activity,Cadence,Segment}, pepper-front/src/app/crm/crm.service.ts e src/utils/Garbage.ts.
Navegação e estado no frontend
A raiz comercial é /home-profilecollaborator/crm. As rotas são board, pipelines, leads, lead, new, view, activities, cadences, import, segments, dashboard e reports.
CrmService é compartilhado. O ID do negócio é guardado por Garbage em sessionStorage; o ID do lead fica em memória no serviço. O funil ativo é persistido em localStorage por empresa (crm.pipeline.<businessId>). A ficha sem ID na URL depende da seleção anterior ou do estado da sessão; não é um permalink autossuficiente.
O quadro realiza movimentação otimista com rollback em erro. Os relatórios selecionam períodos; a agenda envia limites do dia no fuso do aparelho. O calendário combina atividades, previsões e cadências e permite que uma fonte falhe sem apagar os dados das demais.
Contratos internos do CRM
Todos os caminhos abaixo são relativos a /api/v1. As operações internas exigem x-access-token e requesting, além da permissão correspondente.
| Recurso | Operações principais |
|---|---|
| Pipeline | GET/POST /pipeline, POST /pipeline/ensure-default, GET/PUT/DELETE /pipeline/:pipelineId, PUT /pipeline/:pipelineId/stages/reorder |
| Lead | GET/POST /lead, GET/PUT/DELETE /lead/:leadId, PUT /lead/:leadId/convert |
| Captação técnica | POST /lead/form, POST /lead/import/preview, POST /lead/import, POST /lead/webhook-secret |
| Distribuição | GET /lead/unattended, POST /lead/assign-owners |
| Deal | GET/POST /deal, GET /deal/summary, GET/PUT/DELETE /deal/:dealId, GET /deal/:dealId/timeline |
| Decisão do negócio | PUT /deal/:dealId/stage, /win, /lose, /reopen |
| Activity | GET/POST /activity, GET /activity/agenda, GET/PUT/DELETE /activity/:activityId, PUT /activity/:activityId/complete |
| Cadence | GET/POST /cadence, GET/PUT/DELETE /cadence/:cadenceId, PUT /cadence/:cadenceId/toggle |
| Inscrições e A/B | GET /cadence/enrollments, POST /cadence/:cadenceId/enroll, /enroll-segment, PUT /cadence/enrollment/:enrollmentId/unenroll, GET /cadence/:cadenceId/ab-report |
| Segment | GET/POST /segment, GET/PUT/DELETE /segment/:segmentId, GET /segment/fields, POST /segment/preview, GET /segment/rfm, GET /segment/:segmentId/members, /export |
| Relatórios | GET /deal/report/funnel, /deal/report/win-loss, /lead/report/attribution, /lead/report/referral |
Não existe PUT /deal/:dealId/pipeline nas rotas revisadas, embora o frontend o chame. A atualização comum de Deal também não aceita order no schema atual, apesar de um comentário do controller sugerir vínculo posterior de pedido. Confira schemas e rotas, não apenas comentários ou Swagger.
Exemplo: conversão
{
"createDeal": true,
"pipeline": "ID_DO_FUNIL",
"dealTitle": "Implantação — Empresa Exemplo",
"dealValue": 12000
}
Envie em PUT /lead/ID_DO_LEAD/convert, substituindo IDs por ObjectIds reais de 24 caracteres hexadecimais da empresa correta. A resposta contém data.lead, data.customer e data.deal. O telefone do lead é obrigatório; a API recusa reconversão de um lead convertido. Título e valor não entram no pagamento automaticamente.
Exemplo: registrar uma resposta estruturada
{
"business": "ID_DO_PERFIL_EMPRESARIAL",
"lead": "ID_DO_LEAD",
"deal": "ID_DO_NEGOCIO",
"type": "call",
"subject": "Cliente confirmou interesse e pediu reunião",
"outcome": "connected"
}
Envie em POST /activity. O encerramento de cadências reconhece tipos call, whatsapp, email, meeting com resultados connected, rescheduled, refused. Vínculos corretos são necessários para localizar as inscrições. Se ambos forem informados, a função atual prioriza a busca pelo lead.
Captação pública e webhook
| Endpoint público | Uso |
|---|---|
GET /public/business/:businessId/lead-form | Formulário e perguntas publicados pela empresa |
POST /public/lead | Captura da landing ou embed |
POST /public/fair/:fairId/lead | Captura contextualizada pela feira |
POST /lead-webhook/:businessId | Integração externa assinada por HMAC |
GET/POST /public/opt-out/:token | Revogar marketing pelo token de descadastro |
A landing está em /public/campaign/:businessId e o embed em /public/embed/:businessId. A captura pública inclui limite de taxa e honeypot. A API permite salvar perguntas com tipos texto, nota, múltipla escolha e checkbox, associando respostas aos campos BANT quando configurado.
Para o webhook, gere o segredo em POST /lead/webhook-secret com a empresa no corpo. Ele é devolvido na geração; guarde-o fora do código. A rotação substitui o anterior. O contrato espera HMAC-SHA256 dos bytes exatos do corpo, em x-pepper-signature: sha256=<hex> ou x-hub-signature-256. O endpoint aceita corpo simples e field_data no formato de captura de anúncios; esse suporte não equivale a uma configuração completa da integração do provedor.
O parser JSON global é instalado antes do parser que deveria preservar req.rawBody no webhook. Com uma requisição JSON padrão, o corpo já foi consumido e o verify local não é executado. A assinatura pode ser recusada mesmo correta. Corrija a ordem do parser ou preserve o corpo cru no parser global antes de usar essa integração. O consumo do corpo antes do verify foi reproduzido localmente com os parsers Express reais; não foi testado contra produção.
Jobs e dependências de operação
src/server.js habilita scheduler e watchers conforme variáveis/configuração. src/scheduler/index.js carrega schedule.js dos domínios. As strings de ambiente são convertidas com Boolean: a string "false" é verdadeira, e o fallback de configuração pode continuar habilitando o recurso. Confira a inicialização real do ambiente.
| Job | Periodicidade | Responsabilidade |
|---|---|---|
assignLeadOwners | 10 minutos | Distribuir leads abertos sem dono, por regras de etiquetas e menor carga |
recalculateLeadScores | 1 hora | Recalcular até 500 leads abertos por rodada |
sweepStalledDeals | 30 minutos | Marcar/recuperar estagnação e notificar responsáveis |
remindDueActivities | 15 minutos | Lembrar tarefas pendentes até os próximos 30 minutos |
runCadenceEnrollments | 5 minutos | Executar até 200 inscrições vencidas |
recoverAbandonedCarts | 30 minutos | Inscrever recuperações e interromper quando não há pedido elegível aberto |
syncRfmSegments | Cron 0 3 * * * | Recalcular empresas que já têm públicos automáticos RFM |
O cron e os limites diários do scheduler dependem do fuso do processo/Agenda. A agenda visual usa o fuso do aparelho. Não suponha que os dois têm sempre o mesmo limite de dia.
O score total é 40% perfil e 60% engajamento. A distribuição por menor carga usa colaboradores aceitos da empresa e pode priorizar regras de etiquetas. Formulário, roteamento, teto geral de toques e espera de carrinho são configurações técnicas sem editores completos no CRM atual.
Fontes: src/domains/Lead/{routing,scoring,schedule}.js, Deal/schedule.js, Activity/schedule.js, Cadence/{schedule,dispatch,cartRecovery}.js, Segment/{schedule,rfm}.js.
Manter os prints
Execute, na pasta pepper-docs:
npm run capture:crm
O script usa os componentes e estilos reais do pepper-front irmão, em um workspace temporário; fornece respostas fictícias de HttpClient e não inicia a aplicação principal, API, autenticação ou Firebase. Cria um servidor restrito a 127.0.0.1, abre Chrome headless e salva os PNGs em static/img/crm.
Requer dependências instaladas em ambos os projetos e Chrome. PEPPER_FRONT define outro caminho do frontend, e CHROME_PATH permite indicar outro executável Chromium. O caminho padrão do Chrome é o de macOS. Os scripts encerram navegador e servidor ao terminar normalmente.
Os dados, cenários e data fixa estão em scripts/crm-preview/main.ts.template; o driver está em scripts/capture-crm.cjs. Não substitua dados fictícios por tokens ou registros de clientes. Valide visualmente as imagens e rode npm run build antes de publicar.