Webhooks
Events and payloads
Register one HTTPS URL per environment in the portal and we POST a signed JSON envelope whenever something happens to your orders or eSIMs.
The envelope
http
POST https://your-app.example.com/hooks/journeystack
Content-Type: application/json
X-JourneyStack-Event: order.completed
X-JourneyStack-Timestamp: 1768383061
X-JourneyStack-Signature: sha256=6f1c…
{
"id": "evt_9a3c7f1e0b2d4c6a8e5f1b3d",
"event": "order.completed",
"environment": "live",
"created_at": "2026-01-14T09:31:03.402Z",
"data": { "order": { "id": "b1f0a3c9-…", "status": "completed", "esims": [ … ] } }
}Events
| Event | Fires when | Payload |
|---|---|---|
| order.completed | Every eSIM on an order was issued successfully. | data.order — the full order object, eSIMs included. |
| order.failed | Issuing failed and your balance was refunded. | data.order — the order with status: "failed" and an error message. |
| esim.activated | The traveller installed the profile and it attached to a network. | data.esim — the eSIM object. |
| esim.usage_alert | A fixed plan crossed its usage threshold. | data.esim plus data.used_bytes and data.total_bytes. |
| esim.expired | Validity ran out or the bundle was fully consumed. | data.esim — the eSIM object. |
| wallet.low_balance | After an order, when the remaining balance is under three times that order's total. | data.balance and data.currency. |
Choosing events
By default you receive every event. You can narrow the list per environment in the portal — an empty selection means all events. Sandbox and live have separate URLs and separate secrets, so a test never reaches your production handler.
Webhooks are a notification, not a source of truth
Order completion is already in the response to your API call. Use webhooks for things you cannot see synchronously — activation, usage alerts, expiry and low balance — and always confirm against the API before acting on money.