Skip to main content
Every webhook delivery is signed using your webhook’s unique signing secret. You should always verify the signature before processing a delivery to ensure it hasn’t been tampered with and genuinely came from DeepSmith.

How signing works

  1. DeepSmith constructs the full JSON payload (envelope + data)
  2. The JSON string is signed using HMAC-SHA256 with your webhook’s signing_secret as the key
  3. The signature is sent in the X-Webhook-Signature header, prefixed with sha256=
X-Webhook-Signature: sha256=5d41402abc4b2a76b9719d911017c592...

Verification steps

1

Extract the signature header

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

Compute the expected signature

Calculate sha256= + HMAC-SHA256 of the raw request body using your signing secret.
3

Compare using constant-time comparison

Use a timing-safe comparison function to prevent timing attacks.

Code examples

const crypto = require("crypto");

function verifyWebhookSignature(rawBody, signatureHeader, secret) {
  const expected =
    "sha256=" +
    crypto.createHmac("sha256", secret).update(rawBody).digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(signatureHeader),
    Buffer.from(expected)
  );
}

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

  if (!verifyWebhookSignature(req.body, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).json({ error: "Invalid signature" });
  }

  const event = JSON.parse(req.body);
  // Process the event...
  res.status(200).json({ received: true });
});

HTTP headers

Every webhook delivery includes these headers:
HeaderDescriptionExample
Content-TypeAlways application/jsonapplication/json
X-Webhook-SignatureHMAC-SHA256 signature of the request bodysha256=5d41402abc...
X-Webhook-EventThe event typecontent.status_updated
X-Webhook-DeliveryUnique delivery ID (UUID) for idempotency9f2a3b4c-5d6e-...

Idempotency

The X-Webhook-Delivery header contains a unique UUID for each delivery attempt. Use this to deduplicate events if your endpoint receives the same delivery more than once (e.g., due to retries where your server processed the event but failed to return a 2xx response).
// Example: track processed delivery IDs
const processedDeliveries = new Set();

app.post("/webhooks/deepsmith", (req, res) => {
  const deliveryId = req.headers["x-webhook-delivery"];

  if (processedDeliveries.has(deliveryId)) {
    return res.status(200).json({ received: true, duplicate: true });
  }

  processedDeliveries.add(deliveryId);
  // Process the event...
  res.status(200).json({ received: true });
});
In production, use a persistent store (Redis, database) for deduplication instead of an in-memory set.

Security best practices

Always verify signatures

Never process a webhook without verifying the signature first.

Use HTTPS

Always use HTTPS endpoints. DeepSmith will deliver to HTTP URLs, but HTTPS prevents payload interception.

Use constant-time comparison

Avoid == or === for signature comparison. Use timingSafeEqual, hmac.compare_digest, or hash_equals.

Store secrets securely

Keep your signing secret in environment variables or a secrets manager, never in source code.