Skip to main content

Webhooks.

Get notified in real time when scans complete. Register webhook URLs and AppVet will POST results to your endpoint as soon as they are ready.

Register a webhook

Send a POST request with the URL to receive events and the event types you want to subscribe to:

curl -X POST https://appvet.dev/api/webhooks \
  -H "Authorization: Bearer avk_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/webhooks/appvet",
    "events": ["scan.completed", "scan.failed"]
  }'

Response:

{
  "id": "wh_abc123",
  "url": "https://example.com/webhooks/appvet",
  "events": ["scan.completed", "scan.failed"],
  "secret": "whsec_k7J2mN9xR4pQ1vL8...",
  "created_at": "2026-03-31T12:00:00Z"
}

The secret is shown only once at creation. Save it immediately — you will need it to verify webhook signatures.

Events

Event Description
scan.completed Scan finished successfully with a full report.
scan.failed Scan could not complete (target unreachable, timeout, etc.).
scan.partial Scan finished with partial results (some checks could not run).

Webhook payload

AppVet sends a POST request to your URL with a JSON body:

{
  "event": "scan.completed",
  "timestamp": "2026-03-31T12:05:00Z",
  "data": {
    "scan_id": "scan_abc123",
    "url": "https://example.com",
    "type": "security",
    "status": "completed",
    "score": 84,
    "grade": "A-",
    "findings_count": 5,
    "report_url": "https://appvet.dev/report/scan_abc123"
  }
}

Signature verification

Every webhook request includes an X-AppVet-Signature header containing an HMAC-SHA256 signature of the request body, signed with your webhook secret. Always verify this signature to ensure the request is authentic.

The signature format is: sha256=<hex-digest>

Node.js verification example

import crypto from "crypto";

function verifySignature(body, signature, secret) {
  const expected = "sha256=" +
    crypto.createHmac("sha256", secret)
      .update(body, "utf-8")
      .digest("hex");

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

// In your webhook handler:
app.post("/webhooks/appvet", (req, res) => {
  const signature = req.headers["x-appvet-signature"];
  const isValid = verifySignature(
    req.body,           // raw request body string
    signature,
    process.env.APPVET_WEBHOOK_SECRET  // whsec_...
  );

  if (!isValid) {
    return res.status(401).send("Invalid signature");
  }

  const event = JSON.parse(req.body);
  console.log(event.event, event.data.scan_id, event.data.score);

  res.status(200).send("OK");
});

List webhooks

curl https://appvet.dev/api/webhooks \
  -H "Authorization: Bearer avk_live_your_key_here"

# Response:
# [
#   { "id": "wh_abc123", "url": "https://...", "events": [...] },
#   { "id": "wh_def456", "url": "https://...", "events": [...] }
# ]

Delete a webhook

curl -X DELETE https://appvet.dev/api/webhooks/wh_abc123 \
  -H "Authorization: Bearer avk_live_your_key_here"

# Response: 204 No Content

Next steps

  • GitHub Action — scan on every pull request with automatic PR comments.
  • Score Badges — embed live score badges in your README.
  • API Reference — full endpoint documentation for scans, reports, and usage.