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 Completed

transaction.completed — delivered when a transaction that was not declined has posted: a
purchase, ATM withdrawal, refund, reversal, verification, settlement adjustment, or a card issuance
fee.

When it fires#

Whenever a transaction settles against one of your cards. Because card spending happens outside
your control, this webhook is the primary way you learn about transactions — the query API
is for reconciliation and backfill, not for discovery.
Refunds and reversals carry originalTransactionNo pointing at the transaction being reversed.
They are followed by a second event, transaction.refunded / transaction.reversed, once the funds
have actually been returned to your USD balance — do not book both as separate movements.
For PURCHASE, ATM_WITHDRAWAL and CARD_ISSUE, totalDebitAmount is the amount debited from
your USD balance for this transaction. For refunds and reversals it is the amount being returned,
not a debit; for other types no fee applies and it equals billingAmount.

Payload#

Envelope#

FieldTypeDescription
eventIdstringUnique per event. Deduplicate on this value
eventTypestringAlways transaction.completed
versionstringPayload schema version. Additive changes do not bump it
occurredAtstringWhen the event happened, not when it was delivered (ISO 8601)
dataobjectThe transaction, see below

data#

Structurally identical to the response of GET /api/v1/issuing/transaction/{transactionNo}, so
a single parser handles both paths.
FieldTypeDescription
transactionNostringPIK transaction number
cardNostringPIK card number
cardholderNostringPIK cardholder number
typestringPURCHASE / REFUND / REVERSAL / ATM_WITHDRAWAL / VERIFICATION / SETTLEMENT_ADJUST / CARD_ISSUE. CARD_ISSUE is the record for a card issuance fee
statusstringUsually COMPLETED. data reflects the transaction's current state when the event is delivered, so this can be REVERSED if the transaction has already been reversed by then — for example when a posting update arrives after the reversal
originalTransactionNostringSet on refunds and reversals, otherwise null
transactionAmountstringAmount in the merchant's currency. null on CARD_ISSUE records
transactionCurrencystringMerchant currency, ISO 4217. null on CARD_ISSUE records
billingAmountstringAmount posted in the card's settlement currency
billingCurrencystringSettlement currency, ISO 4217
fxobjectForeign exchange details, see below
feesarrayItemised fee breakdown, see below
totalFeeAmountstringSum of all fee amounts
totalDebitAmountstringbillingAmount + totalFeeAmount
merchantobjectname / mcc / city / country. null on CARD_ISSUE records and when no merchant details were supplied
transactionTimestringTime the transaction occurred
postedTimestringTime the transaction was posted
declineReasonstringnull for this event

fx#

FieldTypeDescription
isCrossCurrencybooleanWhether the transaction currency differs from the billing currency
ratestringRate applied to your account, billing currency per unit of transaction currency. 1 when both are the same currency. Not the raw interbank rate

fees[]#

FieldTypeDescription
feeCodestringCARD_TRANSACTION_FEE / CARD_ATM_FEE / CARD_FX_CONVERSION_FEE / CARD_ISSUE_FEE. ATM withdrawals are charged CARD_ATM_FEE only, never both
feeNamestringHuman-readable fee name
amountstringFee amount
currencystringFee currency
baseAmountstringThe amount the fee was calculated on, equal to billingAmount. null for fixed fees
calcModestringPERCENT / FIXED
ratestringRate applied in PERCENT mode, as a decimal (0.015 means 1.5%)
fixedAmountstringFixed amount applied in FIXED mode
minFee / maxFeestringBounds applied, null when unbounded
ruleNostringIdentifies the exact pricing rule applied, for reconciliation. The parameters above are a snapshot taken when the transaction was processed
What baseAmount is calculated on. Percentage fees are calculated on billingAmount —
the amount charged to you in the card's settlement currency, before any fees. So baseAmount
equals billingAmount on every percentage-based item. Fixed fees such as CARD_ISSUE_FEE
have no base and leave it null.

Example — purchase#

{
  "eventId": "evt_01J9X8ZQ4T3P",
  "eventType": "transaction.completed",
  "version": "1.0",
  "occurredAt": "2026-08-11T02:20:18Z",
  "data": {
    "transactionNo": "TX260811K3M5N1",
    "cardNo": "CD260811X9Y8Z7",
    "cardholderNo": "CH260811A1B2C3",
    "type": "PURCHASE",
    "status": "COMPLETED",
    "originalTransactionNo": null,
    "transactionAmount": "40.00",
    "transactionCurrency": "USD",
    "billingAmount": "40.00",
    "billingCurrency": "USD",
    "fx": {
      "isCrossCurrency": false,
      "rate": "1.00000000"
    },
    "fees": [
      {
        "feeCode": "CARD_TRANSACTION_FEE",
        "feeName": "Transaction fee",
        "amount": "0.60",
        "currency": "USD",
        "baseAmount": "40.00",
        "calcMode": "PERCENT",
        "rate": "0.015",
        "fixedAmount": null,
        "minFee": "0.50",
        "maxFee": null,
        "ruleNo": "FR20260814I01"
      }
    ],
    "totalFeeAmount": "0.60",
    "totalDebitAmount": "40.60",
    "merchant": {
      "name": "STEAM PURCHASE",
      "mcc": "5734",
      "city": "Singapore",
      "country": "SG"
    },
    "transactionTime": "2026-08-11T02:20:15Z",
    "postedTime": "2026-08-11T02:20:18Z",
    "declineReason": null
  }
}

Example — refund#

{
  "eventId": "evt_01J9X8ZQ4T4R",
  "eventType": "transaction.completed",
  "version": "1.0",
  "occurredAt": "2026-08-12T01:02:11Z",
  "data": {
    "transactionNo": "TX260812P7Q2R8",
    "cardNo": "CD260811X9Y8Z7",
    "cardholderNo": "CH260811A1B2C3",
    "type": "REFUND",
    "status": "COMPLETED",
    "originalTransactionNo": "TX260811K3M5N1",
    "transactionAmount": "40.00",
    "transactionCurrency": "USD",
    "billingAmount": "40.00",
    "billingCurrency": "USD",
    "fx": {
      "isCrossCurrency": false,
      "rate": "1.00000000"
    },
    "fees": [],
    "totalFeeAmount": "0.00",
    "totalDebitAmount": "40.00",
    "merchant": {
      "name": "STEAM PURCHASE",
      "mcc": "5734",
      "city": "Singapore",
      "country": "SG"
    },
    "transactionTime": "2026-08-12T01:02:08Z",
    "postedTime": "2026-08-12T01:02:11Z",
    "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.
Treat transactionNo as the business key; a transaction can generate several events.
See the Webhooks guide for verification code samples and recommended
handling.
Modified at 2026-09-30 09:26:59
Previous
Card Updated
Next
Transaction Declined
Built with