Overview
Feedspace signs every webhook payload with a secret key using HMAC-SHA256. This signature is included in the X-Feedspace-Signature header of each request. By verifying this signature on your server before processing the payload, you can confirm the request genuinely came from Feedspace and reject any spoofed or tampered requests.
Why Signature Verification Matters
Without signature verification, any party that knows your webhook endpoint URL could send a forged payload to your server. Verifying the signature ensures:
- The payload was sent by Feedspace and has not been altered in transit.
- Your server ignores requests from unauthorized sources.
- Your downstream workflows and data remain trustworthy.
Always validate the signature before reading or acting on the payload.
Steps to Verify a Webhook Signature
Step 1: Get Your Webhook Secret
- In your Feedspace dashboard, go to Automation in the left sidebar.
- Click the Webhook tab.
- Find the webhook you want to verify and click Edit.
- Copy the Webhook Secret shown in the edit panel. This is the key you will use to compute the expected signature.
Store this secret securely as an environment variable on your server. Never hard-code it in your source code or commit it to version control.
Step 2: Read the Signature Header
When Feedspace sends a webhook request to your endpoint, it includes a header named X-Feedspace-Signature. This header contains the HMAC-SHA256 hash of the raw request body, encoded as a hex string. Read this value before you parse or modify the request body.
Step 3: Compute the Expected Signature
Using your webhook secret as the key, compute an HMAC-SHA256 hash of the raw request body (the exact bytes received, before any JSON parsing). Encode the result as a lowercase hex string.
Step 4: Compare the Signatures
Compare the computed hash against the value in the X-Feedspace-Signature header using a constant-time comparison. If they match, the request is authentic. If they do not match, reject the request with a 401 Unauthorized response and do not process the payload.
Use a constant-time comparison (such as crypto.timingSafeEqual in Node.js or hmac.compare_digest in Python) to prevent timing attacks.
Code Examples
Node.js Example
const crypto = require('crypto');
function verifyFeedspaceSignature(req, secret) {
const signatureHeader = req.headers['x-feedspace-signature'];
if (!signatureHeader) {
return false;
}
// req.rawBody must be the raw Buffer or string received before JSON parsing
const computedSignature = crypto
.createHmac('sha256', secret)
.update(req.rawBody)
.digest('hex');
const headerBuffer = Buffer.from(signatureHeader, 'hex');
const computedBuffer = Buffer.from(computedSignature, 'hex');
if (headerBuffer.length !== computedBuffer.length) {
return false;
}
// Use constant-time comparison to prevent timing attacks
return crypto.timingSafeEqual(headerBuffer, computedBuffer);
}
// Example Express.js handler
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const secret = process.env.FEEDSPACE_WEBHOOK_SECRET;
req.rawBody = req.body;
if (!verifyFeedspaceSignature(req, secret)) {
return res.status(401).json({ error: 'Invalid signature' });
}
const payload = JSON.parse(req.rawBody);
// Process payload here
res.status(200).json({ received: true });
});
Python Example
import hmac
import hashlib
import os
from flask import Flask, request, abort
app = Flask(__name__)
def verify_feedspace_signature(raw_body: bytes, signature_header: str, secret: str) -> bool:
if not signature_header:
return False
computed_signature = hmac.new(
secret.encode('utf-8'),
raw_body,
hashlib.sha256
).hexdigest()
# Use constant-time comparison to prevent timing attacks
return hmac.compare_digest(computed_signature, signature_header)
@app.route('/webhook', methods=['POST'])
def webhook():
secret = os.environ.get('FEEDSPACE_WEBHOOK_SECRET', '')
signature_header = request.headers.get('X-Feedspace-Signature', '')
raw_body = request.get_data()
if not verify_feedspace_signature(raw_body, signature_header, secret):
abort(401)
payload = request.get_json(force=True)
# Process payload here
return {'received': True}, 200
Important Notes
- Use the raw request body: Compute the HMAC over the exact bytes received. Parsing the JSON first and re-serialising it can change byte ordering or whitespace and cause a mismatch.
- Use constant-time comparison: Standard string equality checks are vulnerable to timing attacks. Always use
crypto.timingSafeEqual (Node.js) or hmac.compare_digest (Python).
- Store the secret securely: Keep your webhook secret in an environment variable or a secrets manager. Rotate it if you suspect it has been exposed.
- Reject early: Return a
401 response immediately if the signature is missing or invalid. Do not process any part of the payload before verification passes.
Troubleshooting
Signature always fails even with the correct secret
The most common cause is that your framework has already parsed the request body before your handler runs. Make sure you are computing the HMAC over the raw bytes, not over a re-serialised JSON object. In Express.js, use express.raw() middleware instead of express.json() for the webhook route. In Flask, call request.get_data() before request.get_json().
The header is missing
If the X-Feedspace-Signature header is absent, the request was not sent by Feedspace or was stripped by a proxy. Reject it with a 401 response.
I rotated the secret but signatures still fail
After saving a new secret in Feedspace, redeploy or restart your server so it picks up the updated environment variable. If your server cached the old secret value, it will continue to reject valid requests until the cache is cleared.
You might also find helpful
How to Use Webhooks in Feedspace
Overview Feedspace provides webhooks to notify your application about important events in real-time. With webhooks, you can automatically trigger workflows in your system whenever reviews are collected on Feedspace. Reference: Feedspace Webhook Documentation Step 1 – Access Automation Webhook Step 2 – Add Your Endpoint URL Step 3 – Select...
How Do I Test My Feedspace Webhook Integration?
Overview Before connecting your production server, you can test your Feedspace webhook integration using a free temporary URL service. This lets you inspect the exact JSON payload Feedspace sends, confirm your event selection is correct, and validate your signature verification code, all without writing a single line of server code...
What Is the Feedspace REST API?
Overview The Feedspace REST API is a JSON-based HTTP API that gives developers direct, programmatic access to their Feedspace workspace. Using the API, you can submit reviews from your own application, retrieve reviews to display in a custom UI, automate review pipelines, and sync Feedspace data with external systems, all...