# Webhooks > Jazui avisa a tu servidor cuando publicas un artículo o salta una alerta de un sitio. Página original: https://docs.jazui.com/api/webhooks/es Un webhook es una dirección de tu servidor a la que Jazui hace un `POST` con un JSON cuando pasa algo. Hay dos por sitio: el del **blog** (artículos publicados, actualizados o retirados) y el de **alertas** (errores de render, caída de Googlebot, bajada de la nota SEO). Los dos se firman con el mismo secreto del sitio. ## Configurarlos En el panel, abre los ajustes del sitio: - **Blog e IndexNow**: dirección del webhook del blog. Con **Volver a renderizar la página del artículo** (activado por defecto), Jazui además actualiza la página del artículo en la caché. - **Alertas**: dirección del webhook de alertas (si tu plan incluye webhooks de alertas). La dirección tiene que ser pública y con `https` o `http`. Al guardar el primer webhook se genera el **secreto** del sitio (32 caracteres), que verás en los ajustes. Con **Probar** (blog) recibes un evento `ping`, y con **Enviar alerta de prueba** (alertas), una alerta de tipo `Test`. ## Cómo llegan Un `POST` con `Content-Type: application/json` y estas cabeceras: - `X-Jazui-Event`: el tipo (`Published`, `Updated`, `Unpublished`, `alert` o `ping`). - `X-Jazui-Delivery`: identificador del envío, el mismo en los reintentos (úsalo para no procesar dos veces el mismo). - `X-Jazui-Signature`: `sha256=` seguido del HMAC-SHA256 del cuerpo con tu secreto, en hexadecimal en minúsculas. ```http POST https://misitio.com/webhooks/jazui Content-Type: application/json X-Jazui-Event: Published X-Jazui-Delivery: 3f0c9a3e-7a51-4f43-9c1e-0b8d2f6a4e11 X-Jazui-Signature: sha256=5d41402abc4b2a76b9719d911017c592ae8b6e1f0a2c4d3b5e7f9a1c3d5e7f90 ``` ## Eventos del blog `event` vale `article.published`, `article.updated` o `article.unpublished`. El mismo artículo con el mismo evento no se envía dos veces en 30 segundos. Úsalo para regenerar tu web estática, avisar a tu equipo o publicar en otros sitios. ```json { "event": "article.published", "site": "misitio.com", "occurredAt": "2026-11-03T00:47:24Z", "article": { "id": "7c1d0e2f-…", "slug": "como-funciona-el-prerender", "url": "https://misitio.com/blog/como-funciona-el-prerender", "language": "es", "title": "Cómo funciona el prerender" } } ``` ## Alertas `alert.type` vale `RenderErrors` (errores de render en 24 h), `GoogleDrop` (caída de las visitas de Googlebot frente a la semana anterior), `SeoDrop` (bajada de la nota SEO en 7 días) o `Test`. `value` es lo medido y `threshold` el umbral que configuraste. ```json { "event": "site.alert", "site": "misitio.com", "occurredAt": "2026-11-03T09:00:00Z", "alert": { "type": "SeoDrop", "message": "La nota SEO media de misitio.com ha bajado 12 puntos en 7 días.", "value": 12, "threshold": 10, "detail": "90 → 78" } } ``` ## Prueba El botón de prueba del webhook del blog envía: ```json { "event": "ping", "site": "misitio.com", "occurredAt": "2026-11-03T09:00:00Z" } ``` ## Comprobar la firma Calcula el HMAC-SHA256 del **cuerpo tal como llega** (sin volver a serializarlo) con tu secreto y compáralo con la cabecera en tiempo constante. Si no coincide, responde `401` y no lo proceses. `Node.js (Express)`: ```javascript import crypto from 'node:crypto'; import express from 'express'; const app = express(); app.post('/webhooks/jazui', express.raw({ type: 'application/json' }), (req, res) => { const expected = 'sha256=' + crypto.createHmac('sha256', process.env.JAZUI_WEBHOOK_SECRET) .update(req.body).digest('hex'); const received = req.get('X-Jazui-Signature') || ''; if (expected.length !== received.length || !crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received))) { return res.sendStatus(401); } const event = JSON.parse(req.body.toString('utf8')); // event.event: article.published, article.updated, article.unpublished, site.alert o ping res.sendStatus(204); }); ``` `C# (ASP.NET Core)`: ```csharp app.MapPost("/webhooks/jazui", async (HttpRequest request, IConfiguration config) => { using var reader = new StreamReader(request.Body, Encoding.UTF8); var body = await reader.ReadToEndAsync(); var key = Encoding.UTF8.GetBytes(config["Jazui:WebhookSecret"]!); var expected = "sha256=" + Convert.ToHexString(HMACSHA256.HashData(key, Encoding.UTF8.GetBytes(body))).ToLowerInvariant(); var received = request.Headers["X-Jazui-Signature"].ToString(); if (!CryptographicOperations.FixedTimeEquals(Encoding.UTF8.GetBytes(expected), Encoding.UTF8.GetBytes(received))) return Results.Unauthorized(); using var json = JsonDocument.Parse(body); var type = json.RootElement.GetProperty("event").GetString(); return Results.NoContent(); }); ``` ## Respuestas y reintentos - Cualquier respuesta por debajo de `500` se da por entregada: responde `2xx` en cuanto lo recibas y haz el trabajo después. - Con un `5xx` o sin respuesta, Jazui lo reintenta a los 2 y a los 10 segundos (tres intentos en total). - Cada intento espera como mucho 15 segundos y sigue hasta 3 redirecciones.