ドキュメント / 送信 Webhook

送信 Webhook

送信 Webhook とは、ホスティングアカウントに何かが起きたときに、パネルがお客様のアドレスへ送るメッセージです。 これにより、ご自身の請求システム、チャットチャンネル、スプレッドシート、プロビジョニングスクリプトなど、 別のシステムが反応できます。

Webhook を使えるのは 管理者 と リセラー です。管理者はすべてのアカウントについて通知を受け取ります。 リセラーが受け取るのは、そのリセラーに属するアカウントについてだけです。顧客と API トークンは作成できません。 Webhook はパネル自体で、本人のセッション内で設定します。これは、漏えいしたトークンがパネルのイベントを 恒久的に受け取り続ける手段にならないようにするためです。

追加する

アカウントのセキュリティページ(管理者: セキュリティ、リセラー: ログインセキュリティ)を開き、 API トークンの下にある Webhook を探します。

  1. アドレスを入力します。例: https://example.com/hooks/panel
  2. 受け取りたいイベントにチェックを入れます。
  3. Webhook を追加 を押します。

パネルは 署名用シークレット(whsec_ で始まります)を 1 回だけ 表示します。今すぐコピーしてください。 暗号化して保管されており、再表示できません。紛失した場合は、その Webhook を削除して新しく追加してください。

1 人あたり最大 10 件の Webhook を持てます。

アドレス

  • https:// で始まる必要があります。呼び出しにはアカウントの詳細が含まれるため、プレーンな http:// は拒否されます。
  • ユーザー名やパスワードを含めることはできません。代わりに署名を確認してください。
  • 最大 500 文字です。
  • 公開インターネット上にある必要があります。このサーバー上、プライベートネットワーク上、ループバック インターフェース上、またはクラウドプロバイダーのメタデータ範囲にあるアドレスは、アドレスを保存するときと、 パネルが接続する時点の両方で拒否されます(そのため、あとからホスト名がプライベートアドレスを指すように なっても動作しません)。
  • リダイレクトには従いません。最終的なアドレスを指定してください。

イベント

イベント 送信されるタイミング
account.created ホスティングアカウントが作成されたとき
account.suspended アカウントが停止されたとき
account.unsuspended 停止されていたアカウントが再開されたとき
account.deleted アカウントが削除されたとき
account.activity アカウントでその他のアクティビティがあったとき

各 Webhook の テスト ボタンは ping というイベントを送信するので、アドレスが応答することを確認できます。 同時に待機できるテストは 1 件だけです。

送信される内容

各呼び出しは、JSON 本文を持つ HTTP POST です。

{
  "event": "account.created",
  "at": "2026-10-23T08:15:00Z",
  "account": { "id": 42, "username": "example", "domain": "example.com" },
  "detail": "short text about what happened"
}

at は UTC の時刻です。ping イベントの形式は {"event":"ping","at":"…","webhook_id":7} です。

すべての呼び出しには、次のヘッダーが付きます。

ヘッダー 意味
X-Nimbopanel-Event イベント名。例: account.created
X-Nimbopanel-Delivery この配信を識別する番号。同じ配信の再試行では同じ番号が保たれるので、重複の無視に使います
X-Nimbopanel-Timestamp 呼び出しに署名した時刻(Unix 秒)
X-Nimbopanel-Signature sha256= に続く署名(下記)
Content-Type application/json
User-Agent Nimbopanel-Webhook/1

署名を確認する

お客様のアドレスには、誰でもリクエストを送れます。呼び出しがお客様のパネルから来たものだと確認する手段が、 署名です。

署名は、タイムスタンプ、ドット、そして 受信したままの生の本文 をつなげたものに対する HMAC-SHA256 の 16 進表記で、署名用シークレットを使って計算されます。

X-Nimbopanel-Signature == "sha256=" + hex( HMAC_SHA256( secret, X-Nimbopanel-Timestamp + "." + body ) )

次の順序で確認してください。

  1. 上記の値を、生のリクエスト本文(JSON の解析や整形の前のもの)から計算します。
  2. 定数時間 の比較で、ヘッダーと比べます。
  3. タイムスタンプがお客様の時計から 5 分 以上離れている場合は、呼び出しを拒否します。これにより、 古い呼び出しの再送を防げます。
  4. X-Nimbopanel-Delivery を使って、すでに処理した配信を無視します。

PHP の例:

$body = file_get_contents('php://input');
$ts   = $_SERVER['HTTP_X_NIMBOPANEL_TIMESTAMP'] ?? '';
$sig  = $_SERVER['HTTP_X_NIMBOPANEL_SIGNATURE'] ?? '';
$want = 'sha256=' . hash_hmac('sha256', $ts . '.' . $body, $secret);
if (!hash_equals($want, $sig) || abs(time() - (int) $ts) > 300) {
    http_response_code(401);
    exit;
}

Node.js の例:

const crypto = require('crypto');
function valid(rawBody, headers, secret) {
  const ts = headers['x-nimbopanel-timestamp'] || '';
  const want = 'sha256=' + crypto.createHmac('sha256', secret).update(ts + '.' + rawBody).digest('hex');
  const got = headers['x-nimbopanel-signature'] || '';
  return got.length === want.length
    && crypto.timingSafeEqual(Buffer.from(got), Buffer.from(want))
    && Math.abs(Date.now() / 1000 - Number(ts)) <= 300;
}

応答、再試行、一時停止

  • 10 秒 以内に、任意の 2xx ステータスで応答してください。それ以外は失敗として扱われます。
  • 失敗した呼び出しは、1 分 後、続いて 5 分、30 分、2 時間 後に再試行されます。合計 5 回の 試行で、約 2 時間半にわたり、その後その配信は失敗としてマークされます。
  • 1 つのアドレスへの呼び出しは、古いものから順に 1 件ずつ送られます。再試行待ちの配信が、新しい配信を 止めることはないので、イベントが厳密な順序で届くことを前提にしないでください。遅いアドレスが止めるのは、 そのアドレス自身だけです。
  • 50 回連続 で配信に失敗すると、その Webhook は 一時停止 し、新しいイベントはキューに入らなくなります。 テスト が成功するとカウントがリセットされ、イベントが再び流れます。
  • 1 つの Webhook につき、未配信の呼び出しは最大 200 件まで待機します。それを超えると、いくつかが配信される か失敗するまで、その Webhook への新しいイベントはキューに入りません。
  • パネルは、Webhook ごとに完了した配信の最新 50 件を、受け取った応答とともに保持するので、何が失敗したかを 確認できます。直近のものは Webhook の下に一覧表示されます。

パネルがステータスの代わりに理由を表示した場合は、次のいずれかです。アドレスが公開インターネット上にない、 10 秒以内に応答がない、ホスト名が存在しない、HTTPS 証明書が有効でない、またはパネルが接続できなかった。

良い習慣

  • すぐに 200 で応答し、実際の処理はそのあとで行ってください。受信側が 10 秒より長くかかると失敗に見え、 同じイベントが再度届きます。
  • 同じ配信を 2 回処理しても問題が起きないようにしてください。再試行やネットワークエラーにより、重複が 起こり得ます。
  • シークレットをコードリポジトリに入れないでください。漏えいした場合は、その Webhook を削除して新しく 追加してください。

関連: リセラーガイド · トラブルシューティング