Envía un webhook a todos los servicios que lo necesitan
- 01Cómo encajan orígenes, destinos y conexiones
- 02Filtrar qué eventos llegan a qué destino
- 03Qué supone la distribución para los reintentos, el rendimiento y tu factura
- 04Recuperar un destino sin afectar a los demás
- 05Interpretar la cola mientras se vacía
Tres objetos, y solo tres
Un origen es un endpoint de entrada. Tiene un maskId y una ingestUrl, y esa URL es la única credencial que necesita tu proveedor. Un destino es un lugar al que quieres enviar eventos, con sus propias cabeceras y su propio límite de tasa. Una conexión une un origen con un destino y decide qué eventos hacen el viaje.
La distribución es lo que obtienes cuando un origen tiene más de una conexión. Al origen no le cambia nada y el proveedor nunca se entera.
Conecta un segundo destino
Si ya existe un origen, añadir otro consumidor es un destino y una conexión. Ninguno de los dos toca al proveedor.
- 1
Crea el destino
POST /v1/destinationscon la URL a la que quieres llamar. Se admiten cabeceras personalizadas, y las que marques como sensibles se cifran en reposo y la API nunca las devuelve. - 2
Conéctalo al origen
Una conexión indica la pareja y, opcionalmente, restringe lo que pasa por ella. Omite
filterRules.eventTypespara reenviarlo todo.curlcurl -s "$API/v1/connections" \ -H "authorization: Bearer $SDHK_KEY" \ -H 'content-type: application/json' \ -d '{ "name": "Stripe → billing service", "sourceId": "src_...", "destinationId": "dest_...", "filterRules": { "eventTypes": ["invoice.payment_succeeded"] } }'
- 3
Envía un evento de prueba
Haz un POST a la URL de ingesta del origen. Un origen con verificación de firma configurada necesita una firma válida; sin ella, la solicitud recibe un 401 y se guarda con el estado
rejected. Cuando llegue un evento, deberías ver dos entregas asociadas, una por conexión, cada una con su propio estado.
Filtrar sin un lenguaje de reglas
Una conexión compara el tipo de evento y nada más. El tipo sale del eventTypeField del origen, una ruta con puntos dentro de la solicitud. Los proveedores conocidos fijan un valor por defecto razonable, así que un origen de GitHub lee headers.x-github-event y un origen de Stripe lee el tipo del cuerpo sin que configures nada.
Ese único ajuste cubre más de lo que parece, porque tú eliges la ruta. Apunta un origen a body.action y sus conexiones filtrarán por la acción en lugar de por la cabecera.
# A GitHub source filters on the event header by default.# Point eventTypeField at a body path to filter on something else:curl -s -X PATCH "$API/v1/sources/src_..." \ -H "authorization: Bearer $SDHK_KEY" \ -H 'content-type: application/json' \ -d '{ "data": { "eventTypeField": "body.action" } }' # Connections on that source now match on the action, not the header:# "filterRules": { "eventTypes": ["review_requested"] }
filtered. No lo has perdido. Añade la conexión que faltaba y repítelo.Lo que cuesta distribuir
La unidad facturable es la entrega, es decir, un evento enviado a un destino conectado. Tres conexiones en un evento son tres entregas. El precio sigue al trabajo y no al número de plazas, así que añadir un cuarto consumidor cuesta lo mismo tanto si mira el panel una persona como si lo miran cuarenta.
Cuando un destino se cae
Las entregas fallan de forma independiente. Cada una se reintenta con una espera exponencial que empieza en 5 segundos y se amplía hasta 10 horas entre intentos: tres intentos con un techo de una hora en Hobby y ocho con un techo de 36 horas en los planes de pago, que la curva alcanza tras unas 28 horas. Después, la entrega te espera en lugar de desaparecer. Las demás conexiones del mismo evento no se ven afectadas y ya habrán tenido éxito.
No toda respuesta que no sea 2xx cuenta como fallo, y eso importa cuando lees un registro de intentos a las 3 de la madrugada.
Retry-After hasta una hora y no gasta ningún intento. Como máximo cinco esperas por entrega, y se rechaza antes si la espera fuera a superar el techo de reintentos de tu plan; en ambos casos pasa a la ruta de fallo.Hay dos formas de recuperarse y no son intercambiables. POST /v1/deliveries/{id}/retry reenvía una entrega a un destino y no crea nada nuevo, así que es gratis. POST /v1/events/{id}/replay vuelve a pasar el evento por sus conexiones, lo que genera entregas nuevas y se factura. Usa el reintento cuando falló un solo consumidor, y la repetición cuando añadiste una conexión a posteriori.
rejected no superó la verificación de firma, así que repetirlo enviaría contenido no verificado a tus destinos en nombre de ese origen. Devuelve 400. Una solicitud descartada como duplicada no se guarda, así que tampoco hay nada que repetir.Dosificar no es fallar
Hay dos límites de tasa en la salida: las entregas por segundo de tu plan y el límite que fijes en cada destino. Superar cualquiera de los dos pone la entrega en cola en lugar de hacerla fallar. Su estado pasa a throttled con un throttledReason de rate_limit, no se gasta ningún intento y se envía cuando hay capacidad. El mismo estado con destination_busy es el otro tipo de pausa: la pidió el destino con un 429, 502 o 504.
Esto importa sobre todo mientras un destino se recupera. Un servicio que acaba de volver no recibe toda la acumulación de golpe, y no te avisan de guardia por una cola que se está vaciando correctamente. Una entrega que estuvo retenida alguna vez conserva una marca de tiempo throttledAt incluso después de tener éxito, así que la pausa se ve después.
# Everything that was excluded by a filtercurl -s "$API/v1/events?status=filtered" -H "authorization: Bearer $SDHK_KEY" # What is waiting in the outbound queue right nowcurl -s "$API/v1/deliveries/stats" -H "authorization: Bearer $SDHK_KEY"→ { "pending": 4, "throttled": 412 }
Prácticas que conviene adoptar
- Dale a cada consumidor su propia conexión en lugar de compartir una y ramificar dentro de tu handler. Las conexiones separadas te dan estados de reintento separados y comprobantes separados.
- Fija un límite de tasa en cualquier destino que sepas que es frágil. Poner en cola sale más barato a ambas partes que una ráfaga de fallos seguida de esperas.
- Filtra en la conexión, no en tu handler. Un evento que tu servicio iba a descartar sigue costando una entrega si lo dejas pasar.
- Consulta
?status=filteredcuando un consumidor diga que le faltan eventos. La causa habitual es uneventTypeFieldque apunta a otro sitio del que creías. - Haz que tus handlers sean idempotentes. La repetición existe, la gente la usa durante los incidentes, y tu endpoint debería tratar una segunda copia como algo que no hace nada.
Preguntas
Distribuye un origen a todo lo que lo necesita
Crea un origen, añade tantas conexiones como consumidores tengas y observa cómo se asienta cada una.