クイックスタート
本チュートリアルでは、Webhookを使用してリアルタイムでイベント通知を受信するための基本的な手順を説明します。
前提条件
- 本チュートリアルではイベント通知を受信するサーバーとしてNode.js (推奨: v20以上) を利用します。他の言語を利用する際は、適宜読み替えてください。
- 録音・録画機能のクイックスタート が実施できる程度のSkyWayに関する知識が必要です。
- HTTPSエンドポイントをインターネットに公開できる環境を必要とします。
環境構築
Node.js のバージョン 20 以降をインストールし、任意の作業ディレクトリに tutorial ディレクトリを作成します。
tutorial ディレクトリ直下に以下の内容の package.json ファイルを作成します。
{
"name": "skyway-webhook-demo",
"version": "1.0.0",
"type": "module",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.18.0"
}
}
その後、tutorial ディレクトリでターミナルを開き npm i コマンドを実行してください。
署名用共通鍵の準備
Webhook機能を利用開始するためには署名用共通鍵を用いた署名検証が必要です。署名検証の流れは以下の通りです。

Webhookの署名検証には、暗号学的に安全な乱数署名用共通鍵が必要です。ローカルで安全な鍵を生成してください。
ここでは Node.jsのcryptoを使用します。
ターミナルで、以下のコマンドを実行してください。
# 32バイト(256ビット)のランダム文字列を生成
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"重要:
- 32バイト(64文字)を推奨します
- 推測しやすい文字列(辞書の単語、連続する数字等)は使用しないでください
リクエスト検証用の機能
SkyWay コンソールでの設定時に、サーバーが正しく動作するかを確認するための検証リクエストが送信されます。まず、この検証に対応する基本的なサーバーを作成します。
server.js ファイルを作成し、以下の内容を記述します。
// server.js
import express from "express";
import { createHmac, timingSafeEqual } from "node:crypto";
const app = express();
app.use(express.json());
// 先ほど生成した署名用共通鍵をここに設定
const SIGNING_SECRET = "";
// 署名検証関数(検証リクエスト用)
function verifySignature(signingSecret, body, timestamp, receivedHex) {
// 1) タイムスタンプ検証(リプレイ防止)
const tsNum = Number(timestamp);
if (!Number.isFinite(tsNum)) return false;
const ageMs = Math.abs(Date.now() - tsNum * 1000);
// クイックスタートでは60秒の許容範囲を設定
if (ageMs > 60 * 1000) {
return false;
}
// 2) HMAC を計算
const payload = `${body}${String(timestamp)}`;
const expectedHex = createHmac("sha256", signingSecret).update(payload).digest("hex");
// 3) 比較(暗号学的に安全な比較)
const expBuf = Buffer.from(expectedHex, "hex");
const recBuf = Buffer.from(receivedHex, "hex");
if (expBuf.length !== recBuf.length) return false;
return timingSafeEqual(expBuf, recBuf);
}
// Webhookエンドポイント
app.post("/webhook", (req, res) => {
const body = JSON.stringify(req.body);
const timestamp = req.headers["x-skyway-request-timestamp"];
const signature = req.headers["x-skyway-signature"];
console.log("Received request:", body);
// 検証リクエスト処理
if (req.body.type === "WEBHOOK_URL_VERIFICATION") {
const challenge = req.body.data?.challenge;
// 必要なパラメータの存在チェック
if (!challenge || !timestamp || !signature) {
console.log("❌ Bad Request: Missing required parameters");
return res.status(400).send("Bad Request: Missing required parameters");
}
// 署名検証を実行
if (verifySignature(SIGNING_SECRET, body, timestamp, signature)) {
console.log("✅ Verification successful");
res.status(200).send(challenge);
return;
} else {
console.log("❌ Verification failed: Invalid signature");
return res.status(400).send("Bad Request: Invalid signature");
}
}
// その他のWebhookリクエスト
// 実装は後述
});
app.listen(3000, () => {
console.log("Webhook server running on port 3000");
});このコードでは、 type: WEBHOOK_URL_VERIFICATION のリクエストが来た際に、署名の検証を行っています。3秒以内にstatus: 200 のレスポンスを返すことで正式なエンドポイントとして登録されます。
サーバーのソースコードは最終的に次のようになります。
// server.js
import express from "express";
import { createHmac, timingSafeEqual } from "node:crypto";
const app = express();
app.use(express.json());
// 先ほど生成した署名用共通鍵をここに設定
const SIGNING_SECRET = "";
// 署名検証関数(検証リクエスト用)
function verifySignature(signingSecret, body, timestamp, receivedHex) {
// 1) タイムスタンプ検証(リプレイ防止)
const tsNum = Number(timestamp);
if (!Number.isFinite(tsNum)) return false;
const ageMs = Math.abs(Date.now() - tsNum * 1000);
// クイックスタートでは60秒の許容範囲を設定
if (ageMs > 60 * 1000) {
return false;
}
// 2) HMAC を計算
const payload = `${body}${String(timestamp)}`;
const expectedHex = createHmac("sha256", signingSecret).update(payload).digest("hex");
// 3) 比較(暗号学的に安全な比較)
const expBuf = Buffer.from(expectedHex, "hex");
const recBuf = Buffer.from(receivedHex, "hex");
if (expBuf.length !== recBuf.length) return false;
return timingSafeEqual(expBuf, recBuf);
}
// Webhookエンドポイント
app.post("/webhook", (req, res) => {
const body = JSON.stringify(req.body);
const timestamp = req.headers["x-skyway-request-timestamp"];
const signature = req.headers["x-skyway-signature"];
console.log("Received request:", body);
// 検証リクエスト処理
if (req.body.type === "WEBHOOK_URL_VERIFICATION") {
const challenge = req.body.data?.challenge;
// 必要なパラメータの存在チェック
if (!challenge || !timestamp || !signature) {
console.log("❌ Bad Request: Missing required parameters");
return res.status(400).send("Bad Request: Missing required parameters");
}
// 署名検証を実行
if (verifySignature(SIGNING_SECRET, body, timestamp, signature)) {
console.log("✅ Verification successful");
res.status(200).send(challenge);
return;
} else {
console.log("❌ Verification failed: Invalid signature");
return res.status(400).send("Bad Request: Invalid signature");
}
}
// 必要なパラメータの存在チェック
if (!timestamp || !signature) {
console.log("❌ Missing signature headers");
return res.status(400).send("Bad Request: Missing signature headers");
}
if (!verifySignature(SIGNING_SECRET, body, timestamp, signature)) {
console.log("❌ Signature verification failed");
return res.status(400).send("Bad Request: Invalid signature");
}
res.status(200).send("OK");
// 任意のイベント処理
console.log("✅ Event received:", req.body);
});
app.listen(3000, () => {
console.log("Webhook server running on port 3000");
});
サーバーの起動と外部公開
次のコマンドで作成したサーバーを起動できます。
npm start
サーバーがポート3000で起動したら、HTTPSエンドポイントとして外部からアクセス可能にする必要があります。サーバーを外部公開し、エンドポイントのURLを確認してください。
SkyWay コンソールでの設定
Webhook設定手順
- SkyWay コンソールにログイン
- 対象アプリケーションのWebhookのセクションを選択
- エンドポイントURL に外部公開したエンドポイントのURL +
/webhookを入力 - 先ほど生成した署名用共通鍵を入力
- 設定ボタンを押す

コンソール上での設定完了時に検証リクエストが送信されます。
-
成功した場合、コンソール上の画面が次のように変わります

-
サーバーログで「✅ Verification successful」が表示されることを確認してください。
-
失敗した場合は、署名用共通鍵やエンドポイントの設定を確認して再設定してください。
Webhookリクエストを受け取る機能
検証が成功したら、実際のWebhookイベントを受信し、署名検証を行う機能を追加します。
イベント処理の追加
先ほど作成した server.js の「// その他のWebhookリクエスト」の部分を以下のコードに置き換えてください。
// その他のWebhookリクエスト
// 必要なパラメータの存在チェック
if (!timestamp || !signature) {
console.log("❌ Missing signature headers");
return res.status(400).send("Bad Request: Missing signature headers");
}
if (!verifySignature(SIGNING_SECRET, body, timestamp, signature)) {
console.log("❌ Signature verification failed");
return res.status(400).send("Bad Request: Invalid signature");
}
res.status(200).send("OK");
// 任意のイベント処理
console.log("✅ Event received:", req.body);動作確認
サーバーを再起動してください。録音・録画のクイックスタート に従って録音・録画処理を開始すると、次のWebhookイベントを受信します。
- typeがRECORDING_FILE_STARTEDかつ data.file.typeがAUDIOのイベント
- typeがRECORDING_FILE_STARTEDかつ data.file.typeがAUDIO_AND_VIDEOのイベント
録音・録画処理を終了すると次のWebhookイベントを受信します。
- typeがRECORDING_FILE_SUCCEEDEDかつ data.file.typeがAUDIOのイベント
- typeがRECORDING_FILE_SUCCEEDEDかつ data.file.typeがAUDIO_AND_VIDEOのイベント