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}/testping イベントをテスト送信
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 で再送できます。
  • 再送のため、同じイベントが複数回届くことがあります。順序も保証されません。

次: 学習コンテンツを RAG に活用する →