{
  "envelope": {
    "description": "所有 Webhook 使用相同信封。以 `HMAC-SHA256(webhook_secret, timestamp\\nraw_body)` 签名，放入 `X-TKE-Webhook-Signature`（小写 hex）。24 小时轮换窗口内另附 `X-TKE-Webhook-Signature-Old`。至少投递一次，合作伙伴须按 `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`）。绝不以 `threshold` 发送原始 VND 金额。",
      "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 分钟）的变化合并为一个 `changes[]` Webhook，同套餐多次变化仅保留一项。同步数百套餐不会触发数百 Webhook。适用于测试和真实环境。",
      "when": "套餐目录变化：新增、修改、隐藏、基础价格变化或售罄"
    }
  ]
}
