API ReferenceTopups

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
done

Top-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 reference
  • FAILED → failure email with the customer-safe failure_reason
  • CANCELLED → 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:

EventTrigger
topup.deliveredOrder moved to DELIVERED. Final.
topup.failedOrder moved to FAILED. Wallet auto-refunded. Final. failure_code / failure_reason / is_user_fixable are in data.
topup.cancelledOrder 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.

On this page