Webhooks
Use webhooks to get transaction updates automatically.
Overview
Wakapay sends HTTP POST to your server when transaction status changes.
This avoids constant polling.
Configuration
Contact Wakapay account manager and provide:
- Webhook URL (must be HTTPS)
- Environment (Test or Production)
- Signature mode (Current or Legacy)
Webhook Security
Current Signature Method (Default)
To validate the webhook signature, you’ll need to recompute it on your side using your business credentials along with the wakapayReference provided in the webhook payload.
Here’s the step-by-step process:
- Construct a string in the following exact format:
apiKey:apiSecret - Generate a SHA-256 hash of this string
- Hex-encode the resulting hash
- Compare your computed value with the
signaturefield received in the webhook payload (A case-insensitive comparison is recommended)
Node.js Example
const crypto = require("crypto");
function validateWebhookSignature(apiKey, apiSecret, receivedSignature) {
const raw = `${apiKey}:${apiSecret}`;
const expected = crypto.createHash("sha256").update(raw).digest("hex");
return expected.toLowerCase() === (receivedSignature || "").toLowerCase();
}Python Example
import hashlib
def validate_webhook_signature(api_key, api_secret, received_signature):
raw = f"{api_key}:{api_secret}"
expected = hashlib.sha256(raw.encode()).hexdigest()
return expected.lower() == (received_signature or "").lower()Legacy Signature Method
Legacy only (older integrations)
Use only if Wakapay told this partner to use it.
- Header:
X-Wakapay-Signature - Format:
t=<timestamp>,v1=<hmac>
Webhook Event
Business API uses one event:
transaction.updated
Check status to know current transaction state.
Valid Transaction Statuses
Only these statuses are valid for Business API:
termination_pendingtermination_successtermination_failure
Ignore all old status names like completed, failed, or processing.
Webhook Payload Format
{
"businessId": "019d487b-00a5-787a-8aec-a4e5d33689e3",
"businessReference": "EXT-INV-2026-0001",
"wakapayReference": "293cd2ed-2db3-11f1-8c14-0242ac120008",
"status": "termination_success",
"senderCurrency": "USD",
"receiverCurrency": "KES",
"senderAmount": 0.7751937984496124,
"receiverAmount": 100,
"signature": "a1b2c3d4e5f6..."
}Sample Webhooks
Success
{
"businessId": "019d487b-00a5-787a-8aec-a4e5d33689e3",
"businessReference": "EXT-INV-2026-0001",
"wakapayReference": "293cd2ed-2db3-11f1-8c14-0242ac120008",
"status": "termination_success",
"senderCurrency": "USD",
"receiverCurrency": "KES",
"senderAmount": 0.7751937984496124,
"receiverAmount": 100,
"signature": "a1b2c3d4e5f6..."
}Failure
{
"businessId": "019d487b-00a5-787a-8aec-a4e5d33689e3",
"businessReference": "EXT-INV-2026-0002",
"wakapayReference": "4f2e9c1a-8b7d-4e6f-9a0b-123456789abc",
"status": "termination_failure",
"senderCurrency": "USD",
"receiverCurrency": "KES",
"senderAmount": 0.78,
"receiverAmount": 100,
"signature": "..."
}How to Process Webhooks
- Receive webhook
- Verify signature
- Check if
wakapayReferencewas already processed (to avoid duplicates) - Update your transaction using
businessReference - Return HTTP 200 OK quickly
If status is still termination_pending, check transaction again using GET /business/transactions/{businessReference}.
Check Transaction Status (When Still Pending)
If webhook status is still termination_pending, query the transaction to get final status.
Request
GET <BASE_URL>/business/transactions/{businessReference}Headers:
Authorization: Bearer <business-token>Response 200
{
"wakapayReference": "019ce78b-b204-716c-95ca-737ee5ec1559",
"businessReference": "EXT-INV-2026-0001",
"status": "termination_success",
"senderCurrency": "USD",
"receiverCurrency": "KES",
"senderAmount": 100.0,
"receiverAmount": 13025.0,
"createdAt": "2026-04-27T07:10:00Z",
"updatedAt": "2026-04-27T07:12:10Z"
}Response 404
{
"error": "transaction not found"
}Best Practices
- Verify signature before processing
- Handle duplicate deliveries using
wakapayReference - Return 200 OK quickly
- Use HTTPS only
- Keep logs for debugging
Troubleshooting
Signature validation fails
- Use correct
apiKeyandapiSecret - String format must be exactly
apiKey:apiSecret - Use SHA-256
- Compare case-insensitive
Webhook not received
- Check endpoint is public
- Check valid HTTPS certificate
- Confirm URL with Wakapay account manager
Too many retries/timeouts
- Return 200 OK quickly
- Move heavy work to background queue