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

# Consumo de eventos

> Consume eventos de Streaming Hub: verifica una firma HMAC de webhook, maneja dos firmas durante la rotación, lee los headers de correlación X-Lerian, deduplica entregas y consulta eventos mediante pull con cursor-as-ack.

Streaming Hub entrega eventos de dos formas. Los sinks **push** (`webhook`, `sqs`, `rabbitmq`, `eventbridge`) envían cada evento coincidente a un destino que tú controlas. Un sink **pull** mantiene los eventos en un cursor del lado del servidor que tu consumidor lee según su propia planificación. Esta página cubre lo que tu consumidor debe hacer en cada caso.

## Verificar una firma de webhook

***

Cada entrega de webhook se firma con un HMAC sobre el timestamp de la solicitud y el cuerpo exacto de la solicitud, así que puedes demostrar que la solicitud vino de Streaming Hub y no fue manipulada ni reproducida. Dos headers llevan la firma:

| Header                | Valor                                                 |
| --------------------- | ----------------------------------------------------- |
| `X-Webhook-Signature` | `v1,sha256=<hex>` — la firma.                         |
| `X-Webhook-Timestamp` | El timestamp en segundos Unix incorporado a la firma. |

La firma se calcula así:

```
signature_input = "v1:" + X-Webhook-Timestamp + "." + <raw request body>
X-Webhook-Signature = "v1,sha256=" + hex( HMAC-SHA256(key = signing_secret, message = signature_input) )
```

Para verificar una entrega:

1. **Lee el cuerpo en bruto y el timestamp** — verifica **antes** de parsear el cuerpo, sobre los bytes exactos recibidos. Cualquier reserialización cambia los bytes y rompe la firma.
2. **Comprueba la frescura** — rechaza la solicitud si `X-Webhook-Timestamp` está a más de **5 minutos** del momento actual. Esta es la ventana de protección contra reproducción.
3. **Recalcula la firma** — construye `signature_input` como arriba con tu secreto de firma y codifica en hex el HMAC-SHA256.
4. **Compara en tiempo constante** — compara tu `v1,sha256=<hex>` con el `X-Webhook-Signature` recibido usando una comparación de tiempo constante (segura ante ataques de temporización). Rechaza si no coinciden.
5. **Responde** — devuelve `2xx` solo después de que la firma y la frescura pasen ambas.

El secreto de firma es el que recibiste cuando creaste la suscripción (o la última vez que lo rotaste). Nunca se envía en un header; solo la firma derivada. Consulta [crear una suscripción de webhook](/es/streaming-hub/managing-subscriptions) para saber de dónde viene el secreto.

## Manejar dos firmas durante la rotación

***

Cuando [rotas un secreto de firma](/es/streaming-hub/managing-subscriptions), el hub ejecuta un **solapamiento de doble firma de 24 horas**. Durante el solapamiento, cada entrega lleva **dos headers `X-Webhook-Signature`** —uno firmado con el nuevo secreto y otro con el anterior— enviados como **headers repetidos**, así que puedes migrar al nuevo secreto sin perder ninguna entrega.

Verifica contra ambos: recalcula la firma esperada con cada secreto que tengas en ese momento y **acepta la solicitud si cualquiera de las firmas recibidas coincide**. Una vez que hayas desplegado y confirmado el nuevo secreto, retira el antiguo.

<Warning>
  Lee `X-Webhook-Signature` como una **lista de valores de header repetidos** usando el accesor de headers multivalor de tu framework (o su vista de headers en bruto). **No** dividas un único string unido por comas: el propio valor de la firma contiene una coma (`v1,sha256=…`), así que una división ingenua por comas lo corrompe. Algunos stacks HTTP unen los headers repetidos con `", "` por defecto; usa el accesor de lista para obtener cada valor intacto.
</Warning>

## Headers de correlación

***

Cada entrega de webhook también lleva un conjunto de headers de contexto `X-Lerian-*`:

| Header                    | Lleva                                                                                                                                       |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `X-Lerian-Event-Id`       | El id de evento estable (el `ce-id` de CloudEvents). **La clave de deduplicación** — constante a lo largo de los reenvíos del mismo evento. |
| `X-Lerian-Event-Type`     | El tipo de evento, como la cola escueta `<resource>.<event>` (por ejemplo, `transaction.created`).                                          |
| `X-Lerian-Tenant-Id`      | El id del tenant propietario (solo atribución).                                                                                             |
| `X-Lerian-Delivery-Id`    | El id por intento — **cambia en cada reintento** del mismo evento.                                                                          |
| `X-Lerian-Schema-Version` | La versión de esquema del payload.                                                                                                          |

## Deduplicar entregas

***

La entrega es **at-least-once**: el hub puede entregar el mismo evento más de una vez, mediante reintentos o un reenvío después de reiniciar un worker. Deduplica por **`X-Lerian-Event-Id`**: es estable a lo largo de cada reenvío del mismo evento, mientras que `X-Lerian-Delivery-Id` difiere por intento. Trata un `X-Lerian-Event-Id` que ya hayas procesado como un duplicado: confírmalo con un `2xx` y no lo reproceses. Mantén tu handler idempotente.

## Responder con rapidez

***

Devuelve un `2xx` en cuanto hayas verificado y aceptado de forma duradera el evento; luego haz el trabajo real de forma asíncrona. Una respuesta lenta o fallida se trata como una entrega fallida, lo que dispara la [curva de reintentos](/es/streaming-hub/how-streaming-hub-works) y, si el destino permanece roto el tiempo suficiente, con el tiempo [auto-desactiva](/es/streaming-hub/how-streaming-hub-works) la suscripción. Confirma rápido, procesa fuera de banda.

## Consultar eventos mediante pull

***

Una suscripción `pull` no recibe ningún push. En su lugar, tu consumidor lee una página de sus eventos con:

```
GET /v1/events?subscription_id=<uuid>
```

Los eventos vuelven en orden de llegada ascendente, cada uno con su `seq`, su `ceId` (para deduplicar), su tipo y su payload. La respuesta incluye un `next_cursor`.

**La lectura es la confirmación (cursor-as-ack).** Obtener una página avanza el cursor duradero de la suscripción hasta el `seq` más alto devuelto. No hay una llamada de confirmación separada: leer una página la confirma. Esto es monótono, así que nunca retrocede.

Para paginar con normalidad, retoma desde el `next_cursor` del servidor y detente cuando una página corta (con menos que tu `limit`) devuelva `null`. Dos comportamientos a tener en cuenta:

* **Un salto hacia adelante con `?after=` renuncia al hueco.** Pasar `?after=N` mayor que tu cursor actual avanza el cursor más allá de todo lo que llega hasta `N`: los eventos omitidos **nunca se reenvían**. Pedir eventos posteriores a `N` declara consumido todo lo que llega hasta `N`. Esto solo puede ocurrir con un salto hacia adelante hecho a mano, nunca a través de la paginación normal con `next_cursor`.
* **Un salto hacia atrás con `?after=` nunca rebobina.** Un `?after=` obsoleto o más bajo que lee una página más antigua no mueve el cursor hacia atrás, porque el cursor solo avanza.

La lectura pull tiene límite de tasa por tenant. Una lectura denegada devuelve **`429 rate_limited`**; espera (back off) y reintenta. Deduplica los eventos obtenidos por pull según `ceId`, igual que un consumidor de webhook deduplica según `X-Lerian-Event-Id`.

## Próximos pasos

***

<CardGroup cols={2}>
  <Card title="Gestión de suscripciones" icon="gear" href="/es/streaming-hub/managing-subscriptions">
    Crea suscripciones, rota secretos y recupera destinos desactivados.
  </Card>

  <Card title="Operación de Streaming Hub" icon="server" href="/es/streaming-hub/operating-streaming-hub">
    Despliega, configura y observa el hub.
  </Card>
</CardGroup>
