Webhooks
URL mit POST /v1/webhooks registrieren und mit PUT /v1/webhooks/{id} ändern. CHECK_HEALTH muss bei Erstellung 2xx liefern. Ereignisse werden mit einem eigenen, vom API-Schlüssel getrennten Geheimnis signiert.
REST bleibt die maßgebliche Quelle. Fehlende Ereignisse lassen sich mit GET /v1/orders?updated_since=… abgleichen. Planen Sie diesen Abgleich ein.
webhook-events.de.jsonEreigniskatalog
| event_type | Auslöser | Wichtige Datenfelder |
|---|---|---|
order.completed | Bestellung bestätigt, eSIMs bereit | order_idexternal_refitems[] (order_item_id, iccid) |
order.failed | Bestellung nicht abgeschlossen (Bereitstellung fehlgeschlagen usw.) | order_idexternal_refreason |
esim.delivered | Eine eSIM wurde mit Installationsdaten bereitgestellt Zuordnung über `order_id` und `external_ref`. `lpa` wird erst unmittelbar vor POST entschlüsselt und nie im Zustellprotokoll unverschlüsselt gespeichert. Keine QR-Bild-URL: QR aus `lpa` erzeugen oder `qr_code_base64` über `GET /v1/esims/{id}` abrufen. | order_item_idorder_idexternal_reficcidlpa |
esim.activated | Die eSIM wurde installiert und aktiviert (smdp enabled/in_use) Mit `order_id` und `external_ref` (Ihrer beim Kauf gesendeten Referenz) ordnen Sie die eSIM Ihrer Bestellung zu. | order_item_idorder_idexternal_reficcidactivated_at |
esim.usage_threshold | Verbrauch hat 75 % / 90 % erreicht Ordnen Sie die eSIM mit `order_id` und `external_ref` zu. Nur die höchste neu überschrittene Schwelle wird gemeldet (0→95 % sendet nur 90). `threshold` bedeutet mindestens diese Schwelle überschritten. | order_item_idorder_idexternal_refthreshold (75/90)used_bytesdata_bytes |
esim.expiring | 3 Tage / 1 Tag vor Ablauf Mit `order_id` und `external_ref` (Ihrer beim Kauf gesendeten Referenz) ordnen Sie die eSIM Ihrer Bestellung zu. | order_item_idorder_idexternal_refdays_remaining (3/1)expires_at |
wallet.low_balance | Wallet hat die Warnschwelle von 25 % / 10 % erreicht `threshold` ist ein Prozentwert (25/10), nur bei bekannter Prozentzahl enthalten, sonst weggelassen (`balance` selbst vergleichen). Kein VND-Betrag unter `threshold`. | threshold (25/10)balancecurrency |
catalog.updated | Katalog geändert: Paket hinzugefügt, geändert, ausgeblendet, Basispreis geändert oder ausverkauft Keine Preise enthalten, da sie je Organisation variieren. Rufen Sie `GET /v1/catalog/packages` ab. Änderungen im kurzen Zeitfenster (Standard 10 Minuten) werden in einem `changes[]`-Webhook zusammengefasst, je Paket einmal. Hunderte synchronisierte Pakete erzeugen nicht Hunderte Webhooks. Gilt im Test- und Livebetrieb. | changes[] (package_code, change: price|availability|package, occurred_at)change_count |
event_type: "ping"Jeder `POST /v1/webhooks` und `PUT /v1/webhooks/{id}` prüft vor dem Speichern die Erreichbarkeit.
Nicht über `events` an- oder abwählbar (`ping` ergibt dort `internal.validation_failed`); jeder Endpunkt empfängt es. Reale Hülle und Signatur (`X-TKE-Webhook-Signature`, ohne `-Old`) zum Prüfen der Verifikation. Bei unbekanntem `event_type` muss Ihr Handler 2xx liefern: 4xx verhindert das Speichern mit `webhook.health_check_failed` (422). `ping` erscheint nicht in `GET /v1/webhooks/deliveries`.
Diesen Ereignistyp verarbeiten, damit die Registrierung nicht fehlschlägt.
{
"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"
}
}Payload und Signatur-Header
Alle Webhooks nutzen dieselbe Hülle. Signatur `HMAC-SHA256(webhook_secret, timestamp\nraw_body)` in `X-TKE-Webhook-Signature` (Hex-Kleinbuchstaben). Im 24-Stunden-Rotationsfenster zusätzlich `X-TKE-Webhook-Signature-Old`. Mindestens einmal zugestellt; anhand `event_id` deduplizieren.
{
"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"
}
]
}
}| Feld | Beschreibung |
|---|---|
data | Nutzlast je Ereignistyp (siehe unten). |
event_id | Stabile, je Ereignis eindeutige UUID (Deduplizierung). |
event_type | Ereignistyp. |
is_testmode | true bei Erzeugung durch einen Testschlüssel. |
occurred_at | ISO-8601 UTC. |
Katalogabgleich: catalog.updated
Bei neuen, geänderten, verborgenen Tarifen, Basispreis- oder Bestandsänderung. Änderungen stehen gesammelt in changes[] : Hunderte Änderungen ergeben eine einzige Zustellung je Endpunkt.
{
"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
}
} Verkaufspreise sind organisationsabhängig . Ein einheitlicher Preis wäre falsch und vertraulich. Das Ereignis ist ein Auslöser : Rufen Sie GET /v1/catalog/packages?market=… zum Aktualisieren Ihrer Preise auf.
change akzeptiert drei Werte: price (Basispreis geändert), availability (auf Lager/ausverkauft), package (neu/geändert/verborgen). Auch unbekannte Werte als Anlass zum Abgleich behandeln.
Signatur, Wiederholung und Deduplizierung per event_id folgen Standardereignissen, in Test und Live getrennt nach is_testmode.
Webhook-Signaturen prüfen
X-TKE-Signature enthält HMAC-SHA256 aus webhook_secret und den rohen Body-Bytes.
signing_string = timestamp + "\n" + raw_body # timestamp = header X-TKE-Webhook-Timestamp
signature = HMAC_SHA256(webhook_secret, signing_string) → hex lowercase
# Zeitfenster (±300 s) gegen Replay prüfen.
# Exakte empfangene Bytes signieren, JSON nicht erneut serialisieren.Wiederholungsstrategie
Bei Nicht-2xx oder 5 s Timeout: exponentielle Wiederholungen über 24 giờ: 1 phút → 5 phút → 30 phút → 2 giờ → 6 giờ.
4 wichtige Webhook-Regeln
Hinweise zu Feldern
esim.delivered.lpa wird unmittelbar vor POST entschlüsselt, nicht im Klartext gespeichert. QR aus LPA oder GET /v1/esims/{id} erzeugen.
esim.usage_threshold: sendet nur die höchste neu überschrittene Schwelle (0→95 %: threshold=90).
wallet.low_balance.threshold ist ein Prozentsatz (25/10), kein VND-Betrag.
catalog.updated enthält keine Preise oder Bestandsdetails, nur Tarifcode und Änderungstyp.