Blog

Integração ERP + CRM sem retrabalho operacional

O vendedor cadastra o cliente no CRM. O financeiro cadastra o mesmo cliente no ERP. Duas fichas nascem do nada, com CNPJ digitado de jeitos diferentes e endereco divergente. A partir dai todo relatorio mente e alguem gasta a tarde reconciliando planilha. Este guia mostra como ligar ERP e CRM com padroes de sincronizacao, fonte da verdade por entidade e idempotencia, para que o dado entre uma vez e nunca mais precise de cadastro manual.

2026-06-16 / Integrações / 12 min

01

O problema: dois cadastros do mesmo cliente

Sem integracao, cada area opera sua propria base. O CRM tem o cliente do ponto de vista comercial; o ERP tem o mesmo cliente do ponto de vista fiscal e financeiro. Como ninguem combinou quem cria o registro primeiro, o mesmo cliente aparece duas vezes, com pequenas diferencas: nome com ou sem acento, telefone com ou sem DDD, CNPJ com ou sem pontuacao. O resultado e retrabalho cronico.

  • Cadastro em dobro: o mesmo cliente vira dois IDs, um no CRM e outro no ERP, sem ligacao entre eles.
  • Dado divergente: endereco de cobranca atualizado no ERP nunca chega ao CRM, e o vendedor liga para o lugar errado.
  • Digitacao manual repetida: alguem reescreve no ERP o que ja existia no CRM, abrindo espaco para erro de digitacao.
  • Relatorio que nao fecha: faturamento por cliente no ERP nao bate com pipeline no CRM porque sao chaves diferentes.

A solucao nao e escolher um sistema e abandonar o outro. E definir regras claras de quem manda em cada campo e fazer os dois conversarem sem duplicar.

02

Fonte da verdade por entidade e por campo

O erro mais comum e tentar sincronizar tudo nos dois sentidos. O caminho saudavel e decidir, campo a campo, qual sistema e o dono. O dono escreve; o outro apenas le e reflete. Isso elimina a briga de quem sobrescreve quem.

EntidadeCampoDono (fonte da verdade)Sistema que reflete
ClienteDados de contato e oportunidadeCRMERP le para emitir nota
ClienteCNPJ, regime fiscal, limite de creditoERPCRM le para qualificar
ProdutoNome comercial e descricao de vendaCRMERP reflete no catalogo
ProdutoPreco, estoque e codigo fiscalERPCRM le para cotar
PedidoNegociacao e propostaCRMERP recebe ao fechar
PedidoFaturamento, status fiscal e pagamentoERPCRM le para acompanhar

Com a tabela acima documentada, qualquer divergencia tem resposta objetiva: o valor correto e sempre o do sistema dono daquele campo. Reconciliacao deixa de ser debate e vira regra.

03

Estrategias de sincronizacao

Definido o dono de cada campo, escolha como o dado viaja. Tres eixos importam: sentido (one-way ou two-way), gatilho (batch ou orientado a evento) e latencia (periodico ou near-real-time).

One-way (recomendado por campo):
  CRM ==(contato, proposta)==> ERP
  ERP ==(preco, estoque, fiscal)==> CRM

Event-driven near-real-time:
  CRM --webhook--> [Middleware] --upsert--> ERP
  ERP --webhook--> [Middleware] --upsert--> CRM
                       |
                 mapping table

Batch (fallback noturno):
  [Job 02:00] --le delta--> compara --> aplica diferencas
  • One-way por campo: cada campo flui em um unico sentido a partir do seu dono. Simples, previsivel e evita loop de sobrescrita.
  • Two-way: so quando os dois sistemas precisam editar a mesma entidade. Exige regra de conflito explicita, nunca improvise.
  • Batch: job periodico que le um delta e aplica diferencas. Barato e tolerante a falha, porem com atraso de minutos a horas.
  • Event-driven via webhook: o sistema dono emite um evento na mudanca e o middleware propaga em segundos, near-real-time.
  • Padrao pratico: event-driven para o fluxo principal e um batch noturno de reconciliacao como rede de seguranca.

04

Chave de correlacao e idempotencia

Para nunca duplicar, cada entidade precisa de uma chave que ligue o registro do CRM ao do ERP. Guarde esse vinculo numa mapping table: external_id de um lado, external_id do outro. Toda escrita vira um upsert por essa chave, nao um insert cego. Assim, reprocessar o mesmo evento dez vezes produz o mesmo resultado: idempotencia.

  • Mapping table: tabela que relaciona crm_id, erp_id e a chave natural (CNPJ, SKU) para resolver o par sem ambiguidade.
  • Chave natural: quando nao ha mapping ainda, casa pelo CNPJ do cliente ou SKU do produto, normalizados antes de comparar.
  • Upsert por chave: insere se nao existe, atualiza se existe. Nunca um insert direto que cria duplicata em retry.
  • Idempotencia: a mesma mensagem aplicada N vezes deixa o sistema no mesmo estado, essencial porque webhook reenvia.
// sync-customer.js
// Upsert idempotente de cliente entre CRM e ERP usando mapping table.
// Roda no middleware ao receber um evento "customer.updated" do CRM.

async function syncCustomerFromCrm(event) {
  const crmId = event.data.id;
  const cnpj = normalizeCnpj(event.data.cnpj); // remove pontuacao, valida

  // 1. Resolve o par via mapping table; cai para chave natural se nao houver vinculo
  let mapping = await db.mapping.findOne({ crm_id: crmId });
  if (!mapping) {
    const erp = await erpApi.findCustomerByCnpj(cnpj);
    if (erp) {
      // Cliente ja existe no ERP, so faltava o vinculo: nao duplica
      mapping = await db.mapping.upsert({ crm_id: crmId, erp_id: erp.id, cnpj });
    }
  }

  // 2. Monta apenas os campos que o CRM e dono (contato e proposta)
  const payload = {
    nome: event.data.nome,
    email: event.data.email,
    telefone: event.data.telefone,
  };

  // 3. Upsert idempotente: chave de negocio = erp_id (se existe) ou CNPJ
  const erpCustomer = await erpApi.upsertCustomer({
    matchBy: mapping?.erp_id ? { id: mapping.erp_id } : { cnpj },
    data: payload,
    // dedupe_key garante que reprocessar o mesmo evento nao gere efeito duplo
    dedupeKey: `crm:${crmId}:${event.version}`,
  });

  // 4. Persiste/atualiza o vinculo para a proxima sincronizacao
  await db.mapping.upsert({ crm_id: crmId, erp_id: erpCustomer.id, cnpj });
  return erpCustomer.id;
}

Repare que o codigo nunca faz insert direto: ele resolve o par antes, casa por CNPJ quando o vinculo ainda nao existe e usa um dedupeKey baseado na versao do evento. Esse trio (mapping, chave natural e dedupe) e o que mata a duplicata na origem.

05

Reconciliacao e deteccao de divergencia

Mesmo com sincronizacao em tempo real, eventos se perdem: webhook que falhou, deploy no meio de um lote, registro editado direto no banco. Por isso um job de reconciliacao periodico e obrigatorio. Ele compara os dois lados, alerta o que divergiu e, quando seguro, corrige sozinho.

  1. Selecione o conjunto a comparar: todos os clientes ativos com atividade nas ultimas 24 horas, por exemplo.
  2. Para cada registro, resolva o par pela mapping table e busque o estado atual nos dois sistemas.
  3. Compare apenas os campos que tem dono definido, normalizando antes (CNPJ sem pontuacao, texto em caixa unica).
  4. Classifique a divergencia: ausente de um lado, valor diferente ou par quebrado (mapping sem correspondente).
  5. Para campo com dono claro, reaplique o valor do dono via upsert idempotente e registre a correcao.
  6. Para casos ambiguos, gere um alerta e envie para a fila de revisao humana em vez de adivinhar.

Registre cada reconciliacao com contagem de divergencias encontradas e corrigidas. Se esse numero comeca a subir, e sinal de que o fluxo event-driven esta perdendo eventos e merece investigacao antes de virar incidente.

06

Tratamento de conflito

Conflito acontece quando os dois sistemas mudam o mesmo campo antes de sincronizar. A fonte da verdade por campo elimina a maioria dos casos, mas em fluxo two-way voce precisa de uma politica explicita. Escolha conforme o risco do campo.

  • Last-write-wins: vence a escrita mais recente por timestamp. Simples, bom para campos de baixo risco como observacao livre.
  • Resolucao campo-a-campo: cada campo segue seu dono mesmo no two-way, ignorando a alteracao do lado que nao manda naquele campo.
  • Fila de revisao humana: campos criticos (limite de credito, regime fiscal) param em uma fila e um humano decide, sem auto-correcao.
  • Versionamento otimista: cada registro carrega uma versao; escrita com versao defasada e rejeitada e reenfileirada para reavaliar.

Regra geral: quanto maior o impacto financeiro ou fiscal do campo, menos automatica deve ser a resolucao. Observacao comercial pode ser last-write-wins; limite de credito merece revisao humana.

FAQ

Perguntas frequentes

Posso sincronizar tudo nos dois sentidos para garantir que nada falte?

Nao e recomendado. Two-way em todos os campos cria loops de sobrescrita e conflitos constantes: o CRM escreve, o ERP devolve, e os dois ficam piscando. Defina o dono por campo e deixe a maioria dos fluxos one-way. Reserve o two-way apenas para as poucas entidades que os dois sistemas realmente precisam editar, sempre com regra de conflito explicita.

O que uso como chave de correlacao se os IDs dos dois sistemas sao diferentes?

Use uma mapping table que guarda crm_id, erp_id e uma chave natural estavel, como CNPJ para cliente ou SKU para produto. No primeiro encontro, voce casa pela chave natural normalizada e grava o vinculo; nas proximas vezes, resolve direto pelo mapping. Assim os IDs internos podem ser diferentes sem nunca gerar duplicata.

Por que preciso de reconciliacao se ja tenho sincronizacao via webhook?

Porque webhook falha. Entrega perdida, timeout, deploy no meio de um lote ou edicao direta no banco deixam os dois lados fora de sincronia sem ninguem perceber. O job de reconciliacao e a rede de seguranca: compara periodicamente, alerta divergencias e corrige o que tem dono claro. Webhook entrega velocidade; reconciliacao entrega confianca.

Dado entra uma vez e nunca mais

Integrar ERP e CRM sem retrabalho nao depende de mais ferramenta, depende de tres decisoes: definir a fonte da verdade por campo, ligar os sistemas com upsert idempotente sobre uma mapping table e fechar a conta com reconciliacao periodica. Com isso o cliente entra uma unica vez, o relatorio fecha e ninguem mais reescreve cadastro a mao. Posso ajudar a desenhar essa integracao na sua operacao.