Webhook Notifications
Overview
Webhooks allow you to receive real-time notifications about contract events. Instead of polling the API for updates, WePay will send HTTP POST requests to your specified endpoint whenever important events occur.
Key Benefits:
- Real-time Updates: Get notified immediately when events occur
- Reduced API Calls: No need to poll for status changes
- Reliable Delivery: Automatic retries with exponential backoff
- Secure: HMAC-SHA256 signature verification
Webhook Event Types
WePay sends webhooks for the following events:
Contract Events
| Event Type | Description |
|---|---|
| contract.created | A new contract has been created |
| contract.approved | Contract has been approved by the other party |
| contract.rejected | Contract has been rejected |
| contract.cancelled | Contract has been cancelled |
| contract.delivered | Seller has marked the contract as delivered |
| contract.received | Buyer has confirmed receipt |
| contract.released | Funds have been released to the seller |
| contract.completed | Contract has been fully completed |
Payment Events
| Event Type | Description |
|---|---|
| payment.completed | Payment has been completed and funds are in escrow |
| payment.unmatched | A payment arrived that could not be matched to a contract |
| contract.payment.underpaid | Less than the expected amount was received |
| contract.payment.overpaid | More than the expected amount was received |
Bank Transfer Events
See Bank Transfer Events for payloads and recommended handling.
| Event Type | Description |
|---|---|
bank_transfer.initiated | Bank account details were issued to the buyer |
bank_transfer.buyer_acknowledged | Buyer indicated they have sent the transfer |
payment.completed or milestone.payment.completed | The transfer has arrived and the contract is funded |
Dispute Events
Opening or resolving a dispute sends more than one webhook. See Create Dispute and Resolve Dispute for the captured payloads.
| Event Type | Description |
|---|---|
| contract.disputed | A dispute has been raised on a contract without milestones. status becomes Dispute |
| milestone.disputed | A dispute has been raised on a milestone. Milestone status stays Escrow. contractStatus becomes Dispute |
| contract.disput.resolved | A contract dispute has been resolved. Event name is disput.resolved. amount is 0 |
| milestone.disput.resolved | A milestone dispute has been resolved. Milestone status becomes PartiallyRefunded or Released. amount is 0 |
| dispute.created | Dispute has been created. Includes milestoneId when the dispute is on a milestone. Does not include status or amount |
| dispute.resolved | Dispute has been resolved. disputeDecision is PartialRefund or FullRelease. Includes comment. Includes milestoneId when the dispute is on a milestone. Does not include status or amount |
| When | Webhooks |
|---|---|
| Milestone dispute opened | dispute.created, milestone.disputed |
| Contract dispute opened | dispute.created, contract.disputed |
| Milestone dispute resolved with a partial refund | milestone.disput.resolved, dispute.resolved, milestone.refund.partial-initiated, then later milestone.refund.partial-succeeded |
| Milestone dispute resolved with a full release | milestone.disput.resolved, dispute.resolved, milestone.released |
| Contract dispute resolved with a partial refund | contract.disput.resolved, dispute.resolved, refund.partial-initiated |
Refund Events
| Event Type | Description |
|---|---|
| contract.refunded | Contract has been refunded |
| refund.full-initiated | Contract full refund has been initiated |
| refund.full-succeeded | Contract full refund has succeeded |
| refund.partial-initiated | Contract partial refund has been initiated. Status is PartiallyRefunded. transactionId is the refund id. amount is the refunded amount |
| refund.partial-succeeded | Contract partial refund has succeeded |
| refund_request.cancelled | A refund request was cancelled |
| overpaid.refund.succeeded | An overpayment was refunded |
| underpaid.refund.succeeded | An underpayment was refunded |
| unmatched.refund.succeeded | An unmatched payment was refunded |
Milestone Events
| Event Type | Description |
|---|---|
| milestone.payment.completed | Milestone payment has been completed |
| milestone.payment.underpaid | Less than the expected milestone amount was received |
| milestone.payment.overpaid | More than the expected milestone amount was received |
| milestone.released | Milestone has been released. Carries transactionReference and the released amount |
| milestone.disputed | See Dispute Events. Milestone status stays Escrow |
| milestone.disput.resolved | See Dispute Events. amount on this event is 0 |
| milestone.refunded | Milestone has been refunded |
| milestone.completed | Milestone has been fully completed |
| milestone.refund.full-initiated | Milestone full refund has been initiated |
| milestone.refund.full-succeeded | Milestone full refund has succeeded |
| milestone.refund.partial-initiated | Milestone partial refund has been initiated. Status is PartiallyRefunded. transactionId is the refund id |
| milestone.refund.partial-succeeded | Milestone partial refund has succeeded. Carries transactionReference, not transactionId |
Testing
| Event Type | Description |
|---|---|
| webhook.test | Test webhook (sent via /webhooks/test endpoint) |
The properties inside data change depending on the event and on whether the contract uses milestones. On milestone events that include contractStatus, data.status is the milestone status and contractStatus is the contract. dispute.created and dispute.resolved do not include status.
See Payload Reference before writing your handler.