Webhooks

Há eventos que não são disparados por você: alguém deixa uma consulta em um anúncio do seu cliente e o portal nos avisa. Para que cheguem até você, declara-se uma vez para qual URL sua nós os entregamos.

PUT     /property-v1/webhooks/{event}
GET     /property-v1/webhooks/{event}
DELETE  /property-v1/webhooks/{event}

Os três exigem o escopo property-api-webhooks.

Enquanto você não declarar a URL, as consultas dos seus clientes não chegam até você — e não ficam guardadas em lugar nenhum. Não existe uma caixa de entrada nossa que as acumule até você registrar o webhook: se o evento chega e não há destino declarado, ele é descartado.

E nos portais que entregam apenas por este caminho, declará-lo não é uma preferência: é a única forma de receber as consultas. Quais são eles é dito por Capacidades — o campo pushFromPortal de cada portal, e ali mesmo você verá se também pode ir buscá-las por conta própria (pull).

Os eventos

{event}Quando chega até você
leadalguém deixou uma consulta em um anúncio de um dos seus clientes

É um único escopo para todos os eventos, não um por evento. Quando acrescentarmos um novo, você poderá registrá-lo com o token que já tem, sem precisar nos pedir nada.

Declarar para onde entregamos

curl -X PUT "https://property-api.mapaprop.com/property-v1/webhooks/lead" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://seu-sistema.com/hooks/mapaprop-leads" }'
CampoObrigatórioO que é
urlsimo seu endpoint. Tem que ser https

A resposta:

{
  "event": "lead",
  "url": "https://seu-sistema.com/hooks/mapaprop-leads",
  "scope": "app",
  "secret": "9f2c…",
  "createdAt": "2026-10-20T14:02:11.004Z",
  "updatedAt": "2026-10-20T14:02:11.004Z"
}

O secret é devolvido uma única vez. Guarde-o assim que recebê-lo: não há nenhum método que o forneça novamente. Se você o perder, registre o webhook de novo e um novo será emitido — o anterior deixa de funcionar.

Ele serve para verificar que o que chega até você é nosso: assinamos cada entrega com ele.

Registrar de novo substitui a URL

Enviar o PUT outra vez substitui a URL. O createdAt não muda, e o segredo sim: por isso registrar de novo também é a forma de rotacioná-lo.

A URL tem que ser https

Sem exceções, nem mesmo para testes. O que entregamos são dados pessoais de um terceiro — o nome, o e-mail e o telefone de quem fez a consulta — e por http viajariam abertos. Para testar na sua máquina, use um túnel: ele já lhe dá https.

Um destino diferente para um cliente específico

Por padrão, tudo de todos os seus clientes vai para uma URL. Se você precisa que os de um cliente vão para outro lugar, envie o x-client-ref dele:

curl -X PUT "https://property-api.mapaprop.com/property-v1/webhooks/lead" \
  -H "Authorization: Bearer $TOKEN" \
  -H "x-client-ref: inmo-42" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://seu-sistema.com/hooks/inmo-42" }'

Isso cria uma exceção apenas para esse cliente ("scope": "client"): o restante continua indo para o padrão. Quando chega um evento de inmo-42, procuramos primeiro a exceção dele e, se não existir, usamos o padrão.

O cabeçalho é opcional aqui, e esta é a única parte da API em que ele o é. Omiti-lo não é um esquecimento: significa "este é o destino de todos os meus clientes".

Ver o que você tem declarado

curl "https://property-api.mapaprop.com/property-v1/webhooks/lead" \
  -H "Authorization: Bearer $TOKEN"
{
  "event": "lead",
  "url": "https://seu-sistema.com/hooks/mapaprop-leads",
  "scope": "app",
  "createdAt": "2026-10-20T14:02:11.004Z",
  "updatedAt": "2026-10-20T14:02:11.004Z"
}

O segredo não sai aqui. Se você não o tem, registre o webhook novamente.

Acrescente o x-client-ref para consultar a exceção de um cliente em vez do padrão.

O que entregamos para você

Um POST para a sua URL, com o evento em um envelope:

{
  "event": "lead",
  "portal": "argenpropapi",
  "clientRef": "inmo-42",
  "lead": {
    "id": "77349380",
    "propertyCode": "6NN6_01M49G0SE61P525JYAKKQ0T700",
    "name": "Ana",
    "email": "ana@ejemplo.com",
    "phone": "1155556666",
    "message": "Hola, sigue disponible?",
    "date": "2026-10-20T11:30:00",
    "portalMessageId": null,
    "portalAdId": "20703571",
    "portalContactId": null,
    "portalActionId": null
  }
}
Campo do envelopeO que é
evento que aconteceu. Hoje sempre lead
portalde qual portal vem — o mesmo nome que você usa para publicar e no seu escopo
clientRefde qual cliente seu é

Os campos de lead são os mesmos de Consultas, com o mesmo significado: é o mesmo objeto, portanto você não precisa escrever duas leituras do mesmo fato.

propertyCode é o código que a sua publicação escreveu, portanto é com ele que você cruza a consulta com o imóvel do seu lado. Cada portal o monta à sua maneira — na Argenprop o código de publicação da imobiliária vai na frente do paId, na Zonaprop vai um sufixo atrás —, por isso não faça o parse dele: compare com o que o cadastro lhe devolveu.

portal e clientRef viajam no envelope e não dentro de lead porque você precisa deles para saber a quem pertence o que chega: uma URL de escopo app recebe as consultas de todos os seus clientes e de todos os portais.

Verificar que fomos nós que enviamos

Cada entrega leva o header X-Mapaprop-Signature:

X-Mapaprop-Signature: t=1792500000,v1=5f8a…
  • t é o momento da entrega, em segundos.
  • v1 é HMAC-SHA256 de <t>.<corpo cru> com o seu secret, em hexadecimal.
const esperado = crypto.createHmac('sha256', secret)
  .update(`${t}.${cuerpoCrudo}`)
  .digest('hex');

Assine o corpo cru, o que chegou. Se você fizer o parse e serializar de novo, qualquer diferença de espaços ou de ordem das chaves dá outra assinatura e nunca vai coincidir.

Compare com crypto.timingSafeEqual e rejeite se t tiver mais de 5 minutos: assim uma entrega antiga capturada não serve a ninguém para reenviá-la.

O que você tem que nos responder

Você respondeO que fazemos
2xxpronto, não enviamos de novo
qualquer outra coisa, ou nenhuma respostatentamos novamente

Quantas vezes tentamos novamente, exatamente

Três tentativas no total — a primeira mais duas repetições —, com cerca de 2 minutos entre uma e outra. Depois das três, paramos de tentar e essa consulta não volta: ela não fica numa fila esperando que você conserte o seu endpoint, nem há forma de pedir que a reenviemos.

São cerca de 4 minutos de margem. Se o seu endpoint ficar fora do ar por mais do que isso, o que entrou nessa janela foi perdido.

Por isso convém que o seu endpoint faça o mínimo antes de responder: salve o envelope cru e responda 2xx; processe depois, com as suas próprias repetições. Um endpoint que valida, enriquece e grava em três sistemas antes de responder é um endpoint que cai com mais frequência do que o que apenas salva.

Se você salva a consulta de forma assíncrona, responda somente depois de tê-la salvado: um 2xx adiantado nos diz que você já a tem, e se a sua gravação falhar depois, essa consulta não volta.

Faça a deduplicação por lead.id dentro de um mesmo portal: uma nova tentativa nossa traz o mesmo valor.

Deixar de receber

curl -X DELETE "https://property-api.mapaprop.com/property-v1/webhooks/lead" \
  -H "Authorization: Bearer $TOKEN"
{ "deleted": true, "event": "lead" }

Apagar algo que não estava registrado responde {"deleted": false} com 200 — não é um erro, então você pode chamá-lo sem verificar antes.

Com x-client-ref você apaga somente a exceção desse cliente, e os eventos dele voltam a ir para o padrão do seu aplicativo.

Os erros

CódigoerrorO que aconteceu
400url is required · url must use https · url must be a valid absolute URL · url must be at most 512 charsrevise o campo url
400Invalid JSON bodyo corpo do PUT não é um JSON válido
400event must be one of: leado {event} da rota não é um dos listados acima
403Missing scope: property-api-webhookso seu token não tem o escopo. Escreva para nós
404webhook_not_foundvocê não tem nada declarado para esse evento (apenas no GET)

Os 400 trazem ainda o field com o nome do campo, para que você não precise ler o texto.

O que isto NÃO faz

  • Não guarda nada. Nós entregamos e seguimos: não há uma caixa de entrada nossa onde os eventos fiquem esperando que você os consulte, nem antes de você declarar a URL nem depois das três tentativas.
  • Não é um substituto universal de ir buscá-las. Os dois caminhos existem, mas não todos os portais têm os dois: há portais que apenas empurram e portais que apenas são consultados. Antes de escolher, olhe Capacidades — é o mesmo dado que o nosso próprio código usa, portanto nada fica fixo no seu lado.

Veja também

  • Receber consultas — o fluxo, e qual dos dois caminhos é melhor para você
  • Leads — buscá-los você mesmo, em vez de recebê-los