{
  "envelope": {
    "description": "全Webhookは同じ形式です。`HMAC-SHA256(webhook_secret, timestamp\\nraw_body)`を小文字hexで`X-TKE-Webhook-Signature`に設定します。24時間のローテーション中は`X-TKE-Webhook-Signature-Old`も付けます。少なくとも1回配信のため、`event_id`で重複排除してください。",
    "fields": {
      "data": "種類別のペイロード（下記参照）。",
      "event_id": "イベントごとに固定の一意なUUID（重複排除用）。",
      "event_type": "イベントの種類。",
      "is_testmode": "テストキーによる生成時はtrue。",
      "occurred_at": "ISO-8601 UTC."
    }
  },
  "health_check": {
    "data_fields": [
      "message"
    ],
    "event_type": "ping",
    "note": "`events`で登録・解除できず（`ping`指定は`internal.validation_failed`）、全エンドポイントが受信します。実際と同じ形式と署名（`X-TKE-Webhook-Signature`、`-Old`なし）で検証を試せます。不明な`event_type`にも2xxを返してください。4xxは作成・更新を`webhook.health_check_failed`（422）で失敗させ、保存しません。`ping`は`GET /v1/webhooks/deliveries`には記録されません。",
    "when": "`POST /v1/webhooks`と`PUT /v1/webhooks/{id}`の毎回、保存前に接続確認"
  },
  "items": [
    {
      "data_fields": [
        "order_id",
        "external_ref",
        "items[] (order_item_id, iccid)"
      ],
      "event_type": "order.completed",
      "note": "",
      "when": "注文確定、eSIMの準備完了"
    },
    {
      "data_fields": [
        "order_id",
        "external_ref",
        "reason"
      ],
      "event_type": "order.failed",
      "note": "",
      "when": "注文未完了（開通処理失敗など）"
    },
    {
      "data_fields": [
        "order_item_id",
        "order_id",
        "external_ref",
        "iccid",
        "lpa"
      ],
      "event_type": "esim.delivered",
      "note": "`order_id`と`external_ref`で照合します。`lpa`はPOST直前に復号し、配信記録に平文保存しません。QR画像URLは送らないため、`lpa`から生成するか`GET /v1/esims/{id}`で`qr_code_base64`を取得してください。",
      "when": "eSIMを発行し、インストール情報を用意した時"
    },
    {
      "data_fields": [
        "order_item_id",
        "order_id",
        "external_ref",
        "iccid",
        "activated_at"
      ],
      "event_type": "esim.activated",
      "note": "`order_id`と注文時の`external_ref`でeSIMを自社注文と照合してください。",
      "when": "eSIMのインストール・開通時（smdp enabled/in_use）"
    },
    {
      "data_fields": [
        "order_item_id",
        "order_id",
        "external_ref",
        "threshold (75/90)",
        "used_bytes",
        "data_bytes"
      ],
      "event_type": "esim.usage_threshold",
      "note": "`order_id`と`external_ref`で注文と照合します。新たに超えた最高値だけ送信します（0→95%なら90のみ）。`threshold`は少なくともこの値を超えた意味です。",
      "when": "使用量が75%／90%に到達"
    },
    {
      "data_fields": [
        "order_item_id",
        "order_id",
        "external_ref",
        "days_remaining (3/1)",
        "expires_at"
      ],
      "event_type": "esim.expiring",
      "note": "`order_id`と注文時の`external_ref`でeSIMを自社注文と照合してください。",
      "when": "期限の3日前／1日前"
    },
    {
      "data_fields": [
        "threshold (25/10)",
        "balance",
        "currency"
      ],
      "event_type": "wallet.low_balance",
      "note": "`threshold`は百分率（25/10）で、実値がある時だけ含めます。不明なら省略するため`balance`を自分で比較してください。VND金額を`threshold`に入れません。",
      "when": "ウォレットが25%／10%の警告基準に到達"
    },
    {
      "data_fields": [
        "changes[] (package_code, change: price|availability|package, occurred_at)",
        "change_count"
      ],
      "event_type": "catalog.updated",
      "note": "組織ごとに価格が異なるため価格は含みません。`GET /v1/catalog/packages`で取得してください。短時間（既定10分）の変更を1つの`changes[]`にまとめ、同一プランは1件に集約します。数百件の同期でも数百のWebhookは発生しません。テストと本番の両方に適用します。",
      "when": "カタログ変更：追加、編集、非表示、基本価格変更、在庫切れ"
    }
  ]
}
