Learn how to use webhooks to automate actions based on specified events.
Overview
Webhooks are automated messages sent by Truv. These notifications are HTTP POST requests to a predefined URL when an event occurs on the platform.
Use webhooks to track Task status changes during processing stages. Successful payroll connections take time to process data and PDF documents from the payroll provider. Webhooks provide updates for status changes and maintain transparency for the user experience.
Subscribing
Visit the Truv Dashboard Development > Webhooks and add a URL for your environment. Each environment requires a specific and separate URL.
Testing
Test webhooks with the Truv Bridge in the Truv Emulator. For immediate use, ngrok and MockBin provide testing for payloads in either your local environment or your browser, respectively.
Payload response
Truv sends requests to webhook endpoints and the responses depend on the sent event. The table below contains common fields in calls, regardless of the event.
Name | Location | Description |
---|---|---|
user-agent | header | Always set to Truv-Webhook-Service/2.0 |
x-webhook-sign | header | Hash of created request body created with Access secret |
webhook_id | body | Unique ID for specific webhook request |
event_type | body | ID of sent webhook event request |
updated_at | body | Time event occurred |
user_id | body | Unique ID of user |
template_id | body | Unique ID of template |
Note
All header field names are case-insensitive. For more information, refer to HTTP/1.1 Specifications, Section 3.2.
Originating IP addresses
The list below is the collection of IP addresses for Truv webhook calls and communication. Secure your integration and verify events originate from these IP addresses.
34.212.57.93
44.224.243.166
52.25.14.79
Timing
Truv processes webhooks requests for Task statuses in their updated order. For example, full_parse
events must happen before done
events. Webhook request deliveries may experience delays due to things outside of Truv, such as network latency, outages, and other external issues.
Configure your webhook integrations to track the updated_at
field for monitoring events in sequence.
Security
Webhook signatures confirm and verify Truv data for you. Each request contains the X-WEBHOOK-SIGN
value within the header. This hash-based message authentication code (HMAC) uses the Access secret as the key and SHA-256
as the function.
Verification steps
The steps in the section below guide you through verifying requests from Truv.
-
Use a hash library to create an HMAC hash of the raw request body received by Truv. Use the Access secret or access_token from the
X-Access-Secret
header as the hashing key. Confirm theSHA-256
as the hash function and the final hash has hexadecimal conversion. -
Compare the hash to the value in the
X-WEBHOOK-SIGN
header from the webhook request. Truv sends requests for matching values, and you can continue to process the webhook.
import hashlib
import hmac
def generate_webhook_sign(payload: str, key: str) -> str:
generated_hash = hmac.new(
key=key.encode('utf-8'),
msg=payload.encode('utf-8'),
digestmod=hashlib.sha256,
).hexdigest()
return f'v1={generated_hash}'
@app.route('/webhook', methods=['POST'])
def webhook():
return generate_webhook_sign(request.data.decode('UTF-8'), secret)
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"strings"
)
func generate_webhook_sign(body string, key string) string {
mac := hmac.New(sha256.New, []byte(key))
mac.Write([]byte(body))
return hex.EncodeToString(mac.Sum(nil))
}
func webhook(w http.ResponseWriter, r *http.Request) {
b, _ := ioutil.ReadAll(r.Body)
convertedBody := string(b)
signature := generate_webhook_sign(convertedBody, os.Getenv("API_SECRET"))
fullSignature := fmt.Sprintf("v1=%s", signature)
fmt.Fprintf(w, fullSignature)
}
const crypto = require("crypto")
// ensure all request bodies are parsed to JSON. Callback function
// keeps a copy of the raw body for webhooks.
app.use(bodyParser.json({
verify: (req, res, buf) => {
req.rawBody = buf
}
}))
const generate_webhook_sign = (body, key) => {
return crypto.createHmac("sha256", key)
.update(body)
.digest("hex")
}
app.post("/webhook", async (req, res) => {
const body = req.rawBody.toString()
const webhook_sign = generate_webhook_sign(body, API_SECRET)
res.send(`v1=${webhook_sign}`).end()
})
class Webhook
def self.generate_webhook_sign(body, key)
digest = OpenSSL::Digest.new('sha256')
return "v1=" + OpenSSL::HMAC.hexdigest(digest, key, body)
end
def self.post(body)
return self.generate_webhook_sign(body, Citadel.client_secret)
end
end
using System.Threading.Tasks;
using System.IO;
using Microsoft.AspNetCore.Mvc;
using System.Text;
using System.Security.Cryptography;
using System;
namespace c_sharp.Controllers
{
[ApiController]
[Route("webhook")]
public class WebhookController : ControllerBase
{
[HttpPost]
public async Task<string> Post()
{
using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8))
{
string body = await reader.ReadToEndAsync();
return generateWebhookSign(body, Environment.GetEnvironmentVariable("API_SECRET"));
}
}
private string generateWebhookSign(string body, string key)
{
using (HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key)))
{
// Compute the hash of the input file.
byte[] hashValue = hmac.ComputeHash(Encoding.UTF8.GetBytes(body));
return "v1=" + BitConverter.ToString(hashValue).Replace("-", "").ToLower();
}
}
}
}
HTTP timeouts and retries
HTTP request timeout have strict limits. Unsuccessful requests are limited to three attempts and a 30 second window between each retry. Additional unsuccessful retries results in failed requests and further attempts are blocked.
Status codes
The list below contains information for webhook status codes.
- Endpoints must return a successful
2xx
status code within 10 seconds of a request - Redirects signaled with
3xx
status codes are valid with Truv - Any
4xx
and5xx
response status codes result as an error
Webhook event descriptions
Find all of the webhooks reference information on the Webhook Event Descriptions page. The sample content below covers payloads from changes to Tasks.
Task status change
This sample webhook is the payload for task-status-updated
.
{
"webhook_id":"488f424096d3461aa0e4cf11a985f6df",
"task_id":"521442a087604e599c637db310d07402",
"link_id":"d39d86dcc20c46e0ae75ba9ab1311a21",
"product":"income",
"data_source": "payroll",
"tracking_info":null,
"event_type":"task-status-updated",
"event_created_at":"2022-08-24T13:55:12.845645Z",
"updated_at":"2022-08-24T13:55:12.845666+00:00",
"status":"parse",
"user_id": "88fef4cea64c40b5ad6727cc9b0b9fdc",
"template_id": null
}
The task-status-updated
event occurs when the status
of a Task changes. When receiving a task-status-updated
event with a status
of done
, all data from the Task is downloaded and document processing is complete.
Use the link_id
value to locate the access_token in your system. This allow you to retrieve the latest payroll data from the respective endpoint. View more information about the fields in the list below.
task_id
- ID of Task associated with eventlink_id
- ID of Link associated to Taskproduct
- Product specified for Task,employment
,income
oradmin
data_source
- source of data.payroll
,docs
,insurance
,financial_accounts
,tax
tracking_info
- Info passed to bridge_token for Link , nullablestatus
- Connection Lifecycle status update