1. Payment Links
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. Payment Links

Overview

Payment Links Webhook — Overview#

PIK Payment pushes real-time transaction notifications to your server via HTTPS POST. Whenever a blockchain transaction relevant to your account is detected, confirmed, or fails, our system delivers a signed JSON payload to the webhook URL configured in your API client.
This document covers the transport contract: request format, signature verification, retry policy, and the lifecycle (state machine) common to all event types.
For the JSON payload and business semantics of each individual event, see the per-event documents:
DocumentEvent TypeWhen it fires
webhook-customer-payment.mdCUSTOMER_PAYMENTA customer pays via a payment link to an order address
webhook-web3-payment.mdWEB3_DIRECT_PAYMENTA customer pays a payment link directly from a Web3 wallet to the master address
webhook-master-recharge.mdMASTER_RECHARGEFunds are deposited directly to the master address (no payment link)
webhook-collect-out.mdORDER_COLLECT_OUTOrder address funds are swept into the master address (internal sweep)
webhook-withdraw-out.mdWITHDRAW_OUTWithdrawal from the master address to an external destination
webhook-customer-refund.mdCUSTOMER_REFUNDA refund is issued from an order address back to the payer

1. Request Format#

POST {your_webhook_url}
Content-Type: application/json
X-Webhook-Signature: <HMAC-SHA256 hex digest>
X-Webhook-Timestamp: <Unix timestamp in milliseconds>

Common payload envelope#

Every webhook shares the same outer envelope; only data varies by event type.
{
  "event": "transaction.created",
  "timestamp": 1738800000000,
  "data": {
    "fundEventCode": "FE20260206120000001",
    "paymentLinkName": "My Store Payment",
    "businessRefType": "PAYMENT",
    "chain": "Ethereum",
    "tokenSymbol": "USDC",
    "tokenAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
    "txHash": "0xabc123def456...",
    "fromAddress": "0x1234567890abcdef...",
    "toAddress": "0xfedcba0987654321...",
    "amount": 100.00,
    "direction": "IN",
    "eventType": "CUSTOMER_PAYMENT",
    "status": "PENDING",
    "createTimeUtc": "2026-02-06 12:00:00"
  }
}

Envelope fields#

FieldTypeDescription
eventStringAlways transaction.created for the current API version
timestampLongServer time (Unix ms) when the webhook was generated. Used for signature & replay protection
dataObjectEvent-specific payload (see per-event docs)

Common data fields#

FieldTypeDescription
fundEventCodeStringUnique identifier for the fund event. Use this as the idempotency key
paymentLinkNameString|nullPayment link name (only present for payment events)
businessRefTypeStringHigh-level business class — see table below
chainStringBlockchain network: Ethereum, Tron, BSC, Solana, Polygon, etc.
tokenSymbolStringToken symbol: USDC, USDT, ETH, TRX, etc.
tokenAddressStringContract address. Empty string for native tokens (ETH, TRX, etc.)
txHashStringOn-chain transaction hash
fromAddressStringSender wallet address
toAddressStringReceiver wallet address
amountBigDecimalTransaction amount (decimal, in token units, not wei/sat)
directionStringIN = funds entering the merchant's PIK wallet; OUT = leaving
eventTypeStringSpecific event classification — see per-event docs
statusStringLifecycle state — see §3 State Machine
createTimeUtcStringUTC creation time, format yyyy-MM-dd HH:mm:ss

businessRefType reference#

ValueDescriptionDirection
PAYMENTCustomer-initiated payment to merchantIN
COLLECTInternal sweep from order address to master addressIN (from master's perspective)
WITHDRAWMerchant withdrawal to external addressOUT
REFUNDRefund back to customerOUT

2. Signature Verification#

Every request includes an X-Webhook-Signature header. You must verify it before trusting the payload.

Algorithm#

signature = HMAC-SHA256(appSecret, timestamp + "." + rawRequestBody)
appSecret — the App Secret assigned in your API client configuration. Never expose it client-side.
timestamp — the value of X-Webhook-Timestamp (treat as a string, do not parse and re-stringify).
rawRequestBody — the raw JSON body as received, byte-for-byte. Do not re-serialize; whitespace and key ordering matter.
Digest is lowercase hex.

Verification steps#

1.
Read X-Webhook-Signature and X-Webhook-Timestamp from headers.
2.
Read the raw request body as a string (before any JSON parsing).
3.
Concatenate timestamp + "." + body.
4.
Compute HMAC-SHA256 with your appSecret as the key.
5.
Compare the result to X-Webhook-Signature using a constant-time comparison.
6.
Reject the request if |now - timestamp| > 5 minutes (replay protection).

Code examples#

Java
Python
Node.js
PHP

3. State Machine#

Every fund event progresses through a status lifecycle. PIK pushes a webhook on every state transition, so a single business transaction may produce multiple webhooks sharing the same fundEventCode.
        ┌──────────┐    on-chain confirmed    ┌────────────┐
        │ PENDING  │ ───────────────────────► │ CONFIRMED  │  (terminal)
        └──────────┘                          └────────────┘
              │
              │  on-chain reverted / internal error
              ▼
        ┌──────────┐
        │  FAILED  │  (terminal)
        └──────────┘
StatusMeaningNext states
PENDINGTransaction detected on-chain but not yet finalized. Balance has not been credited/debited yet.CONFIRMED, FAILED
CONFIRMEDTransaction reached sufficient confirmations; balance has been updated. Terminal.—
FAILEDTransaction reverted on-chain or rejected by an internal validation. Terminal.—

Multi-event lifecycle example#

A successful customer payment generates two webhooks:
1.
status = PENDING — transaction first detected
2.
status = CONFIRMED — sufficient confirmations reached, balance credited
Both webhooks carry the same fundEventCode. Your handler must be idempotent.

Direction & status combinations per event type#

eventTypedirectionStates that fire
CUSTOMER_PAYMENTINPENDING, CONFIRMED, FAILED
WEB3_DIRECT_PAYMENTINPENDING, CONFIRMED, FAILED
MASTER_RECHARGEINPENDING, CONFIRMED, FAILED
ORDER_COLLECT_OUTIN (funds entering master)PENDING, CONFIRMED, FAILED
WITHDRAW_OUTOUTPENDING, CONFIRMED, FAILED
CUSTOMER_REFUNDOUTPENDING, CONFIRMED, FAILED
Note on ORDER_COLLECT_OUT: the name reflects the order address perspective (funds leaving the order address), but direction is reported from the merchant master account perspective (IN, because the master's balance grows).

4. Response Requirements#

Your endpoint must:
Return HTTP 2xx (typically 200 OK) to acknowledge receipt.
Respond within 5 seconds. Heavy processing must happen asynchronously after acknowledgement.
Return any non-2xx (or time out) to signal failure; PIK will retry.
Response body content is ignored — only the status code matters.

5. Retry Policy#

AttemptDelay after previous failure
1— (immediate)
21 second
35 seconds
After 3 failed deliveries the webhook is permanently marked failed and logged for manual investigation. There is no exponential backoff beyond attempt 3 and no automatic dead-letter retry — make sure your endpoint is highly available, or contact PIK support to manually resend.

6. Configuration#

Configure your webhook on the API Client settings page:
FieldDescription
webhookUrlHTTPS URL to receive callbacks. Must accept POST and return 2xx
webhookStatus1 = enabled, 0 = disabled. Webhooks are skipped silently when disabled
appSecretUsed for HMAC-SHA256 signing. Rotate via the API Client page
⚠️ HTTPS only. Plain HTTP endpoints are rejected.

7. Best Practices#

1.
Verify signatures before processing — never trust an unsigned or wrongly-signed request.
2.
Use fundEventCode as your idempotency key — the same event may arrive more than once due to retries or state transitions.
3.
Respond fast (< 5s), process asynchronously — push to a queue, return 200, then handle.
4.
Validate timestamp freshness (±5 minutes) to mitigate replay attacks.
5.
Persist every webhook for audit and debugging, including failed-signature attempts.
6.
Handle terminal-status arrival without a prior PENDING — if your endpoint was down during the PENDING delivery, CONFIRMED may be the first webhook you actually see for a given fundEventCode.
7.
Don't rely on delivery order — although PIK sends in transition order, network reordering is possible. Trust status and updateTimeUtc, not arrival order.
Modified at 2026-05-25 08:32:55
Previous
Virtual Account Webhook
Next
Order Collect Out Webhook
Built with