AnswerLine Sign in Start free

Engineering · Tutorials

Verify webhook signatures before trusting task results

A webhook receiver is an unauthenticated public endpoint by definition. Anyone who finds the URL can POST a fake task.completed and poison your data. Signature verification is the one line of defense — here’s how to do it correctly.

What the signature proves

Every delivery carries a signature header computed over the raw request body with your webhook secret. Verifying it proves the payload came from us and wasn’t modified in transit. The timestamp in the header bounds replay: reject events older than a few minutes even with a valid signature.

Python

import hashlib, hmac, time
from flask import request

SECRET = "whsec_…"

def verify() -> bool:
    sig = request.headers.get("X-Signature", "")
    ts = request.headers.get("X-Timestamp", "")
    if abs(time.time() - int(ts)) > 300:
        return False
    expected = hmac.new(SECRET.encode(), f"{ts}.".encode() + request.get_data(), hashlib.sha256).hexdigest()
    return hmac.compare_digest(sig, expected)

@app.post("/hook")
def hook():
    if not verify():
        return "bad signature", 401
    event = request.get_json()
    if event["type"] == "task.completed":
        store(event["task"]["result"])
    return "", 200

Node

import crypto from "node:crypto";

function verify(rawBody: Buffer, sig: string, ts: string): boolean {
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
  const expected = crypto.createHmac("sha256", SECRET).update(`${ts}.`).update(rawBody).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}

The mistakes that matter

Full delivery format, retry schedule and event types in the webhooks guide.

Try it on your own prompts

500 free credits a month, no card. One POST returns the answer, sources and citations as JSON.

Keep reading