Webhooks
How top-up status changes are surfaced - polling, email notifications, and the planned webhook events
Top-up orders fire topup.delivered / topup.failed / topup.cancelled webhooks at terminal status transitions, delivered to any webhook URL subscribed to those events. The envelope, headers, signing, retries, and idempotency are identical to voucher webhooks — see the Webhooks overview.
You can also (or instead) track status by polling or email, described below.
Tracking Top-Up Status Without Webhooks
1. Poll the order endpoint
GET /api/v1/topups/orders/:id refreshes the latest status on each call for in-flight orders. A reasonable poll loop:
# Pseudo-shell: poll every 5s for the first minute, then 30s for a few minutes
# An order that's still PENDING after that is Octopus Cards retrying transient
# fulfilment issues — stop foreground polling and lean on notifications.
delay=5
elapsed=0
while [ "$elapsed" -lt 660 ]; do
status=$(curl -s "$HOST/api/v1/topups/orders/$ORDER_ID" \
-H "Authorization: Bearer $TOKEN" | jq -r '.data.status')
case "$status" in
DELIVERED|FAILED|CANCELLED) echo "Terminal: $status"; break ;;
esac
sleep $delay
elapsed=$((elapsed + delay))
[ "$elapsed" -ge 60 ] && delay=30
doneTop-ups typically resolve within 5–30 seconds. The retry cron handles slower fulfilment paths in the background, so your poll will eventually see a terminal status even if the first attempt hung.
2. Attach a notification email
PATCH /api/v1/topups/orders/:id/notification-email registers an email address that is sent a templated receipt on terminal transitions:
DELIVERED→ success email with the variant name and referenceFAILED→ failure email with the customer-safefailure_reasonCANCELLED→ cancellation email with refund confirmation
Email notifications fire at the same terminal points as webhook events. They are server-driven (no polling needed) but only useful for end-user-facing flows where you actually have a customer email.
Webhook Events
The envelope matches the voucher webhook shape (see Voucher Webhooks for the established pattern):
{
"id": "evt_topup_abc123",
"type": "topup.delivered",
"created_at": "2026-05-15T14:31:12Z",
"data": {
"id": 9123456,
"client_reference": "RECHARGE_REQ_4391",
"status": "DELIVERED",
"product_name": "PUBG Mobile UC",
"variant_name": "325 UC",
"amount": 4.7405,
"currency": "USD",
"completed_at": "2026-05-15T14:31:12Z"
}
}Event types:
| Event | Trigger |
|---|---|
topup.delivered | Order moved to DELIVERED. Final. |
topup.failed | Order moved to FAILED. Wallet auto-refunded. Final. failure_code / failure_reason / is_user_fixable are in data. |
topup.cancelled | Order cancelled administratively. Wallet refunded. Final. |
In-flight sub-statuses will not fire webhooks — events only fire at terminal transitions. This matches the voucher webhook contract.
For shared HMAC verification mechanics, see Signature Verification.