securityToken — a shared secret between Raise and the partner endpoint. Raise uses this token to sign outgoing webhook deliveries; the partner endpoint verifies the signature on every incoming request to confirm the request actually came from Raise rather than from a forger.
Signature verification is not optional. Without it, any party that learns the partner’s webhook URL can deliver forged events to the integration — and the partner endpoint has no way to know the difference. This page covers the verification pattern, the practical defenses, and what’s known about the exact algorithm.
The signing model
The mechanism follows the standard webhook signature pattern used by Stripe, GitHub, and most other major API platforms: The two sides hold the same shared secret. Raise computes a signature over the request body using the secret; the partner computes the same signature using its copy of the secret. If both signatures match, the partner knows the request came from Raise. If the partner’s recomputed signature doesn’t match what Raise sent, the request didn’t come from Raise — or the request body was tampered with in transit. Either way, reject it.What the spec confirms
The Raise OpenAPI spec is explicit about one thing: theWebhookRequest carries a securityToken field that the partner sets when creating or updating a subscription. This is the shared secret.
| Concern | Documented? |
|---|---|
| Where the signature is sent in the HTTP request (header name) | No |
| The hashing algorithm (HMAC-SHA256? SHA1?) | No |
| The exact bytes that are signed (body? body + timestamp? canonicalized body?) | No |
| The signature encoding (hex? base64?) | No |
| Whether a timestamp is included for replay protection | No |
⚠️ Spec gap: The Raise OpenAPI spec doesn’t document the exact signature algorithm or the header name that carries the signature. The patterns on this page describe the general HMAC-based approach that most webhook systems use and that the
securityToken field strongly suggests Raise uses; partners building production integrations should confirm the exact algorithm and header name against the live API or with the platform team before deployment.Until the spec is updated, the practical approach is to inspect a few real webhook deliveries (via the webhook log endpoints, or by capturing a request at the partner endpoint) to identify the signature header and confirm the algorithm.Generating a strong security token
The strength of signature verification is bounded by the strength of the secret. A predictable or short secret can be guessed; a strong secret cannot. Generate thesecurityToken with a cryptographically secure random source — at least 32 bytes of entropy:
JavaScript
| Don’t use | Use instead |
|---|---|
A human-chosen password (MyWebhookSecret123!) | Cryptographically random bytes |
| A short string (less than 16 bytes of entropy) | At least 32 bytes |
| The same token across all customers | One token per customer subscription |
| The same token across the partner’s API key | A separate token specific to the webhook |
The verification pattern
The general verification pattern, in pseudocode:Step 1: read the signature header
The exact header name isn’t documented in the spec. Partner integrations need to identify it by inspecting real deliveries. Likely candidates based on common webhook conventions:X-Raise-SignatureX-SignatureSignatureX-Webhook-Signature
serverResponse field on a webhook log entry, or capture an incoming request at the partner endpoint and read the headers.
JavaScript
Step 2: read the raw body
Don’t parse the JSON yet. The signature is typically computed over the exact bytes of the request body. If your HTTP framework parses the body to JSON before you can access the raw bytes, the signature comparison will silently fail because re-serializing the parsed JSON produces different bytes than the original. For Express:JavaScript
JavaScript
Step 3: compute the HMAC
The standard webhook signature algorithm is HMAC-SHA256. Until the Raise spec confirms the exact algorithm, start with this assumption:JavaScript
digest('base64')instead of'hex''sha1'instead of'sha256'- Including a timestamp prefix (
${timestamp}.${rawBody}) if Raise includes a timestamp header - A different secret encoding (base64-decoded vs. raw string)
Step 4: constant-time comparison
A naive string-equality comparison can leak information about the secret through timing attacks. Use a constant-time comparison:JavaScript
crypto.timingSafeEqual (in Node.js) and equivalent functions in other languages produce a comparison that takes the same amount of time regardless of how many characters match. This prevents an attacker from learning the signature byte-by-byte through timing observation.
Step 5: parse and process
Only after verification passes should the body be parsed and processed:JavaScript
A complete verification function
JavaScript
sha256), the digest encoding (hex), and the header name (x-raise-signature) against actual deliveries before relying on this in production. The function structure stays the same; only the specifics may need adjustment.
Rotating the secret
Webhook secrets should be rotated periodically — typically every 90 days for high-sensitivity integrations, every 6–12 months for lower-sensitivity ones. The challenge: rotation must be coordinated so both sides hold the same secret at all times, without dropping deliveries during the cutover.The dual-secret rotation pattern
The standard pattern: the partner accepts either the old or the new secret during the rotation window.Generate a new secret
On the partner side, generate a fresh
securityToken. Store it alongside the existing one in your secrets manager, both marked as valid.Update the verification to accept either secret
The verification function tries the new secret first, then falls back to the old one. Both pass during the rotation window.
JavaScript
Update the webhook subscription with the new secret
PUT /api/Webhook/{id} with securityToken set to the new value. Raise begins signing with the new secret immediately on the next delivery.Confirm new deliveries verify with the new secret
Check webhook logs for successful deliveries after the update. Confirm signatures verify with the new secret.
PUT /api/Webhook/{id} completes — but the window is what prevents any in-flight requests from being rejected during the cutover.
When to force a rotation
In addition to scheduled rotation:| Trigger | When |
|---|---|
| Suspected compromise | If the secret may have leaked (log exposure, debugging session, etc.) — rotate immediately, don’t wait for the schedule. |
| Personnel changes | If someone with access to the secret leaves the partner organization. |
| Repository exposure | If the secret was accidentally committed to source control. |
| Cutover to a new partner endpoint | When migrating to a new notificationUrl, also rotate the secret. |
Handling verification failures
A signature verification failure typically indicates one of:| Cause | Likelihood | Action |
|---|---|---|
| Genuine forged request | Low for most integrations | Reject. Don’t process. Log for review. |
| Misconfigured secret | High during initial setup | Re-check the secrets manager — partner copy must match Raise’s. |
| Body parsing before verification | Common implementation bug | Make sure raw body is read before any JSON parsing. |
| Mid-rotation timing | Possible during rotation windows | Dual-secret pattern (above) prevents this. |
| Wrong algorithm or header | Possible during initial integration | Inspect real deliveries to confirm. |
- Always reject the request with
401 Unauthorized. Don’t try to “recover” by processing anyway — a forged request that’s processed is worse than a real request that’s rejected. - Log the failure (without logging the secret or the raw signature) for diagnostic review.
- Alert on patterns — sustained verification failures over time indicate either a configuration issue or an active attack worth investigating.
Metrics worth tracking
| Metric | Threshold for alert |
|---|---|
| Verification failure rate | >1% sustained over 10 minutes |
| Verification failures per minute | Spike >10x baseline |
| Time since last successful verification | >1 hour during active delivery window |
Layered defenses
Signature verification is the primary defense against forged events. A few layered defenses add resilience:HTTPS only
The webhook URL must use HTTPS. Plain HTTP would expose the signature in transit and make man-in-the-middle attacks easier. Most modern HTTP frameworks enforce HTTPS for webhook receivers by default; confirm yours does.IP allowlisting (optional)
For partner integrations with very high security requirements, allowlisting Raise’s delivery IPs adds a network-layer defense before requests even reach the application. Coordinate with the platform team for the canonical IP ranges, and update the allowlist whenever they change. This is belt-and-suspenders — signature verification alone is sufficient for most integrations. IP allowlisting helps but doesn’t replace signature verification.Rate limiting on the webhook endpoint
Even with signature verification, an attacker can flood the endpoint with forged requests forcing the application to compute HMAC for each. Apply rate limiting at the edge (load balancer, API gateway) to prevent this.No echoing of received content
Don’t include the received request body in error responses or logs without sanitization. A misconfigured endpoint that echoes unverified content can leak information about the integration’s processing logic.Where to go next
Retry Behavior
What happens when the partner endpoint returns a non-success response — including signature failures.
Idempotency and Safe Reprocessing
Handle duplicate deliveries from retries.
Webhooks Overview
The full webhook subscription lifecycle.
Security and Credential Management
Storing and rotating webhook secrets alongside other integration credentials.