Skip to content
PGSwift Docs
Documentation/Webhook V2

Webhook V2

V2 webhooks use a unified envelope payload structure, expanding coverage beyond the existing v1 events. V2 runs in parallel with v1 — clients can subscribe to both independently.

Unified Envelope Format

All v2 webhooks share the same outer structure:

{
  "event": "<event_name>",
  "timestamp": "2026-03-22T10:00:00+07:00",
  "data": { }
}
Field Type Description
event string The event name that triggered this webhook
timestamp string Webhook dispatch timestamp (ISO 8601)
data object Event-specific payload (see individual event docs)

Webhook Registration

Webhook endpoints are configured during client onboarding or can be set up later via the admin panel. Each event type requires a separate webhook configuration with:

The complete webhook URL is built as: {base_url}{path}

V2 event names follow a structured naming convention: payment.v2.completed, disbursement.v2.completed, etc. Register each event independently — you can subscribe to v1, v2, or both simultaneously.

Response Requirements

Your webhook endpoint must return a standard HTTP status code:

  • 200-299: Success (webhook handled successfully)
  • 4xx: Client error (retries will not be attempted, except 408 and 429 — see Error Handling)
  • 5xx: Server error (retries will be attempted)

Keep response time under 5 seconds to prevent timeout.

Idempotency

Request Headers

All v2 webhook requests include the same authentication headers as v1:

Header Description
X-PARTNER-ID Your client identifier
X-TIMESTAMP Request timestamp in ISO8601 format (e.g., 2024-12-18T16:21:07+07:00)
X-SIGNATURE RSA signature for request verification (see Signature Generation)

Security Best Practices

  1. Always validate the X-SIGNATURE header to authenticate requests
  2. Confirm the X-PARTNER-ID matches your client ID
  3. Validate the X-TIMESTAMP to prevent replay attacks
  4. Use HTTPS for your webhook endpoint
  5. Implement idempotency checks using identifiers inside data to prevent duplicate processing

V2 Events

Event Description
payment.v2.completed Payment is successfully completed
disbursement.v2.completed Disbursement is successfully completed
disbursement.v2.fail Disbursement fails
disbursement.v2.expired Disbursement expires
transaction_settlement.settled A transaction settlement is finalised and funds are transferred

Error Handling

If your endpoint returns a non-2xx status code or times out:

  • 5xx responses: retried (up to 3 attempts total, including the first, with 10-second and 30-second delays between retries)
  • 408 (Request Timeout) and 429 (Too Many Requests): also retried, on the same schedule as 5xx
  • Other 4xx responses: No retry will be attempted — review your endpoint logic
  • Timeout (> 5 seconds): Treated as a failure and retried. The 5-second timeout applies per attempt — each retry gets a fresh window, not a cumulative budget across all 3 attempts.
  • Connection errors (connection refused, DNS failure, SSL errors, unreachable host): also treated as a failure and retried, same as the cases above.

Ensure your webhook handler processes quickly and offloads heavy work asynchronously.

V1 to V2 Migration Guide

The key structural difference between v1 and v2 is the unified envelope:

  • V1 payment — fields are at the root level of the payload (flat structure)
  • V1 disbursement — uses an { event, data } envelope (no timestamp)
  • V2 all events — always use { event, timestamp, data } — consistent across all event types

V2 keeps every v1 field, with the same name and type (amount is a string in both). The differences:

V1 event V2 event Fields added in V2
payment.completed payment.v2.completed payment_channel, payment_at, service_fee_rate_str, service_fee_amount_str, nett_amount_str
disbursement.completed disbursement.v2.completed (success), disbursement.v2.fail (fail), disbursement.v2.expired (expired) valid_account_name, reason, payment_at

V2 may add more fields later; each V2 event page lists its current fields. V2 also has transaction_settlement.settled, with no v1 equivalent.

To migrate:

  1. Register a new webhook for the corresponding v2 event name (e.g., payment.v2.completed)
  2. Update your handler to read fields from data.* instead of the root level (for payment events)
  3. Add handling for the new timestamp envelope field
  4. Verify the updated event string matches the v2 name
  5. Once confirmed working, you may unregister the v1 webhook
© 2026 PGSwift. All rights reserved.