Webhooks firmados
Recibí cada alerta como un POST JSON en tu propia API y verificá que viene de ganyant.
Formato
POST /hooks/ganyant HTTP/1.1
Content-Type: application/json
X-Ganyant-Signature: 4f1c…e9a2
{
"event": "incident.opened",
"title": "🔴 Caído: API de pagos",
"data": { "monitor": "API de pagos", "target": "https://api.example.com/health", "monitor_id": "…" },
"sent_at": 1794761152
}Eventos
incident.opened | Un monitor cayó (confirmado) |
incident.resolved | El monitor se recuperó |
incident.escalated | Nadie acusó recibo y se escaló la guardia |
ssl.expiring | Certificado por vencer |
security.new_findings | Hallazgos nuevos en la auditoría |
disk.high | Disco sobre el umbral |
intrusion | Fuerza bruta u otra señal de intrusión |
backup.failed | Falló un backup |
test | Prueba desde el panel |
Verificar la firma
La cabecera X-Ganyant-Signature es el HMAC-SHA256 en hexadecimal del cuerpo exacto recibido, con el secreto propio de ese canal. Lo ves en Panel → Alertas y guardias, en “Secreto de firma” debajo del webhook. Cada canal tiene un secreto distinto.
Node.js
import crypto from 'node:crypto';
export function verify(rawBody, signature, secret) {
const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(signature, 'hex'));
}Python
import hmac, hashlib
def verify(raw_body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)Rechazá además los eventos cuyo sent_at tenga más de 5 minutos de antigüedad para evitar reenvíos.
Respuestas y reintentos
Respondé con un código 2xx en menos de 15 segundos. Cualquier otra respuesta se reintenta con espera exponencial hasta 6 veces.