Skip to main content
Stairoids signs every webhook delivery with an HMAC-SHA256 signature so you can verify that the payload was genuinely sent by Stairoids and has not been tampered with in transit. Verifying signatures is strongly recommended for any production endpoint — without it, your webhook handler has no way to distinguish a legitimate Stairoids delivery from a spoofed or replayed request.
Always use a constant-time comparison function when comparing signatures — timingSafeEqual in Node.js, hmac.compare_digest in Python, hash_equals in PHP. Standard string equality (===, ==) is vulnerable to timing attacks that can allow an attacker to forge a valid signature byte by byte.

How Signatures Work

When Stairoids delivers a webhook, it includes an X-Stairoids-Signature header formatted as:
X-Stairoids-Signature: sha256=<hex-digest>
The hex digest is computed by running HMAC-SHA256 over the raw request body bytes using your webhook secret as the key. To verify the signature in your handler:
1

Extract the signature header

Read the X-Stairoids-Signature header from the incoming request.
2

Get the raw request body

Capture the raw bytes of the request body before any JSON parsing. Parsing and re-serialising the JSON may alter whitespace or key ordering and will produce a different digest.
3

Compute the expected signature

Compute HMAC-SHA256(raw_body, webhook_secret) and prepend sha256= to the hex-encoded result.
4

Compare using constant-time equality

Compare your computed signature against the header value using a timing-safe comparison function. If they match, the payload is authentic.
Use the raw request body bytes — do not parse and re-serialize the JSON before hashing. Even a single whitespace difference between the original bytes and your re-serialised string will cause verification to fail.

Verification Code

const crypto = require('crypto');

/**
 * Verifies an incoming Stairoids webhook signature.
 *
 * @param {Buffer | string} rawBody   - The raw, unparsed request body bytes.
 * @param {string}          signature - The value of the X-Stairoids-Signature header.
 * @param {string}          secret    - Your webhook endpoint secret from the dashboard.
 * @returns {boolean} true if the signature is valid, false otherwise.
 */
function verifySignature(rawBody, signature, secret) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');

  // Use timingSafeEqual to prevent timing attacks.
  const expectedBuf = Buffer.from(expected);
  const signatureBuf = Buffer.from(signature);

  if (expectedBuf.length !== signatureBuf.length) {
    return false;
  }

  return crypto.timingSafeEqual(expectedBuf, signatureBuf);
}

// Example usage in an Express handler:
app.post('/hooks/stairoids', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-stairoids-signature'];
  const secret = process.env.STAIROIDS_WEBHOOK_SECRET;

  if (!verifySignature(req.body, signature, secret)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  const event = JSON.parse(req.body);
  // Handle event...
  res.status(200).send('OK');
});

Finding Your Webhook Secret

Your webhook secret is unique to each registered endpoint. To find it:
  1. Open the Stairoids dashboard and navigate to Settings → Webhooks.
  2. Click on the endpoint whose secret you need.
  3. Click Reveal secret to display the value.
  4. Copy the secret and store it as an environment variable in your application — never hardcode it in source code.
Your webhook secret grants the ability to forge valid signatures. Treat it like a password: store it in a secrets manager or environment variable, never commit it to source control, and rotate it immediately if you suspect it has been compromised.

Rotating Your Webhook Secret

If you need to rotate the secret — for example, after a suspected exposure — follow these steps to avoid dropping events during the transition:
1

Generate a new secret

In the Stairoids dashboard, go to Settings → Webhooks, click your endpoint, and click Rotate secret. Stairoids will generate a new secret and briefly accept signatures from both the old and new secret during a 15-minute overlap window.
2

Update your application

Deploy the new secret as an environment variable in your webhook handler. Verify that your application is using the new secret and that verification is passing on incoming deliveries.
3

Revoke the old secret

Return to Settings → Webhooks, click your endpoint, and click Confirm rotation to immediately invalidate the old secret. Deliveries signed with the old secret will now fail verification.
During the rotation overlap window you can run both old and new secrets in parallel in your verification code — try the new secret first, and fall back to the old secret — to achieve a zero-downtime rotation.