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

# Nova Mensagem

> Receba notificações quando uma nova mensagem é enviada ou recebida em uma conversa.

## 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`).

<Note>
  Mensagens de sistema (funções internas, logs) **não** são enviadas por este webhook — apenas mensagens visíveis de `user` e `assistant`.
</Note>

## Payload

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

<Note>
  O campo `deal` pode ser `undefined` em cenários raros onde a mensagem não está vinculada a um deal.
</Note>

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

### metadata

| Campo                | Tipo   | Descrição                                |
| -------------------- | ------ | ---------------------------------------- |
| `metadata.timestamp` | string | Data/hora do envio do webhook (ISO 8601) |
| `metadata.eventType` | string | Sempre `"message"`                       |

## Payload com erro

Quando uma mensagem falha no envio (ex: erro do provedor, rate limit), o campo `errorDetails` é incluído:

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

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

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

      if "errorDetails" in message:
          print(f"Erro: {message['errorDetails']['message']}")
      else:
          print(f"[{message['actor']}] {person['name']}: {message['content']}")

      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>
