Signature Verification
Every webhook request sent by Sangho contains a Sangho-Signature header. This header is an HMAC-SHA256
signature computed with your webhook secret over the raw body of the
request.
Why verify the signature
- Guarantee that the request genuinely comes from Sangho.
- Protect against replay attacks.
- Validate the integrity of the request body.
- Prevent the processing of forged webhooks.
Header format
The Sangho-Signature header contains the HMAC-SHA256
signature of the body in hexadecimal, prefixed with sha256=.
Two complementary headers are also sent:
| Header | Description |
|---|---|
Sangho-Signature | sha256=<hex> — HMAC signature of the raw body. |
Sangho-Event-ID | Unique identifier of the event — use it for deduplication. |
Sangho-Timestamp | Unix timestamp of when it was sent — protects against replays. |
The signature is computed over the raw body (raw bytes) of the
request, before any JSON parsing. In Express.js, configure express.raw({ type: 'application/json' }) — never go
through express.json(), which would transform the body
before verification.
Best practices
- Verify the signature before processing the event.
- Return HTTP 200 immediately — process in the background.
- Store the
Sangho-Event-IDto deduplicate multiple deliveries. - Compare signatures using a method resistant to timing attacks (
timingSafeEqual,hmac.compare_digest,hash_equals).
Headers of a webhook request
Manual verification (openssl)
Implementation by language
Each example on the right illustrates the complete verification: reading the raw body, computing the HMAC-SHA256, a comparison resistant to timing attacks, then JSON parsing.
For the official SDKs, use the helper method directly: sangho.webhooks.constructEvent(body, signature, secret) in
JavaScript.
Your endpoint must return a 200 within
30 seconds. Queue the task in an asynchronous queue (Celery, Bull,
Sidekiq…) and respond immediately. Past this delay, Sangho considers the delivery
failed and triggers the retry policy.