> ## Documentation Index
> Fetch the complete documentation index at: https://docs.morada.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Eventos Genéricos

> Receba notificações sobre diversos eventos: criação de deals, encerramento de conversas, tickets e mais.

## 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

| Evento                           | Descrição                                                           |
| -------------------------------- | ------------------------------------------------------------------- |
| `Deal.created`                   | Novo deal criado                                                    |
| `Deal.owner-assigned`            | Responsável atribuído ao deal                                       |
| `DealLog.created`                | Log registrado no deal                                              |
| `Conversation.created`           | Nova conversa iniciada                                              |
| `Conversation.closed`            | Conversa encerrada                                                  |
| `Conversation.updated`           | Conversa atualizada                                                 |
| `ConversationFunction.completed` | Função executada durante conversa (ex: busca de imóveis, simulação) |
| `DeskTicket.created`             | Ticket de atendimento criado                                        |
| `DeskTicket.owner-assigned`      | Responsável atribuído ao ticket                                     |
| `Person.updated`                 | Dados do contato atualizados                                        |

## Payload

```json theme={null}
{
  "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

| Campo             | Tipo   | Descrição                                                                                             |
| ----------------- | ------ | ----------------------------------------------------------------------------------------------------- |
| `event.type`      | string | Tipo completo do evento no formato `Entidade.ação` (ex: `Conversation.closed`)                        |
| `event.entity`    | string | Entidade do evento: `Deal`, `Conversation`, `DeskTicket`, `ConversationFunction`, `DealLog`, `Person` |
| `event.action`    | string | Ação do evento: `created`, `updated`, `closed`, `completed`, `owner-assigned`                         |
| `event.timestamp` | string | Data/hora do evento (ISO 8601)                                                                        |
| `event.id`        | string | ID ú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:

<AccordionGroup>
  <Accordion title="Deal.created">
    ```json theme={null}
    {
      "id": "uuid",
      "personId": "uuid",
      "partnerId": "uuid",
      "status": "active",
      "stageCode": "new_lead"
    }
    ```
  </Accordion>

  <Accordion title="Conversation.closed">
    ```json theme={null}
    {
      "id": "uuid",
      "dealId": "uuid",
      "channel": "whatsapp",
      "status": "closed",
      "closedAt": "2026-05-13T18:30:00.000Z"
    }
    ```
  </Accordion>

  <Accordion title="DeskTicket.created">
    ```json theme={null}
    {
      "id": "uuid",
      "dealId": "uuid",
      "conversationId": "uuid",
      "status": "open",
      "priority": "normal"
    }
    ```
  </Accordion>

  <Accordion title="ConversationFunction.completed">
    ```json theme={null}
    {
      "id": "uuid",
      "dealId": "uuid",
      "functionName": "searchProperties",
      "result": { "..." : "..." }
    }
    ```
  </Accordion>

  <Accordion title="Person.updated">
    ```json theme={null}
    {
      "id": "uuid",
      "name": "Ana Lima",
      "email": "ana@email.com",
      "phone": "5511966666666"
    }
    ```
  </Accordion>
</AccordionGroup>

### context

<Note>
  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.
</Note>

| Campo                    | Tipo           | Descrição                       |
| ------------------------ | -------------- | ------------------------------- |
| `context.person.id`      | string         | ID do contato                   |
| `context.person.name`    | string \| null | Nome completo                   |
| `context.person.email`   | string \| null | E-mail                          |
| `context.person.phone`   | string \| null | Telefone com DDI                |
| `context.deal.id`        | string         | ID do deal                      |
| `context.deal.status`    | string \| null | Status: `active`, `won`, `lost` |
| `context.deal.stageCode` | string \| null | Código da etapa no funil        |
| `context.product.id`     | string         | ID do produto/empreendimento    |
| `context.product.name`   | string \| null | Nome do produto/empreendimento  |
| `context.source.id`      | string         | ID da origem                    |
| `context.source.code`    | string         | Código da origem                |
| `context.source.name`    | string         | Nome da origem                  |

## Exemplo de uso

<CodeGroup>
  ```javascript Node.js theme={null}
  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 });
  });
  ```

  ```python Python theme={null}
  @app.route("/webhook/morada", methods=["POST"])
  def handle_webhook():
      data = request.json
      event = data["event"]
      event_data = data["data"]
      context = data.get("context", {})

      print(f"Evento: {event['type']} ({event['id']})")

      if event["type"] == "Conversation.closed":
          person = context.get("person", {})
          print(f"Conversa encerrada - Contato: {person.get('name')}")

      elif event["type"] == "Deal.created":
          print(f"Novo deal: {event_data['id']}")

      return jsonify({"received": True}), 200
  ```
</CodeGroup>

## Resposta esperada

Retorne um status HTTP `200` para confirmar o recebimento:

```json theme={null}
{
  "received": true
}
```

<Warning>
  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.
</Warning>
