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

# Mudança de Etapa

> Receba notificações quando um deal muda de etapa no funil de vendas.

## Visão Geral

O webhook de **Mudança de Etapa** é disparado pela Morada.ai sempre que um deal avança ou retrocede no funil de vendas. O payload inclui dados completos do deal, contato, produto, origem e um resumo da conversa gerado por IA.

<Note>
  Se a opção `shouldSendOnlyOnDealTransfer` estiver ativada, o webhook será enviado **apenas** quando o deal for transferido para atendimento humano — ignorando mudanças intermediárias de etapa.
</Note>

## Payload

```json theme={null}
{
  "deal": {
    "id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
    "status": "active",
    "stageCode": "deal_transferred",
    "name": "João Silva - Apt 302 Torre A",
    "conversationId": "f1e2d3c4-5678-90ab-cdef-1234567890ab",
    "partnerId": "b2c3d4e5-6789-01ab-cdef-2345678901bc"
  },
  "person": {
    "id": "c3d4e5f6-7890-12ab-cdef-3456789012cd",
    "name": "João Silva",
    "email": "joao.silva@email.com",
    "phone": "5511999999999"
  },
  "product": {
    "id": "d4e5f6a7-8901-23ab-cdef-4567890123de",
    "name": "Edifício Aurora - Torre A"
  },
  "source": {
    "id": "e5f6a7b8-9012-34ab-cdef-5678901234ef",
    "code": "whatsapp",
    "name": "WhatsApp - Site"
  },
  "conversationReport": "O lead demonstrou interesse no apartamento de 2 quartos com suíte. Informou renda familiar de R$ 15.000 e tem aprovação de crédito prévia. Solicitou agendamento de visita para o final de semana.",
  "appointmentDate": "17-05-2026 14:30",
  "metadata": {
    "timestamp": "2026-05-13T18:30:00.000Z",
    "eventType": "deal_stage_change",
    "stageId": "f6a7b8c9-0123-45ab-cdef-6789012345f0"
  }
}
```

## Campos

### deal

| Campo                 | Tipo           | Descrição                             |
| --------------------- | -------------- | ------------------------------------- |
| `deal.id`             | string         | ID único do deal                      |
| `deal.status`         | string         | Status atual: `active`, `won`, `lost` |
| `deal.stageCode`      | string         | Código da etapa atual no funil        |
| `deal.name`           | string         | Nome do deal                          |
| `deal.conversationId` | string \| null | ID da conversa associada              |
| `deal.partnerId`      | string         | ID do parceiro                        |

### 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: `5511999999999`) |

### product

<Note>
  O campo `product` só é enviado quando o deal possui um produto/empreendimento associado.
</Note>

| Campo          | Tipo   | Descrição                      |
| -------------- | ------ | ------------------------------ |
| `product.id`   | string | ID do produto/empreendimento   |
| `product.name` | string | Nome do produto/empreendimento |

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

### Outros campos

| Campo                | Tipo           | Descrição                                                                                                            |
| -------------------- | -------------- | -------------------------------------------------------------------------------------------------------------------- |
| `conversationReport` | string \| null | Resumo da conversa gerado por IA — contém os principais pontos discutidos, dados coletados e intenções identificadas |
| `appointmentDate`    | string \| null | Data do agendamento associado, se houver. Formato: `dd-MM-yyyy HH:mm` (horário de Brasília)                          |
| `metadata.timestamp` | string         | Data/hora do envio do webhook (ISO 8601)                                                                             |
| `metadata.eventType` | string         | Sempre `"deal_stage_change"`                                                                                         |
| `metadata.stageId`   | string         | ID da nova etapa no funil                                                                                            |

## Exemplo de uso

<CodeGroup>
  ```javascript Node.js theme={null}
  app.post("/webhook/morada", (req, res) => {
    const { deal, person, conversationReport, metadata } = req.body;

    console.log(`Deal ${deal.id} movido para etapa ${deal.stageCode}`);
    console.log(`Contato: ${person.name} (${person.phone})`);

    if (conversationReport) {
      // Salvar resumo da conversa no seu CRM
      console.log(`Resumo IA: ${conversationReport}`);
    }

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

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

      deal = data["deal"]
      person = data["person"]
      report = data.get("conversationReport")

      print(f"Deal {deal['id']} movido para {deal['stageCode']}")
      print(f"Contato: {person['name']} ({person['phone']})")

      if report:
          print(f"Resumo IA: {report}")

      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>
