1. Global Account
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
    • Webhooks
      • Unified Webhook Entry
      • Virtual Account Webhook
      • Deposit Webhook
      • Payout Status Webhook
  • Payment Links
    • Payment Links
      • Create Payment Link
      • Update Payment Link
      • Get Payment Link Detail
      • Get Payment Link List
    • Transactions
      • Get Transaction List
  • 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
  1. Global Account

Virtual Account Webhook

PIK pushes a webhook to your endpoint when a virtual account (VA) you requested becomes ready to receive funds. Use this event to surface the new account details (account number, bank name, routing key, etc.) to your end user, or to mark the VA as active in your own system.
This document covers everything needed to integrate the Virtual Account webhook end-to-end: delivery format, signature verification, retry behaviour, the event lifecycle, and a JSON sample.

1. How delivery works#

ItemValue
HTTP methodPOST
Content typeapplication/json; charset=utf-8
Request timeout10 seconds
Success ruleHTTP 2xx is treated as ack; anything else is a failure
Retry policyUp to 5 retries, 5 minutes between attempts

Required headers#

HeaderDescription
Content-TypeAlways application/json.
X-Webhook-EventEvent group. For this document the value is always VIRTUAL.
X-Webhook-Event-TypeConcrete event type. Currently only virtual.account.update is forwarded to clients.
X-Webhook-SignatureHex-encoded HMAC-SHA256 of the raw request body using your App Secret. Present only when an App Secret is configured for your account.

Signature verification#

The signature is computed as:
X-Webhook-Signature = HEX( HMAC_SHA256( app_secret, raw_request_body ) )
Sign the raw request body bytes (UTF-8), not a re-serialized JSON, and verify in constant time.
Node.js example
Python example

Client response requirements#

Reply with HTTP 200 (or any 2xx) as soon as you have persisted the event.
Any non-2xx response, network error, or timeout will be retried.
Keep ack bodies small (e.g. {"received":true}) — we only log the first 1000 chars of the response.

Retry behaviour#

AttemptTrigger
1Immediately when PIK processes the source event
2–65 minutes after the previous failure
FinalAfter 5 retries the task is marked EXHAUSTED and is not sent again

Idempotency#

VA provisioning may produce duplicate deliveries during retries. Use event_id as the idempotency key in your handler.
For business identity across events for the same VA, use data.account_bank_id (stable across the lifetime of the VA).

2. Envelope#

{
  "version": "V1.6.0",
  "event_name": "VIRTUAL",
  "event_type": "virtual.account.update",
  "event_id": "<uuid, unique per event delivery>",
  "source_id": "<account_bank_id this event refers to>",
  "data": { ... }
}
FieldTypeDescription
versionstringWebhook payload schema version.
event_namestringEvent group. Always VIRTUAL.
event_typestringConcrete event. See §3.
event_idstringUnique ID of this event delivery. Use as idempotency key.
source_idstringThe business object the event is about. For VAs this is account_bank_id.
dataobjectEvent-specific payload. See §5.

3. Event types#

event_typeMeaningForwarded to clients?
virtual.account.createInternal — the VA creation request has been accepted and provisioning has started. Not forwarded to clients.No
virtual.account.updateThe VA has been fully provisioned and is active. Full account details (account number, bank, routing) are now populated.Yes
Only virtual.account.update (Active) is delivered to your endpoint. The earlier virtual.account.create (Processing) phase is handled internally by PIK; you do not need to react to it.

4. State machine#

   VA created via API
   (local status = Creating)
              │
              │   PIK starts provisioning the account
              ▼
   ┌──────────────────────────────────────┐
   │  virtual.account.create              │   internal only
   │  (local status = Processing)         │   ── NOT forwarded ──
   │  account_id assigned, provisioning   │
   └──────────────────┬───────────────────┘
                      │
                      │   provisioning completes
                      ▼
   ┌──────────────────────────────────────┐
   │  virtual.account.update              │  ✓ forwarded to your endpoint
   │  (local status = Active)             │
   │  account_number, bank, routing are   │
   │  now populated and usable            │
   └──────────────────────────────────────┘
                  (terminal)
Notes:
Treat virtual.account.update as the green-light signal that the VA can receive funds. Until you see it, the account is not usable.
If the VA is later closed, data.close_reason will be populated. The event_type for closure is reserved for a future version and is not yet emitted.

5. data field reference#

FieldTypeAlways present?Description
request_idstringyesThe client request UUID supplied when the VA was created. Use to correlate with your own VA-creation record.
direct_idstringyesParent direct account identifier. "0" if not applicable.
account_idstringyesThe owning sub-account identifier on PIK.
account_bank_idstringyesThe VA's unique identifier. Stable for the lifetime of the VA — use as VA primary key in your system.
account_holderstringyesThe legal holder name on the VA.
account_numberstringyesThe bank account number assigned to the VA.
country_codestringyesISO 3166-1 alpha-2 country code (e.g. SG, AU).
currencystringyesISO 4217 currency code (e.g. USD, SGD).
bank_namestringyesHuman-readable bank name.
bank_addressstringoptionalBank postal address, if available.
clearing_system.typestringyesRouting key type, e.g. SWIFT, BSB, IBAN, LOCAL.
clearing_system.valuestringyesRouting key value matching the type above.
capability.payment_methodstringoptionalSupported payment rails on this VA (e.g. LOCAL,SWIFT).
close_reasonstringoptionalPopulated only when the VA has been closed.

6. Sample payload — virtual.account.update#

{
  "version": "V1.6.0",
  "event_name": "VIRTUAL",
  "event_type": "virtual.account.update",
  "event_id": "a2b8e3d4-91fe-4d65-b41b-08e6c1f4d771",
  "source_id": "abk_3f9d0a51e2bc4a7c",
  "data": {
    "request_id": "8c2f33ee-7a91-4f9a-9e8e-0a91e0d6f200",
    "direct_id": "0",
    "account_id": "ac1e31ab-f0fd-4432-91fb-b06ec1b3d7b9",
    "account_bank_id": "abk_3f9d0a51e2bc4a7c",
    "account_holder": "ACME PTE LTD.",
    "account_number": "1234567890",
    "country_code": "SG",
    "currency": "SGD",
    "bank_name": "DBS Bank Ltd.",
    "bank_address": "12 Marina Boulevard, Singapore 018982",
    "clearing_system": {
      "type": "LOCAL",
      "value": "7171"
    },
    "capability": {
      "payment_method": "LOCAL,SWIFT"
    },
    "close_reason": null
  }
}

Modified at 2026-05-25 08:15:33
Previous
Payout Webhook
Next
Overview
Built with