1. Issuing
PIK
  • Start
    • Getting Started
  • Authentication
    • Authentication Token
      POST
  • Global Account
    • Contacts
      • Create Contact
      • List Contacts
      • Get Contact
      • Count Contacts
    • Virtual Accounts
      • Create Virtual Account
      • List Virtual Accounts
      • Get Virtual Account
    • Transactions
      • List Transactions
      • Get Transaction
    • Account Balance
      • List Account Balances
      • Get Balance by Currency
    • Payout
      • Create Payout
  • Payment Links
    • Payment Links
      • Create Payment Link
      • Update Payment Link
      • Get Payment Link Detail
      • Get Payment Link List
    • Transactions
      • Get Transaction List
  • Issuing
    • Card Products
      • List Card Products
    • Cardholders
      • Create Cardholder
      • List Cardholders
      • Get Cardholder
    • Cards
      • Issue Card
      • List Cards
      • Get Card
      • Create Card Secure Session
      • Adjust Card Limit
      • Freeze Card
      • Unfreeze Card
    • Transactions
      • List Transactions
      • Get Transaction
  • Webhook
    • Global Account
      • Deposit Webhook
      • Payout Webhook
      • Virtual Account Webhook
    • Payment Links
      • Overview
      • Order Collect Out Webhook
      • Customer Payment Webhook
      • Customer Refund Webhook
      • Master Recharge Webhook
      • Web3 Direct Payment Webhook
      • Withdraw Out Webhook
    • Issuing
      • Card Operation Failed
      • Card OTP
      • Card Updated
      • Transaction Completed
      • Transaction Declined
      • Transaction Refunded
      • Transaction Reversed
      • Card Activated
      • Card Failed
  1. Issuing

Transaction Refunded

transaction.refunded — delivered when a refund's funds have been returned to your USD
balance.

When it fires#

A refund produces two events for the same transactionNo, and they mean different things:
EventMeaningMoney moved?
transaction.completedThe refund transaction has postedNot necessarily yet
transaction.refundedThe funds have been returned to the balanceYes
Returning the money is a separate step from recording the refund, and it can lag behind. If it
does not succeed the first time it is retried independently, so the gap between the two events is
occasionally minutes rather than milliseconds.
Do not book both as separate movements. Use transaction.completed to learn that a refund
exists, and this event to learn that the balance has actually changed.

Partial and repeated refunds#

A single purchase can be refunded several times, up to the original amount. Each refund is its own
transaction with its own transactionNo and its own pair of events.
Receiving this event does not mean the original purchase has been refunded in full. It means
this one refund has been paid out. To track how much of a purchase has been refunded so far, sum
the refunds that point at it through originalTransactionNo, or read the original transaction
back from the query API.
A refund is also capped: if the merchant sends a refund larger than the remaining refundable
amount, PIK returns only what is left. The billingAmount in this event is what was actually
returned
, which may be less than the merchant asked for.

Payload#

Envelope#

FieldTypeDescription
eventIdstringUnique per event. Deduplicate on this value
eventTypestringAlways transaction.refunded
versionstringPayload schema version. Additive changes do not bump it
occurredAtstringWhen the funds were returned — not when the refund posted (ISO 8601)
dataobjectThe refund transaction, see below

data#

Structurally identical to the response of GET /api/v1/issuing/transaction/{transactionNo}, and
to the data of transaction.completed for the same transaction. One parser handles all paths.
FieldTypeDescription
transactionNostringPIK transaction number of the refund
originalTransactionNostringThe purchase being refunded
cardNostringPIK card number
cardholderNostringPIK cardholder number
typestringAlways REFUND for this event
statusstringCOMPLETED
transactionAmount / transactionCurrencystringAmount and currency at the merchant
billingAmount / billingCurrencystringAmount actually returned in the card's settlement currency
fxobjectForeign exchange details
feesarrayItemised fee breakdown
totalFeeAmountstringSum of all fee amounts
totalDebitAmountstringbillingAmount + totalFeeAmount
merchantobjectname / mcc / city / country
transactionTime / postedTimestringTransaction and posting times
declineReasonstringnull for this event
See Transaction Completed for the full field reference of fx
and fees[].

Example — partial refund#

{
  "eventId": "evt_01J9X8ZQ4TA1",
  "eventType": "transaction.refunded",
  "version": "1.0",
  "occurredAt": "2026-09-23T06:02:44Z",
  "data": {
    "transactionNo": "TX260923U8V1W3",
    "originalTransactionNo": "TX260811K3M5N1",
    "cardNo": "CD260811X9Y8Z7",
    "cardholderNo": "CH260811A1B2C3",
    "type": "REFUND",
    "status": "COMPLETED",
    "transactionAmount": "4.00",
    "transactionCurrency": "USD",
    "billingAmount": "4.00",
    "billingCurrency": "USD",
    "fx": {
      "isCrossCurrency": false,
      "rate": "1.00000000"
    },
    "fees": [],
    "totalFeeAmount": "0.00",
    "totalDebitAmount": "4.00",
    "merchant": {
      "name": "STEAM PURCHASE",
      "mcc": "5734",
      "city": "Singapore",
      "country": "SG"
    },
    "transactionTime": "2026-09-23T06:02:30Z",
    "postedTime": "2026-09-23T06:02:33Z",
    "declineReason": null
  }
}

Verifying the signature#

Three headers accompany every delivery:
HeaderValue
X-Webhook-EventEvent category, always ISSUING for this event
X-Webhook-Event-TypeThe specific event type, see above
X-Webhook-SignatureHMAC-SHA256 signature, lowercase hex
X-Webhook-Signature = hex_lower( HMAC_SHA256( appSecret, rawBody ) )
The signed content is the raw request body only, with no timestamp and no separator. The signing
key is your appSecret — there is no separate webhook secret.
1.
Sign the raw bytes of the request body, before any JSON parsing.
2.
Compare in constant time.
3.
Nothing time-based is signed, so a captured delivery stays replayable — deduplicating on
eventId is mandatory, not optional
.

Delivery#

Return any 2xx within 10 seconds to acknowledge.
Failed deliveries are retried on a fixed interval: 5 attempts, 5 minutes apart (about
20 minutes in total), after which the event is marked exhausted and never retried again.
This endpoint receives Issuing events only; still return 2xx for event types you do not handle.
Delivery is at least once — deduplicate on eventId.
Ordering is not guaranteed — use occurredAt to decide which version is newer. In particular,
do not assume this event arrives after the matching transaction.completed.
Treat transactionNo as the business key; a transaction generates several events.
See the Webhooks guide for verification code samples and recommended
handling.
Modified at 2026-09-30 09:27:01
Previous
Transaction Declined
Next
Transaction Reversed
Built with