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

# Estructura de payloads

> Estructura detallada de los payloads de webhook

# Estructura de Payloads

Cada evento tiene una estructura de payload específica. Esta página documenta los más importantes.

***

## message.received

El payload de `message.received` usa una **estructura híbrida**: el campo `message` contiene el mensaje **exactamente como lo envía Meta**, más IDs propios de Whaapy y extras de valor agregado.

### Campos Base

Estos campos siempre están presentes:

| Campo               | Tipo   | Descripción                                                         |
| ------------------- | ------ | ------------------------------------------------------------------- |
| `whaapy_message_id` | string | ID interno del mensaje en Whaapy                                    |
| `conversation_id`   | string | ID de la conversación en Whaapy                                     |
| `contact_id`        | string | ID del contacto en Whaapy                                           |
| `message`           | object | **Mensaje original de Meta** (estructura idéntica a la API de Meta) |
| `contact_name`      | string | Nombre del contacto (si está disponible)                            |

### Campos Extras (Valor Agregado)

Estos campos aparecen según el tipo de mensaje:

| Campo           | Tipo   | Cuándo aparece                         | Descripción                              |
| --------------- | ------ | -------------------------------------- | ---------------------------------------- |
| `media_url`     | string | image, video, audio, document, sticker | URL con token (24h) para descargar media |
| `transcription` | string | audio                                  | Texto transcrito de la nota de voz       |

<Tip>
  **¿Por qué estructura híbrida?** El campo `message` usa exactamente el formato de Meta, permitiéndote reutilizar código existente. Los extras de Whaapy (`media_url`, `transcription`) te ahorran llamadas adicionales a la API.
</Tip>

***

### Ejemplos por Tipo de Mensaje

<Tabs>
  <Tab title="Texto">
    ```json theme={null}
    {
      "event": "message.received",
      "timestamp": "2025-01-15T10:30:00.000Z",
      "businessId": "uuid",
      "data": {
        "whaapy_message_id": "msg-uuid",
        "conversation_id": "conv-uuid",
        "contact_id": "contact-uuid",
        "message": {
          "from": "5215512345678",
          "id": "wamid.HBgNNTIxNTUxMjM0NTY3OA==",
          "timestamp": "1705312200",
          "type": "text",
          "text": {
            "body": "Hola, necesito ayuda"
          }
        },
        "contact_name": "Juan Pérez"
      }
    }
    ```
  </Tab>

  <Tab title="Audio">
    ```json theme={null}
    {
      "event": "message.received",
      "timestamp": "2025-01-15T10:30:00.000Z",
      "businessId": "uuid",
      "data": {
        "whaapy_message_id": "msg-uuid",
        "conversation_id": "conv-uuid",
        "contact_id": "contact-uuid",
        "message": {
          "from": "5215512345678",
          "id": "wamid.HBgNNTIxNTUxMjM0NTY3OA==",
          "timestamp": "1705312200",
          "type": "audio",
          "audio": {
            "mime_type": "audio/ogg; codecs=opus",
            "sha256": "abc123def456...",
            "id": "media_id_de_meta",
            "voice": true
          }
        },
        "contact_name": "Juan Pérez",
        "media_url": "https://api.whaapy.com/messages/msg-uuid/media?token=...",
        "transcription": "Hola, quería preguntar sobre el precio del producto"
      }
    }
    ```

    <Note>
      `transcription` solo aparece si la transcripción fue exitosa. El `media_url` tiene un token válido por 24 horas.
    </Note>
  </Tab>

  <Tab title="Imagen">
    ```json theme={null}
    {
      "event": "message.received",
      "timestamp": "2025-01-15T10:30:00.000Z",
      "businessId": "uuid",
      "data": {
        "whaapy_message_id": "msg-uuid",
        "conversation_id": "conv-uuid",
        "contact_id": "contact-uuid",
        "message": {
          "from": "5215512345678",
          "id": "wamid.HBgNNTIxNTUxMjM0NTY3OA==",
          "timestamp": "1705312200",
          "type": "image",
          "image": {
            "caption": "Mira esta foto del producto",
            "mime_type": "image/jpeg",
            "sha256": "abc123def456...",
            "id": "media_id_de_meta"
          }
        },
        "contact_name": "Juan Pérez",
        "media_url": "https://api.whaapy.com/messages/msg-uuid/media?token=..."
      }
    }
    ```
  </Tab>

  <Tab title="Video">
    ```json theme={null}
    {
      "event": "message.received",
      "timestamp": "2025-01-15T10:30:00.000Z",
      "businessId": "uuid",
      "data": {
        "whaapy_message_id": "msg-uuid",
        "conversation_id": "conv-uuid",
        "contact_id": "contact-uuid",
        "message": {
          "from": "5215512345678",
          "id": "wamid.HBgNNTIxNTUxMjM0NTY3OA==",
          "timestamp": "1705312200",
          "type": "video",
          "video": {
            "caption": "Video del problema que tengo",
            "mime_type": "video/mp4",
            "sha256": "abc123def456...",
            "id": "media_id_de_meta"
          }
        },
        "contact_name": "Juan Pérez",
        "media_url": "https://api.whaapy.com/messages/msg-uuid/media?token=..."
      }
    }
    ```
  </Tab>

  <Tab title="Documento">
    ```json theme={null}
    {
      "event": "message.received",
      "timestamp": "2025-01-15T10:30:00.000Z",
      "businessId": "uuid",
      "data": {
        "whaapy_message_id": "msg-uuid",
        "conversation_id": "conv-uuid",
        "contact_id": "contact-uuid",
        "message": {
          "from": "5215512345678",
          "id": "wamid.HBgNNTIxNTUxMjM0NTY3OA==",
          "timestamp": "1705312200",
          "type": "document",
          "document": {
            "filename": "factura-enero-2025.pdf",
            "mime_type": "application/pdf",
            "sha256": "abc123def456...",
            "id": "media_id_de_meta"
          }
        },
        "contact_name": "Juan Pérez",
        "media_url": "https://api.whaapy.com/messages/msg-uuid/media?token=..."
      }
    }
    ```
  </Tab>

  <Tab title="Ubicación">
    ```json theme={null}
    {
      "event": "message.received",
      "timestamp": "2025-01-15T10:30:00.000Z",
      "businessId": "uuid",
      "data": {
        "whaapy_message_id": "msg-uuid",
        "conversation_id": "conv-uuid",
        "contact_id": "contact-uuid",
        "message": {
          "from": "5215512345678",
          "id": "wamid.HBgNNTIxNTUxMjM0NTY3OA==",
          "timestamp": "1705312200",
          "type": "location",
          "location": {
            "latitude": 19.4326,
            "longitude": -99.1332,
            "name": "Mi Casa",
            "address": "Av. Reforma 123, CDMX"
          }
        },
        "contact_name": "Juan Pérez"
      }
    }
    ```
  </Tab>

  <Tab title="Contacto">
    ```json theme={null}
    {
      "event": "message.received",
      "timestamp": "2025-01-15T10:30:00.000Z",
      "businessId": "uuid",
      "data": {
        "whaapy_message_id": "msg-uuid",
        "conversation_id": "conv-uuid",
        "contact_id": "contact-uuid",
        "message": {
          "from": "5215512345678",
          "id": "wamid.HBgNNTIxNTUxMjM0NTY3OA==",
          "timestamp": "1705312200",
          "type": "contacts",
          "contacts": [
            {
              "name": {
                "formatted_name": "María García",
                "first_name": "María",
                "last_name": "García"
              },
              "phones": [
                { "phone": "+5215598765432", "type": "CELL", "wa_id": "5215598765432" }
              ],
              "emails": [
                { "email": "maria@example.com", "type": "WORK" }
              ]
            }
          ]
        },
        "contact_name": "Juan Pérez"
      }
    }
    ```
  </Tab>

  <Tab title="Sticker">
    ```json theme={null}
    {
      "event": "message.received",
      "timestamp": "2025-01-15T10:30:00.000Z",
      "businessId": "uuid",
      "data": {
        "whaapy_message_id": "msg-uuid",
        "conversation_id": "conv-uuid",
        "contact_id": "contact-uuid",
        "message": {
          "from": "5215512345678",
          "id": "wamid.HBgNNTIxNTUxMjM0NTY3OA==",
          "timestamp": "1705312200",
          "type": "sticker",
          "sticker": {
            "mime_type": "image/webp",
            "sha256": "abc123def456...",
            "id": "media_id_de_meta",
            "animated": false
          }
        },
        "contact_name": "Juan Pérez",
        "media_url": "https://api.whaapy.com/messages/msg-uuid/media?token=..."
      }
    }
    ```
  </Tab>

  <Tab title="Flow Reply">
    ```json theme={null}
    {
      "event": "message.received",
      "timestamp": "2026-03-09T18:30:00.000Z",
      "businessId": "uuid",
      "data": {
        "whaapy_message_id": "msg-uuid",
        "conversation_id": "conv-uuid",
        "contact_id": "contact-uuid",
        "message": {
          "from": "5215512345678",
          "id": "wamid.HBgNNTIxNTUxMjM0NTY3OA==",
          "timestamp": "1741545000",
          "type": "interactive",
          "interactive": {
            "type": "nfm_reply",
            "nfm_reply": {
              "name": "booking_flow",
              "body": "Sucursal Centro · 2026-03-10 16:00",
              "response_json": "{\"screen\":\"BOOK_APPOINTMENT\",\"values\":{\"branch\":\"Sucursal Centro\",\"slot\":\"2026-03-10 16:00\",\"service\":\"demo\"}}"
            }
          },
          "context": {
            "id": "wamid.HBgNflowOriginal"
          }
        },
        "contact_name": "Juan Pérez"
      }
    }
    ```

    <Note>
      Cuando Whaapy recibe un `interactive.type = "nfm_reply"`, lo persiste como mensaje `interactive` en el inbox y mantiene el payload original de Meta dentro de `message.interactive.nfm_reply`.
    </Note>
  </Tab>
</Tabs>

***

## Sobre media\_url vs message.\*.id

<CardGroup cols={2}>
  <Card title="message.image.id (Meta)" icon="meta">
    ID de media de Meta. Requiere llamada adicional a su API para obtener la URL de descarga.
  </Card>

  <Card title="media_url (Whaapy)" icon="bolt">
    URL lista para descargar con token incluido. Sin llamadas adicionales.
  </Card>
</CardGroup>

**Limitaciones:**

* **Token válido**: 24 horas
* **Expiración de media**: WhatsApp elimina media después de 30 días

<Tip>
  Para referencia del formato Meta, consulta la [documentación oficial](https://developers.facebook.com/docs/whatsapp/cloud-api/webhooks/components#messages-object).
</Tip>

***

## message.sent

Cuando tu negocio envía un mensaje (manual o por IA).

```json theme={null}
{
  "event": "message.sent",
  "timestamp": "2025-01-15T10:31:00.000Z",
  "businessId": "uuid",
  "data": {
    "message_id": "msg-uuid",
    "conversation_id": "conv-uuid",
    "to": "+5215512345678",
    "contact_name": "Juan Pérez",
    "type": "text",
    "content": "¡Hola! ¿En qué puedo ayudarte?",
    "sent_by_ai": true,
    "timestamp": "2025-01-15T10:31:00.000Z",
    "provider": "meta"
  }
}
```

| Campo             | Tipo    | Descripción                                   |
| ----------------- | ------- | --------------------------------------------- |
| `message_id`      | string  | ID del mensaje en Whaapy                      |
| `conversation_id` | string  | ID de la conversación                         |
| `to`              | string  | Número de destino                             |
| `type`            | string  | Tipo de mensaje (text, image, template, etc.) |
| `content`         | string  | Contenido del mensaje                         |
| `sent_by_ai`      | boolean | `true` si fue enviado por el agente de IA     |

***

## conversation.created

Cuando se inicia una nueva conversación.

```json theme={null}
{
  "event": "conversation.created",
  "timestamp": "2025-01-15T10:30:00.000Z",
  "businessId": "uuid",
  "data": {
    "conversation_id": "conv-uuid",
    "contact_id": "contact-uuid",
    "phone_number": "+5215512345678",
    "contact_name": "Juan Pérez",
    "timestamp": "2025-01-15T10:30:00.000Z"
  }
}
```

***

## conversation.assigned

Cuando una conversación se asigna a un agente humano.

```json theme={null}
{
  "event": "conversation.assigned",
  "timestamp": "2025-01-15T10:32:00.000Z",
  "businessId": "uuid",
  "data": {
    "conversation_id": "conv-uuid",
    "phone_number": "+5215512345678",
    "contact_name": "Juan Pérez",
    "assigned_to": "agent-uuid",
    "assigned_by": "admin-uuid",
    "method": "manual",
    "timestamp": "2025-01-15T10:32:00.000Z"
  }
}
```

| Campo         | Tipo   | Descripción              |
| ------------- | ------ | ------------------------ |
| `assigned_to` | string | UUID del agente asignado |
| `assigned_by` | string | UUID de quien asignó     |
| `method`      | string | `manual` o `auto`        |

***

## broadcast.completed

Cuando un envío masivo termina.

```json theme={null}
{
  "event": "broadcast.completed",
  "timestamp": "2025-01-15T11:00:00.000Z",
  "businessId": "uuid",
  "data": {
    "broadcast_id": "bc-uuid",
    "name": "Promoción Enero",
    "total_recipients": 500,
    "sent_count": 495,
    "failed_count": 5,
    "success_rate": 99,
    "completed_at": "2025-01-15T11:00:00.000Z"
  }
}
```

***

## message.delivered

Cuando WhatsApp confirma que el mensaje fue entregado al dispositivo del usuario.

```json theme={null}
{
  "event": "message.delivered",
  "timestamp": "2026-01-15T10:31:05.000Z",
  "businessId": "uuid",
  "data": {
    "message_id": "msg-uuid",
    "wamid": "wamid.HBgNNTIxNTUxMjM0NTY3OA==",
    "conversation_id": "conv-uuid",
    "to": "+5215512345678",
    "contact_name": "Juan Pérez",
    "status": "delivered",
    "timestamp": "2026-01-15T10:31:05.000Z"
  }
}
```

| Campo        | Tipo   | Descripción                |
| ------------ | ------ | -------------------------- |
| `message_id` | string | ID del mensaje en Whaapy   |
| `wamid`      | string | ID del mensaje en WhatsApp |
| `status`     | string | Siempre `delivered`        |

***

## message.read

Cuando el usuario abre y lee el mensaje (doble check azul).

```json theme={null}
{
  "event": "message.read",
  "timestamp": "2026-01-15T10:35:00.000Z",
  "businessId": "uuid",
  "data": {
    "message_id": "msg-uuid",
    "wamid": "wamid.HBgNNTIxNTUxMjM0NTY3OA==",
    "conversation_id": "conv-uuid",
    "to": "+5215512345678",
    "contact_name": "Juan Pérez",
    "status": "read",
    "timestamp": "2026-01-15T10:35:00.000Z"
  }
}
```

<Note>
  El evento `message.read` solo se recibe si el usuario tiene habilitada la confirmación de lectura en WhatsApp.
</Note>

***

## message.failed

Cuando un mensaje no se pudo enviar.

```json theme={null}
{
  "event": "message.failed",
  "timestamp": "2026-01-15T10:31:00.000Z",
  "businessId": "uuid",
  "data": {
    "message_id": "msg-uuid",
    "conversation_id": "conv-uuid",
    "to": "+5215512345678",
    "contact_name": "Juan Pérez",
    "status": "failed",
    "error": {
      "code": "131047",
      "title": "Re-engagement message",
      "message": "La ventana de conversación de 24 horas ha expirado",
      "details": "More than 24 hours have passed since the customer last replied"
    },
    "timestamp": "2026-01-15T10:31:00.000Z"
  }
}
```

| Campo           | Tipo   | Descripción                      |
| --------------- | ------ | -------------------------------- |
| `error.code`    | string | Código de error de WhatsApp      |
| `error.title`   | string | Título corto del error           |
| `error.message` | string | Descripción del error en español |
| `error.details` | string | Detalles técnicos (en inglés)    |

<Warning>
  Los códigos de error más comunes son `131047` (ventana cerrada), `131051` (número no válido), y `131026` (usuario bloqueó el número).
</Warning>

***

## conversation.unassigned

Cuando una conversación es desasignada de un agente.

```json theme={null}
{
  "event": "conversation.unassigned",
  "timestamp": "2026-01-15T11:00:00.000Z",
  "businessId": "uuid",
  "data": {
    "conversation_id": "conv-uuid",
    "phone_number": "+5215512345678",
    "contact_name": "Juan Pérez",
    "previous_agent": "agent-uuid",
    "unassigned_by": "admin-uuid",
    "reason": "manual",
    "timestamp": "2026-01-15T11:00:00.000Z"
  }
}
```

| Campo            | Tipo   | Descripción                                      |
| ---------------- | ------ | ------------------------------------------------ |
| `previous_agent` | string | UUID del agente que tenía la conversación        |
| `unassigned_by`  | string | UUID de quien desasignó (null si fue automático) |
| `reason`         | string | `manual`, `timeout`, o `reassigned`              |

***

## conversation.closed

Cuando una conversación es marcada como cerrada.

```json theme={null}
{
  "event": "conversation.closed",
  "timestamp": "2026-01-15T12:00:00.000Z",
  "businessId": "uuid",
  "data": {
    "conversation_id": "conv-uuid",
    "phone_number": "+5215512345678",
    "contact_name": "Juan Pérez",
    "closed_by": "agent-uuid",
    "reason": "resolved",
    "duration_minutes": 45,
    "message_count": 12,
    "timestamp": "2026-01-15T12:00:00.000Z"
  }
}
```

| Campo              | Tipo   | Descripción                                  |
| ------------------ | ------ | -------------------------------------------- |
| `closed_by`        | string | UUID de quien cerró (null si fue automático) |
| `reason`           | string | `resolved`, `spam`, `timeout`, `manual`      |
| `duration_minutes` | number | Duración total de la conversación            |
| `message_count`    | number | Número total de mensajes                     |

***

## contact.created

Cuando se crea un nuevo contacto (primera vez que alguien escribe).

```json theme={null}
{
  "event": "contact.created",
  "timestamp": "2026-01-15T10:30:00.000Z",
  "businessId": "uuid",
  "data": {
    "contact_id": "contact-uuid",
    "phone_number": "+5215512345678",
    "name": "Juan Pérez",
    "wa_id": "5215512345678",
    "source": "inbound_message",
    "timestamp": "2026-01-15T10:30:00.000Z"
  }
}
```

| Campo    | Tipo   | Descripción                           |
| -------- | ------ | ------------------------------------- |
| `source` | string | `inbound_message`, `import`, `manual` |

***

## contact.updated

Cuando se actualizan los datos de un contacto.

```json theme={null}
{
  "event": "contact.updated",
  "timestamp": "2026-01-15T14:00:00.000Z",
  "businessId": "uuid",
  "data": {
    "contact_id": "contact-uuid",
    "phone_number": "+5215512345678",
    "name": "Juan Carlos Pérez",
    "previous_name": "Juan Pérez",
    "updated_by": "agent-uuid",
    "updated_fields": ["name", "email", "tags"],
    "timestamp": "2026-01-15T14:00:00.000Z"
  }
}
```

| Campo            | Tipo      | Descripción                 |
| ---------------- | --------- | --------------------------- |
| `updated_by`     | string    | UUID de quien actualizó     |
| `updated_fields` | string\[] | Lista de campos modificados |
| `previous_name`  | string    | Nombre anterior (si cambió) |

***

## contact.deleted

Cuando se elimina un contacto.

```json theme={null}
{
  "event": "contact.deleted",
  "timestamp": "2026-01-15T15:00:00.000Z",
  "businessId": "uuid",
  "data": {
    "contact_id": "contact-uuid",
    "phone_number": "+5215512345678",
    "name": "Juan Pérez",
    "deleted_by": "admin-uuid",
    "reason": "gdpr_request",
    "timestamp": "2026-01-15T15:00:00.000Z"
  }
}
```

| Campo        | Tipo   | Descripción                                 |
| ------------ | ------ | ------------------------------------------- |
| `deleted_by` | string | UUID de quien eliminó                       |
| `reason`     | string | `gdpr_request`, `spam`, `manual`, `cleanup` |

<Warning>
  Al eliminar un contacto, también se eliminan todas sus conversaciones y mensajes. Esta acción es irreversible.
</Warning>

***

## broadcast.sent

Cuando un broadcast comienza a enviarse.

```json theme={null}
{
  "event": "broadcast.sent",
  "timestamp": "2026-01-15T10:00:00.000Z",
  "businessId": "uuid",
  "data": {
    "broadcast_id": "bc-uuid",
    "name": "Promoción Enero",
    "template_name": "promo_enero_2026",
    "total_recipients": 500,
    "started_at": "2026-01-15T10:00:00.000Z",
    "created_by": "admin-uuid"
  }
}
```

| Campo              | Tipo   | Descripción                     |
| ------------------ | ------ | ------------------------------- |
| `template_name`    | string | Nombre del template usado       |
| `total_recipients` | number | Número de destinatarios         |
| `created_by`       | string | UUID de quien creó el broadcast |

***

## broadcast.failed

Cuando un broadcast falla antes de completarse.

```json theme={null}
{
  "event": "broadcast.failed",
  "timestamp": "2026-01-15T10:05:00.000Z",
  "businessId": "uuid",
  "data": {
    "broadcast_id": "bc-uuid",
    "name": "Promoción Enero",
    "template_name": "promo_enero_2026",
    "total_recipients": 500,
    "sent_count": 150,
    "failed_count": 350,
    "error": {
      "code": "rate_limit_exceeded",
      "message": "Se excedió el límite de mensajes por minuto"
    },
    "failed_at": "2026-01-15T10:05:00.000Z"
  }
}
```

| Campo           | Tipo   | Descripción                       |
| --------------- | ------ | --------------------------------- |
| `sent_count`    | number | Mensajes enviados antes del fallo |
| `failed_count`  | number | Mensajes que no se enviaron       |
| `error.code`    | string | Código de error                   |
| `error.message` | string | Descripción del error             |

<Tip>
  Si un broadcast falla parcialmente, los mensajes ya enviados no se revierten. Puedes crear un nuevo broadcast con los contactos pendientes.
</Tip>

***

## Próximos Pasos

<CardGroup cols={2}>
  <Card title="Seguridad" icon="shield-check" href="/api-reference/webhooks/security">
    Verificar la autenticidad de los webhooks
  </Card>

  <Card title="Gestión" icon="gear" href="/api-reference/webhooks/management">
    Actualizar, probar y eliminar webhooks
  </Card>
</CardGroup>
