Skip to main content

Integration guide

Outbound Webhooks

Gjall can POST a JSON payload to any HTTPS endpoint when a vendor alert is detected. Use webhooks to route alerts into your own systems, trigger automations, or fan out to services not natively supported.

Configure your webhook

1
Go to Settings → Integrations.
2
Under Webhook, enter your HTTPS endpoint URL and click Save.
3
Copy the webhook secretshown after saving. Store it as an environment variable on your server — you'll need it to verify request authenticity.

Tip: Gjall retries failed deliveries up to 3 times with exponential backoff. Your endpoint should return a 2xx status to acknowledge receipt.

Payload format

Every webhook delivery sends a POST with Content-Type: application/json.

{
  "event":       "alert.created",
  "vendor":      "Stripe",
  "severity":    "high",
  "type":        "cve",
  "title":       "CVE-2025-12345 in Stripe API library",
  "description": "A reflected XSS vulnerability was found in ...",
  "source_url":  "https://nvd.nist.gov/vuln/detail/CVE-2025-12345",
  "detected_at": "2025-06-09T03:15:22Z",
  "customer_id": "cust_abc123"
}

Verifying the signature

Every request includes a X-Gjall-Signature header containing an HMAC-SHA256 signature of the raw request body. Always verify this before processing the payload.

Python

import hashlib
import hmac
import os

WEBHOOK_SECRET = os.environ["GJALL_WEBHOOK_SECRET"]

def verify_signature(body: bytes, signature_header: str) -> bool:
    """Return True if the request is from Gjall."""
    expected = "sha256=" + hmac.new(
        WEBHOOK_SECRET.encode(),
        body,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, signature_header)

# Flask example
from flask import Flask, request, abort

app = Flask(__name__)

@app.post("/gjall-webhook")
def gjall_webhook():
    sig = request.headers.get("X-Gjall-Signature", "")
    if not verify_signature(request.data, sig):
        abort(401)

    event = request.json
    print(f"Alert: {event['title']} — {event['vendor']} ({event['severity']})")
    return {"ok": True}

Node.js / TypeScript

import crypto from "crypto"

const WEBHOOK_SECRET = process.env.GJALL_WEBHOOK_SECRET!

function verifySignature(body: Buffer, signatureHeader: string): boolean {
  const expected =
    "sha256=" +
    crypto.createHmac("sha256", WEBHOOK_SECRET).update(body).digest("hex")
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signatureHeader)
  )
}

// Express example
import express from "express"
const app = express()

app.post("/gjall-webhook", express.raw({ type: "*/*" }), (req, res) => {
  const sig = req.headers["x-gjall-signature"] as string ?? ""
  if (!verifySignature(req.body as Buffer, sig)) {
    return res.status(401).json({ error: "Invalid signature" })
  }

  const event = JSON.parse(req.body.toString())
  console.log(`Alert: ${event.title} — ${event.vendor} (${event.severity})`)
  res.json({ ok: true })
})

Headers reference

HeaderValue
Content-Typeapplication/json
X-Gjall-Signaturesha256=<hex-digest>
X-Gjall-Eventalert.created
User-AgentGjall-Webhook/1.0

Ready to connect?

Add your webhook URL in Settings.

Open Settings →