Webhook
Webhook を登録すると、組織で起きたイベント(レッスン完了、修了証の発行、メンバー追加など)を、お客様のサーバーに HTTPS POST で受け取れます。
1. 登録と管理
Webhook は API で登録します。webhooks:manage スコープと、組織管理者の権限が必要です。
| エンドポイント | 説明 |
|---|---|
| POST /api/v1/organizations/{organizationId}/webhooks | 登録(url・eventTypes・description)。署名用のシークレットは、このレスポンスでだけ返ります。 |
| GET /api/v1/organizations/{organizationId}/webhooks | 登録済みの Webhook 一覧 |
| DELETE /api/v1/organizations/{organizationId}/webhooks/{subscriptionId} | 削除 |
| POST /api/v1/organizations/{organizationId}/webhooks/{subscriptionId}/test | ping イベントをテスト送信 |
| GET /api/v1/organizations/{organizationId}/webhooks/{subscriptionId}/deliveries | 配信履歴(ステータス、試行回数、最後の HTTP ステータスとエラー) |
| POST /api/v1/organizations/{organizationId}/webhooks/deliveries/{deliveryId}/replay | 配信をやり直す(試行回数をリセットして再送) |
| GET /api/v1/webhook-events | 購読できるイベントの一覧 |
url は https:// で始まる必要があります。eventTypes を空にすると、すべてのイベントを受け取ります(今後追加されるイベントも含みます)。存在しないイベント名は無視されます。
2. イベント一覧
| イベント | 説明 |
|---|---|
| lesson.completed | 受講者がレッスンを完了した |
| course.started | 受講者がコースを開始した |
| certificate.issued | 修了証が発行された |
| member.added | 組織にメンバーが追加された |
| subscription.activated | 有料プランの契約が有効になった |
| session.started / session.answered / session.hint / session.revealed / session.ended | 演習セッションの開始・回答・ヒント・解答表示・終了 |
| mastery.updated | 受講者の習熟度が更新された |
| session.deleted / learner.data_deleted | 演習セッション、または受講者の学習データが削除された |
| ping | テスト送信 |
コースやレッスンの追加・更新・削除を通知するイベントはありません。
3. リクエストの形式
すべてのイベントは、同じ形の JSON で送られます。data の中身はイベントごとに異なります。
POST https://your-server.example.com/manabu2-webhook
Content-Type: application/json
X-Manabu-Event: lesson.completed
X-Manabu-Delivery: 3f2a... (配信ごとの ID)
X-Manabu-Timestamp: 1767225600 (送信時刻(UNIX 秒))
X-Manabu-Signature: sha256=9c1e...
{
"id": "b81d...",
"type": "lesson.completed",
"createdAt": "2026-10-04T09:00:00+00:00",
"organizationId": "...",
"data": {
"userId": "...",
"lessonId": "...",
"courseId": "..."
}
}
id はイベントごとの ID で、再送しても変わりません。同じ id を 2 回受け取った場合は、2 回目を無視してください。
4. 署名の検証
X-Manabu-Signature は、登録時に返されたシークレットを鍵にした HMAC-SHA256 です。署名の対象は「タイムスタンプ + . + リクエスト本文」で、16 進小文字に sha256= を付けて送られます。
signature = hex( HMAC_SHA256( secret, "{X-Manabu-Timestamp}.{raw request body}" ) )
■ C# での検証例
var secret = "whsec_...";
var body = await new StreamReader(Request.Body).ReadToEndAsync();
var timestamp = Request.Headers["X-Manabu-Timestamp"].ToString();
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
var computed = Convert.ToHexString(
hmac.ComputeHash(Encoding.UTF8.GetBytes($"{timestamp}.{body}"))).ToLowerInvariant();
var received = Request.Headers["X-Manabu-Signature"].ToString().Replace("sha256=", "");
if (!CryptographicOperations.FixedTimeEquals(
Encoding.ASCII.GetBytes(computed), Encoding.ASCII.GetBytes(received)))
{
return Unauthorized();
}
タイムスタンプも署名に含まれるため、古いリクエストの再利用を防ぐには、X-Manabu-Timestamp が現在時刻から大きく離れているリクエストを拒否してください。
5. 応答と再送
- 2xx を 10 秒以内に返すと配信成功です。重い処理は応答の後に行ってください。
- 2xx 以外の応答、タイムアウト、接続エラーの場合は、30 秒・2 分・10 分・30 分・1 時間の間隔で再送します(最初の送信を含めて最大 6 回)。
- 6 回とも失敗した配信は「失敗」として配信履歴に残り、replay で再送できます。
- 再送のため、同じイベントが複数回届くことがあります。順序も保証されません。