B2B platform for digital goods

FoxReload Webhooks Integration β€” HMAC-SHA256, Retries, Idempotency

Set up reliable webhook handling for FoxReload order events: signature verification, retry windows, and idempotency.

FoxReload Webhooks Integration β€” HMAC-SHA256, Retries, Idempotency

Polling GET /v1/orders/{id} every few seconds wastes quota and adds latency. FoxReload webhooks push order-status transitions to your endpoint within ~200 ms of the event firing in our fulfilment engine. This guide is for engineers who need a battle-tested webhook integration β€” signature verification, retry semantics, and idempotency.

1. Register your endpoint

In Settings β†’ Webhooks, register an HTTPS endpoint (HTTP is rejected). You will receive a unique webhook_secret per endpoint β€” a 64-byte random value used to sign payloads. Subscribe to specific event types to avoid noise:

  • order.reserved
  • order.processing
  • order.delivered
  • order.failed
  • balance.low

You can register up to 10 endpoints per account (e.g. one for production, one for staging, one for internal Slack alerts).

2. Verify the HMAC-SHA256 signature

Every webhook arrives with a X-FoxReload-Signature header of the form t=1715990400,v1=5257a8.... Compute HMAC-SHA256 over the string {timestamp}.{raw_body} using your webhook_secret and compare in constant time.

import hmac, hashlib, time

def verify(raw_body: bytes, header: str, secret: str) -> bool:
    parts = dict(p.split("=") for p in header.split(","))
    ts, sig = parts["t"], parts["v1"]
    if abs(time.time() - int(ts)) > 300:   # reject if older than 5 min
        return False
    signed = f"{ts}.{raw_body.decode()}".encode()
    expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, sig)

Reject any request that fails verification with HTTP 401. The 5-minute timestamp window blocks replay attacks.

3. Retry logic and idempotency

FoxReload retries failed deliveries (any non-2xx response or timeout >10 s) on this schedule: 30 s, 2 min, 10 min, 1 h, 6 h, 24 h β€” up to 6 attempts. After the final attempt the event is moved to your Dead Letter Queue, visible in the dashboard for 30 days.

Every delivery includes X-FoxReload-Event-Id (UUID). Store this ID with a unique constraint in your database and ignore duplicates. Network blips routinely cause our retry layer to deliver the same event twice β€” your handler must be safe to call N times.

4. Acknowledge fast, process async

Respond HTTP 200 within 5 seconds. If your downstream processing (DB writes, email, reconciliation) takes longer, enqueue the payload onto SQS/Kafka and acknowledge immediately. Slow handlers cause cascading retries, duplicate events, and rate-limit warnings on your endpoint.

If your endpoint is down for >24 hours, FoxReload auto-pauses deliveries and sends an email alert β€” you can replay missed events from the dashboard once you redeploy.

Want a working reference implementation? FoxReload publishes open-source webhook handler examples in Node.js, Python, and Go, plus a hosted webhook tester β€” register at foxreload.com and skip the typical week of debugging signature verification edge cases.

Get FoxReload API access

Related articles