Skip to main content

Visão Geral

Os Eventos Genéricos são webhooks disparados pela Morada.ai para eventos que não possuem um payload especializado. Eles seguem uma estrutura padronizada com os dados brutos do evento e um contexto opcional com informações do deal, contato e produto.

Eventos suportados

EventoDescrição
Deal.createdNovo deal criado
Deal.owner-assignedResponsável atribuído ao deal
DealLog.createdLog registrado no deal
Conversation.createdNova conversa iniciada
Conversation.closedConversa encerrada
Conversation.updatedConversa atualizada
ConversationFunction.completedFunção executada durante conversa (ex: busca de imóveis, simulação)
DeskTicket.createdTicket de atendimento criado
DeskTicket.owner-assignedResponsável atribuído ao ticket
Person.updatedDados do contato atualizados

Payload

{
  "event": {
    "type": "Conversation.closed",
    "entity": "Conversation",
    "action": "closed",
    "timestamp": "2026-05-13T18:30:00.000Z",
    "id": "a1b2c3d4-5678-90ab-cdef-1234567890ab"
  },
  "data": {
    "id": "f1e2d3c4-5678-90ab-cdef-1234567890ab",
    "dealId": "b2c3d4e5-6789-01ab-cdef-2345678901bc",
    "channel": "whatsapp",
    "status": "closed",
    "closedAt": "2026-05-13T18:30:00.000Z"
  },
  "context": {
    "person": {
      "id": "c3d4e5f6-7890-12ab-cdef-3456789012cd",
      "name": "Ana Lima",
      "email": "ana@email.com",
      "phone": "5511966666666"
    },
    "deal": {
      "id": "b2c3d4e5-6789-01ab-cdef-2345678901bc",
      "status": "active",
      "stageCode": "new_lead"
    },
    "product": {
      "id": "d4e5f6a7-8901-23ab-cdef-4567890123de",
      "name": "Loteamento Solar"
    },
    "source": {
      "id": "e5f6a7b8-9012-34ab-cdef-5678901234ef",
      "code": "whatsapp",
      "name": "WhatsApp Orgânico"
    }
  }
}

Campos

event

CampoTipoDescrição
event.typestringTipo completo do evento no formato Entidade.ação (ex: Conversation.closed)
event.entitystringEntidade do evento: Deal, Conversation, DeskTicket, ConversationFunction, DealLog, Person
event.actionstringAção do evento: created, updated, closed, completed, owner-assigned
event.timestampstringData/hora do evento (ISO 8601)
event.idstringID único do evento

data

O campo data contém os dados brutos do evento. A estrutura varia conforme a entidade — os exemplos abaixo ilustram os campos mais comuns:
{
  "id": "uuid",
  "personId": "uuid",
  "partnerId": "uuid",
  "status": "active",
  "stageCode": "new_lead"
}
{
  "id": "uuid",
  "dealId": "uuid",
  "channel": "whatsapp",
  "status": "closed",
  "closedAt": "2026-05-13T18:30:00.000Z"
}
{
  "id": "uuid",
  "dealId": "uuid",
  "conversationId": "uuid",
  "status": "open",
  "priority": "normal"
}
{
  "id": "uuid",
  "dealId": "uuid",
  "functionName": "searchProperties",
  "result": { "..." : "..." }
}
{
  "id": "uuid",
  "name": "Ana Lima",
  "email": "ana@email.com",
  "phone": "5511966666666"
}

context

O campo context é opcional e preenchido com base no dealId do evento. Se o evento não estiver vinculado a um deal, o contexto pode ser parcial ou ausente.
CampoTipoDescrição
context.person.idstringID do contato
context.person.namestring | nullNome completo
context.person.emailstring | nullE-mail
context.person.phonestring | nullTelefone com DDI
context.deal.idstringID do deal
context.deal.statusstring | nullStatus: active, won, lost
context.deal.stageCodestring | nullCódigo da etapa no funil
context.product.idstringID do produto/empreendimento
context.product.namestring | nullNome do produto/empreendimento
context.source.idstringID da origem
context.source.codestringCódigo da origem
context.source.namestringNome da origem

Exemplo de uso

app.post("/webhook/morada", (req, res) => {
  const { event, data, context } = req.body;

  console.log(`Evento: ${event.type} (${event.id})`);

  switch (event.type) {
    case "Conversation.closed":
      console.log(`Conversa encerrada para deal ${data.dealId}`);
      if (context?.person) {
        console.log(`Contato: ${context.person.name}`);
      }
      break;

    case "Deal.created":
      console.log(`Novo deal criado: ${data.id}`);
      break;

    case "DeskTicket.created":
      console.log(`Ticket aberto: ${data.id}`);
      break;

    default:
      console.log(`Evento não tratado: ${event.type}`);
  }

  res.status(200).json({ received: true });
});

Resposta esperada

Retorne um status HTTP 200 para confirmar o recebimento:
{
  "received": true
}
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.