Webhooks
When an order is paid, delivered, updated, or refunded, we send a POST to your configured callback URL. Signing rules are identical to inbound Open API calls.
Three things to remember:
- Only 3
eventTypevalues are exposed (see table below). - Each delivery is a full order snapshot (same shape as Query Order), not a delta patch.
- To see what changed, read
changeHintsfirst, thenorderStatus,deliveryStatus, anditems[].profiles[].
Configure callback URL
Option A: Admin console → API credentials → callback URL
Option B: POST /open-api/v1/webhook/set
{ "callbackUrl": "https://your-server.com/openapi/callback" }Event types
| eventType | When sent |
|---|---|
ORDER_PAID | Balance payment succeeded; fulfillment started |
DELIVERY_UPDATED | Delivery or profile state changed (see common scenarios). Sent only when that order actually changed |
REFUND_COMPLETED | Refund credited (sync or async completion) |
Delivery, install, activation, and usage updates all use
DELIVERY_UPDATED. There is no separateESIM_STATUSorDATA_USAGEevent type—use payload fields andchangeHints.
Recommended handler flow
Receive POST → verify signature → return HTTP 200 quickly → process async
If eventType == DELIVERY_UPDATED:
1. Read changeHints and update UI / local state
2. Replace local order snapshot with full payload
If eventType == ORDER_PAID:
Mark paid; wait for DELIVERY_UPDATED
If eventType == REFUND_COMPLETED:
Mark refund complete; update refund amountIdempotency: the same orderNo + eventType may arrive more than once on retry—dedupe on that pair.
Understanding changeHints
The body is a full order snapshot. changeHints summarizes changes since the last successful delivery for the same orderNo and eventType:
- On the first
DELIVERY_UPDATED(or no prior snapshot), hints are inferred vs an empty baseline (e.g. newiccid→ICCID_READY). - One delivery may include multiple hints.
- Retries reuse the same hints.
Delivery & install
| changeHints | Meaning | Suggested action |
|---|---|---|
ICCID_READY | ICCID appeared for the first time | Show ICCID; guide install |
QR_CODE_READY | QR install URL appeared | Show QR |
ACTIVATION_CODE_READY | SM-DP+ activation code appeared | Show LPA string |
Order & item status
| changeHints | Meaning | Suggested action |
|---|---|---|
DELIVERY_STATUS_CHANGED | deliveryStatus changed | Update delivery progress |
ORDER_STATUS_CHANGED | orderStatus changed | Update order state |
ITEM_STATUS_CHANGED | itemStatus changed | Update line item state |
PROFILE_LIFECYCLE_CHANGED | Profile lifecycleStatus changed | Update card lifecycle |
ESIM_STATUS_CHANGED | Profile esimStatus changed | Optional raw eSIM state |
SMDP_STATUS_CHANGED | Profile smdpStatus changed | Optional install state |
Plan & usage
| changeHints | Meaning | Suggested action |
|---|---|---|
PLAN_USE_STARTED | planUseStatus → STARTED | Mark plan in use |
PLAN_USE_ENDED | planUseStatus → ENDED | Mark plan ended |
USAGE_UPDATED | Usage bytes changed | Refresh usage UI; live usage via profile/usage API |
Refund & other
| changeHints | Meaning | Suggested action |
|---|---|---|
PROFILE_CANCELLED | Profile cancelled → true | Mark card cancelled |
REFUND_AMOUNT_UPDATED | Cumulative refund increased | Update refund amount |
ORDER_PAID | With ORDER_PAID event | Mark paid |
REFUND_COMPLETED | With REFUND_COMPLETED event | Mark refund complete |
ORDER_SNAPSHOT_SYNC | Push with no finer rule | Replace local snapshot from payload |
Status field values
Prefer lifecycleStatus and changeHints for business logic. esimStatus / smdpStatus are channel-specific raw values—use as supplementary only.
deliveryStatus
| Value | Meaning |
|---|---|
PENDING | Not delivered |
PARTIAL | Some profiles ready |
DELIVERED | All profiles ready |
FAILED | Delivery failed |
orderStatus (common)
| Value | Meaning |
|---|---|
PAID | Paid; awaiting fulfillment |
FULFILLING | Fulfilling |
COMPLETED | Completed |
REFUNDING | Refund in progress (usually no separate webhook—poll query) |
REFUNDED | Refunded |
itemStatus
| Value | Meaning |
|---|---|
PENDING | Awaiting codes |
DELIVERED | Codes issued |
ACTIVATING | Activating |
ACTIVATED | Activated |
FAILED | Failed |
REFUNDED | Refunded |
Profile lifecycleStatus (recommended)
| Value | Meaning |
|---|---|
PENDING | Awaiting codes |
DELIVERED | Codes ready (iccid / qr) |
ACTIVATING | Install or activation in progress |
ACTIVATED | Activated |
CANCELLED | Cancelled |
DELIVERY_UPDATED common scenarios
| Scenario | Typical payload changes |
|---|---|
| ICCID / QR / activation code ready | items[].iccid, qrCodeUrl, activationCode; deliveryStatus → PARTIAL / DELIVERED |
| Profile install or eSIM state | items[].profiles[].esimStatus, smdpStatus |
| Plan usage start / end | items[].profiles[].planUseStatus, planUseStartTime, planUseEndTime |
| Usage or validity update | items[].profiles[].dataUsageBytes, remainDataBytes, usagePercent, etc. |
| Activation / lifecycle | items[].profiles[].activated, lifecycleStatus |
| Profile update on refund rejection | items[].profiles[].cancelled; credit via REFUND_COMPLETED |
Events not sent separately
| Scenario | Notes |
|---|---|
Refund in progress (REFUNDING) | Refund API may return REFUNDING; no webhook until credited—poll query or wait for REFUND_COMPLETED |
| Account top-up approved | Account flow; not order webhooks |
| Payment failed at order time | No order row; no webhook |
Delivery policy
- Webhooks are order-scoped; sent only when that order actually changed.
- Supported profile fields may vary by package—use
items[].profiles[]in the payload.
Request format
Headers match inbound Open API (signed with your AppSecret).
ORDER_PAID example
{
"eventType": "ORDER_PAID",
"transactionId": "your-transaction-id",
"orderNo": "ORD20260101001",
"orderId": 123456,
"payStatus": "PAID",
"deliveryStatus": "PENDING",
"orderStatus": "PAID",
"currency": "USD",
"items": [
{
"itemNo": "2075463266738958341",
"packageCode": "PKG001",
"quantity": 1,
"itemStatus": "PAID"
}
]
}DELIVERY_UPDATED example
{
"eventType": "DELIVERY_UPDATED",
"transactionId": "your-transaction-id",
"orderNo": "ORD20260101001",
"orderId": 123456,
"payStatus": "PAID",
"deliveryStatus": "DELIVERED",
"orderStatus": "COMPLETED",
"currency": "USD",
"changeHints": ["ICCID_READY", "DELIVERY_STATUS_CHANGED"],
"items": [
{
"itemNo": "2075463266738958341",
"packageCode": "PKG001",
"quantity": 1,
"itemStatus": "DELIVERED",
"profiles": [
{
"profileId": "2075464693179805698",
"iccid": "8901234567890123456",
"lifecycleStatus": "ACTIVATED",
"planUseStatus": "STARTED"
}
]
}
]
}REFUND_COMPLETED example
{
"eventType": "REFUND_COMPLETED",
"transactionId": "your-transaction-id",
"orderNo": "ORD20260101001",
"orderId": 123456,
"payStatus": "REFUNDED",
"orderStatus": "REFUNDED",
"refundAmount": 3.50,
"currency": "USD",
"items": []
}See WebhookCallbackPayload in OpenAPI Spec.
Retries & integration tips
Failed deliveries retry with backoff (default up to 8 attempts).
- Verify signature before business logic
- Return HTTP 200 quickly; queue heavy work
- Dedupe on
orderNo+eventType; keep full snapshots for reconciliation