Skip to main content
Accounting Operation — This webhook fires on settlement lifecycle events (completion, refund, refund cancellation). It reports money movements between your pending_balance, available_balance, and your bank account.

Information

SingaPay sends a POST request to your configured settlement_notif_url whenever a settlement batch reaches a final state, or when a settled transaction is refunded / has its refund cancelled.
This webhook may share a callback URL with other event types. Always route by the event value in the body. See Shared webhook endpoints.

Events

This single webhook covers three settlement events, distinguished by the event field:
Settlement currently supports money-in transactions only (Virtual Account, QRIS, Payment Link, E-Wallet). Refund events apply only to settlements using the balance or auto-balance method.

Request Details

Headers

The Authorization token for settlement webhooks is a randomly generated string — not a user access token. Settlement events are triggered by an admin approval, a scheduler, or a refund action (system events), not a user request. Extract the token as-is and use it in the string to sign. See Security and signature validation.

Body Parameters — common envelope

Every settlement webhook shares the same top-level envelope:
number
required
HTTP Status Code. Always 200.
boolean
required
Indicates a successful event. Always true.
string
required
Event type identifier. One of "settlement.completed", "settlement.refunded", "settlement.refund_cancelled".
string
required
Event timestamp in format "d M Y H:i:s". Example: "18 Jun 2026 10:00:00"
object
required
Container for the settlement details (and, for refund events, the refund details).

data.settlement (present on all events)

object
required
The settlement batch the event relates to.

data.total_transactions (settlement.completed only)

integer
required
Number of transactions included in the completed settlement batch. Example: 5

data.refund (settlement.refunded & settlement.refund_cancelled only)

object
required
Details of the individual transaction that was refunded or had its refund cancelled.

Payload Examples

settlement.completed
settlement.completed (bank-account)
settlement.refunded
settlement.refund_cancelled

Security and responses

Return HTTP 200 promptly after validating the request. For retry behavior, see Webhook retry mechanism. Verify every webhook using Security and signature validation. Use your configured settlement_notif_url path when building StringToSign. Handle duplicate deliveries idempotently using stable identifiers from the payload — for settlement.completed use data.settlement.reference_no; for refund events combine data.settlement.reference_no with data.refund.settlement_detail_id (and the event).

Settlement Specific Notes

All three events are delivered to the same settlement_notif_url. Always branch on the event field. A settlement.completed payload has total_transactions; refund events have a refund object instead.
is_auto_created = false means a sequential settlement approved by Finance (approved_by is the approver’s name). is_auto_created = true means a parallel auto settlement created by the scheduler (approved_by = "SYSTEM"). Both arrive as settlement.completed.
amount is the total net amount of the settled transactions. total_to_transfer is what was actually moved out — amount - settlement_fee. The settlement_fee is only non-zero for the bank-account method.
settlement.refunded and settlement.refund_cancelled fire only for settlements using the balance or auto-balance method. Refund deducts (and cancel restores) the transaction’s net amount from your available_balance — fees are not refunded.
Track refund state with is_refunded and is_refund_cancelled. An active refund is is_refunded=true, is_refund_cancelled=false. A cancelled refund is is_refunded=true, is_refund_cancelled=true — note is_refunded stays true.
transfer_status and the recipient.* fields are populated only for the bank-account method and are null for all other methods. Actor fields (refunded_by, refund_cancelled_by, actor) and account_id may be null. Handle null gracefully.
All timestamps (timestamp, approved_at, start_date, end_date, refunded_at, refund_cancelled_at) use the format "d M Y H:i:s" (e.g. "18 Jun 2026 10:00:00") in Asia/Jakarta (WIB) timezone — not UTC.