
# Changelog の Webhook を登録する

`POST https://api.keenauth.com/v1/accounts/{account_id}/changelog-subscriptions` · アカウントの所有者または管理者

選択したカテゴリのいずれかに属する [Changelog](/docs/changelog) のエントリが公開されるたびに `changelog.entry_published` Webhook を受け取る、お客様自身の HTTPS エンドポイントを登録します。このリクエストは `keenauth login` の認証情報で行います。アカウントは、お客様がサインインしたアカウントである必要があり、お客様はその所有者または管理者である必要があります。1 つのアカウントで有効にできる購読は最大 5 件です。

購読ごとに専用の署名シークレットがあり、レスポンスで一度だけ返されます。配信は Standard Webhooks に従います。すべてのリクエストには `webhook-id`、`webhook-timestamp`、`webhook-signature` ヘッダーが含まれ、署名（`v1,<base64>`）は `<webhook-id>.<webhook-timestamp>.<body>` に対する HMAC-SHA256 です。HMAC キーは、`idp_whsec_` プレフィックスの後にある 43 文字がエンコードする 32 バイトです（base62、ビッグエンディアン）。末尾の 6 文字はチェックサムであり、キーの一部ではありません。本文を信頼する前に、署名を検証し、現在時刻から離れすぎているタイムスタンプを拒否してください（Standard Webhooks のライブラリでは 5 分まで許容されます）。

URL に個人データ、認証情報、トークンを含めないでください。URL は購読情報とともに保存され、アカウントのすべての所有者と管理者に表示されます。

## リクエスト

ヘッダー:

| ヘッダー | 必須 | 説明 |
| --- | --- | --- |
| `Authorization` | はい | `DPoP <access token>`: サインイン時に CLI が受け取ったアクセストークンです。 |
| `DPoP` | はい | このリクエストの DPoP 証明（`htm` `POST`、`htu` はこのリクエストの URL）。アクセストークンに紐づく鍵で署名します。アクセストークンのハッシュとサーバー nonce を含みます。 |
| `Content-Type` | はい | `application/json`。 |
| `Idempotency-Key` | いいえ | `A-Z`、`a-z`、`0-9`、`_`、`-` からなる 16〜64 文字。同じキーと本文で 24 時間以内にリクエストを繰り返すと、シークレットを含まない同じ購読が `secret_redacted: true` と `Idempotent-Replayed: true` ヘッダーとともに返されます。 |

本文:

| フィールド | 必須 | 説明 |
| --- | --- | --- |
| `url` | はい | エンドポイント: `https`、ポート 443、ホスト名（IP アドレスや `.localhost`、`.local`、`.internal` などローカルのみで有効な名前は不可）。ユーザー情報やフラグメントを含めることはできず、2048 バイト以内です。 |
| `categories` | いいえ | `api`、`sdk`、`deprecation`、`security`、`incident` のいずれかを、それぞれ最大 1 回指定します。フィールドを省略するとすべてのカテゴリを受け取ります。空の配列は拒否されます。 |

## レスポンス

`201 Created` を `Cache-Control: no-store` とともに返します:

| フィールド | 説明 |
| --- | --- |
| `id` | 購読（`chlg_…`）。 |
| `account_id` | アカウント（`acct_…`）。 |
| `url` | エンドポイント。 |
| `categories` | 受け取るカテゴリ。順序は `api`、`sdk`、`deprecation`、`security`、`incident` です。 |
| `status` | `enabled`。配信が失敗し続けた場合は `disabled`。 |
| `created_at` | 購読が作成された日時（RFC 3339、UTC）。 |
| `signing_secret` | 署名シークレット（`idp_whsec_…`）。このレスポンスでのみ返されるため、シークレットマネージャーに保存してください。 |
| `secret_redacted` | 冪等な再送時の `true`。`signing_secret` は含まれません。 |

## エラー

| ステータス | コード | 条件 |
| --- | --- | --- |
| 400 | `malformed_request` | 本文が JSON ではない、または `Idempotency-Key` ヘッダーが重複している、空である、または形式が不正です。 |
| 400 | `unknown_field` | 本文に `url` と `categories` 以外のフィールドが含まれています。 |
| 401 | `unauthenticated` | アクセストークンが送信されていません。 |
| 401 | `invalid_token` | アクセストークンが無効、期限切れ、または CLI のものではありません。`keenauth login` で再度サインインしてください。 |
| 401 | `invalid_dpop_proof` | DPoP 証明が存在しない、重複している、またはリクエストやトークンの鍵と一致しません。 |
| 401 | `use_dpop_nonce` | `DPoP-Nonce` レスポンスヘッダーの nonce を含む証明で再試行してください。 |
| 401 | `insufficient_user_authentication` | 多要素認証（または所有者および管理者の場合はパスキーまたはセキュリティキー）が必要です。再度サインインしてください。 |
| 403 | `insufficient_permissions` | お客様はこのアカウントの所有者または管理者ではありません。 |
| 403 | `account_restricted` | アカウントのステータスでは変更が許可されていません。 |
| 403 | `quota_exceeded` | アカウントに有効な購読がすでに 5 件あります（`errors[].code` `changelog_subscription_limit_exceeded`）。 |
| 404 | `not_found` | アカウントが存在しない、お客様がサインインしたアカウントではない、またはお客様がそのメンバーではありません。 |
| 413 | `payload_too_large` | 本文が 8 KiB を超えています。 |
| 415 | `unsupported_media_type` | 本文が `application/json` ではありません。 |
| 422 | `validation_failed` | `url` が存在しないか無効、カテゴリが不明または重複している、`categories` が空、またはフィールドの型が誤っています。 |
| 422 | `idempotency_key_reuse` | `Idempotency-Key` が 24 時間以内に別の本文で使用されました。 |
| 429 | `rate_limited` | 直近 1 時間にこのアカウントで 10 件を超える購読が作成されました。`Retry-After` 待ってください。 |

## Webhook の配信

エントリが公開されると、KeenAuth は、カテゴリにエントリのいずれかのカテゴリが含まれる有効な購読ごとに、URL へ `POST` を 1 件送信します。同じエントリが 1 つの購読に 2 回送信されることはありません。

### リクエスト

| ヘッダー | 説明 |
| --- | --- |
| `Content-Type` | `application/json`。 |
| `webhook-id` | イベントの `id`（`evt_…`）。同じエントリの再送では毎回同じ値になるため、重複を無視する際に使用してください。 |
| `webhook-timestamp` | この試行に署名した時刻（Unix 時間、秒）。試行ごとに新しい値になります。 |
| `webhook-signature` | `v1,<base64>`: 上で説明した HMAC-SHA256。対象は `<webhook-id>.<webhook-timestamp>.<body>` です。 |

本文は最大 16 KiB です。KeenAuth はリダイレクトに従いません。

### イベント

```json
{
  "id": "evt_01jb0000000000000000000000",
  "type": "changelog.entry_published",
  "schema_version": 1,
  "occurred_at": "2026-11-04T00:00:00Z",
  "recorded_at": "2026-11-04T08:30:12Z",
  "data": {
    "id": "2026-11-04-example",
    "url": "https://docs.keenauth.com/en/changelog/#2026-11-04-example",
    "title": "Example entry",
    "date_published": "2026-11-04T00:00:00Z",
    "tags": ["deprecation"],
    "categories": ["api", "deprecation"],
    "surfaces": [{"kind": "api", "name": "management-v1", "version": "v1"}],
    "breaking": false,
    "sunset_at": "2027-11-04"
  }
}
```

| フィールド | 説明 |
| --- | --- |
| `id` | イベント（`evt_…`）。`webhook-id` ヘッダーと同じ値です。 |
| `type` | 常に `changelog.entry_published`。 |
| `schema_version` | 常に `1`。バージョン 1 の範囲内で `data` に任意フィールドが追加されることがあります。未知のフィールドは無視してください。 |
| `occurred_at` | エントリが公開された日時（RFC 3339）。 |
| `recorded_at` | KeenAuth が配信をキューに入れた日時（RFC 3339）。 |
| `data.id`、`data.url`、`data.title`、`data.date_published` | Changelog フィードと同じエントリ。 |
| `data.tags` | `added`、`changed`、`fixed`、`removed`、`deprecation`、`security`、`breaking`、`incident` のうち 1 つ以上。 |
| `data.categories` | エントリが属する購読カテゴリ。 |
| `data.surfaces` | 影響を受ける対象: `kind`（`api`、`sdk`、`cli`、`provider`、`mcp`、`docs` のいずれか）、`name`、判明している場合は `version`。 |
| `data.breaking` | 変更が互換性を損なう場合は `true`。 |
| `data.sunset_at` | 非推奨機能の削除予定日（`YYYY-MM-DD`）。それ以外は `null`。 |

機械可読なスキーマは `changelog.entry_published.v1` です。

### レスポンスと再試行

15 秒以内に `2xx` のステータスで応答してください。レスポンス本文は無視されます。それ以外の結果はすべて失敗した試行とみなされ、再試行されます。接続または TLS エラー、タイムアウト、リダイレクト、`429`、`5xx`、および `410` 以外のすべてのステータスが該当します。最初の試行はすぐに行われます。その後の試行は、前回の失敗からそれぞれ約 1 分、5 分、30 分、2 時間、8 時間、24 時間、36 時間後に行われます（各間隔は最大 10 パーセント変動します）。合計で約 3 日間に 8 回試行します。

8 回目の試行も失敗した場合、配信は中止され、購読は無効になります（`status` `disabled`）。その購読向けにすでにキューに入っている配信はキャンセルされます。

### 410 Gone

配信を希望しなくなったことを KeenAuth に伝えるには、`410 Gone` で応答してください。購読は直ちに無効になり、それ以上の試行は行われず、キューに入っている配信はキャンセルされます。無効になった購読は再び有効になることはなく、5 件の上限にも含まれません。エントリを再び受け取るには、新しい購読を作成してください。無効化はすべて、アカウントの監査ログに `changelog.subscription_disabled` として記録され、理由は `receiver_gone`、`permanent_failure`、`schedule_exhausted` のいずれかです。

## 例

```bash
curl -X POST https://api.keenauth.com/v1/accounts/acct_01jb0000000000000000000000/changelog-subscriptions \
  -H "Authorization: DPoP $ACCESS_TOKEN" \
  -H "DPoP: $DPOP_PROOF" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: changelog-hooks-2026-10" \
  -d '{"url": "https://hooks.example.com/keenauth/changelog", "categories": ["deprecation", "security"]}'
```
