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

Verify signatures

Check the ChartDeck-Signature header with Node, Python, the SDKs, or plain HMAC-SHA256 in any language.

ChartDeck-Signature: t=<unix seconds>,v1=<hex> where the hex is HMAC-SHA256, keyed with your endpoint's secret, of the string <t>.<raw body>. A header may carry two v1= values while a secret is being replaced: accept the delivery when any of them matches.

Rules:

  1. Use the raw body bytes as received, before any JSON parsing (re-serialised JSON will not match).
  2. Compare in constant time.
  3. Reject when t is more than 300 seconds away from your clock (replay protection).

#Node (no SDK)

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); });
}

#Python (no SDK)

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)

#With the SDKs

Python
from chartdeck import construct_event, SignatureVerificationError
event = construct_event(request.body, request.headers["ChartDeck-Signature"], secret)   # raises when it does not verify
JavaScript
import { constructEvent } from '@chartdeck/api';
const event = constructEvent(rawBody, req.headers['chartdeck-signature'], secret);     // throws WebhookSignatureError

#Other languages

Compute HMAC_SHA256(key = secret, message = t + "." + raw_body), hex-encode it in lower case, and compare it to each v1= value with a constant-time comparison; check t against your clock.

The Developer API documentation is in English.