# Truework Webhooks

Truework sends a webhook when an income or employment verification finishes (`order.completed`) or when a verification request within an order changes state (`verification_request.state.change`, used with Smart Outreach). Each is an HTTPS `POST` with a JSON body.

**Truework does not sign webhook payloads.** There is no HMAC and no signature header. Each webhook you create has an **authentication token** (shown in Developer Settings) that Truework sends unchanged in the `X-Truework-Token` header. You authenticate a request by comparing that header with the token, in constant time. A deleted and recreated webhook gets a new token.

## When to Use This Skill

* How do I receive Truework webhooks?
* How do I verify a Truework webhook? (**There is no signature: check `X-Truework-Token`.**)
* How do I handle `order.completed` or `verification_request.state.change` events?
* Why does my Truework webhook return 401 after I recreated the webhook?
* How do I react to a Smart Outreach `action-required` state?

## Verification (core): compare the X-Truework-Token header, constant-time

Node:

```javascript
const crypto = require('crypto');

// Hash both sides so timingSafeEqual gets equal-length buffers and never throws.
function safeEqual(a, b) {
  if (typeof a !== 'string' || typeof b !== 'string') return false;
  const h = (s) => crypto.createHash('sha256').update(s).digest();
  return crypto.timingSafeEqual(h(a), h(b));
}

const token = process.env.TRUEWORK_WEBHOOK_TOKEN;
// Fail closed: never let an empty token match an empty header
if (!token) return res.status(500).send('Auth not configured');
if (!safeEqual(req.get('X-Truework-Token'), token)) return res.status(401).send('Unauthorized');

```

Python:

```python
import hashlib, hmac, os
token = os.environ.get("TRUEWORK_WEBHOOK_TOKEN", "")
if not token:  # fail closed
    raise HTTPException(500, "Webhook authentication not configured")
h = lambda s: hashlib.sha256(s.encode()).digest()
if not hmac.compare_digest(h(request.headers.get("x-truework-token") or ""), h(token)):
    raise HTTPException(401, "Invalid or missing X-Truework-Token")

```

Nothing is signed, so no raw-body handling is needed for verification. The examples still read the raw body (`express.raw({ type: '*/*' })`) so any content type works, and parse JSON only after the token check passes.

> **For complete handlers with tests**, see [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/truework-webhooks/examples/express/), [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/truework-webhooks/examples/nextjs/), [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/truework-webhooks/examples/fastapi/).

## Common Event Types

| Event (`hook.event`) | Triggered When | Notes |
| --- | --- | --- |
| `order.completed` | All verifications on the order are completed or canceled | `data.order_id`; fetch results from the API. Recommended for Credentials and Documents verification |
| `verification_request.state.change` | A verification request within an order changes state | `data.order_id`, `data.verification_request_id`, `data.credentials_session_token`, `data.state` (`pending-approval`, `processing`, `action-required`, `completed`, `canceled`, `invalid`; the event reference page spells it `action-requred`, so accept both), `data.metadata`. Recommended for Smart Outreach |

Payload: `{ "version", "hook": { "id", "event", "target", "triggered_at" }, "data": { ... } }`. Dispatch on `hook.event`. `hook.id` is the webhook's ID, not an event ID.

Webhooks can be delivered more than once, so handlers must be idempotent. Failed deliveries are retried for 48 hours, and Truework emails the service account about failures.

## Environment Variables

```bash
TRUEWORK_WEBHOOK_TOKEN=...   # authentication token from Developer Settings > Webhooks

```

## Local Development

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

```

Use the printed URL as the webhook URL in Truework's Developer Settings.

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/truework-webhooks/references/overview.md) - Events, payloads, retries, limits
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/truework-webhooks/references/setup.md) - Create a webhook and find its token
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/truework-webhooks/references/verification.md) - Token check, IP allowlist, gotchas