Ligar o KdObras ao seu CRM
Cada vez que um negócio do seu funil é criado, alterado ou excluído, o KdObras envia um aviso (um webhook) para o endereço que você cadastrou. Com o Zapier, ou com um pequeno código seu, esse aviso vira um registro no Salesforce, no HubSpot ou em qualquer sistema que você use. Para importar uma vez só, não precisa de nada disso: use o CSV "Para importar em outro CRM" do botão Exportar, na tela Negócios (o botão aparece só para o administrador da conta).
O que sai
Sai somente o que você colocou nos seus negócios (o funil). Cada aviso traz um objeto negocio com estes campos:
| Campo | Conteúdo |
|---|---|
id | Identificador do negócio. Use como chave para criar ou atualizar. |
titulo, empresa, responsavel | Nome do negócio, empresa e responsável. |
contato_nome | Nome da pessoa de contato no cliente. |
contato_email | E-mail da pessoa de contato. |
contato_telefone | Telefone da pessoa de contato. |
etapa, situacao, temperatura | Etapa do funil, situação (Aberto por padrão) e temperatura. |
ganho_em | Data AAAA-MM-DD em que o negócio foi marcado como Ganho (vazio se não foi). |
perdido_em | Data AAAA-MM-DD em que o negócio foi marcado como Perdido (vazio se não foi). |
motivo_perda | Motivo registrado ao perder o negócio (vazio se não houver). |
valor | Número (0 se vazio). |
fechamento_previsto | Data AAAA-MM-DD. |
endereco, latitude, longitude, regiao | Localização. |
cnpj_responsavel_obra | Documento do responsável pela obra, se você o informou. |
telefone, site | Contato da empresa. |
observacoes | Texto livre. |
ultima_atividade, ultima_atividade_data | Última atividade concluída e sua data. |
proxima_atividade, proxima_atividade_data | Próxima atividade pendente e sua data. |
obra_id | Identificador da obra à qual o negócio está ligado (vazio se não houver). |
criado_em, atualizado_em | atualizado_em em ISO com fuso -03:00. |
socios | Só se você ligar a opção "incluir sócios": nomes e qualificação, separados por ponto e vírgula. Nunca o CPF. |
Campos sem valor chegam como null.
O que não sai
- Fotos e anexos.
- Identificadores internos do KdObras além do
iddo negócio e doobra_id. - O CPF dos sócios, em nenhuma hipótese. Os nomes dos sócios só saem se você ligar a opção.
- Dados de outros clientes e a base geral de obras: só saem os seus negócios.
Como o aviso chega
É um POST em JSON, sempre por https (porta 443), sem seguir redirecionamentos. O corpo tem esta forma:
{ "evento": "negocio.atualizado",
"entrega": "…", "enviado_em": "2026-10-23T14:05:00-03:00",
"conta": "…",
"negocio": { "id": "…", "titulo": "…", … },
"etapa_anterior": "Proposta" }
negocio.criado: negócio novo.negocio.atualizado: qualquer alteração. Quando a etapa mudou, vem tambémetapa_anterior.negocio.excluido: o objetonegociotraz sóideexcluido_em.
Cabeçalhos: X-KdObras-Evento, X-KdObras-Entrega (identificador desta entrega), X-KdObras-Timestamp (segundos Unix), X-KdObras-Assinatura (sha256= seguido de hexadecimal) e User-Agent: KdObras-Webhook/1.
Regras de entrega
- Seu endereço deve responder 2xx em até 10 segundos. Responda logo e processe depois.
- Se falhar, o KdObras tenta de novo, até 6 novas tentativas ao longo de cerca de 33 horas (no mínimo). Por isso o mesmo evento pode chegar mais de uma vez e fora de ordem.
- Use
negocio.idcomo chave de criar-ou-atualizar (upsert) e ignore um evento cujoatualizado_emseja mais antigo que o já gravado. - Se passarem 24 horas sem nenhuma resposta 2xx e houver 20 ou mais falhas, a integração se desliga sozinha e o responsável recebe um aviso por e-mail. Corrija o endereço e religue nas configurações.
Passo a passo: Zapier para Salesforce
- No Salesforce, em Configuração, Gerenciador de Objetos, Lead (ou o objeto que você usa), crie um campo customizado de texto chamado
KdObras_Id__c. Marque External ID e Unique. É ele que impede duplicar o negócio quando o aviso chega duas vezes. - No Zapier, crie um Zap com o gatilho Webhooks by Zapier, Catch Hook. Copie o endereço gerado e cole nas configurações do webhook no KdObras. Envie um teste para o Zapier reconhecer os campos.
- Segundo passo: Salesforce, Find Record, objeto Lead, campo
KdObras_Id__c, valornegocio iddo gatilho. - Terceiro passo: Salesforce, Create Record / Update Record. Se o Find achou o registro, atualize; senão, crie. Preencha
KdObras_Id__ccomnegocio ide mapeie os demais campos (empresa, título, telefone, etapa…). - Para
negocio.excluido, adicione um filtro no Zap (evento igual anegocio.excluido) e decida se quer apagar ou só marcar o registro.
O Zapier não confere a assinatura. Se quiser garantir que o aviso veio do KdObras, use um código seu (veja abaixo) antes de repassar ao CRM.
Passo a passo: Zapier para HubSpot
- No HubSpot, em Configurações, Propriedades, objeto Negócio (Deal), crie uma propriedade de texto de linha única com nome interno
kdobras_ide marque Exigir valores únicos. - No Zapier, use o mesmo gatilho Catch Hook do passo a passo acima.
- Segundo passo: HubSpot, Find Deal (busca por propriedade),
kdobras_idigual anegocio id. - Terceiro passo: HubSpot, Create Deal / Update Deal, preenchendo
kdobras_ide mapeando nome, valor, data de fechamento e etapa. A etapa do HubSpot tem nomes próprios: crie uma correspondência com as etapas do seu funil.
Verificar a assinatura
A assinatura é HMAC-SHA256(segredo, timestamp + "." + corpo), em hexadecimal, onde segredo é o que o KdObras mostrou ao criar a integração e corpo é o texto exato recebido, antes de qualquer conversão de JSON. Recuse também o aviso cujo timestamp tenha mais de 5 minutos.
Node
const crypto = require('crypto');
function verificar(segredo, cabecalhos, corpoCru) {
const ts = String(cabecalhos['x-kdobras-timestamp'] || '');
const recebida = String(cabecalhos['x-kdobras-assinatura'] || '');
if (!/^\d+$/.test(ts) || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const esperada = 'sha256=' + crypto.createHmac('sha256', segredo)
.update(ts + '.' + corpoCru).digest('hex');
const a = Buffer.from(esperada), b = Buffer.from(recebida);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
Python
import hashlib, hmac, time
def verificar(segredo, cabecalhos, corpo_cru):
ts = cabecalhos.get('X-KdObras-Timestamp', '')
recebida = cabecalhos.get('X-KdObras-Assinatura', '')
if not ts.isdigit() or abs(time.time() - int(ts)) > 300:
return False
msg = ts.encode() + b'.' + corpo_cru # corpo_cru em bytes
esperada = 'sha256=' + hmac.new(segredo.encode(), msg, hashlib.sha256).hexdigest()
return hmac.compare_digest(esperada.encode(), recebida.encode())
No Node, passe o corpo cru como texto (por exemplo, com express.raw e toString('utf8')), não o objeto já interpretado.