Webhooks
O MB Suite avisa com um POST assinado quando algo acontece na sua organização: como assinar, verificar a assinatura e receber as novas tentativas.
Um webhook é uma assinatura: você escolhe os eventos e uma URL https:// pública, e cada vez que um acontece o MB Suite envia um POST com o evento assinado. Eles são criados e administrados em Configurações → API e MCP → Webhooks (owners e admins). Pela API e pelo MCP se leem as assinaturas, o catálogo de eventos e as entregas.
Criar uma assinatura
- 1Criar webhook: a URL do seu servidor, os eventos, o formato (Completo ou Somente IDs) e, num workspace, se escuta também as suas Contas.
- 2Em Avançado, até 5 cabeçalhos fixos se o seu receptor exige um token próprio (Make, Monday). Os valores ficam guardados cifrados e não aparecem de novo.
- 3Copie o segredo de assinatura (
whsec_…): ele aparece uma única vez. Pode ser rotacionado; durante 24 horas assina-se com o novo e com o anterior. - 4Enviar evento de teste envia um
webhook.testassinado, mesmo com a assinatura desligada.
Cada organização tem um limite de assinaturas ativas conforme o plano, e nunca mais de 20. O destino tem que ser um servidor público: endereços privados, internos ou do MB Suite são recusados, e são revisados de novo antes de cada envio.
O que o seu servidor recebe
Um POST com Content-Type: application/json, um corpo de até 256 KB e estes cabeçalhos:
| Cabeçalho | Valor |
|---|---|
MB-Signature | t=<unix>,v1=<hex> (veja abaixo). |
MB-Event-Id | O id do evento (evt_…). |
MB-Event-Type | O tipo, por exemplo crm.deal.stage_changed. |
MB-Webhook-Id | A assinatura. |
MB-Delivery-Id | A entrega (um reenvio manual cria outra). |
MB-Api-Version | A versão com que o corpo foi montado. |
Idempotency-Key | O id do evento: igual em todas as novas tentativas. |
User-Agent | MB-Suite-Webhooks/1.0 (+https://developers.mb-suite.com/webhooks) |
{
"id": "evt_4f9c1a7e2b6d0c3a8e5f1b9d2c",
"type": "crm.deal.stage_changed",
"created_at": "2025-11-01T12:26:40.000Z",
"api_version": "2026-11-01",
"org_id": "ORG_ID",
"data": { "…": "…" }
}- Completo (
snapshot, recomendado):datatraz os registros inteiros, na versão da assinatura. - Somente IDs (
thin): só ids e valores simples; o seu sistema consulta o resto pela API. - Um evento que não cabe em 256 KB é enviado como Somente IDs, com
"truncated": trueemdata.
A lista completa de eventos, com um exemplo de cada, está em Eventos. Cada operação da referência diz quais eventos pode emitir; o que um evento ainda não cobre está na descrição dele.
Verificar a assinatura
MB-Signature é t=<unix>,v1=<hex>, com v1 = HMAC-SHA256(segredo, "<t>.<corpo>") sobre o corpo exato, sem serializá-lo de novo. Durante uma rotação chegam dois v1=: vale qualquer um. Recuse um t de mais de 5 minutos (replay) e compare em tempo constante.
import crypto from "node:crypto";
import express from "express";
function verifyMbSignature(header, rawBody, secret, toleranceSec = 300) {
if (!header) return false;
const parts = header.split(",").map((p) => p.trim().split("="));
const t = Number(parts.find(([k]) => k === "t")?.[1]);
const signatures = parts.filter(([k]) => k === "v1").map(([, v]) => v);
if (!t || signatures.length === 0) return false;
if (Math.abs(Math.floor(Date.now() / 1000) - t) > toleranceSec) return false; // replay
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
return signatures.some((sig) => sig.length === expected.length && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected)));
}
const app = express();
// O corpo cru: express.raw, NÃO express.json (re-serializar muda a assinatura).
app.post("/mb-suite/webhook", express.raw({ type: "application/json" }), (req, res) => {
if (!verifyMbSignature(req.get("MB-Signature"), req.body, process.env.MB_WEBHOOK_SECRET)) {
return res.sendStatus(400);
}
const event = JSON.parse(req.body);
// event = { id, type, created_at, api_version, org_id, data }
// Idempotência: `event.id` (também vem como `Idempotency-Key`) se repete quando reenviamos.
switch (event.type) {
case "crm.lead.created":
// event.data.person, event.data.prospect | event.data.deal, event.data.source
break;
case "crm.deal.stage_changed":
// event.data.deal, event.data.stage, event.data.previous.stage
break;
}
res.sendStatus(200); // qualquer 2xx = entregue; outra coisa ou mais de 10 s = nova tentativa
});Responder e novas tentativas
- Responda 2xx em menos de 10 segundos e deixe o trabalho pesado para uma fila. Qualquer outra resposta, um redirecionamento (3xx) ou o silêncio contam como falha.
- Novas tentativas: na hora, depois de 5 minutos, de 30, de 2 horas, de 5, de 10 e depois a cada 12 horas: 11 tentativas em cerca de 72 horas.
- Uma nova tentativa traz o mesmo
idde evento: use-o para não processar duas vezes. - Depois de 24 horas de falhas seguidas, os owners e admins recebem um aviso por e-mail; às 72 horas a assinatura se desliga sozinha. Ao reativá-la você pode reenviar o que falhou nas 72 horas anteriores, e cada entrega pode ser reenviada à mão em Ver entregas.
- Eventos e entregas são guardados por 30 dias.
Receitas: Make, Odoo e Monday
Make
- 1No Make, crie um cenário que comece com Webhooks → Custom webhook e copie a URL (
https://hook.us1.make.com/…). - 2No MB Suite, Criar webhook com essa URL e os eventos que quiser (por exemplo
crm.lead.createdecrm.deal.stage_changed). - 3No Make, Re-determine data structure; no MB Suite, Enviar evento de teste (ou uma mudança real) para o Make aprender a forma do JSON.
- 4O Make não verifica HMAC: adicione um cabeçalho fixo (
X-Make-Token: <um valor longo>) e compare-o num Filter do cenário.
Odoo
O Odoo 17 ou posterior tem regras de automação com o gatilho On webhook: cada regra dá uma URL própria (https://<seu-odoo>/web/hook/<uuid>) e uma ação Executar código Python lê payload (o evento inteiro). Como essa ação não vê o corpo cru, proteja-a com um cabeçalho fixo e guarde payload["id"] para não criar o lead duas vezes numa nova tentativa. Em versões anteriores, coloque o Make no meio com Odoo → Create a Record.
Monday
O Monday não recebe webhooks de outros sistemas: o mais simples é o Make no meio (Custom webhook → Monday → Create an Item), mapeando data.person.full_name, data.deal.title, data.stage.name ou data.source.name. Para não duplicar itens numa nova tentativa, filtre pelo id do evento ou busque o item por data.deal.id antes de criá-lo.