Gareth Wilson Gareth Wilson

Guide to Truework Webhooks: Features and Best Practices

Published


Truework webhooks tell you when an income or employment verification has moved on: an order has finished, or a single verification request inside it has changed state. If you order verifications through the Truework API, webhooks save you from polling each order until its results are ready.

This guide covers how Truework webhooks are created and authenticated, the two events and their payloads, what Truework leaves undocumented, and the practices that keep a Truework receiver reliable.

What are Truework webhooks?

A Truework webhook is a subscription created in your service account's Developer Settings. It has an HTTPS URL, an optional description, and one or more event types. When a subscribed event happens, Truework sends a JSON POST to the URL.

Truework does not sign webhook payloads. Instead, each webhook has its own authentication token, shown in Developer Settings, and Truework sends that token unchanged in the X-Truework-Token header on every request. Your receiver authenticates a request by comparing the header with the token it expects. Truework also publishes the IP addresses it sends from, which you can use as a second check.

Truework webhook features

FeatureDetails
ConfigurationDeveloper Settings > Developer > Webhooks > Sandbox or Production
Eventsorder.completed and verification_request.state.change
SubscriptionsOne webhook can subscribe to several event types
LimitTen webhooks per service account
AuthenticationPer-webhook token, sent verbatim in X-Truework-Token
Body signatureNone
Replay windowNone. No timestamp is sent or signed
VersioningEach subscription is to a version of the event. Upgrades are one-way
RetriesFailed webhooks are retried periodically for 48 hours
Failure alertsEmails to the service account address
DuplicatesPossible. Handlers should be idempotent
Source IPsProduction 18.218.101.165, 3.133.214.186, 3.23.196.239; Sandbox 3.22.163.100

Truework says its IP addresses are subject to change and that it will try to announce changes ahead of time. It does not document a response timeout, a retry interval or what counts as a failed delivery.

Common events

Truework has two webhook events. Which one you need depends on the verification method you use.

EventFires whenRecommended for
order.completedEvery verification on the order has been completed or canceledCredentials and Documents verifications
verification_request.state.changeA verification request within the order changes stateSmart Outreach

Instant verifications return their result straight away and need no webhook.

Every payload has three top-level keys: version, hook and data. The event name is in hook.event, alongside the webhook's numeric id, its target URL and triggered_at. Truework publishes field lists and a JSON schema for each event, but no example body. Built from those field lists, an order.completed delivery looks like this:

{
  "version": "<subscription version>",
  "hook": {
    "id": 123,
    "event": "order.completed",
    "target": "https://example.com/webhooks/truework",
    "triggered_at": "<datetime>"
  },
  "data": {
    "order_id": "<order id>",
    "metadata": {}
  }
}

When you receive it, fetch the order's results from the API. verification_request.state.change adds verification_request_id, state and credentials_session_token to data. state is one of pending-approval, processing, action-required, completed, canceled or invalid. A completed state means new data is available on the order. A canceled state means the verification could not be completed but may still hold information or documents from the applicant.

action-required means Smart Outreach needs more information. You can supply it in the Truework app, or have your integration cancel the order when this state arrives.

Setting up Truework webhooks

  1. Sign in to your service account and open the developer settings page.
  2. Select < > Developer in the left-hand panel, scroll to Webhooks, and click Edit under Sandbox or Production.
  3. Choose the events in the subscriptions dropdown, enter the URL, and optionally add a description.
  4. Click Create.
  5. Copy the webhook's authentication token and store it server-side, for example as TRUEWORK_WEBHOOK_TOKEN.

Sandbox and Production webhooks are configured separately and have different tokens. If you delete a webhook and create a new one with the same URL, the new one gets a different token, so update your receiver at the same time.

If you receive through Hookdeck, create a Truework source, use its URL as the webhook URL, and paste the token into the source's Authentication Token field. Hookdeck accepts each request, then records any request whose X-Truework-Token does not match as failed verification, so it never becomes an event for your service.

Securing Truework webhooks

There is nothing to compute. Read X-Truework-Token and compare it with your stored token in constant time. Fail closed if no token is configured, so an empty header can never match an empty secret.

const crypto = require('crypto');
const express = require('express');

function verifyTrueworkToken(headerValue, expectedToken) {
  if (!headerValue || !expectedToken) return false;
  const received = Buffer.from(headerValue, 'utf8');
  const expected = Buffer.from(expectedToken, 'utf8');
  return received.length === expected.length && crypto.timingSafeEqual(received, expected);
}

const app = express();

app.post('/webhooks/truework', express.raw({ type: '*/*' }), (req, res) => {
  const token = process.env.TRUEWORK_WEBHOOK_TOKEN;
  if (!token) return res.sendStatus(500); // fail closed

  if (!verifyTrueworkToken(req.get('x-truework-token'), token)) {
    return res.sendStatus(401);
  }

  let event;
  try {
    event = JSON.parse(req.body.toString('utf8'));
  } catch {
    return res.sendStatus(400);
  }

  // Route on event.hook.event. Answer 200 to events you don't handle.
  res.sendStatus(200);
});

The same check in Python:

import hmac


def verify_truework_token(header_value, expected_token: str) -> bool:
    if not header_value or not expected_token:
        return False
    return hmac.compare_digest(header_value.encode("utf-8"), expected_token.encode("utf-8"))


# In FastAPI: verify_truework_token(request.headers.get("x-truework-token"),
#                                   os.environ["TRUEWORK_WEBHOOK_TOKEN"])

The token travels with every request, so anyone who captures one can send forged requests until you rotate it. Keep the endpoint on HTTPS, and if your network allows it, restrict inbound traffic to Truework's published IP addresses as well.

Make Truework webhooks production-ready. Hookdeck Event Gateway checks the X-Truework-Token header, answers Truework straight away, and durably queues every order and verification event before your service sees it.

Truework webhook limitations and pain points

A static token instead of a signature

The Problem: Nothing protects the body from being changed, and a captured request can be replayed.

Why It Happens: Truework sends the token itself rather than a signature computed over the body. There is no timestamp, so nothing tells a fresh request from an old one.

Workarounds:

  • Use HTTPS only and allowlist Truework's source IP addresses.
  • Treat the webhook as a notification: fetch the order from the Truework API before you act on it, which Truework recommends anyway for order.completed.

How Hookdeck Can Help: Hookdeck holds the token and checks it on every request, so your own services don't need it, and requests with a missing or wrong token never reach them.

Recreated webhooks break authentication

The Problem: After cleaning up webhooks in Developer Settings, every delivery fails authentication.

Why It Happens: Tokens belong to webhook instances. A new webhook for the same URL gets a new token, and Sandbox and Production webhooks have different tokens too.

Workarounds:

  • Update the stored token whenever you recreate a webhook.
  • Keep separate configuration for Sandbox and Production, and label each webhook with a description.

How Hookdeck Can Help: With one Hookdeck source per environment, you update the token in one place, and the request log shows clearly when verification starts failing.

Version upgrades can change the payload

The Problem: A subscription upgrade changes field names or structure under your handler.

Why It Happens: Subscriptions are tied to an event version. New subscriptions always use the latest version, upgrades can't be undone, and Truework only promises that the top-level version field stays the same across versions.

Workarounds:

  • Log version on every delivery and branch on it if you run more than one.
  • Test an upgrade in Sandbox before applying it in Production.

How Hookdeck Can Help: Hookdeck stores the full body of every request, so you can compare payloads before and after an upgrade and replay old ones against new code.

Little documented delivery behaviour

The Problem: You don't know how long Truework waits for a response, how often it retries, or what it counts as a failure.

Why It Happens: Truework says only that failed webhooks are retried periodically for 48 hours and that failures are emailed to the service account.

Workarounds:

  • Respond with 200 as soon as the token checks out, and do the work afterwards.
  • Make sure the service account's email reaches someone who will act on it.
  • After an outage longer than 48 hours, reconcile open orders through the API.

How Hookdeck Can Help: Hookdeck accepts each delivery when it arrives, so Truework's 48-hour clock stops mattering, and retries delivery to your service on a schedule you set.

Inconsistent state spelling

The Problem: A handler that checks for action-required might miss a state.

Why It Happens: The webhooks overview spells the state action-required, while the event reference page lists action-requred. Neither page shows a real payload.

Workarounds:

  • Accept both spellings until you have seen a real delivery with this state.
  • Log and alert on any state value you don't recognise.

How Hookdeck Can Help: Hookdeck filters can match several values for data.state, so you can route either spelling to the same destination.

Best practices

Answer 200 to events you don't handle

Truework says handlers should ignore unknown events but still respond with 200. Otherwise, adding a subscription can cause errors, retries and failure emails.

Deduplicate deliveries

Truework says an event can be sent more than once, and documents no event ID. hook.id identifies the webhook, not the event. Build a key from hook.event, data.order_id, data.verification_request_id and data.state, and store it before processing. See our guide to webhook idempotency.

Fetch results from the API

Payloads carry IDs and states, not verification results. After order.completed, or a completed state, fetch the order from the Truework API.

Pick the event that matches your verification method

Use order.completed for Credentials and Documents verifications, and verification_request.state.change when you use Smart Outreach and need to act on action-required.

Acknowledge fast and process asynchronously

Verify the token, store the event, return 200, and do the work afterwards. See why to process webhooks asynchronously.

Conclusion

Truework webhooks are unsigned JSON POSTs, authenticated by a per-webhook token in X-Truework-Token. Compare the token in constant time, allowlist Truework's IP addresses where you can, dispatch on hook.event, answer 200 to anything you don't recognise, deduplicate, and fetch results from the API rather than trusting the payload alone.

Hookdeck Event Gateway sits in front of your receiver, checks the Truework token, and holds each event until your service has processed it.

Get started with Hookdeck for free and handle Truework webhooks reliably in minutes.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.