Símbolo da AmivuSímbolo da AmivuDevelopers

Sincronizar um CRM

Mantenha contatos alinhados sem criar duplicados.

O objetivo: quando alguém vira cliente no seu CRM, ele existe na Amivu — e continua sendo a mesma pessoa depois de dez sincronizações.

O desenho

Cliente criado no CRM
        ↓
POST /v1/contacts  com external_id = id do CRM
        ↓
Guarde o contact_id devolvido (opcional — o external_id já basta)

A peça que faz isso funcionar é o external_id. Com ele, a criação vira encontre ou crie: repetir devolve o mesmo contato, e você não precisa de uma tabela de-para nem de uma consulta antes de cada escrita.

Guardar o `contact_id` é opcional, e recomendado
Você pode sempre buscar por external_id. Guardar o id da Amivu economiza uma requisição em cada atualização — vale quando o volume cresce.

Os passos

Crie os campos personalizados uma vez

O que precisa aparecer no atendimento e filtrar listas vira campo personalizado. O resto vai em metadata.

A resposta traz a key gerada (plano, vendedor, cidade) — é ela que você usa daqui em diante, e ela nunca muda.

Terminal
for CAMPO in "Plano" "Vendedor" "Cidade"; do
  curl https://api.amivu.com.br/v1/custom-fields \
    -H "Authorization: Bearer $AMIVU_API_KEY" \
    -H "Content-Type: application/json" \
    -d "{\"name\": \"$CAMPO\", \"scope\": \"contact\"}"
done

Espelhe cada cliente

Um contato por cliente do seu CRM, identificado pelo external_id. A chave de idempotência amarra a operação ao cliente, e não ao instante — rodar a sincronização inteira de novo não cria ninguém em dobro.

JavaScript
async function espelhar(cliente) {
  return amivu("/v1/contacts", {
    method: "POST",
    // A operação é uma só por cliente; repetir não pode criar dois.
    headers: { "Idempotency-Key": `crm-sync-${cliente.id}` },
    body: {
      external_id: String(cliente.id),
      first_name: cliente.primeiroNome,
      last_name: cliente.sobrenome,
      email: cliente.email,
      phone: cliente.telefone,
      custom_fields: {
        plano: cliente.plano,
        vendedor: cliente.vendedor?.nome ?? null,
        cidade: cliente.cidade,
      },
      metadata: { crm_url: cliente.url },
    },
  });
}

Atualize quando o CRM mudar

PATCH muda só o que você mandar. Um campo personalizado com null é apagado; ausente fica como estava.

JavaScript
async function atualizar(cliente) {
  const [existente] = (
    await amivu(`/v1/contacts?external_id=${cliente.id}`)
  ).data;

  if (!existente) return espelhar(cliente);

  return amivu(`/v1/contacts/${existente.id}`, {
    method: "PATCH",
    body: {
      email: cliente.email,
      custom_fields: { plano: cliente.plano },
    },
  });
}

Trate a carga inicial com respeito ao limite

Sincronizar 50 mil clientes de uma vez bate no teto de 600 por minuto. Leia X-RateLimit-Remaining e desacelere antes do 429 — e, quando ele vier, respeite o Retry-After.

JavaScript
for (const cliente of clientes) {
  const resposta = await espelharComRecuo(cliente);

  // Uma pausa curta quando a folga acaba vale mais que uma rajada de 429.
  const restante = Number(resposta.headers.get("x-ratelimit-remaining") ?? 600);
  if (restante < 50) await esperar(5000);
}

A volta: Amivu → CRM

Para manter o CRM ciente do que acontece no atendimento, assine contact.created e contact.updated. O corpo do evento traz o contato inteiro, com o external_id — que é o seu próprio identificador de volta.

JavaScript
app.post("/webhooks/amivu", async (req, res) => {
  if (!assinaturaValida(req)) return res.status(401).end();
  res.status(200).end();

  const evento = req.body;
  if (evento.type !== "contact.updated") return;

  const { external_id: idDoCrm, phone, custom_fields } = evento.data.contact;
  // Contato criado pelo canal ainda não tem par no CRM: crie um lead lá.
  if (!idDoCrm) return criarLeadNoCrm(evento.data.contact);

  await atualizarClienteNoCrm(idDoCrm, { phone, ...custom_fields });
});
Cuidado com o laço
Se o seu CRM dispara “cliente atualizado” ao receber a atualização da Amivu, e isso faz um PATCH de volta, os dois ficam se atualizando para sempre. Compare os valores antes de escrever, dos dois lados.

Escrevemos sobre operação de atendimento, canais e IA no blog da Amivu.