What are Webhooks?

Webhooks allow your application to receive real-time notifications when events occur in HOST Pay. Instead of polling the API, HOST Pay will send HTTP POST requests to your specified endpoint whenever a transaction or account event takes place.

Common Use Cases

Payment Confirmation

Get notified the moment deposits are successfully processed

Transfer Updates

Track wallet-to-wallet transfers in real time

Payout Status

Monitor withdrawal processing and failures

Balance Changes

React to wallet balance and fee events as they happen

Webhook Events

HOST Pay sends webhooks for the following events. You can subscribe to individual events or use wildcards (e.g., payment.*) to receive all events in a group.

Payment Events

EventDescription
payment.createdA payment transaction has been initiated
payment.processingThe payment is being processed by the provider
payment.succeededThe payment was successfully completed
payment.failedThe payment could not be completed

Transfer Events

EventDescription
transfer.createdA wallet-to-wallet transfer has been initiated
transfer.succeededThe transfer completed successfully
transfer.failedThe transfer could not be completed

Payout Events

EventDescription
payout.createdA payout request has been created
payout.succeededThe payout was successfully settled
payout.failedThe payout could not be processed

Wallet Events

EventDescription
wallet.balance.updatedA wallet’s balance has changed
wallet.fee.appliedA fee has been deducted from a wallet

Transaction Lifecycle Events

EventDescription
transaction.createdA new transaction record has been created
transaction.updatedA transaction’s status or data has changed
transaction.reconciledA transaction has been reconciled

System & Admin Events

EventDescription
webhook.secret.rotatedYour webhook signing secret was rotated
webhook.subscription.enabledA webhook subscription was activated
webhook.subscription.disabledA webhook subscription was deactivated
account.updatedYour application account was updated
test.pingA test event to verify your endpoint

Webhook Payload

Every webhook delivers a JSON body with this structure:
{
  "data": {
    "id": "txn_abc123xyz",
    "wallet_id": "wallet_def456",
    "amount": 100.0,
    "currency": "USD",
    "status": "succeeded",
    "payment_method": "mobile_money",
    "reference_id": "pmc_789xyz",
    "created_at": "2025-01-16T10:25:00Z",
    "updated_at": "2025-01-16T10:30:00Z"
  },
  "metadata": {}
}
The status field inside data reflects the outcome extracted from the event type (e.g., succeeded, failed, created). The metadata object carries any additional context attached to the event.
The event type itself (payment.succeeded, payout.failed, etc.) is identified by the X-Webhook-Version and event delivery headers, not an event field in the body.

Request Headers

Every webhook delivery includes these headers:
HeaderDescription
X-HostPay-SignatureHMAC-SHA256 signature of the payload (format: v1=<hex>)
X-Webhook-TimestampUnix timestamp (seconds) when the webhook was sent
X-Webhook-VersionAPI version string (e.g., 2025-01)
X-Webhook-SourceAlways HostPay
X-HostPay-Delivery-IdUnique delivery ID for idempotency tracking
Idempotency-KeySame as X-HostPay-Delivery-Id

Setting Up Webhooks

1

Create Endpoint

Set up an HTTPS endpoint to receive POST requests (e.g., https://yourapp.com/webhooks/hostpay)
2

Configure in Dashboard

Add your webhook URL and select the events you want to subscribe to in the HOST Pay dashboard
3

Store Your Secret

Save the webhook secret displayed during setup — you’ll need it to verify signatures
4

Verify Signatures

Validate every incoming webhook’s signature before processing it
5

Test

Send a test.ping event from the dashboard and confirm your endpoint receives it

Security

Always verify webhook signatures before processing any event. Skipping this step opens your application to spoofed requests.

How Signatures Work

HOST Pay signs every outgoing webhook using HMAC-SHA256. The signing input is:
timestamp + "." + compact_json_body
Where:
  • timestamp is the Unix epoch value from the X-Webhook-Timestamp header (as a string).
  • compact_json_body is the raw request body serialized with no extra whitespace.
The resulting signature is hex-encoded and placed in the X-HostPay-Signature header with a v1= prefix (e.g., v1=abc123...).

Verification Steps

  1. Extract the raw timestamp from X-Webhook-Timestamp.
  2. Read the raw request body before parsing it as JSON.
  3. Construct the signed string: timestamp + "." + raw_body.
  4. Compute HMAC-SHA256(secret, signed_string) and hex-encode it.
  5. Compare your result to the value after the v1= prefix using a constant-time comparison.
  6. Optional but recommended: Reject requests where the timestamp is more than 5 minutes old to prevent replay attacks.

Handling Webhooks

from flask import Flask, request, abort
import hmac
import hashlib
import time
import json

app = Flask(__name__)
WEBHOOK_SECRET = "your_webhook_secret"

@app.route("/webhooks/hostpay", methods=["POST"])
def handle_webhook():
    # Read raw body before any parsing
    raw_body = request.get_data(as_text=True)

    # Extract headers
    signature_header = request.headers.get("X-HostPay-Signature", "")
    timestamp = request.headers.get("X-Webhook-Timestamp", "")

    # Reject stale webhooks (optional but recommended)
    if abs(time.time() - int(timestamp)) > 300:
        abort(400, "Webhook timestamp too old")

    # Verify the signature
    if not verify_signature(raw_body, timestamp, signature_header):
        abort(401, "Invalid signature")

    # Parse and handle the event
    event = json.loads(raw_body)
    event_type = request.headers.get("X-Webhook-Version")  # dispatched via subscription
    data = event.get("data", {})

    # Route by status in the data payload
    status = data.get("status")
    if status == "succeeded":
        handle_success(data)
    elif status == "failed":
        handle_failure(data)

    return "OK", 200


def verify_signature(raw_body: str, timestamp: str, signature_header: str) -> bool:
    # Signature header format: "v1=<hex_digest>"
    if not signature_header.startswith("v1="):
        return False
    provided_sig = signature_header[3:]

    signed_string = f"{timestamp}.{raw_body}"
    expected_sig = hmac.new(
        WEBHOOK_SECRET.encode("utf-8"),
        signed_string.encode("utf-8"),
        hashlib.sha256,
    ).hexdigest()

    return hmac.compare_digest(provided_sig, expected_sig)


def handle_success(data):
    print(f"Transaction {data['id']} succeeded: {data['amount']} {data['currency']}")


def handle_failure(data):
    print(f"Transaction {data['id']} failed")

Best Practices

Return a 200 OK as soon as you receive the webhook, then process it in the background. HOST Pay considers any non-2xx response a failure and will retry.
The same event may be delivered more than once. Use the X-HostPay-Delivery-Id header or data.id to detect duplicates and skip re-processing.
Check that X-Webhook-Timestamp is within 5 minutes of your server time to prevent replay attacks. Reject requests that are too old.
Your endpoint must be served over HTTPS. HTTP endpoints will not receive webhooks.
Track failed deliveries in your logs and set up alerts. After all retry attempts are exhausted, no further automatic retries will occur.
Use Test Mode and the test.ping event to validate your endpoint before processing live events.

Retry Policy

If your endpoint returns a non-2xx status code or times out, HOST Pay retries the delivery automatically:
  • Up to 10 retry attempts after the initial delivery failure
  • Exponential backoff: the delay between attempts doubles each time, starting at 60 seconds and capping at 30 minutes
  • After all attempts are exhausted, the delivery is marked failed and no further retries occur
You can view delivery history and manually re-trigger failed webhooks from the HOST Pay dashboard.

Subscribing to Events

You can subscribe to specific events or use wildcard patterns:
PatternEvents Received
payment.*All payment events
payout.*All payout events
transfer.*All transfer events
wallet.*All wallet state change events
transaction.*All transaction lifecycle events
*Every event emitted by the platform

Testing Webhooks

Test Mode

All transactions made in Test Mode trigger real webhook deliveries to your subscribed endpoint

test.ping Event

Use the test.ping event from the dashboard to verify your endpoint is reachable and responding correctly

Next Steps

Currency & Conversions

Understand how multi-currency amounts appear in webhook payloads

Deposits Guide

Learn about deposit events and flows

Payouts Guide

Understand payout events and flows