> ## Documentation Index
> Fetch the complete documentation index at: https://docs.singapay.id/llms.txt
> Use this file to discover all available pages before exploring further.

# Virtual Account Webhook

> Real-time notifications for Virtual Account transactions processed through the SingaPay platform. Monitor payment events and keep your system in sync instantly.

<Note>
  **Money In Operation** — This webhook fires when a customer completes a Virtual Account payment, transferring funds into your SingaPay account.
</Note>

## Information

<div className="overflow-x-auto">
  | Method                                                                                                              | Path                                | Format | Authentication        |
  | ------------------------------------------------------------------------------------------------------------------- | ----------------------------------- | ------ | --------------------- |
  | <span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-bold bg-blue-500 text-white">POST</span> | `https://your-webhook-url/callback` | json   | HMAC SHA512 Signature |
</div>

SingaPay sends a `POST` request to your configured `transaction_notif_url` when a Virtual Account transaction is successfully processed.

<Info>
  This webhook may share a callback URL with other event types. See [Shared webhook endpoints](/api-reference/webhooks/shared-endpoints) for routing by `event` value.
</Info>

***

## Request Details

### Headers

| Field           | Value                     | Type         | Mandatory | Description                                                                |
| --------------- | ------------------------- | ------------ | :-------: | -------------------------------------------------------------------------- |
| `Content-Type`  | `application/json`        | Alphabetic   |    Yes    | Specifies JSON format for the request body                                 |
| `User-Agent`    | `SingaPaymentGateway/1.0` | Alphabetic   |    Yes    | Identifies the source of the webhook                                       |
| `Accept`        | `application/json`        | Alphabetic   |    Yes    | Expected response format                                                   |
| `X-PARTNER-ID`  | —                         | Alphanumeric |    Yes    | Your API Key from the merchant dashboard                                   |
| `X-Signature`   | —                         | Alphanumeric |    Yes    | HMAC SHA512 signature (128 chars) for request verification                 |
| `X-Timestamp`   | —                         | Numeric      |    Yes    | Unix timestamp in **seconds** when the request was sent                    |
| `Authorization` | `Bearer <random_token>`   | Alphanumeric |    Yes    | System-generated random bearer token; used as a component in the signature |

<Note>
  The `Authorization` token for VA webhooks is a **randomly generated string** — not a user access token. This is because the webhook is triggered by a bank payment notification (system event), not a user action. Extract it as-is from the header and use it in the string to sign. See [How to Validate Signature](#how-to-validate-signature) below.
</Note>

***

### Body Parameters

<ParamField body="status" type="number" required>
  HTTP Status Code. Example: `200`
</ParamField>

<ParamField body="success" type="boolean" required>
  Indicates if the transaction was successful. Example: `true`
</ParamField>

<ParamField body="event" type="string" required>
  Event type identifier. Always `"va-transaction"` for this webhook.
</ParamField>

<ParamField body="timestamp" type="string" required>
  Event timestamp in format `"d M Y H:i:s"`. Example: `"26 Dec 2025 13:35:45"`
</ParamField>

<ParamField body="data" type="object" required>
  Container for transaction and payment details.

  <Expandable title="data fields">
    <ParamField body="transaction" type="object" required>
      Transaction details.

      <Expandable title="transaction fields">
        <ParamField body="reff_no" type="string" required>
          Merchant reference number if provided during VA creation (`merchant_reff_no`), otherwise falls back to `transaction_id`. Example: `"INV-2026-001"`
        </ParamField>

        <ParamField body="transaction_id" type="string" required>
          Internal system transaction ID. Example: `"3211120250926133543246"`
        </ParamField>

        <ParamField body="type" type="string" required>
          Transaction type. Always `"va"` for Virtual Account.
        </ParamField>

        <ParamField body="status" type="string" required>
          Transaction status. Always `"paid"` for this webhook.
        </ParamField>

        <ParamField body="amount" type="object" required>
          Transaction amount details.

          <Expandable title="amount fields">
            <ParamField body="value" type="number" required>
              Transaction amount. Example: `100000`
            </ParamField>

            <ParamField body="currency" type="string" required>
              ISO 4217 currency code. Example: `"IDR"`
            </ParamField>
          </Expandable>
        </ParamField>

        <ParamField body="amount_type" type="string" required>
          VA amount type: `"closed"` (fixed amount) or `"open"` (any amount).
        </ParamField>

        <ParamField body="tip" type="null" required>
          Tip amount. Always `null` for VA transactions.
        </ParamField>

        <ParamField body="post_timestamp" type="string" required>
          Transaction post time. Example: `"26 Dec 2025 13:35:43"`
        </ParamField>

        <ParamField body="processed_timestamp" type="string" required>
          Transaction processed time. Example: `"26 Dec 2025 13:35:45"`
        </ParamField>
      </Expandable>
    </ParamField>

    <ParamField body="payment" type="object" required>
      Payment method details.

      <Expandable title="payment fields">
        <ParamField body="method" type="string" required>
          Payment method. Always `"va"`.
        </ParamField>

        <ParamField body="additional_info" type="object" required>
          Payment method specific information.

          <Expandable title="additional_info fields">
            <ParamField body="va_number" type="string" required>
              Virtual Account number used for payment (16 digits). Example: `"7872955146576837"`
            </ParamField>

            <ParamField body="va_name" type="string" required>
              Virtual Account name/label. Example: `"Toko Budi"`
            </ParamField>

            <ParamField body="bank" type="object" required>
              Bank details.

              <Expandable title="bank fields">
                <ParamField body="short_name" type="string" required>Short name of the bank. Example: `"Maybank"`</ParamField>
                <ParamField body="number" type="string" required>Bank number/code (3 digits). Example: `"016"`</ParamField>
                <ParamField body="swift_code" type="string" required>Bank SWIFT code (8 chars). Example: `"IBBKIDJA"`</ParamField>
                <ParamField body="bank_code" type="string" required>Internal bank code. Example: `"MAYBANK"`</ParamField>
              </Expandable>
            </ParamField>

            <ParamField body="fees" type="object" required>
              Transaction fee details.

              <Expandable title="fees fields">
                <ParamField body="name" type="string" required>Payment vendor fee name. Example: `"VA Maybank"`</ParamField>
                <ParamField body="amount" type="number" required>Fee amount charged. Example: `1500`</ParamField>
                <ParamField body="currency" type="string" required>Fee currency code. Example: `"IDR"`</ParamField>
              </Expandable>
            </ParamField>
          </Expandable>
        </ParamField>
      </Expandable>
    </ParamField>
  </Expandable>
</ParamField>

***

### Payload Example

```json Success theme={null}
{
  "status": 200,
  "success": true,
  "event": "va-transaction",
  "timestamp": "26 Dec 2025 13:35:45",
  "data": {
    "transaction": {
      "reff_no": "INV-2026-001",
      "transaction_id": "3211120250926133543246",
      "type": "va",
      "status": "paid",
      "amount": {
        "value": 100000,
        "currency": "IDR"
      },
      "amount_type": "closed",
      "tip": null,
      "post_timestamp": "26 Dec 2025 13:35:43",
      "processed_timestamp": "26 Dec 2025 13:35:45"
    },
    "payment": {
      "method": "va",
      "additional_info": {
        "va_number": "7872955146576837",
        "va_name": "Toko Budi",
        "bank": {
          "short_name": "Maybank",
          "number": "016",
          "swift_code": "IBBKIDJA",
          "bank_code": "MAYBANK"
        },
        "fees": {
          "name": "VA Maybank",
          "amount": 1500,
          "currency": "IDR"
        }
      }
    }
  }
}
```

***

***

## Security and responses

Return HTTP `200` promptly after validating the request. For retry behavior, see [Webhook retry mechanism](/api-reference/webhooks/retry-mechanism).

Verify every webhook using [Security and signature validation](/api-reference/webhooks/security-and-signature). Use your configured callback path when building `StringToSign`.

Handle duplicate deliveries idempotently using stable identifiers from the payload (for example `transaction_id` or `reff_no`).
