Tổng Kho eSIM
Webhooks sortants · 8 événements

Webhooks

Enregistrez l’URL via POST /v1/webhooks et modifiez-la via PUT /v1/webhooks/{id}. CHECK_HEALTH exige 2xx à la création. Les événements sont signés avec un secret distinct du secret API.

REST reste la source de vérité. Si un événement manque, GET /v1/orders?updated_since=… permet de reconstituer l’état. Prévoyez ce rapprochement.

webhook-events.fr.json

Catalogue d’événements

event_typeDéclenchementChamps principaux
order.completedCommande confirmée, eSIM prêtes
order_idexternal_refitems[] (order_item_id, iccid)
order.failedCommande non terminée (échec de fourniture, etc.)
order_idexternal_refreason
esim.deliveredUne eSIM a été fournie avec ses données d'installation

Rattachez via `order_id` et `external_ref`. `lpa` est déchiffré juste avant POST, jamais stocké en clair dans les envois. Aucune URL d'image QR : générez depuis `lpa` ou lisez `qr_code_base64` avec `GET /v1/esims/{id}`.

order_item_idorder_idexternal_reficcidlpa
esim.activatedL'eSIM a été installée et activée (smdp enabled/in_use)

Utilisez `order_id` et `external_ref` (votre référence envoyée à l'achat) pour rattacher l'eSIM à votre commande.

order_item_idorder_idexternal_reficcidactivated_at
esim.usage_thresholdUtilisation ayant atteint le seuil de 75 % / 90 %

Rattachez l'eSIM via `order_id` et `external_ref`. Seul le plus haut seuil nouvellement franchi est émis (0→95 % émet seulement 90). `threshold` signifie au moins ce seuil dépassé.

order_item_idorder_idexternal_refthreshold (75/90)used_bytesdata_bytes
esim.expiring3 jours / 1 jour avant expiration

Utilisez `order_id` et `external_ref` (votre référence envoyée à l'achat) pour rattacher l'eSIM à votre commande.

order_item_idorder_idexternal_refdays_remaining (3/1)expires_at
wallet.low_balanceLe portefeuille a atteint le seuil d'alerte de 25 % / 10 %

`threshold` est un pourcentage (25/10), inclus seulement si connu, sinon absent (comparez `balance`). Jamais de montant VND brut dans `threshold`.

threshold (25/10)balancecurrency
catalog.updatedCatalogue modifié : forfait ajouté, modifié, masqué, changement de prix de base ou rupture de stock

Aucun prix inclus car les prix varient selon l'organisation. Consultez `GET /v1/catalog/packages`. Les changements d'une fenêtre courte (10 minutes par défaut) sont regroupés dans un seul webhook `changes[]`, une fois par forfait. Une synchronisation de centaines de forfaits n'envoie pas des centaines de webhooks. Modes test et réel.

changes[] (package_code, change: price|availability|package, occurred_at)change_count
json
{
  "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"
  }
}

Enveloppe et headers de signature

Tous les webhooks partagent une enveloppe. Signature `HMAC-SHA256(webhook_secret, timestamp\nraw_body)` dans `X-TKE-Webhook-Signature` (hex minuscule). Pendant la rotation de 24 h, ajout de `X-TKE-Webhook-Signature-Old`. Livraison au moins une fois ; dédupliquez par `event_id`.

json
{
  "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"
      }
    ]
  }
}
ChampDescription
dataContenu selon le type d'événement (voir ci-dessous).
event_idUUID stable, unique par événement (déduplication).
event_typeType d'événement.
is_testmodetrue si généré par une clé de test.
occurred_atISO-8601 UTC.

Synchronisation du catalogue : catalog.updated

Émis sur ajout, modification, masquage, prix de base ou stock. Les changements sont regroupés dans changes[] : des centaines de mises à jour donnent un seul envoi par endpoint.

json
{
  "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
  }
}
Aucun prix : c’est volontaire

Les prix sont propres à l’organisation . Un prix unique serait faux et divulguerait des informations. L’événement est un signal : appelez GET /v1/catalog/packages?market=… pour actualiser vos prix.

change accepte trois valeurs : price (coût modifié), availability (stock disponible/épuisé), package (ajout/modification/masquage). Une valeur inconnue doit aussi lancer la synchronisation.

Signature, tentatives et déduplication par event_id suivent les événements habituels, en test et réel, séparés par is_testmode.

Vérifier la signature

X-TKE-Signature contient HMAC-SHA256 calculé avec webhook_secret et les octets bruts du corps.

text
signing_string = timestamp + "\n" + raw_body   # timestamp = header X-TKE-Webhook-Timestamp
signature      = HMAC_SHA256(webhook_secret, signing_string)   → hex lowercase

# Vérifiez la fraîcheur (±300 s) contre le rejeu.
# Signez les octets reçus, sans resérialiser le JSON.

Politique de nouvelles tentatives

1 phút→5 phút→30 phút→2 giờ→6 giờ

Échec non-2xx ou délai de 5 s dépassé : nouvelles tentatives exponentielles sur 24 giờ : 1 phút → 5 phút → 30 phút → 2 giờ → 6 giờ.

4 règles essentielles des webhooks

1. Traitement asynchrone et réponse 200 immédiate
Mettez le message en file et retournez 200 en moins d’une seconde. Évitez le traitement lourd : le délai maximal est de 5 secondes.
2. Idempotence et déduplication
Livraison au moins une fois : dédupliquez avec event_id ou data.order_id / data.order_item_id.
3. Séparer réel et test
La configuration est propre à chaque clé. Séparez les URL pour éviter les données test dans la base réelle.
4. Rapprochement REST périodique
Une longue panne peut perdre des événements. Interrogez périodiquement GET /v1/orders?updated_since=… pour les retrouver.

Notes sur les champs

esim.delivered.lpa est déchiffré avant POST, sans stockage en clair. Créez le QR depuis LPA ou GET /v1/esims/{id}.

esim.usage_threshold: émet uniquement le seuil maximal dépassé (0→95 % : threshold=90).

wallet.low_balance.threshold est un pourcentage (25/10), pas un montant VND.

catalog.updated ne contient ni prix ni stock détaillé : code forfait et type de changement uniquement.