Docs
Integrations

Webhooks

Webhooks deliver real-time event notifications to your application when breached passwords are detected or credential exposures are discovered.

Event types

LeakJar sends webhook events for the following scenarios:

password_protect.hitBPD

Fired when a password check returns a match in the breach dataset. Includes the exposure count but never the password or hash.

password_protect.blockedBPD

Fired when a breached password is blocked by a policy action. Useful for tracking enforcement effectiveness.

monitoring.exposure_detectedMonitoring

Fired when the exposure monitoring system discovers new credential exposures matching your monitored domains or user identifiers.

Webhook payload

All webhook events share a common envelope structure:

webhook-payload.jsonjson
{
  "id": "evt_abc123def456",
  "type": "password_protect.hit",
  "created_at": "2026-02-15T14:30:00Z",
  "project_id": "proj_xyz789",
  "data": {
    "check_id": "chk_001",
    "flow": "signup",
    "exposure_count": 3861493,
    "policy_action": "block",
    "user_identifier_hash": "sha256:a1b2c3..."
  }
}

id — unique identifier for idempotent processing.

type — the event type string.

data — event-specific payload. Never contains plaintext passwords or full hashes.

user_identifier_hash — a one-way hash of the user identifier for correlation, not the identifier itself.

Setup instructions

  1. Open your project in the LeakJar Console and navigate to Settings → Webhooks.
  2. Add an endpoint URL — this must be an HTTPS endpoint on your server that can receive POST requests.
  3. Select event types you want to receive. You can subscribe to all events or pick specific ones.
  4. Copy the signing secret — you'll use this to verify webhook signatures.
  5. Send a test event to confirm your endpoint is receiving and processing events correctly.

Signature verification

Every webhook request includes a X-LeakJar-Signature header containing an HMAC-SHA256 signature of the request body. Always verify this signature before processing the event to ensure the request is authentic.

verify-webhook.tstypescript
import { createHmac, timingSafeEqual } from "crypto";

function verifyWebhookSignature(
  body: string,
  signature: string,
  secret: string
): boolean {
  const expected = createHmac("sha256", secret)
    .update(body, "utf8")
    .digest("hex");

  // Use timing-safe comparison to prevent timing attacks
  return timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// In your webhook handler:
// const isValid = verifyWebhookSignature(
//   rawBody,
//   req.headers["x-leakjar-signature"],
//   process.env.LEAKJAR_WEBHOOK_SECRET
// );

Handling events

Your webhook endpoint should respond with a 200 status code as quickly as possible. Process events asynchronously to avoid timeouts.

webhook-handler.tstypescript
export async function POST(request: Request) {
  const body = await request.text();
  const signature = request.headers.get(
    "x-leakjar-signature"
  ) ?? "";

  if (!verifyWebhookSignature(body, signature, secret)) {
    return new Response("Invalid signature", { status: 401 });
  }

  const event = JSON.parse(body);

  // Acknowledge immediately
  // Process the event asynchronously
  switch (event.type) {
    case "password_protect.hit":
      await queue.enqueue("process-breach-hit", event.data);
      break;
    case "password_protect.blocked":
      await queue.enqueue("log-blocked-attempt", event.data);
      break;
    case "monitoring.exposure_detected":
      await queue.enqueue("handle-exposure", event.data);
      break;
  }

  return new Response("OK", { status: 200 });
}
Note: Webhooks are UI-only for MVP; events are mocked in the demo environment. In production, LeakJar will deliver events via HTTP POST to your configured endpoints with automatic retries and exponential back-off.