Webhook Setup Guide
Integrate webhook to receive updates about your live order.
Webhooks let you receive real-time updates whenever a certificate file is created or updated.
Follow these steps to set up and handle webhooks securely.
Registering Your Webhook
Section titled “Registering Your Webhook”- Go to your Organisation Dashboard.
- Navigate to Accounts & Support → API.
- Register your Webhook URL.
- Copy your Webhook Secret (you’ll need it for verification).
Webhook Payload
Section titled “Webhook Payload”Whenever a certificate file is created or updated, we’ll send a POST request to your webhook URL with the following JSON body:
{ "url": "https://example.com/certificates/12345.pdf", "name": "certificate.pdf", "orderId": "abc123", // the documentId of your live order "orderFulfillmentId": "xyz789" // the documentId of your live order fulfillment}Security & Signature Verification Flow
Section titled “Security & Signature Verification Flow”To ensure that webhook requests are authentic and untampered, you must verify the
x-signature header included with every webhook request.
Step-by-step Flow
Section titled “Step-by-step Flow”-
Receive Webhook
- Your server receives a
POSTrequest with JSON body. - Request contains a header:
x-signature: <hex-string>
- Your server receives a
-
Extract Raw Body
- Get the raw (unparsed) request body exactly as received.
- ⚠️ Using parsed JSON may lead to mismatches.
-
Compute HMAC SHA256
- Use your
WEBHOOK_SECRET(from dashboard). - Compute:
HMAC_SHA256(raw_body, WEBHOOK_SECRET) → hex digest
- Use your
-
Compare Signatures
- Compare computed digest with
x-signatureheader. - If they match → ✅ request is authentic.
- If they don’t match → ❌ reject request (possible spoofing or tampering).
- Compare computed digest with
-
Process Payload
- Once verified, you can trust the request and safely handle the payload.
Sample Code
Section titled “Sample Code”import express from "express";import crypto from "crypto";
const app = express();const PORT = process.env.PORT || 3000;const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET || "your_webhook_secret_here";
app.use( express.json({ verify: (req, res, buf) => { req.rawBody = buf; // keep raw body for signature verification }, }));
function verifySignature(req) { const signature = req.headers["x-signature"]; if (!signature) return false;
const computedSig = crypto .createHmac("sha256", WEBHOOK_SECRET) .update(req.rawBody) .digest("hex");
return crypto.timingSafeEqual( Buffer.from(signature, "utf8"), Buffer.from(computedSig, "utf8") );}
app.post("/webhook", (req, res) => { if (!verifySignature(req)) { return res.status(401).send("Invalid or missing signature"); }
const { url, name, orderId, orderFulfillmentId } = req.body; console.log("✅ Certificate:", url, name, orderId, orderFulfillmentId);
res.send("Webhook received and verified");});
app.listen(PORT, () => { console.log(`🚀 Server running on http://localhost: ${PORT}`);});import hmacimport hashlibfrom flask import Flask, request, abort
app = Flask(__name__)WEBHOOK_SECRET = "your_webhook_secret_here"
def verify_webhook(raw_body: bytes, signature: str) -> bool: computed_sig = hmac.new( WEBHOOK_SECRET.encode("utf-8"), raw_body, hashlib.sha256 ).hexdigest() return hmac.compare_digest(computed_sig, signature)
@app.route("/webhook", methods=["POST"])def webhook(): signature = request.headers.get("x-signature") if not signature: abort(400, "Missing signature")
if not verify_webhook(request.data, signature): abort(401, "Invalid signature")
url = payload.get("url") name = payload.get("name") order_id = payload.get("orderId") order_fulfillment_id = payload.get("orderFulfillmentId")
print("✅ Certificate:", url, name, order_id, order_fulfillment_id)
return "Webhook received and verified", 200
if __name__ == "__main__": app.run(port=3000, debug=True)