Agent skill

Check Webhooks Skill

Receive and verify Check (checkhq.com) payroll API webhooks. Use when setting up Check webhook handlers, debugging Check-Signature verification, or handling payroll, payment and employee events like payroll updated, payment status_change, and employee created.

Install this skill

npx skills add hookdeck/webhook-skills --skill check-hq-webhooks


Check (checkhq.com) is a payroll API. This skill covers its webhooks, not generic "check" events.

When to Use This Skill

  • How do I receive Check payroll webhooks?
  • How do I verify the Check-Signature header?
  • Why does my Check webhook signature verification fail?
  • How do I handle payroll updated, payment status_change, or employee created events?
  • How do I test a Check Webhook Config with the ping endpoint?

Verification (core)

Check sends Check-Signature: the base-16 (hex) HMAC-SHA256 of the raw request body, keyed with the Webhook Config's key. Use the key as UTF-8 text (it looks like 40 hex characters but is not hex-decoded). There is no timestamp in the signed content. Check has no SDK for this, so verify manually.

Node:

const crypto = require('crypto');

function verify(rawBody, signatureHeader, key) {
  if (!signatureHeader) return false;
  const expected = crypto.createHmac('sha256', key).update(rawBody).digest('hex');
  const a = Buffer.from(signatureHeader.trim().toLowerCase());
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Python:

import hmac, hashlib

def verify(raw_body: bytes, signature_header: str, key: str) -> bool:
    expected = hmac.new(key.encode("utf-8"), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected.encode(), (signature_header or "").strip().lower().encode())

In Express read the body with express.raw({ type: '*/*' }) so any content type verifies against the exact bytes.

For complete handlers with route wiring, event dispatch, and tests, see:

Payload and Headers

The body is {"event": "...", "data": {...}}. The resource type (the "topic") is not in the body; it is in the Check-Topic header, so dispatch on topic + event. The ping test webhook is the one exception: a flat {"message": "Test webhook"}.

HeaderDescription
Check-SignatureHex HMAC-SHA256 of the raw body (verify this one)
Check-TopicCategory of the webhook, e.g. payroll, payment, employee, ping
Check-WebhookEvent-IDUnique ID; store it to ignore duplicates
Check-Livetrue for live, false for sandbox

Check also still sends deprecated X-... duplicates of these (such as X-Live). Ignore them and verify Check-Signature.

Common Event Types

Topic (Check-Topic)EventDescription
payrollcreatedA payroll was created
payrollupdatedA payroll was updated (API version 2025-01-01 or later)
payrollstatus_changePayroll status changed (deprecated in API version 2025-01-01)
paymentstatus_changePayment moved to processing, paid, failed or refunded
employeecreatedAn employee was created
employeeupdatedAn employee was updated (also used for termination)
bank_accountcreated / updatedBank account changes

For the full topic and event list, see Webhook event types

Environment Variables

CHECK_WEBHOOK_KEY=your_webhook_config_key   # "key" from POST /webhook_configs

Local Development

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

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 3, 2026

View on GitHub →