# Peticiones de webhook

> El formato exacto de las peticiones de webhook de Emailit, desde el método y las cabeceras hasta el array JSON de eventos, además del orden, los duplicados y cómo responder.

En esta página se describe lo que llega a tu endpoint: la petición HTTP, sus cabeceras, el cuerpo JSON y lo que Emailit espera como respuesta. Úsala cuando escribas o depures un controlador de webhooks.

## Petición

| Propiedad | Valor |
| --- | --- |
| Método | `POST` |
| Cuerpo | Un array JSON de 1 a 100 eventos, compacto (sin espacios adicionales) |
| Tiempo de espera | 30 segundos |
| Redirecciones | No se siguen. Una respuesta `3xx` cuenta como fallo. |
| Concurrencia | Una petición a la vez por webhook. Los lotes se envían uno tras otro. |
| Latencia | Emailit busca eventos nuevos cada pocos segundos, así que la mayoría de los eventos llegan en cuestión de segundos. |

```http
POST /webhooks/emailit HTTP/1.1
Host: acme.com
Content-Type: application/json
User-Agent: Emailit-Webhooks/2.0
X-Emailit-Signature: 7242b580f77ec488f8ac21b57f092e3db61a431694f13074c7b4c85a4dd04be3
X-Emailit-Timestamp: 1790846045
```

## Cabeceras

| Cabecera | Descripción |
| --- | --- |
| `Content-Type` | Siempre `application/json`. |
| `User-Agent` | Siempre `Emailit-Webhooks/2.0`. |
| `X-Emailit-Signature` | HMAC-SHA256 codificado en hexadecimal de `{timestamp}.{raw body}`, con el secreto completo del webhook (incluido `whsec_`) como clave. Consulta [Firma de las peticiones](/es/docs/webhooks/request-signature/). |
| `X-Emailit-Timestamp` | Hora Unix, en segundos, del momento en que Emailit firmó la petición. Cada intento, incluidos los reintentos, se vuelve a firmar con una marca de tiempo nueva. |

## Cuerpo

El cuerpo es siempre un array JSON, aunque contenga un solo evento. Cada elemento tiene esta forma:

| Campo | Tipo | Descripción |
| --- | --- | --- |
| `event_id` | string | El ID del evento, que empieza por `evt_`. Un mismo evento conserva el mismo ID en todos los reintentos. Los eventos de prueba empiezan por `evt_test_`. |
| `type` | string | El [tipo de evento](/es/docs/webhooks/event-types/), como `email.delivered`. |
| `object` | object | El recurso al que se refiere el evento. |
| `data` | object | Vuelve a contener `object`. `data.object` y el `object` del nivel superior son idénticos; lee el que prefieras. |

Los campos de `object` dependen del tipo. Los eventos de estado de email describen el email; `email.clicked` y `email.loaded` describen el clic o la carga y anidan el email; los eventos de recursos, como `contact.created`, describen el recurso.

### Ejemplo de lote

Esta petición lleva dos eventos: una entrega y un clic posterior en un enlace del mismo email.

```json
[
  {
    "event_id": "evt_2xGk7Nq1VbD5sR8tLmW3eYhC6aP",
    "type": "email.delivered",
    "object": {
      "id": "em_2xGk7Lr3XcB8pQ1wYzK4dTfG9hJ",
      "object": "email",
      "from": "billing@acme.com",
      "to": "ada@example.com",
      "subject": "Your receipt #1042",
      "status": "delivered",
      "meta": { "order_id": "1042" },
      "updated_at": "2026-10-01T09:14:03.512000+00:00",
      "created_at": "2026-10-01T09:14:01.207000+00:00"
    },
    "data": {
      "object": {
        "id": "em_2xGk7Lr3XcB8pQ1wYzK4dTfG9hJ",
        "object": "email",
        "from": "billing@acme.com",
        "to": "ada@example.com",
        "subject": "Your receipt #1042",
        "status": "delivered",
        "meta": { "order_id": "1042" },
        "updated_at": "2026-10-01T09:14:03.512000+00:00",
        "created_at": "2026-10-01T09:14:01.207000+00:00"
      }
    }
  },
  {
    "event_id": "evt_2xGkC8w2TnF4yR7bMqL1sVdJ6pE",
    "type": "email.clicked",
    "object": {
      "id": "click_2xGkC7v5PmD3xQ9aLnK2rUcH8oW",
      "object": "click",
      "email_id": "em_2xGk7Lr3XcB8pQ1wYzK4dTfG9hJ",
      "email": {
        "id": "em_2xGk7Lr3XcB8pQ1wYzK4dTfG9hJ",
        "rcpt_to": "ada@example.com",
        "mail_from": "billing@acme.com",
        "subject": "Your receipt #1042",
        "created_at": "2026-10-01T09:14:01.207000+00:00",
        "campaign": null,
        "meta": { "order_id": "1042" }
      },
      "link": {
        "id": "link_2xGk7Mb6QwE1zS8cNpT4vXfK3gB",
        "url": "https://acme.com/orders/1042"
      },
      "contact": { "id": "con_2xFh3Ka9LmP2nR6tVwB8cDqY5sZ", "email": "ada@example.com" },
      "ip_address": "203.0.113.24",
      "user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X)",
      "created_at": "2026-10-01T09:20:47.884000+00:00"
    },
    "data": {
      "object": {
        "id": "click_2xGkC7v5PmD3xQ9aLnK2rUcH8oW",
        "object": "click",
        "email_id": "em_2xGk7Lr3XcB8pQ1wYzK4dTfG9hJ",
        "email": {
          "id": "em_2xGk7Lr3XcB8pQ1wYzK4dTfG9hJ",
          "rcpt_to": "ada@example.com",
          "mail_from": "billing@acme.com",
          "subject": "Your receipt #1042",
          "created_at": "2026-10-01T09:14:01.207000+00:00",
          "campaign": null,
          "meta": { "order_id": "1042" }
        },
        "link": {
          "id": "link_2xGk7Mb6QwE1zS8cNpT4vXfK3gB",
          "url": "https://acme.com/orders/1042"
        },
        "contact": { "id": "con_2xFh3Ka9LmP2nR6tVwB8cDqY5sZ", "email": "ada@example.com" },
        "ip_address": "203.0.113.24",
        "user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X)",
        "created_at": "2026-10-01T09:20:47.884000+00:00"
      }
    }
  }
]
```

Las marcas de tiempo están en formato ISO 8601 y en UTC. `meta` son los metadatos que adjuntaste al enviar, o `null`. `contact` es `null` cuando el destinatario no es uno de tus contactos, y `campaign` es `null` en el correo enviado por API y SMTP.

## Orden

Dentro de una petición, los eventos se ordenan del más antiguo al más reciente. Entre peticiones no hay garantía de orden: un lote que falló se reintenta más tarde, cuando puede que ya hayan llegado eventos más recientes, y un reintento manual puede volver a enviar eventos antiguos después de otros nuevos.

No des por supuesto el orden de llegada. En los eventos de email, usa el `status` y el `updated_at` del email, y quédate con el estado más avanzado que hayas visto (por ejemplo, no sobrescribas `clicked` con un `delivered` que llegue tarde). Si tienes dudas, obtén el estado actual con [Obtener un email](/es/docs/api-reference/emails/get/).

## Duplicados

Un mismo evento puede llegar más de una vez:

- Cuando se agota el tiempo de espera de tu endpoint o devuelve un error después de procesar parte de un lote, se reintenta el lote completo, incluidos los eventos que ya procesaste.
- Cuando usas **Retry failed** o reintentas una sola petición.

Guarda cada `event_id` que hayas procesado, con una restricción de unicidad, y omite los eventos que ya hayas visto.

## Respuesta

| Tu respuesta | Resultado |
| --- | --- |
| Cualquier `2xx` en menos de 30 segundos | Todos los eventos del lote se marcan como **Delivered**. El cuerpo de la respuesta se ignora. |
| `3xx`, `4xx` o `5xx` | El lote ha fallado y se reintenta. |
| Sin respuesta en 30 segundos, error de conexión, error de TLS | El lote ha fallado y se reintenta. El panel muestra el código de estado `0`. |

En las peticiones fallidas, Emailit guarda los primeros 2000 caracteres del cuerpo de tu respuesta. Puedes leerlos en la pestaña **Requests** del webhook, así que devolver un mensaje de error breve resulta útil para depurar.

Recomendaciones:

- **Verifica y confirma rápido.** Comprueba la firma, pon los eventos en una cola o en una tabla y devuelve `200`. Haz el trabajo lento, como llamar a otras API, en una tarea en segundo plano.
- **Devuelve `2xx` para los eventos que ignoras.** Si no, se reintentan durante días.
- **Devuelve `401` o `403` si la firma no es válida.** Se reintenta como cualquier fallo, lo que te da tiempo para corregir un secreto mal configurado.

Consulta [Reintentos y fallos](/es/docs/webhooks/retries-and-failures/) para ver el calendario de reintentos y qué ocurre cuando un endpoint sigue fallando.

## Ver también

  - [Firma de las peticiones](/es/docs/webhooks/request-signature/)
  - [Tipos de eventos](/es/docs/webhooks/event-types/)

---
Fuente: https://emailit.com/es/docs/webhooks/webhook-requests/
