Agent skill

VGS Webhooks Skill

Receive and verify VGS (Very Good Security) webhook notifications. Use when setting up VGS webhook handlers, debugging vgs-signature verification, or handling events like route.updated, route.delete, vault.created, or user.logged_in.

Install this skill

npx skills add hookdeck/webhook-skills --skill vgs-webhooks


When to Use This Skill

  • How do I receive VGS (Very Good Security) webhooks?
  • How do I verify the vgs-signature header?
  • How do I handle route.updated, vault.created, or user.logged_in events?
  • Why is my VGS webhook signature verification failing, or why are retries rejected?

Essential Code (USE THIS)

VGS sends a vgs-signature header on every notification:

vgs-signature: t=1623691785,v0=9370aa8513da0a14d21478f11f039708f7e664bfffebb939f574e57818ce08de
  • t: UTC Unix timestamp in seconds
  • v0: HMAC-SHA256 (lowercase hex) of "<t>.<raw body>", keyed with the per-integration secret as UTF-8

VGS retries reuse the original t, and Hookdeck retries and replays do too. The VGS docs example rejects timestamps older than 60 seconds, which would reject any retry. The examples therefore skip the freshness check by default; set VGS_TIMESTAMP_TOLERANCE_SECONDS to a positive number to opt in.

Verification (Node.js)

const crypto = require('crypto');

// rawBody: Buffer of the exact request bytes (express.raw, not express.json)
function verifyVgs(rawBody, signatureHeader, secret, toleranceSeconds = 0) {
  if (!signatureHeader || !secret) return false;
  const parts = {};
  for (const kv of signatureHeader.split(',')) {
    const i = kv.indexOf('=');
    if (i > 0) parts[kv.slice(0, i).trim()] = kv.slice(i + 1).trim();
  }
  if (!parts.t || !parts.v0) return false;
  if (toleranceSeconds > 0 && Math.abs(Date.now() / 1000 - Number(parts.t)) > toleranceSeconds) return false;

  const expected = crypto.createHmac('sha256', secret)
    .update(`${parts.t}.`).update(rawBody).digest('hex');
  const a = Buffer.from(parts.v0), b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Verification (Python)

import hashlib, hmac, time

def verify_vgs(raw_body: bytes, signature_header: str, secret: str, tolerance: int = 0) -> bool:
    if not signature_header or not secret:
        return False
    parts = dict(kv.strip().split("=", 1) for kv in signature_header.split(",") if "=" in kv)
    if "t" not in parts or "v0" not in parts:
        return False
    if tolerance > 0 and abs(time.time() - int(parts["t"])) > tolerance:
        return False
    expected = hmac.new(secret.encode(), parts["t"].encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(parts["v0"], expected)

VGS has no SDK for webhook verification, so the manual HMAC above is the supported approach in every framework.

For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.

Common Event Types

The event name is the top-level event field in the JSON body (there is no event header).

EventScopeDescription
route.createdVaultA route was created
route.updatedVaultA route was changed
route.deleteVaultA route was deleted
vault.createdOrganizationA vault was created
user.permissions_updatedUserA user's permissions changed
user.permissions_deletedUserA user's permissions were removed
user.logged_inUserA user logged in
user.password_updatedUserA user changed their password
user.mfa_createdUserA user added MFA
user.mfa_deletedUserA user removed MFA
proxy.upstream_errorVaultProxy upstream error (grouped; see occurrence)
alias.reveal_failedVaultAlias reveal failed (grouped; see occurrence)

For the full event reference, see VGS webhook notifications.

Important Headers

HeaderDescription
vgs-signaturet=<unix seconds>,v0=<hex HMAC-SHA256>

Environment Variables

VGS_WEBHOOK_SECRET=your_integration_secret   # unique secret generated for each webhook integration
VGS_TIMESTAMP_TOLERANCE_SECONDS=0            # optional; a positive value opts in to the freshness check

Local Development

# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 vgs --path /webhooks/vgs

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 5, 2026

View on GitHub →