# Mensagens editadas e apagadas

Quando um contato **edita** uma mensagem (por exemplo, para corrigir um valor), chega **`message.edited`**; quando a **apaga**, **`message.revoked`**. Os dois trazem `originalMessageId`: o `wamid` da mensagem original, o mesmo `data.message.id` que chegou no `message.received` dela. Com ele você atualiza a linha que já guardou.

**O que chega depende do tipo de número** (quem decide é a Meta):

**Número em coexistência (app + API).** Edição → `message.edited` com `textAvailable: true` e `data.message` = a **versão final** (texto, ou foto/vídeo com a nova legenda), com a mesma forma de `message.received`. Exclusão → `message.revoked`.

**Número somente Cloud API.** Edição → `message.edited` com `textAvailable: false`, `message: null` e `reason: "META_EDIT_TEXT_NOT_PROVIDED"`: a Meta avisa que houve uma edição mas **não envia o novo texto**. Exclusão → a Meta não avisa.

**O WhatsApp só deixa editar nos 15 minutos** seguintes ao envio (e apagar, até 2 dias). Uma edição sem texto é uma limitação da Meta: não há como pedir o texto depois.

**Não guardamos o texto anterior** (o WAIA Connect não guarda o conteúdo das mensagens): se precisar, ele está na sua base, em `originalMessageId`.

## Exemplos

### cURL

```bash
# Nothing to call: WAIA Connect POSTs message.edited / message.revoked to your webhook.
# Read a saved body: the final text (or null when Meta did not send it) and the original id.
jq -r 'if .type == "message.edited"
  then "\(.data.originalMessageId) edited -> " + (if .data.textAvailable then (.data.message.text.body // .data.message.image.caption // .data.message.video.caption // "") else "(text not provided by Meta)" end)
  else "\(.data.originalMessageId) deleted" end' body.json
```

### Node.js

```javascript
// Inside your webhook handler, after verifying the signature.
const evt = req.body;
if (evt.type === "message.edited") {
  const d = evt.data;
  if (d.textAvailable) {
    const m = d.message; // same shape as message.received → data.message
    const text = m.text?.body ?? m.image?.caption ?? m.video?.caption ?? null;
    await db.messages.update({ wamid: d.originalMessageId }, { text, editedAt: d.editedAt }); // your code
  } else {
    // Cloud API-only number: Meta says it was edited but sends no text (reason META_EDIT_TEXT_NOT_PROVIDED)
    if (d.originalMessageId) await db.messages.update({ wamid: d.originalMessageId }, { editedAt: d.editedAt, textOutdated: true });
  }
} else if (evt.type === "message.revoked") {
  await db.messages.update({ wamid: evt.data.originalMessageId }, { deletedAt: evt.data.revokedAt }); // your code
}
```

### PHP

```php
<?php
// Inside your webhook handler, after verifying the signature.
$evt = json_decode(file_get_contents("php://input"), true);
$d = $evt["data"] ?? [];
if ($evt["type"] === "message.edited") {
  if ($d["textAvailable"]) {
    $m = $d["message"]; // same shape as message.received → data.message
    $text = $m["text"]["body"] ?? $m["image"]["caption"] ?? $m["video"]["caption"] ?? null;
    update_message($d["originalMessageId"], ["text" => $text, "edited_at" => $d["editedAt"]]); // your code
  } elseif ($d["originalMessageId"]) {
    // Cloud API-only number: Meta sends no text (reason META_EDIT_TEXT_NOT_PROVIDED)
    update_message($d["originalMessageId"], ["edited_at" => $d["editedAt"], "text_outdated" => true]);
  }
} elseif ($evt["type"] === "message.revoked") {
  update_message($d["originalMessageId"], ["deleted_at" => $d["revokedAt"]]); // your code
}
```

### Python

```python
# Inside your webhook handler, after verifying the signature.
evt = request.get_json()
d = evt.get("data", {})
if evt["type"] == "message.edited":
    if d["textAvailable"]:
        m = d["message"]  # same shape as message.received -> data.message
        text = (m.get("text") or {}).get("body") or (m.get("image") or {}).get("caption") or (m.get("video") or {}).get("caption")
        update_message(d["originalMessageId"], text=text, edited_at=d["editedAt"])  # your code
    elif d["originalMessageId"]:
        # Cloud API-only number: Meta sends no text (reason META_EDIT_TEXT_NOT_PROVIDED)
        update_message(d["originalMessageId"], edited_at=d["editedAt"], text_outdated=True)
elif evt["type"] == "message.revoked":
    update_message(d["originalMessageId"], deleted_at=d["revokedAt"])  # your code
```

## Notas

- **Um webhook criado antes destes eventos com «todos os eventos» não os recebe sozinho**: escolha «todos» de novo (`PATCH` com `events: null`, ou o botão da linha dele no painel) ou some-os à lista. `newEventsAvailable` diz quais faltam.
- Um endpoint que recebe `message.edited`/`message.revoked` deixa de receber essa edição ou exclusão crua como `message.received` (`type: "edit"`, `"revoke"` ou `"unsupported"`). Um que não os recebe continua igual.
- `originalConnectMessageId` é o nosso `msg_…` quando você enviou o original pela API; para uma mensagem do contato vem `null`.
- Deduplique como qualquer evento: pelo `id` do envelope (`evt_…`).
