Aller au contenu

Guides

Webhooks

Un point de terminaison webhook reçoit ce que les règles d’alerte d’un espace de travail lui envoient : une publication bloquée, un résultat qui attend une personne, une exécution gérée terminée, l’alarme d’une surveillance de production. Chaque envoi est un corps JSON signé avec le secret du point de terminaison, pour que votre serveur puisse vérifier qu’il vient de votre espace de travail avant d’agir.

Ajouter un point de terminaison

Sur la page Intégrations de l’espace de travail, un Owner ou un Admin ajoute un webhook signé avec un nom et une URL https publique sur le port 443 ou 8443. La page affiche alors le secret de signature du point de terminaison, qui commence par whsec_. Copiez-le à ce moment : il est stocké chiffré et n’est plus jamais affiché. Pour le remplacer, supprimez le point de terminaison et ajoutez-le de nouveau.

Ensuite, sur la page Alertes, choisissez le point de terminaison comme canal d’une ou plusieurs règles. « Envoyer un test » poste un envoi oloproof.test et affiche le statut renvoyé par votre serveur.

Ce qui est envoyé

{
  "type": "oloproof.gate_blocked",
  "id": "dlv_8c1f0a2b3c4d5e6f7a8b9c0d",
  "created_at": "2026-10-26T14:03:00.000Z",
  "workspace": "acme",
  "rule": { "id": "rul_1a2b3c4d5e6f7a8b9c0d1e2f", "name": "Do not ship" },
  "event": "gate_blocked",
  "items": [
    {
      "project": "support",
      "kind": "run",
      "id": "run_20261026T140100_4f2a",
      "release_action": "BLOCK",
      "rules": [["faithfulness", "FAIL"]],
      "href": "https://oloproof.com/app/workspaces/acme/projects/support/runs/run_20261026T140100_4f2a"
    }
  ],
  "more": 0
}

Un élément porte des identifiants, les états de décision du moteur, l’action de publication, l’état et l’issue d’un job, le niveau, le seuil et l’intervalle d’une surveillance tels que le moteur les a écrits, et des liens. Il ne porte jamais l’entrée, la sortie, la réponse attendue ni les étiquettes d’un cas. Un envoi contient au plus 50 éléments ; more compte les événements au-delà.

Vérifier un envoi

Chaque envoi porte trois en-têtes, au format Standard Webhooks :

  • webhook-id : l’identifiant de l’envoi, le même à chaque nouvelle tentative.
  • webhook-timestamp : le moment où cette tentative a été envoyée, en secondes depuis 1970.
  • webhook-signature : v1, suivi du HMAC-SHA256 en base64 de id.timestamp.body, avec pour clé les octets du secret (la partie après whsec_, décodée du base64).

Calculez la signature sur le corps brut tel que reçu, comparez-la en temps constant, et refusez un horodatage à plus de cinq minutes de votre horloge.

import base64, hashlib, hmac, time

def verify(secret: str, headers: dict, body: bytes) -> bool:
    key = base64.b64decode(secret.removeprefix("whsec_"))
    msg = f"{headers['webhook-id']}.{headers['webhook-timestamp']}.".encode() + body
    expected = base64.b64encode(hmac.new(key, msg, hashlib.sha256).digest()).decode()
    fresh = abs(time.time() - int(headers["webhook-timestamp"])) <= 300
    given = [part.split(",", 1)[-1] for part in headers["webhook-signature"].split()]
    return fresh and any(hmac.compare_digest(expected, sig) for sig in given)
import { createHmac, timingSafeEqual } from "node:crypto";

export function verify(secret, headers, body) {
  const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");
  const id = headers["webhook-id"], ts = headers["webhook-timestamp"];
  const expected = createHmac("sha256", key).update(`${id}.${ts}.${body}`).digest("base64");
  const fresh = Math.abs(Date.now() / 1000 - Number(ts)) <= 300;
  return fresh && headers["webhook-signature"].split(" ").some((part) => {
    const sig = Buffer.from(part.split(",").pop());
    return sig.length === expected.length && timingSafeEqual(sig, Buffer.from(expected));
  });
}

Les bibliothèques Standard Webhooks vérifient les mêmes en-têtes.

Livraison

Répondez avec n’importe quel statut 2xx en moins de dix secondes. Toute autre réponse, redirection comprise, est une tentative échouée, renouvelée après 1, 5, 30, 120 et 360 minutes, puis abandonnée. Comme une réponse peut se perdre après que votre serveur a agi, le même envoi peut arriver deux fois : utilisez webhook-id pour écarter un doublon. Une règle se déclenche au plus une fois par événement, donc un nouvel identifiant est un nouvel envoi.

Avant chaque tentative, le nom du point de terminaison est résolu de nouveau, et la requête ne part que vers une adresse publique : les adresses de bouclage, privées, link-local, partagées, réservées et de métadonnées cloud sont refusées, de même que les formes IPv6 qui en contiennent une. La connexion se fait vers l’adresse vérifiée, et le certificat est vérifié par rapport au nom.

Slack

Un point de terminaison Slack est une URL d’incoming webhook. Il reçoit un court message en anglais, une ligne par événement, avec un lien vers chaque résultat. Slack ne vérifie pas les en-têtes de signature, qui sont tout de même envoyés.