Início rápido
Primeira entrega ponta a ponta
Prepare o receptor antes de ativar a assinatura. Assim, o primeiro evento já pode ser validado e confirmado.
- 1
Disponibilize um endpoint HTTPS
O destino deve ser público, usar HTTPS na porta 443 e aceitarPOSTcomapplication/json. Redirecionamentos e endereços de rede privada são bloqueados. - 2
Crie a assinatura no Muda
Abra Configurações → Integrações e API → Central de Webhooks. Informe a URL, escolha a conexão e selecione os eventos necessários. - 3
Guarde o segredo exibido
A credencial deve ficar em um cofre de segredos no servidor receptor. Nunca a exponha no frontend, em logs ou no repositório. - 4
Valide a assinatura e responda rápido
Verifique os bytes brutos antes de interpretar o JSON. Persista ou enfileire o evento e responda 2xx; o trabalho pesado deve acontecer de forma assíncrona.Receptor Node.js / Express
javascript
import express from 'express'; import { verifyMudaWebhook } from './verify-muda-webhook.js'; const app = express(); app.post( '/webhooks/muda', express.raw({ type: 'application/json', limit: '256kb' }), async (req, res) => { const rawBody = req.body.toString('utf8'); const signature = req.header('x-muda-webhook-signature'); const idempotencyKey = req.header('idempotency-key'); if (!verifyMudaWebhook(rawBody, signature, process.env.MUDA_WEBHOOK_SECRET)) { return res.status(401).json({ error: 'invalid_signature' }); } if (!idempotencyKey) { return res.status(400).json({ error: 'missing_idempotency_key' }); } try { const event = JSON.parse(rawBody); // Use uma restricao unica por Idempotency-Key. A funcao deve retornar // "duplicate" (sem erro) quando a chave ja tiver sido persistida. const result = await persistWebhookEvent({ idempotencyKey, event }); if (result === 'duplicate') { return res.status(200).json({ received: true, duplicate: true }); } return res.status(202).json({ received: true }); } catch (error) { return res.status(503).json({ error: 'durable_persistence_failed' }); } }); Acompanhe o histórico
Confirme o status, duração, número da tentativa e resposta do destino. Falhas temporárias entram em retry automaticamente.
Autenticidade
Valide HMAC antes de confiar no evento
A assinatura usa SHA-256 sobre timestamp + "." + corpo bruto. Rejeite timestamps fora da tolerância de 300 segundos.
Verificação HMAC com comparação constante
javascript
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verifyMudaWebhook(rawBody, signatureHeader, secret) {
const parts = Object.fromEntries(
String(signatureHeader || '').split(',').map((part) => part.trim().split('='))
);
const timestamp = Number(parts.t);
if (!Number.isSafeInteger(timestamp)) return false;
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - timestamp) > 300) return false;
if (!/^[a-f0-9]{64}$/i.test(parts.v1 || '')) return false;
const expected = createHmac('sha256', secret)
.update(timestamp + '.' + rawBody)
.digest('hex');
const receivedBytes = Buffer.from(parts.v1, 'hex');
const expectedBytes = Buffer.from(expected, 'hex');
return receivedBytes.length === expectedBytes.length
&& timingSafeEqual(receivedBytes, expectedBytes);
}HTTP
Cabeçalhos enviados em cada tentativa
| Cabeçalho | Finalidade |
|---|---|
Idempotency-Key | Identificador estável da entrega, mantido entre tentativas |
X-Muda-Delivery-Id | Identificador operacional da entrega |
X-Muda-Event-Id | Mesmo id do envelope do evento |
X-Muda-Event-Type | Tipo canônico do catálogo |
X-Muda-Event-Version | Versão 2026-07-01 |
X-Muda-Request-Id | Correlação para diagnóstico |
X-Muda-Webhook-Secret-Version | Versão da credencial usada |
X-Muda-Webhook-Signature | t=<unix>,v1=<hmac hexadecimal> |
X-Muda-Webhook-Timestamp | Timestamp Unix usado na assinatura |
Assinaturas
Escolha somente os eventos necessários
Filtrar na origem reduz tráfego, custo de processamento e superfície de dados no sistema receptor.
Leads
lead.addedLead adicionado
lead.updatedLead editado
lead.deletedLead excluido
lead.restoredLead restaurado
lead.stage_changedEtapa do lead alterada
lead.responsible_changedResponsavel do lead alterado
Contatos
contact.addedContato adicionado
contact.updatedContato editado
contact.deletedContato excluido
contact.restoredContato restaurado
contact.responsible_changedResponsavel do contato alterado
Empresas
company.addedEmpresa adicionada
company.updatedEmpresa editada
company.deletedEmpresa excluida
company.restoredEmpresa restaurada
company.responsible_changedResponsavel da empresa alterado
Tarefas
task.addedTarefa adicionada
task.updatedTarefa editada
task.deletedTarefa excluida
task.responsible_changedResponsavel da tarefa alterado
Leads de entrada
incoming_lead.addedLead de entrada adicionado
incoming_lead.updatedLead de entrada editado
incoming_lead.deletedLead de entrada excluido
Mensagens
message.incomingMensagem recebida
Conversas
talk.addedConversa adicionada
talk.updatedConversa editada
Notas
note.lead_addedNota adicionada ao lead
note.contact_addedNota adicionada ao contato
note.company_addedNota adicionada a empresa
Produtos
product.addedProduto adicionado
product.updatedProduto editado
product.deletedProduto excluido
Confiabilidade
Retries, circuit breaker e reenvio
Tentativas automáticas
Falhas temporárias recebem novas tentativas com backoff, sem mudar a chave idempotente.
Circuit breaker
Falhas consecutivas pausam pressão sobre o destino até que ele volte a responder.
Histórico operacional
Cada tentativa registra status, duração, request id e erro resumido, sem expor segredo.
Contrato do receptor
Como o Muda interpreta sua resposta
| Resposta | Resultado | Comportamento |
|---|---|---|
2xx | Entrega confirmada | Não haverá nova tentativa para esta entrega. |
400–499 (exceto 408 / 425 / 429) | Rejeição permanente | Revise autenticação, rota ou contrato antes de reativar. |
408 / 425 / 429 | Falha temporária | O Muda agenda nova tentativa respeitando sua política. |
500–599 | Falha temporária do destino | O Muda agenda nova tentativa com backoff. |
Timeout | Destino não respondeu no limite | A conexão é encerrada e a entrega entra em retry. |
Diagnóstico
Solução de problemas
Assinatura inválida
Confirme que o segredo correto foi usado e que a verificação recebeu exatamente o corpo bruto da requisição.
Timestamp expirado
Sincronize o relógio via NTP e use o timestamp do cabeçalho, não o momento do processamento assíncrono.
Entregas duplicadas
Use Idempotency-Key como chave única e confirme duplicatas com uma resposta 2xx.
Timeout recorrente
Responda após persistir/enfileirar. Não execute integrações lentas dentro da requisição.
Assinatura pausada
Revise o histórico, corrija o destino e reative somente depois de um teste saudável.
Checklist
Receptor pronto para produção
- A URL usa HTTPS público e não redireciona para redes privadas.
- O segredo está em um cofre e existe procedimento de rotação.
- A assinatura é validada antes do JSON ser interpretado.
- O timestamp expirado e assinaturas inválidas são rejeitados com 401.
- Idempotency-Key possui restrição única no armazenamento do receptor.
- Logs guardam request id e duração, mas não guardam segredo ou payload sensível.
Monitore p95/p99 de resposta, taxa de 5xx e volume de retries. A confirmação rápida protege o sistema receptor e reduz latência de entrega.