Skip to main content
Webhooks are inbound HTTP requests from Robase to your server. Without verification, an attacker who knows your endpoint URL can post fake events — “this OTP was delivered” — and walk past your auth. This guide covers the three layers you need.

1. Verify the signature

Every webhook request includes:
v1 is the hex of HMAC-SHA256 over {t}.{raw_body} with your webhook’s secret. Always verify before reading the body.
Verify against the raw body bytes, not a re-serialized version. JSON parsers often reorder keys or normalize whitespace — that breaks HMAC. In Express you need express.raw; in Flask get_data(); in Go io.ReadAll(r.Body) before any decode.

2. Reject stale timestamps

Signatures expire. The SDK helpers reject any signature whose timestamp is more than 5 minutes old by default. This blocks replay attacks where an attacker captures a signed request and re-posts it later. To loosen or tighten the window:
If your server’s clock drifts (containers without NTP can drift minutes), 300 seconds is a reasonable default. Monitor clock skew — your SRE checklist should include NTP sync.

3. Idempotent processing

Robase retries failed webhooks with exponential backoff (1s → 5s → 30s → 2m → 15m → 1h → 6h, 7 attempts). Your handler may receive the same event more than once. Use the id field (guaranteed unique, ULID-like) to deduplicate:
Or, if you persist events to a table with id as the primary key, a duplicate insert + ON CONFLICT DO NOTHING gets you the same guarantee with one SQL round trip.

4. Respond fast, process later

Your webhook handler should return 200 in under a few hundred milliseconds. If downstream work (sending email, updating a CRM, running a background job) takes longer, enqueue it:
If you block the handler waiting on downstream work, you risk:
  • Timeouts — Robase treats >10s as a failure and retries. Repeated retries pile up on your queue.
  • Chain failures — your slow handler ties up worker slots, slowing unrelated traffic.

5. Don’t leak the secret

A developer debugging a signature failure might console.log(secret) — and suddenly it’s in every SaaS log aggregator you send to. Log only the first 6 chars + if you need a hint.
An attacker who knows the URL can send crafted payloads (they’ll fail signature verification, but they cost you CPU). Put your webhook behind a path only you know, e.g. /webhooks/robase/<random-16-char-token>.
Dashboard → Webhooks → your webhook → Rotate secret. The old secret is invalidated immediately; you’ll need to redeploy with the new one.

Common bugs