Webhook
POST /v1/webhooksでURLを登録し、PUT /v1/webhooks/{id}で更新します。登録時はCHECK_HEALTHに2xxを返す必要があります。APIキーとは別の専用シークレットで署名します。
正しい状態の基準はRESTです。イベントを取り逃してもGET /v1/orders?updated_since=…で照合できるよう設計してください。
webhook-events.ja.jsonイベント一覧
| event_type | 発生条件 | 主要データ項目 |
|---|---|---|
order.completed | 注文確定、eSIMの準備完了 | order_idexternal_refitems[] (order_item_id, iccid) |
order.failed | 注文未完了(開通処理失敗など) | order_idexternal_refreason |
esim.delivered | eSIMを発行し、インストール情報を用意した時 `order_id`と`external_ref`で照合します。`lpa`はPOST直前に復号し、配信記録に平文保存しません。QR画像URLは送らないため、`lpa`から生成するか`GET /v1/esims/{id}`で`qr_code_base64`を取得してください。 | order_item_idorder_idexternal_reficcidlpa |
esim.activated | eSIMのインストール・開通時(smdp enabled/in_use) `order_id`と注文時の`external_ref`でeSIMを自社注文と照合してください。 | order_item_idorder_idexternal_reficcidactivated_at |
esim.usage_threshold | 使用量が75%/90%に到達 `order_id`と`external_ref`で注文と照合します。新たに超えた最高値だけ送信します(0→95%なら90のみ)。`threshold`は少なくともこの値を超えた意味です。 | order_item_idorder_idexternal_refthreshold (75/90)used_bytesdata_bytes |
esim.expiring | 期限の3日前/1日前 `order_id`と注文時の`external_ref`でeSIMを自社注文と照合してください。 | order_item_idorder_idexternal_refdays_remaining (3/1)expires_at |
wallet.low_balance | ウォレットが25%/10%の警告基準に到達 `threshold`は百分率(25/10)で、実値がある時だけ含めます。不明なら省略するため`balance`を自分で比較してください。VND金額を`threshold`に入れません。 | threshold (25/10)balancecurrency |
catalog.updated | カタログ変更:追加、編集、非表示、基本価格変更、在庫切れ 組織ごとに価格が異なるため価格は含みません。`GET /v1/catalog/packages`で取得してください。短時間(既定10分)の変更を1つの`changes[]`にまとめ、同一プランは1件に集約します。数百件の同期でも数百のWebhookは発生しません。テストと本番の両方に適用します。 | changes[] (package_code, change: price|availability|package, occurred_at)change_count |
event_type: "ping"`POST /v1/webhooks`と`PUT /v1/webhooks/{id}`の毎回、保存前に接続確認.
`events`で登録・解除できず(`ping`指定は`internal.validation_failed`)、全エンドポイントが受信します。実際と同じ形式と署名(`X-TKE-Webhook-Signature`、`-Old`なし)で検証を試せます。不明な`event_type`にも2xxを返してください。4xxは作成・更新を`webhook.health_check_failed`(422)で失敗させ、保存しません。`ping`は`GET /v1/webhooks/deliveries`には記録されません。
登録に失敗しないよう、このイベントにも対応してください。
{
"event_id": "018f3c00-0000-7000-8000-0000000000c1",
"event_type": "ping",
"occurred_at": "2026-07-11T07:29:00Z",
"is_testmode": true,
"data": {
"message": "TongKho eSIM webhook health check"
}
}ペイロードと署名ヘッダー
全Webhookは同じ形式です。`HMAC-SHA256(webhook_secret, timestamp\nraw_body)`を小文字hexで`X-TKE-Webhook-Signature`に設定します。24時間のローテーション中は`X-TKE-Webhook-Signature-Old`も付けます。少なくとも1回配信のため、`event_id`で重複排除してください。
{
"event_id": "018f3c00-0000-7000-8000-0000000000b0",
"event_type": "order.completed",
"occurred_at": "2026-07-11T07:30:00Z",
"is_testmode": false,
"data": {
"order_id": "018f2b00-0000-7000-8000-00000000009a",
"external_ref": "partner-cart-9931",
"items": [
{
"order_item_id": "018f2b00-0000-7000-8000-000000000001",
"iccid": "8984012345678901234"
}
]
}
}| フィールド | 説明 |
|---|---|
data | 種類別のペイロード(下記参照)。 |
event_id | イベントごとに固定の一意なUUID(重複排除用)。 |
event_type | イベントの種類。 |
is_testmode | テストキーによる生成時はtrue。 |
occurred_at | ISO-8601 UTC. |
カタログ同期: catalog.updated
プランの追加・変更・非表示、原価・在庫の変更時に発生し、変更をまとめた配列 changes[] で送ります。多数の更新でも 1回の 配信にまとめます。
{
"event_id": "018f3c00-0000-7000-8000-0000000000c1",
"event_type": "catalog.updated",
"occurred_at": "2026-07-11T08:10:00Z",
"is_testmode": false,
"data": {
"changes": [
{
"package_code": "THAY-BANG-MA-GOI-CUA-BAN",
"change": "price",
"occurred_at": "2026-07-11T08:02:11Z"
},
{
"package_code": "VN_10GB_30D",
"change": "availability",
"occurred_at": "2026-07-11T08:04:52Z"
},
{
"package_code": "JP_3GB_7D",
"change": "package",
"occurred_at": "2026-07-11T08:09:30Z"
}
],
"change_count": 3
}
} 販売価格は 組織ごとに異なります 。共通価格の配信は不正確で情報漏洩にもなるため、このイベントは 同期のきっかけです :次を呼び出し GET /v1/catalog/packages?market=… 価格を更新してください。
change は3つの値を取ります: price (原価変更)、 availability (在庫あり・なし)、 package (新規・更新・非表示)。未知の値も同期のきっかけとして扱ってください。
署名・再試行・重複排除のキー event_id は通常イベントと同じです。テストと本番は次で分離します: is_testmode.
Webhook署名を検証
X-TKE-Signatureはwebhook_secretと本文の生バイトを使うHMAC-SHA256です。
signing_string = timestamp + "\n" + raw_body # timestamp = header X-TKE-Webhook-Timestamp
signature = HMAC_SHA256(webhook_secret, signing_string) → hex lowercase
# リプレイ防止に時刻差±300秒を確認。
# 受信した正確なバイトに署名し、JSONを再シリアライズしない。再試行ポリシー
2xx以外の応答や5秒のタイムアウト時は24 giờの間、指数的に再試行します:1 phút → 5 phút → 30 phút → 2 giờ → 6 giờ。
Webhookの4つの指針
各フィールドの注意点
esim.delivered.lpa はPOST直前に復号し平文保存しません。LPAやGET /v1/esims/{id}からQRを取得してください。
esim.usage_threshold: は新たに超えた最大閾値だけ送ります(0→95%ではthreshold=90)。
wallet.low_balance.threshold は割合(25/10)で、VNDの金額ではありません。
catalog.updated は価格や在庫詳細を含まず、プランコードと変更種別だけです。