# Check 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:

```javascript
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:

```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:
> 
> * [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/check-hq-webhooks/examples/express/)
> * [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/check-hq-webhooks/examples/nextjs/)
> * [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/check-hq-webhooks/examples/fastapi/)

## 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"}`.

| Header | Description |
| --- | --- |
| `Check-Signature` | Hex HMAC-SHA256 of the raw body (verify this one) |
| `Check-Topic` | Category of the webhook, e.g. `payroll`, `payment`, `employee`, `ping` |
| `Check-WebhookEvent-ID` | Unique ID; store it to ignore duplicates |
| `Check-Live` | `true` 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`) | Event | Description |
| --- | --- | --- |
| `payroll` | `created` | A payroll was created |
| `payroll` | `updated` | A payroll was updated (API version 2025-01-01 or later) |
| `payroll` | `status_change` | Payroll status changed (deprecated in API version 2025-01-01) |
| `payment` | `status_change` | Payment moved to processing, paid, failed or refunded |
| `employee` | `created` | An employee was created |
| `employee` | `updated` | An employee was updated (also used for termination) |
| `bank_account` | `created` / `updated` | Bank account changes |

> **For the full topic and event list**, see [Webhook event types](https://docs.checkhq.com/docs/webhook-event-types)

## Environment Variables

```bash
CHECK_WEBHOOK_KEY=your_webhook_config_key   # "key" from POST /webhook_configs

```

## Local Development

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

```

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/check-hq-webhooks/references/overview.md) - Check webhook concepts, topics and events
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/check-hq-webhooks/references/setup.md) - Create a Webhook Config and get the key
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/check-hq-webhooks/references/verification.md) - Signature verification details and gotchas