Documentation Index
Fetch the complete documentation index at: https://apidocs.hopnow.io/llms.txt
Use this file to discover all available pages before exploring further.
Signature Verification
HopNow signs every webhook payload using HMAC-SHA256 with your webhook secret. The signature is sent in the X-Webhook-Signature header:
X-Webhook-Signature: sha256=a1b2c3d4...
Always verify webhook signatures before processing events.
Implementation
import hmac
import hashlib
import json
from flask import Flask, request, abort
app = Flask(__name__)
WEBHOOK_SECRET = "your_webhook_secret"
def verify_signature(payload, signature, secret):
if signature.startswith("sha256="):
signature = signature[7:]
expected = hmac.new(
secret.encode("utf-8"),
payload.encode("utf-8"),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
@app.route("/webhook", methods=["POST"])
def handle_webhook():
payload = request.get_data(as_text=True)
signature = request.headers.get("X-Webhook-Signature")
if not verify_signature(payload, signature, WEBHOOK_SECRET):
abort(401)
event = json.loads(payload)
process_event(event)
return "", 200
Endpoint Requirements
- HTTPS only — HTTP URLs are rejected during endpoint creation
- Respond within 30 seconds — return 2xx quickly, process asynchronously if needed
- Return 2xx — any non-2xx response triggers a retry
Testing Signatures
Test your verification with known values:
def test_signature():
secret = "test_secret_123"
payload = '{"id":"evt_test","type":"account.created"}'
signature = hmac.new(
secret.encode("utf-8"),
payload.encode("utf-8"),
hashlib.sha256
).hexdigest()
assert verify_signature(payload, f"sha256={signature}", secret)
assert not verify_signature(payload, "sha256=invalid", secret)