Estado · webhooks
Suscribirse →Recibe cada incidente en tu endpoint.
POST JSON firmado en cada cambio. Encaja con tu stack de observabilidad.
Eventos emitidos
Tres tipos cubren el ciclo de vida del incidente.
incident.openedUn servicio pasa a degradado, caído o mantenimiento.incident.updatedNota pública añadida por el equipo durante el incidente.incident.resolvedLos servicios vuelven a estar operativos.
Cabeceras HTTP
Toda solicitud lleva las siguientes cabeceras.
POST /your/endpoint HTTP/1.1
Host: ops.example.com
Content-Type: application/json
User-Agent: Coffrify-Status-Webhook/1.0
X-Coffrify-Event: incident.opened
X-Coffrify-Timestamp: 1717248000
X-Coffrify-Signature: t=1717248000,v1=5b8c…f3a2Forma del payload
Objeto JSON compacto.
{
"type": "incident.opened",
"ts": "1717248000",
"incident": {
"id": "8c6a0c5e-…",
"slug": "eu-west-uploads-degraded",
"title": "Uploads dégradés en région eu-west",
"severity": "major",
"summary": "Latence accrue sur les uploads multipart depuis ~ 14:22 UTC.",
"affected_services": ["uploads", "api"],
"url": "https://status.coffrify.com/incident/eu-west-uploads-degraded"
},
"message": null
}Verificar la firma
HMAC-SHA256 sobre `timestamp.body`. Comparación en tiempo constante. Rechaza si el drift supera 5 min.
Node.js / TypeScript
import { createHmac, timingSafeEqual } from "node:crypto";
export function verify(rawBody: string, header: string, secret: string): boolean {
const m = /t=(\d+),v1=([0-9a-f]+)/.exec(header);
if (!m) return false;
const [, ts, sig] = m;
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const expected = createHmac("sha256", secret).update(`${ts}.${rawBody}`).digest("hex");
const a = Buffer.from(expected, "hex");
const b = Buffer.from(sig, "hex");
return a.length === b.length && timingSafeEqual(a, b);
}Python
import hmac, hashlib, time
def verify(raw_body: bytes, header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
ts, sig = parts.get("t"), parts.get("v1")
if not ts or not sig: return False
if abs(time.time() - int(ts)) > 300: return False
expected = hmac.new(
secret.encode(),
f"{ts}.{raw_body.decode()}".encode(),
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, sig)Canal Slack
Si suscribes una URL de hooks.slack.com, recibes Block Kit:
- Cabecera coloreada por severidad.
- Severidad + estado.
- Resumen + última actualización.
- Botón al incidente.
Entrega y reintento
Se espera 2xx en 6s. Sin reintento automático.
2xx · 6 s · no retry