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

# Entrega, reintentos y deduplicación

> Cómo responder rápido y procesar eventos sin duplicar acciones

Cobrix entrega webhooks con semántica **al menos una vez**. Una caída de red puede ocurrir después de que tu servidor procese el evento pero antes de que Cobrix reciba la respuesta; por eso debes aceptar que el mismo evento llegue nuevamente.

## Qué respuesta confirma la entrega

| Protocolo           | Éxito                | Timeout     |
| ------------------- | -------------------- | ----------- |
| General             | Cualquier HTTP `2xx` | 30 segundos |
| Documentos públicos | HTTP `200` exacto    | 30 segundos |

No necesitas devolver el objeto procesado. Una respuesta pequeña es suficiente:

```http theme={null}
HTTP/1.1 200 OK
Content-Type: application/json

{"received":true}
```

## Patrón recomendado

<Steps>
  <Step title="Verifica la firma">
    Si falla, responde `401` y no guardes ni proceses el contenido.
  </Step>

  <Step title="Inserta el identificador de forma única">
    Para eventos generales usa `payload.id`. Si la inserción choca con la restricción única, ya fue recibido.
  </Step>

  <Step title="Guarda el payload">
    Conserva el JSON, tipo, fecha de recepción y estado de procesamiento para auditoría.
  </Step>

  <Step title="Encola el trabajo">
    Publica una tarea interna que procese el evento fuera del request HTTP.
  </Step>

  <Step title="Responde inmediatamente">
    Devuelve `200` antes de llamar APIs lentas, enviar mensajes o ejecutar conciliaciones.
  </Step>
</Steps>

## Reintentos

### Pendientes de envío

En los webhooks generales configurados por empresa, Cobrix guarda el evento antes
de iniciar HTTP. Si no hay un cupo disponible o se prepara un lote de recordatorios,
el resultado puede ser **En cola** (`queued`): Cobrix conserva el evento para
enviarlo, pero todavía no hay confirmación de recepción del destino.

Las respuestas que exponen el despacho incluyen la cantidad `queued` y, por
destino, `eventLogId` y `nextRetryAt` cuando están disponibles. Conserva esa
referencia para consultar su avance; no generes otro evento para reemplazarlo.
Cada destino mantiene su resultado independiente. El correo y el webhook también
son independientes: un correo enviado no confirma el envío de WhatsApp.

### Cómo interpretar cada resultado

| Evidencia                                                              | Qué significa                                                  | Qué no demuestra                                                        |
| ---------------------------------------------------------------------- | -------------------------------------------------------------- | ----------------------------------------------------------------------- |
| `queued` / En cola                                                     | Cobrix guardó el evento; aún no confirma recepción del destino | No es omitido ni enviado a WhatsApp                                     |
| `sent` en el resultado del despacho / `delivered` en el log del evento | El receptor respondió con el HTTP de éxito de su protocolo     | No confirma que n8n terminara el flujo ni que Meta entregara el mensaje |
| Intento fallido con `nextRetryAt`                                      | Hubo un error y el evento tiene otro intento programado        | No es lo mismo que esperar cupo sin haber intentado HTTP                |
| Fallido sin próximo reintento                                          | No tiene recuperación automática programada                    | No debe reenviarse sin revisar el motivo y posibles duplicados          |

El estado persistido de un pendiente nuevo puede ser `retry` con `attempts: 0`:
en ese caso aún no se realizó una solicitud HTTP. Consulta también `attempts`,
`lastResponseStatus`, `lastError` y `nextRetryAt`; `retry` por sí solo no distingue
un evento esperando su primer envío de uno que ya tuvo un error.

<Warning>
  Si el receptor es n8n, una respuesta HTTP exitosa confirma su aceptación HTTP,
  no el resultado completo del flujo. Para afirmar «entregado» o «leído» por
  WhatsApp necesitas la confirmación de Meta vinculada al mensaje exacto.
</Warning>

### Capacidad y tiempos

El recuperador revisa eventos elegibles cada cinco segundos, en lotes de hasta 50,
compartiendo los cupos HTTP con los envíos inmediatos. Esperar cupo no consume
intentos. No existe un descarte fijo al llegar a 60 despachos por minuto.
La fecha de elegibilidad no garantiza el instante exacto del envío ni su entrega
por un proveedor final como Meta.

Este comportamiento no cambia el protocolo específico de documentos públicos
ni activa automáticamente eventos históricos que no eran elegibles.

La capacidad se configura en el backend mediante `WEBHOOK_DISPATCH_CONCURRENCY`:
entero de **1 a 50**, predeterminado **10 por proceso**. No es un límite por minuto
ni una opción del panel de empresa. Los procesos no comparten un regulador global.
La cantidad que sale por minuto depende de la latencia y capacidad del receptor.

No hay un tiempo garantizado para completar un lote. El productor de suscripciones
conserva su pausa de correo de 600 ms por destinatario cuando ese canal está
habilitado; el recorrido sólo webhook no la aplica. Las rutas predeterminadas
fuera de este dispatcher conservan su comportamiento. Esta mejora no añade una
separación global de 60 segundos entre mensajes al mismo teléfono.

### Después de un error HTTP

Los eventos generales realizan hasta cinco intentos totales. Después del intento inicial, las ventanas de reintento vigentes son aproximadamente 5 minutos, 30 minutos, 2 horas y 24 horas; el worker se ejecuta periódicamente, por lo que la hora exacta puede desplazarse.

Los eventos de documentos públicos realizan hasta cinco intentos totales: el inicial y reintentos aproximadamente después de 1 minuto, 5 minutos, 30 minutos y 2 horas.

<Info>
  Un reenvío manual puede producir un nuevo intento HTTP. En documentos públicos, deduplica por `X-Cobrix-Event-Id`: Cobrix conserva el mismo valor en todos los intentos del evento.
</Info>

## Diagnóstico por código HTTP

| Resultado                                | Qué interpreta Cobrix             | Qué revisar                                           |
| ---------------------------------------- | --------------------------------- | ----------------------------------------------------- |
| `200–299` general                        | Entregado                         | Nada; conserva el registro                            |
| `200` documentos públicos                | Entregado                         | Nada; conserva el registro                            |
| `201`, `202` o `204` documentos públicos | No entregado; se reintentará      | Responder `200` exacto después de persistir el evento |
| `400`                                    | Tu receptor rechazó el payload    | Parser, campos obligatorios y versión                 |
| `401/403`                                | Tu receptor rechazó autenticación | Secreto, cuerpo crudo y fórmula usada                 |
| `404`                                    | La ruta configurada no existe     | URL y despliegue del receptor                         |
| `409`                                    | El receptor reportó conflicto     | Idempotencia o estado interno                         |
| `429`                                    | El receptor está limitado         | Capacidad y política de reintentos                    |
| `500–599`                                | El receptor falló                 | Logs internos correlacionados por evento              |
| Timeout                                  | No hubo respuesta antes de 30 s   | Responder antes y procesar asíncronamente             |

## Qué guardar para soporte

* En eventos generales, `payload.id` y `payload.event`.
* En documentos públicos, `X-Cobrix-Event-Id` y `X-Cobrix-Event-Type`.
* Fecha de recepción y duración.
* Resultado de firma sin guardar el secreto.
* Código y cuerpo de respuesta enviados.
* Estado del trabajo asíncrono y último error interno.

<Warning>
  Que Cobrix haya iniciado el despacho no demuestra que el endpoint lo recibió correctamente.
  Una respuesta exitosa registrada es evidencia de recepción HTTP, no de entrega al cliente final.
</Warning>

## Activación sin reproducción histórica

El endpoint de documentos comienza a suscribirse cuando se crea activo o cuando pasa de inactivo a activo. La recuperación automática no genera eventos ocurridos antes de ese momento. Cambiar la URL o rotar el secreto mientras el endpoint continúa activo conserva el inicio de la suscripción.

Los eventos que ya estaban pendientes mientras el endpoint estaba activo mantienen sus reintentos.
