Visão Geral
O webhook de Nova Mensagem é disparado pela Morada.ai sempre que uma mensagem é enviada ou recebida em uma conversa ativa. Isso inclui tanto mensagens do lead (user) quanto respostas da IA ou atendente (assistant).
Mensagens de sistema (funções internas, logs) não são enviadas por este webhook — apenas mensagens visíveis de user e assistant.
Payload
{
"message": {
"id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"actor": "user",
"type": "text",
"content": "Quero saber mais sobre o apartamento de 3 quartos",
"timestamp": "2026-05-13T18:25:00.000Z"
},
"person": {
"id": "c3d4e5f6-7890-12ab-cdef-3456789012cd",
"name": "Maria Oliveira",
"email": "maria@email.com",
"phone": "5521988888888"
},
"deal": {
"id": "b2c3d4e5-6789-01ab-cdef-2345678901bc",
"status": "active",
"productProps": {
"bedrooms": 3
}
},
"source": {
"id": "e5f6a7b8-9012-34ab-cdef-5678901234ef",
"code": "whatsapp",
"name": "WhatsApp - Site"
},
"metadata": {
"timestamp": "2026-05-13T18:25:01.000Z",
"eventType": "message"
}
}
Campos
message
| Campo | Tipo | Descrição |
|---|
message.id | string | ID único da mensagem |
message.actor | string | Autor da mensagem: user (lead/cliente) ou assistant (IA/atendente) |
message.type | string | Tipo do conteúdo: text, image, document, audio, video |
message.content | string | Conteúdo textual da mensagem |
message.timestamp | string | Data/hora da mensagem (ISO 8601) |
message.errorDetails | object | undefined | Presente apenas quando a mensagem falhou no envio (ver abaixo) |
person
| Campo | Tipo | Descrição |
|---|
person.id | string | ID único do contato |
person.name | string | null | Nome completo |
person.email | string | null | E-mail |
person.phone | string | null | Telefone com DDI (ex: 5521988888888) |
deal
O campo deal pode ser undefined em cenários raros onde a mensagem não está vinculada a um deal.
| Campo | Tipo | Descrição |
|---|
deal.id | string | ID do deal associado |
deal.status | string | null | Status: active, won, lost |
deal.productProps | object | null | Propriedades do produto vinculado ao deal |
source
| Campo | Tipo | Descrição |
|---|
source.id | string | ID da origem |
source.code | string | Código da origem (ex: whatsapp, instagram, web) |
source.name | string | Nome descritivo da origem |
| Campo | Tipo | Descrição |
|---|
metadata.timestamp | string | Data/hora do envio do webhook (ISO 8601) |
metadata.eventType | string | Sempre "message" |
Quando uma mensagem falha no envio (ex: erro do provedor, rate limit), o campo errorDetails é incluído:
{
"message": {
"id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"actor": "assistant",
"type": "text",
"content": "",
"timestamp": "2026-05-13T18:25:00.000Z",
"errorDetails": {
"message": "Rate limit exceeded | 429 Too Many Requests",
"code": "rate_limit"
}
},
"person": {
"id": "c3d4e5f6-7890-12ab-cdef-3456789012cd",
"name": "Maria Oliveira",
"email": "maria@email.com",
"phone": "5521988888888"
},
"metadata": {
"timestamp": "2026-05-13T18:25:01.000Z",
"eventType": "message"
}
}
| Campo | Tipo | Descrição |
|---|
errorDetails.message | string | undefined | Descrição do erro |
errorDetails.code | string | undefined | Código do erro retornado pelo provedor |
Exemplo de uso
app.post("/webhook/morada", (req, res) => {
const { message, person, deal } = req.body;
if (message.errorDetails) {
console.error(`Erro na mensagem ${message.id}: ${message.errorDetails.message}`);
// Alertar equipe sobre falha de envio
} else {
console.log(`[${message.actor}] ${person.name}: ${message.content}`);
// Salvar mensagem no seu sistema
}
res.status(200).json({ received: true });
});
Resposta esperada
Retorne um status HTTP 200 para confirmar o recebimento:
Se o seu endpoint retornar um status diferente de 2xx ou não responder dentro do timeout, a Morada.ai poderá tentar reenviar o evento. Implemente idempotência para lidar com entregas duplicadas.