Skip to content
ChartDeck Developer API docs Account Get an API key

Webhooks

Signed event deliveries to your server.

#Verify

Every webhook ChartDeck sends is signed: API webhook endpoints and the webhook you can put on a ChartDeck alert. The header is

Text
ChartDeck-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256(secret, "<t>.<raw body>")>

The secret starts with whsec_cd_: for an API endpoint it is shown once when you add it; for alert webhooks it is the signing secret in your Alert settings (one per account). To verify a delivery:

  1. Take the raw body bytes as received, before parsing the JSON.
  2. Compute HMAC-SHA256 with the secret over t + "." + raw body, hex in lower case.
  3. Compare it with each v1= value in constant time; accept when one matches.
  4. Reject when t is more than 300 seconds from your clock.

Python:

Python
import hashlib, hmac, time

def verify(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
    parts = [p.split("=", 1) for p in (header or "").split(",") if "=" in p]
    t = next((v for k, v in parts if k.strip() == "t"), None)
    sigs = [v.strip() for k, v in parts if k.strip() == "v1"]
    if t is None or not sigs or abs(time.time() - int(t)) > tolerance:
        return False
    want = hmac.new(secret.encode(), t.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    return any(hmac.compare_digest(s, want) for s in sigs)

JavaScript (Node):

JavaScript
import { createHmac, timingSafeEqual } from 'node:crypto';

export function verify(rawBody, header, secret, toleranceSec = 300) {
  if (!header || !secret) return false;
  let t = NaN; const sigs = [];
  for (const part of header.split(',')) {
    const [k, v] = part.split('=');
    if (k?.trim() === 't') t = Number(v); else if (k?.trim() === 'v1') sigs.push(v.trim());
  }
  if (!Number.isFinite(t) || !sigs.length || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
  const want = Buffer.from(createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex'));
  return sigs.some((s) => { const got = Buffer.from(s); return got.length === want.length && timingSafeEqual(got, want); });
}

The SDKs have this built in (chartdeck.construct_event, constructEvent in @chartdeck/api). More detail: Verify signatures and Signed alert webhooks.

The Developer API documentation is in English.