本文へスキップ

ガイド

Webhooks

webhook エンドポイントは、ワークスペースのアラートルールがそこへ送るものを受け取ります。ブロックされたリリース、人の判断を待つ結果、終了したマネージド実行、本番モニターのアラームです。各配信はエンドポイントのシークレットで署名された JSON 本文なので、サーバーは処理する前に、それがあなたのワークスペースから来たことを確認できます。

エンドポイントを追加する

ワークスペースの Integrations ページで、Owner または Admin が名前と、ポート 443 または 8443 上の公開 https URL を指定して署名付き webhook を追加します。するとページに、whsec_ で始まるエンドポイントの署名シークレットが表示されます。そのときにコピーしてください。シークレットは暗号化して保存され、二度と表示されません。置き換えるには、エンドポイントを削除してから追加し直します。

次に Alerts ページで、1 つ以上のルールのチャネルとしてそのエンドポイントを選びます。「Send a test」は oloproof.test の配信を送り、サーバーが返したステータスを表示します。

送られる内容

{
  "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
}

項目には、id、エンジンの判定状態、リリースアクション、ジョブの状態と結果、エンジンが書いたとおりのモニターの水準、しきい値、区間、そしてリンクが含まれます。ケースの入力、出力、期待される回答、ラベルは決して含まれません。1 回の配信に含まれる項目は最大 50 件で、more はそれを超えたイベントの数です。

配信を検証する

すべての配信は、Standard Webhooks の形式で 3 つのヘッダーを持ちます。

  • webhook-id: 配信の id。再試行でも同じです。
  • webhook-timestamp: この試行が送られた時刻。1970 年からの秒数です。
  • webhook-signature: v1, と、シークレットのバイト列(whsec_ の後の部分を base64 デコードしたもの)をキーとする id.timestamp.body の base64 HMAC-SHA256。

受け取ったままの生の本文に対して署名を計算し、定数時間で比較し、あなたの時計から 5 分以上ずれたタイムスタンプは拒否してください。

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

Standard Webhooks のライブラリも同じヘッダーを検証します。

配信

10 秒以内に任意の 2xx ステータスで応答してください。それ以外(リダイレクトを含む)は失敗した試行とみなされ、1、5、30、120、360 分後に再試行され、その後は断念されます。サーバーが処理した後に応答が失われることがあるため、同じ配信が 2 回届くことがあります。webhook-id を使って重複を捨ててください。ルールは 1 つのイベントにつき最大 1 回しか発火しないため、新しい id は新しい配信です。

各試行の前にエンドポイントの名前が改めて解決され、リクエストは公開アドレスにのみ送られます。ループバック、プライベート、リンクローカル、共有、予約済み、クラウドのメタデータのアドレスは拒否され、それらを含む IPv6 形式も同様です。接続は確認されたアドレスに対して行われ、証明書は名前に対して検証されます。

Slack

Slack エンドポイントは incoming webhook の URL です。イベントごとに 1 行の短い英語のメッセージを受け取り、各結果へのリンクが付きます。Slack は署名ヘッダーを確認しませんが、ヘッダーはそれでも送られます。