テーマ
Alert Webhook API
外部のセンサー・IoT機器・監視サービスから、SISAへアラートを送るためのWebhook API(/alert)です。
このエンドポイントは安全に関わる通知の入り口という位置づけのため、利用料金は常に無料です (公開APIの月間コール数上限やAPIオプションの対象外・回数を気にせず呼び出せます)。プロジェクト・インシデントなどを操作する有料のExternal APIとは別のAPIです。
エンドポイント
POST https://api.sisa.jp/alert認証
リクエストヘッダーに、対象の監視ユニット(SU)ごとに発行されるWebhook API Keyを指定します。
Authorization: Bearer <SUのWebhook API Key>Webhook API Keyは監視ユニットの作成時に自動で発行され、consoleの監視ユニット詳細画面で確認できます。キーが不正・未指定の場合は 401 Unauthorized({"code": "UNAUTHORIZED", "message": "..."})が返ります。
署名検証
監視ユニットは、作成した時点で署名検証が有効になっています。有効な監視ユニットには、送信内容の改ざんやなりすましを防ぐため、以下のヘッダーが必須です。
有効・無効は、コンソールの監視ユニット詳細画面の「Webhook署名」で確認できます。コンソールからは変更できず、無効にできるのはExternal APIで監視ユニットを作成・更新する場合だけです。
X-Sisa-Signature-256: sha256=<署名>署名は、リクエストボディの生バイト列に対する HMAC-SHA256 を、Webhook API Keyを鍵として計算した16進文字列です。
signature = HMAC-SHA256(key = Webhook API Key, message = リクエストボディの生バイト列)実装上の注意
JSONは一度だけシリアライズし、その同じ文字列(バイト列)を署名の計算にも送信にも使ってください。HTTPクライアントやライブラリが送信直前に本文を再エンコードすると、送った内容と署名の対象がずれて検証に失敗することがあります。下のサンプルコードもこの順序を守っています。
署名検証を有効にしている監視ユニットに対して、ヘッダーが無い・不正な場合も 401 Unauthorized になります。
署名ヘッダーを付け忘れたとき
署名検証が有効なのに X-Sisa-Signature-256 ヘッダーが無い場合は、code が WEBHOOK_SIGNATURE_MISSING の 401 が返ります(鍵は合っています。足りないのは署名です)。鍵が違う場合や署名の値が一致しない場合は、従来どおり code: "UNAUTHORIZED" で、どちらが違うかは返しません。
リクエストボディ
| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
situation_unit_id | string | ○ | アラートの対象となる監視ユニットのID |
level | string | ○ | 深刻度。low / medium / high / critical のいずれか |
title | string | ○ | アラートのタイトル(1〜255文字) |
description | string | ○ | アラートの詳細説明(1〜1000文字) |
レスポンス
| フィールド | 型 | 説明 |
|---|---|---|
alert_event_id | string | null | 作成されたアラートイベントのID(ae_で始まる文字列)。クールダウンで抑制された場合はnull |
status | string | created(作成された)または skipped_cooldown(下記参照) |
auth_status.api_key | string | 認証結果(verified) |
auth_status.hmac_signature | string | 署名の検証結果。verified / missing / invalid / skipped |
warnings | array | 警告(通常は空配列) |
レスポンス例
json
{
"alert_event_id": "ae_01a0933c8f1e7d2b9c4a5e6f7a8b9c0d",
"status": "created",
"auth_status": {
"api_key": "verified",
"hmac_signature": "verified"
},
"warnings": []
}クールダウン(連続アラートの抑制)
同じ監視ユニットから短時間に何度もアラートが送られた場合、直前の通知から一定時間(既定60秒)以内のリクエストは新しいアラートイベントを作らず、status: "skipped_cooldown" を返します。この場合も HTTPステータスは200で、エラーとしては扱われません。連続送信自体を止める必要はありません。
利用制限
/alert は公開APIの月間コール数上限や、APIオプションのレート制限の対象外です。安全に関わる通知を止めないための設計です。呼び出し回数を気にせず、検知したタイミングでそのまま送信してください。
SDKについて
SISAはJavaScript・Python・Go・Rust・C++・Arduino(ESP32/ESP8266)向けの公式クライアントSDKを用意しています (現在OSS公開の準備中です)。SDKを使うと、ここまでで説明した「JSONを1回だけシリアライズし、同じ文字列を署名と送信の両方に使う」という手順を意識せず、SDKが内部で正しく処理してくれます。
SDKの公開までは、以下のサンプルコードを参考に直接HTTPリクエストを組み立ててください。どの言語でも考え方は同じです。
- リクエストボディをJSON文字列として1回だけ組み立てる
- その文字列(バイト列)に対してHMAC-SHA256で署名を計算する(署名検証を有効にしている場合)
- 同じ文字列をそのままリクエストボディとして送信する
cURL
bash
curl -X POST https://api.sisa.jp/alert \
-H "Authorization: Bearer <SUのWebhook API Key>" \
-H "Content-Type: application/json" \
-d '{
"situation_unit_id": "su_xxxxxxxxxxxx",
"level": "high",
"title": "温度異常を検知",
"description": "現場の気温がしきい値を超えました"
}'Node.js(標準ライブラリのみ)
javascript
const crypto = require("node:crypto");
const apiKey = "<SUのWebhook API Key>";
const body = JSON.stringify({
situation_unit_id: "su_xxxxxxxxxxxx",
level: "high",
title: "温度異常を検知",
description: "現場の気温がしきい値を超えました",
});
// 署名検証を有効にしている場合のみ必要
const signature = crypto.createHmac("sha256", apiKey).update(body).digest("hex");
const res = await fetch("https://api.sisa.jp/alert", {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
"X-Sisa-Signature-256": `sha256=${signature}`,
},
body, // JSON.stringify した文字列をそのまま送る(再エンコードしない)
});
console.log(await res.json());Python(標準ライブラリのみ)
python
import hmac
import hashlib
import json
import urllib.request
api_key = "<SUのWebhook API Key>"
body = json.dumps({
"situation_unit_id": "su_xxxxxxxxxxxx",
"level": "high",
"title": "温度異常を検知",
"description": "現場の気温がしきい値を超えました",
}).encode("utf-8")
# 署名検証を有効にしている場合のみ必要
signature = hmac.new(api_key.encode("utf-8"), body, hashlib.sha256).hexdigest()
req = urllib.request.Request(
"https://api.sisa.jp/alert",
data=body, # json.dumps した同じバイト列をそのまま送る
method="POST",
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
"X-Sisa-Signature-256": f"sha256={signature}",
},
)
with urllib.request.urlopen(req) as res:
print(res.read())