Vai al contenuto

Guide

Webhooks

Un endpoint webhook riceve ciò che le regole di allerta di uno spazio di lavoro gli inoltrano: un rilascio bloccato, un risultato che attende una persona, un'esecuzione gestita terminata, l'allarme di un monitor di produzione. Ogni invio è un corpo JSON firmato con il segreto dell'endpoint, così il tuo server può verificare che proviene dal tuo spazio di lavoro prima di agire.

Aggiungere un endpoint

Nella pagina Integrazioni dello spazio di lavoro, un Owner o un Admin aggiunge un webhook firmato con un nome e un URL https pubblico sulla porta 443 o 8443. La pagina mostra allora il segreto di firma dell'endpoint, che inizia con whsec_. Copialo subito: viene conservato cifrato e non viene più mostrato. Per sostituirlo, elimina l'endpoint e aggiungilo di nuovo.

Poi, nella pagina Allerte, scegli l'endpoint come canale di una o più regole. «Invia una prova» pubblica un invio oloproof.test e mostra lo stato con cui ha risposto il tuo server.

Che cosa viene inviato

{
  "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 elemento contiene id, gli stati di decisione del motore, l'azione di rilascio, lo stato e l'esito di un job, il livello, la soglia e l'intervallo di un monitor così come il motore li ha scritti, e link. Non contiene mai l'input, l'output, la risposta attesa o le etichette di un caso. Un invio contiene al massimo 50 elementi; more conta gli eventi oltre quelli.

Verificare un invio

Ogni invio porta tre intestazioni, nella forma di Standard Webhooks:

  • webhook-id: l'id dell'invio, lo stesso a ogni nuovo tentativo.
  • webhook-timestamp: quando è stato inviato questo tentativo, in secondi dal 1970.
  • webhook-signature: v1, e l'HMAC-SHA256 in base64 di id.timestamp.body, con i byte del segreto come chiave (la parte dopo whsec_, decodificata da base64).

Calcola la firma sul corpo grezzo così come è stato ricevuto, confrontala in tempo costante e rifiuta un timestamp che dista più di cinque minuti dal tuo orologio.

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));
  });
}

Le librerie di Standard Webhooks verificano le stesse intestazioni.

Consegna

Rispondi con un qualsiasi stato 2xx entro dieci secondi. Qualsiasi altra risposta, compreso un reindirizzamento, è un tentativo fallito, ripetuto dopo 1, 5, 30, 120 e 360 minuti e poi abbandonato. Poiché una risposta può perdersi dopo che il tuo server ha agito, lo stesso invio può arrivare due volte: usa webhook-id per scartare un duplicato. Una regola scatta al massimo una volta per evento, quindi un nuovo id è un nuovo invio.

Prima di ogni tentativo il nome dell'endpoint viene risolto di nuovo, e la richiesta va solo a un indirizzo pubblico: gli indirizzi di loopback, privati, link-local, condivisi, riservati e di metadati cloud vengono rifiutati, così come le forme IPv6 che ne contengono uno. La connessione viene fatta verso l'indirizzo verificato, e il certificato viene verificato rispetto al nome.

Slack

Un endpoint Slack è un URL di incoming webhook. Riceve un breve messaggio in inglese, una riga per evento, con un link a ciascun risultato. Slack non verifica le intestazioni di firma, che vengono comunque inviate.