Skip to main content
Use this page for the security and operational rules around webhook delivery:
  • verify every delivery with X-WEBHOOK-SIGN
  • respond within the supported timeout window
  • handle duplicate deliveries safely
  • process events using updated_at for sequencing
  • allowlist Truv IPs or configure mTLS when your network requires it
Use Webhook Events only as the event catalog. Use Webhooks for endpoint configuration and payload structure.

Webhook signature verification

Every webhook request from Truv includes an X-WEBHOOK-SIGN header containing an HMAC-SHA256 hash. Use this to verify that requests are authentic and haven’t been tampered with.

How it works

  1. Truv computes an HMAC-SHA256 hash of the raw request body using your Access Secret
  2. The hash is sent in the X-WEBHOOK-SIGN header with a v1= prefix
  3. Your server recomputes the hash and compares it to the header value
Use the raw request body (not parsed JSON) when computing the hash. Parsing and re-serializing may change the byte representation.

Code examples

import hashlib
import hmac

def verify_webhook(payload: str, signature: str, secret: str) -> bool:
    generated_hash = hmac.new(
        key=secret.encode('utf-8'),
        msg=payload.encode('utf-8'),
        digestmod=hashlib.sha256,
    ).hexdigest()
    expected = f'v1={generated_hash}'
    return hmac.compare_digest(expected, signature)
const crypto = require("crypto");
const express = require("express");
const bodyParser = require("body-parser");

const app = express();

app.use(bodyParser.json({
  verify: (req, res, buf) => {
    req.rawBody = buf;
  }
}));

function verifyWebhook(rawBody, signature, secret) {
  const hash = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");
  const expected = `v1=${hash}`;
  return signature === expected;
}

app.post("/webhooks/truv", (req, res) => {
  const signature = req.headers["x-webhook-sign"];
  const isValid = verifyWebhook(req.rawBody, signature, process.env.TRUV_SECRET);

  if (!isValid) {
    return res.status(401).send("Invalid signature");
  }

  res.status(200).send("OK");
});
import (
  "crypto/hmac"
  "crypto/sha256"
  "encoding/hex"
  "fmt"
)

func verifyWebhook(body string, signature string, secret string) bool {
  mac := hmac.New(sha256.New, []byte(secret))
  mac.Write([]byte(body))
  expected := fmt.Sprintf("v1=%s", hex.EncodeToString(mac.Sum(nil)))
  return hmac.Equal([]byte(expected), []byte(signature))
}
require 'openssl'

def verify_webhook(body, signature, secret)
  digest = OpenSSL::Digest.new('sha256')
  expected = "v1=" + OpenSSL::HMAC.hexdigest(digest, secret, body)
  Rack::Utils.secure_compare(expected, signature)
end
using System.Security.Cryptography;
using System.Text;

private bool VerifyWebhook(string body, string signature, string secret)
{
    using (HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret)))
    {
        byte[] hashValue = hmac.ComputeHash(Encoding.UTF8.GetBytes(body));
        string expected = "v1=" + BitConverter.ToString(hashValue)
            .Replace("-", "").ToLower();
        return expected == signature;
    }
}

Delivery behavior

Truv webhook deliveries follow these operating constraints:
  • endpoints should return a successful 2xx response within 10 seconds
  • 3xx redirects are accepted
  • non-success responses trigger retries
  • unsuccessful deliveries are retried up to 3 times with a 30-second gap between attempts
Respond quickly, acknowledge receipt, and move heavier processing into an asynchronous job or queue.

Ordering and idempotency

Webhook deliveries are asynchronous, so your application should not assume arrival order alone is authoritative.
  • use the updated_at field to process status changes in sequence
  • implement idempotency using webhook_id so duplicate deliveries do not trigger duplicate work
  • fetch fresh data from the API when you need the latest full resource state

Originating IP addresses

Allowlist the current Truv webhook IP addresses if your network requires source filtering:
  • 34.212.57.93
  • 44.224.243.166
  • 52.25.14.79
For the webhook schema and delivery object details, see Webhooks.

Additional authentication options

Beyond webhook signature verification, Truv supports:
  • Truv-signed certificates for webhook mTLS
  • client-signed certificates for webhook mTLS
  • custom headers such as client ID and client secret, configured with Truv
Use mTLS if you need mutual certificate-based authentication for webhook delivery.

Webhook implementation checklist

// Verify signature on the raw body
// Return 200 quickly
// Queue processing asynchronously
// Deduplicate by webhook_id
// Use updated_at for ordering checks