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

MethodPathFormatAuthentication
POSThttps://your-webhook-url/callbackjsonHMAC SHA512 Signature
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:
eventFired whendata contains
settlement.completedA settlement batch is completed — either a sequential settlement is approved by Finance, or a parallel (auto) settlement is created by the scheduler.settlement, total_transactions
settlement.refundedA single settled transaction is refunded back from your balance (settlement method balance / auto-balance only).settlement, refund
settlement.refund_cancelledA previous refund is cancelled and the balance is restored.settlement, refund
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

FieldValueTypeMandatoryDescription
Content-Typeapplication/jsonAlphabeticYesSpecifies JSON format for the request body
User-AgentSingaPaymentGateway/1.0AlphabeticYesIdentifies the source of the webhook
Acceptapplication/jsonAlphabeticYesExpected response format
X-PARTNER-IDAlphanumericYesYour API Key from the merchant dashboard
X-SignatureAlphanumericYesHMAC SHA512 signature (128 chars) for request verification
X-TimestampNumericYesUnix timestamp in seconds when the request was sent
AuthorizationBearer <random_token>AlphanumericYesSystem-generated random bearer token; used as a component in the signature
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 How to Validate Signature below.

Body Parameters — common envelope

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

data.settlement (present on all events)

settlement
object
required
The settlement batch the event relates to.

data.total_transactions (settlement.completed only)

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

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

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

Payload Examples

settlement.completed
{
  "status": 200,
  "success": true,
  "event": "settlement.completed",
  "timestamp": "18 Jun 2026 10:00:00",
  "data": {
    "settlement": {
      "id": 1234,
      "reference_no": "SETTLEMENT-1-ABC123",
      "title": "Settlement Acme (01 Jun 2026 - 17 Jun 2026)",
      "status": "completed",
      "settlement_type": "ALL",
      "settlement_method": "balance",
      "is_auto_created": false,
      "start_date": "01 Jun 2026 00:00:00",
      "end_date": "17 Jun 2026 23:59:59",
      "amount": 1000000,
      "total_admin_fee": 5000,
      "total_vendor_fee": 3000,
      "total_our_margin": 2000,
      "settlement_fee": 0,
      "total_to_transfer": 1000000,
      "total_refunded": 0,
      "currency": "IDR",
      "transfer_status": null,
      "approved_by": "Jane Finance",
      "approved_at": "18 Jun 2026 10:00:00",
      "recipient": {
        "bank_code": null,
        "account_number": null,
        "account_name": null
      }
    },
    "total_transactions": 5
  }
}
settlement.completed (bank-account)
{
  "status": 200,
  "success": true,
  "event": "settlement.completed",
  "timestamp": "18 Jun 2026 10:05:00",
  "data": {
    "settlement": {
      "id": 1240,
      "reference_no": "SETTLEMENT-1-XYZ789",
      "title": "Settlement Acme (17 Jun 2026 - 17 Jun 2026)",
      "status": "completed",
      "settlement_type": "VA",
      "settlement_method": "bank-account",
      "is_auto_created": false,
      "start_date": "17 Jun 2026 00:00:00",
      "end_date": "17 Jun 2026 23:59:59",
      "amount": 2000000,
      "total_admin_fee": 10000,
      "total_vendor_fee": 6000,
      "total_our_margin": 4000,
      "settlement_fee": 13000,
      "total_to_transfer": 1987000,
      "total_refunded": 0,
      "currency": "IDR",
      "transfer_status": "success",
      "approved_by": "Jane Finance",
      "approved_at": "18 Jun 2026 10:05:00",
      "recipient": {
        "bank_code": "BRI",
        "account_number": "1234567890",
        "account_name": "PT Acme Indonesia"
      }
    },
    "total_transactions": 8
  }
}
settlement.refunded
{
  "status": 200,
  "success": true,
  "event": "settlement.refunded",
  "timestamp": "19 Jun 2026 09:30:00",
  "data": {
    "settlement": {
      "id": 1234,
      "reference_no": "SETTLEMENT-1-ABC123",
      "title": "Settlement Acme (01 Jun 2026 - 17 Jun 2026)",
      "status": "completed",
      "settlement_type": "ALL",
      "settlement_method": "balance",
      "is_auto_created": false,
      "start_date": "01 Jun 2026 00:00:00",
      "end_date": "17 Jun 2026 23:59:59",
      "amount": 1000000,
      "total_admin_fee": 5000,
      "total_vendor_fee": 3000,
      "total_our_margin": 2000,
      "settlement_fee": 0,
      "total_to_transfer": 1000000,
      "total_refunded": 95000,
      "currency": "IDR",
      "transfer_status": null,
      "approved_by": "Jane Finance",
      "approved_at": "18 Jun 2026 10:00:00",
      "recipient": {
        "bank_code": null,
        "account_number": null,
        "account_name": null
      }
    },
    "refund": {
      "settlement_detail_id": 987,
      "transaction_type": "VATransaction",
      "transaction_id": 42,
      "transaction_reff": "INV-2026-001",
      "account_id": 7,
      "net_amount": {
        "value": 95000,
        "currency": "IDR"
      },
      "is_refunded": true,
      "is_refund_cancelled": false,
      "refunded_by": "Bob Admin",
      "refunded_at": "19 Jun 2026 09:30:00",
      "refund_cancelled_by": null,
      "refund_cancelled_at": null,
      "actor": "Bob Admin"
    }
  }
}
settlement.refund_cancelled
{
  "status": 200,
  "success": true,
  "event": "settlement.refund_cancelled",
  "timestamp": "19 Jun 2026 11:00:00",
  "data": {
    "settlement": {
      "id": 1234,
      "reference_no": "SETTLEMENT-1-ABC123",
      "title": "Settlement Acme (01 Jun 2026 - 17 Jun 2026)",
      "status": "completed",
      "settlement_type": "ALL",
      "settlement_method": "balance",
      "is_auto_created": false,
      "start_date": "01 Jun 2026 00:00:00",
      "end_date": "17 Jun 2026 23:59:59",
      "amount": 1000000,
      "total_admin_fee": 5000,
      "total_vendor_fee": 3000,
      "total_our_margin": 2000,
      "settlement_fee": 0,
      "total_to_transfer": 1000000,
      "total_refunded": 0,
      "currency": "IDR",
      "transfer_status": null,
      "approved_by": "Jane Finance",
      "approved_at": "18 Jun 2026 10:00:00",
      "recipient": {
        "bank_code": null,
        "account_number": null,
        "account_name": null
      }
    },
    "refund": {
      "settlement_detail_id": 987,
      "transaction_type": "VATransaction",
      "transaction_id": 42,
      "transaction_reff": "INV-2026-001",
      "account_id": 7,
      "net_amount": {
        "value": 95000,
        "currency": "IDR"
      },
      "is_refunded": true,
      "is_refund_cancelled": true,
      "refunded_by": "Bob Admin",
      "refunded_at": "19 Jun 2026 09:30:00",
      "refund_cancelled_by": "Bob Admin",
      "refund_cancelled_at": "19 Jun 2026 11:00:00",
      "actor": "Bob Admin"
    }
  }
}

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 null for balance / auto-balance settlements. 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.