Pipes.bot

Webhook Security

Verify webhook signatures to ensure payloads are from Pipes.bot.

When you configure a webhook secret, Pipes.bot signs every payload with an HMAC-SHA256 signature. You should verify this signature on your server to ensure the request actually came from Pipes.bot.

Signature header

Pipes.bot includes the signature in the X-Pipes-Signature header as a hex-encoded HMAC-SHA256 digest of the raw request body.

X-Pipes-Signature: a1b2c3d4e5f6...

Verifying the signature

Compute the HMAC-SHA256 digest of the raw request body using your webhook secret, then compare it to the header value using a timing-safe comparison.

Node.js / Express

import crypto from "crypto";

function verifySignature(rawBody: string | Buffer, signature: string, secret: string): boolean {
  const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");

  return crypto.timingSafeEqual(Buffer.from(signature, "hex"), Buffer.from(expected, "hex"));
}

// Express middleware
app.post("/webhook", express.text({ type: "application/json" }), (req, res) => {
  const signature = req.headers["x-pipes-signature"] as string;

  if (!signature || !verifySignature(req.body, signature, WEBHOOK_SECRET)) {
    return res.status(401).send("Invalid signature");
  }

  const payload = JSON.parse(req.body);
  // Process verified payload...
  res.sendStatus(200);
});

Python / Flask

import hmac
import hashlib

def verify_signature(raw_body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(), raw_body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)

@app.route("/webhook", methods=["POST"])
def webhook():
    signature = request.headers.get("X-Pipes-Signature", "")

    if not verify_signature(request.data, signature, WEBHOOK_SECRET):
        return "Invalid signature", 401

    payload = request.get_json()
    # Process verified payload...
    return "", 200

Best practices

  • Always verify signatures in production. Without verification, anyone who discovers your endpoint URL can send fake payloads.
  • Use a strong secret. Generate one with the dashboard button or use at least 32 random hex characters.
  • Use timing-safe comparison. Standard string equality (===, ==) is vulnerable to timing attacks. Use crypto.timingSafeEqual (Node.js) or hmac.compare_digest (Python).
  • Parse after verifying. Verify the signature against the raw request body before parsing JSON to avoid discrepancies from re-serialization.

On this page