WiPay
Webhooks

WAPI events

Withdrawal lifecycle events delivered to WAPI webhook endpoints.

WAPI events track every status change on a withdrawal. Every event uses the shared webhook envelope, headers, signature, and retry model. The event name maps one-to-one to the withdrawal's numeric status field; consumers can switch on either event or data.status.

Event catalog

Eventdata.statusMeaning
withdrawal.not_on_ach_file0Withdrawal created; not yet added to the ACH file.
withdrawal.being_processed_by_bank1Submitted to the bank for processing.
withdrawal.transaction_completed2Bank confirmed the funds have been paid out.
withdrawal.held_by_compliance3Held pending compliance review.
withdrawal.released_by_compliance4Compliance released the hold; processing resumes.
withdrawal.refunded_to_account5Funds returned to the merchant's WiPay balance.
withdrawal.taken_for_chargeback6Funds taken from the withdrawal to cover a chargeback.
withdrawal.taken_for_refund7Funds taken from the withdrawal to cover a refund.
withdrawal.held_for_refund8Withdrawal held while a refund is processed.
withdrawal.held_for_chargeback9Withdrawal held while a chargeback is reviewed.

data payload

FieldTypeNotes
amountstring2-decimal amount being withdrawn.
bank_accountstring | nullDestination bank account number.
bank_account_typestring | nulle.g. chequing, savings. May be null for sub-banked or legacy bank-transaction deliveries.
bank_branchstring | nullBranch name.
bank_namestringDestination bank.
created_atstringISO 8601 timestamp the withdrawal was created.
currencystringISO 4217 alpha code.
deleted_atstring | nullSoft-delete timestamp; populated only for cancelled withdrawals.
emailstring | nullRecipient email (where collected).
fee_amountstring | null2-decimal fee amount.
id_numberstring | nullRecipient government ID number (sub-unbanked flow).
id_typestring | nullGovernment ID type, e.g. national_id, drivers_license, passport.
name_in_bankstringAccount-holder name as registered with the bank.
phonestring | nullRecipient phone (where collected).
recipient_namestring | nullDisplay name of the recipient.
referencestring | nullMerchant-supplied reference / memo.
statusint09, see catalog table above.
status_descriptionstringHuman-readable status text, e.g. Being Processed By Bank, Transaction Completed. For sub_unbanked withdrawals, statuses 0 and 2 are described as Not Redeemed and Redeemed.
transaction_idstring | nullLinked WiPay transaction reference, when one exists.
updated_atstringISO 8601 timestamp of the most recent status change.
withdrawal_typestring | nullOne of normal, sub_banked, sub_unbanked. null on legacy bank-transaction deliveries.

Legacy bank-transaction payloads

Some deliveries originate from a legacy bank-transaction path with no linked WAPI withdrawal record. In those cases, the same envelope is sent but the withdrawal-form-only fields are null: email, phone, id_number, id_type, recipient_name, reference, fee_amount, bank_branch, bank_account_type, and withdrawal_type. The core columns — amount, bank_name, bank_account, name_in_bank, currency, status, status_description, created_at, and updated_at — remain populated.

meta

KeyDescription
previous_statusNumeric status before the transition, or null for the first transition (typically seen on withdrawal.not_on_ach_file).

Example — withdrawal.transaction_completed (sub-banked)

{
  "id": "a14d3e2f-0b9c-4d1e-9f80-5a6b7c8d9e0f",
  "api_family": "wapi",
  "event": "withdrawal.transaction_completed",
  "occurred_at": "2026-04-17T19:14:25+00:00",
  "data": {
    "amount": "100.00",
    "bank_account": "1234567890",
    "bank_account_type": null,
    "bank_branch": "Port of Spain",
    "bank_name": "Republic Bank",
    "created_at": "2026-04-17T19:14:25+00:00",
    "currency": "TTD",
    "deleted_at": null,
    "email": "jane.customer@example.com",
    "fee_amount": "0.00",
    "id_number": null,
    "id_type": null,
    "name_in_bank": "Jane Customer",
    "phone": "+18680000000",
    "recipient_name": "Jane Customer",
    "reference": null,
    "status": 2,
    "status_description": "Transaction Completed",
    "transaction_id": null,
    "updated_at": "2026-04-17T19:14:25+00:00",
    "withdrawal_type": "sub_banked"
  },
  "meta": {
    "previous_status": 1
  }
}

Example — withdrawal.being_processed_by_bank (legacy bank-transaction path)

{
  "id": "b25e4f30-1c0d-4e2f-8091-6b7c8d9e0f12",
  "api_family": "wapi",
  "event": "withdrawal.being_processed_by_bank",
  "occurred_at": "2026-04-17T15:14:25-04:00",
  "data": {
    "amount": "50.00",
    "bank_account": "1234567890",
    "bank_account_type": null,
    "bank_branch": null,
    "bank_name": "Republic Bank",
    "created_at": "2026-04-17T15:14:25-04:00",
    "currency": "TTD",
    "deleted_at": null,
    "email": null,
    "fee_amount": null,
    "id_number": null,
    "id_type": null,
    "name_in_bank": "John Merchant",
    "phone": null,
    "recipient_name": null,
    "reference": null,
    "status": 1,
    "status_description": "Being Processed By Bank",
    "transaction_id": null,
    "updated_at": "2026-04-17T15:14:25-04:00",
    "withdrawal_type": null
  },
  "meta": {
    "previous_status": 0
  }
}

Ordering

Status changes fire in whatever order the underlying systems report them. Consumers must not assume a fixed sequence — always reconcile using data.status (authoritative) and meta.previous_status. Because the event is per endpoint, a given transition is emitted once. If a delivery is retried, it is a redelivery of the same transition rather than a new status change.