Skip to main content
Every webhook subscription in Raise carries a 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.
Verify the signature on every incoming webhook request — before processing, before logging the payload, before any side effects. An endpoint that processes unverified requests is open to forged events that could create duplicate gifts, send unwanted emails, sync bad data, or trigger any other side effect the integration takes on event arrival.

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: the WebhookRequest carries a securityToken field that the partner sets when creating or updating a subscription. This is the shared secret.
What the spec does not document:
⚠️ 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 the securityToken with a cryptographically secure random source — at least 32 bytes of entropy:
JavaScript
The format (base64, hex, or any other encoding) doesn’t matter as long as the underlying entropy is strong. What matters: Store the token in your secrets manager. The partner side will read it on every incoming request to verify signatures.

The verification pattern

The general verification pattern, in pseudocode:
Each step matters:

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-Signature
  • X-Signature
  • Signature
  • X-Webhook-Signature
Inspect a real delivery via the 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
For Next.js API routes:
JavaScript
The exact mechanism for capturing the raw body depends on your framework. The principle is the same: bytes as Raise sent them, before any deserialization.

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
If the comparison consistently fails despite known-good test deliveries, try variants:
  • 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)
The signature algorithm is consistent across deliveries — once you’ve identified the correct combination by inspecting real deliveries, code it directly.

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
The order matters: verify, acknowledge, queue. Don’t process inside the request handler.

A complete verification function

JavaScript
Confirm the algorithm (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.
1

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.
2

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
3

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.
4

Confirm new deliveries verify with the new secret

Check webhook logs for successful deliveries after the update. Confirm signatures verify with the new secret.
5

Remove the old secret

After a brief window (long enough to confirm no stragglers signed with the old secret), remove the old secret from your secrets manager. The verification now only accepts the new secret.
The dual-secret window can be brief — Raise’s signing changes happen on the very next delivery after 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:

Handling verification failures

A signature verification failure typically indicates one of: For each failure:
  • 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

These metrics help distinguish “the secret was rotated incorrectly” from “an attacker is probing the endpoint.”

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.
Last modified on May 20, 2026