Skip to main content
Some webhook types share a single callback URL configured in your merchant dashboard. Use the top-level event field in the JSON body to route each payload to the correct handler.

Money In — transaction_notif_url

SingaPay delivers these money-in events to the same transaction_notif_url:
event valueWebhook documentation
va-transactionVirtual Account Transaction
qris-acquirer-transactionQRIS Acquirer Transaction
payment-link-transactionPayment Link Transaction
ewallet-native-transactionE-Wallet Native Transaction
Subscription Cycle uses a dedicated subscription_cycle_notif_url, not transaction_notif_url.

Routing example

PHP
<?php
$payload = json_decode(file_get_contents('php://input'), true);
$event   = $payload['event'] ?? null;

switch ($event) {
    case 'va-transaction':
        handleVaTransaction($payload['data']);
        break;
    case 'qris-acquirer-transaction':
        handleQrisTransaction($payload['data']);
        break;
    case 'payment-link-transaction':
        handlePaymentLinkTransaction($payload['data']);
        break;
    case 'ewallet-native-transaction':
        handleEwalletNativeTransaction($payload['data']);
        break;
    default:
        // Unknown or future event type — still return 200 after logging
        break;
}

http_response_code(200);
echo json_encode(['status' => 'success']);

Money Out — disbursement_notif_url

SingaPay delivers these money-out events to the same disbursement_notif_url:
event valueWebhook documentation
disbursementDisbursement Transaction
ewallet-topupE-Wallet Top Up Transaction
qris-issuerQRIS Issuer Transaction
You can also distinguish payloads using nested fields such as data.bank, data.ewallet, or data.qr_data when present.

Routing example

PHP
<?php
$payload = json_decode(file_get_contents('php://input'), true);
$event   = $payload['event'] ?? null;

switch ($event) {
    case 'disbursement':
        handleDisbursement($payload['data']);
        break;
    case 'ewallet-topup':
        handleEwalletTopUp($payload['data']);
        break;
    case 'qris-issuer':
        handleQrisIssuer($payload['data']);
        break;
    default:
        break;
}

http_response_code(200);
echo json_encode(['status' => 'success']);

Dedicated URLs (not shared)

Dashboard fieldUsed for
subscription_cycle_notif_urlSubscription Cycle
payment_link_inquiry_notif_urlPayment Link Inquiry
product_expiration_notif_urlProduct Expiration
transaction_expiration_notif_urlTransaction Money-In Expiration
Validate and acknowledge all webhooks using Security and signature validation.