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

Compatibility: signing is required

With the webhook safeguards release, HOST Pay no longer sends an unsigned webhook when signing fails. A missing, inactive or unreadable secret is recorded as a delivery error before HTTP is attempted. This rule applies to automatic and manual delivery, in both live and sandbox mode. Use the raw webhook signing secret returned when you create or rotate the subscription. This is separate from your API authentication secret. Do not use a stored hash or masked preview as the HMAC key, and do not hash the raw secret first. If the Delivery Log reports a signing error, contact your HOST Pay operator to check the encryption configuration. If rotation is necessary, save the new secret and update your receiver immediately; rotation changes the secret used for future attempts, including retries of older deliveries. There is no timed dual-signing grace period. Coordinate the change without disabling signature verification. See the upgrade notice for endpoint requirements. Requests to live endpoints verify the TLS certificate and hostname; self-signed or mismatched certificates will fail delivery.

How signatures work

HOST Pay signs every outgoing webhook using HMAC-SHA256. The signing input is:
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.
The official SDKs implement all of the steps above — constant-time comparison and replay protection included. Pass the raw request body and headers straight from your framework:
Deliveries older than 5 minutes are rejected by default (pass a custom tolerance in seconds to change this).

Verifying Manually

Implementing the check yourself in another language:
Read the raw request body before any JSON parsing — re-serializing the parsed body changes whitespace and breaks signature verification.