Agent skill
Truework Webhooks Skill
Receive and authenticate Truework webhooks for income and employment verification. Use when setting up a Truework webhook endpoint, checking the X-Truework-Token header (Truework has no payload signature, so compare the webhook's authentication token in constant time), or handling order.completed and verification_request.state.change events.
Install this skill
npx skills add hookdeck/webhook-skills --skill 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_, 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.completedorverification_events?request.state.change - Why does my Truework webhook return 401 after I recreated the webhook?
- How do I react to a Smart Outreach
action-requiredstate?
Verification (core): compare the X-Truework-Token header, constant-time
Node:
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:
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/, examples/nextjs/, 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_ | A verification request within an order changes state | data.order_id, data.verification_, data.credentials_, 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_. 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
TRUEWORK_WEBHOOK_TOKEN=... # authentication token from Developer Settings > Webhooks
Local Development
# 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 - Events, payloads, retries, limits
- references/setup.md - Create a webhook and find its token
- references/verification.md - Token check, IP allowlist, gotchas