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ê |
|---|---|
lead | algué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" }'
| Campo | Obrigatório | O que é |
|---|---|---|
url | sim | o 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 envelope | O que é |
|---|---|
event | o que aconteceu. Hoje sempre lead |
portal | de qual portal vem — o mesmo nome que você usa para publicar e no seu escopo |
clientRef | de 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-SHA256de<t>.<corpo cru>com o seusecret, 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ê responde | O que fazemos |
|---|---|
2xx | pronto, não enviamos de novo |
| qualquer outra coisa, ou nenhuma resposta | tentamos 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ódigo | error | O que aconteceu |
|---|---|---|
400 | url is required · url must use https · url must be a valid absolute URL · url must be at most 512 chars | revise o campo url |
400 | Invalid JSON body | o corpo do PUT não é um JSON válido |
400 | event must be one of: lead | o {event} da rota não é um dos listados acima |
403 | Missing scope: property-api-webhooks | o seu token não tem o escopo. Escreva para nós |
404 | webhook_not_found | você 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