Skip to content

Webhook Specifications

See which events and shipment fields Tracksuite sends in a webhook, and how to verify the request with the signing secret.

The body is a JSON array, and one request can contain several shipment updates. event names what happened on that update, and it is chosen from the shipment’s current status. The pairs are in the table below. status and statusLabel are defined in Shipment status.

Each update has eventId, eventTime, and data. If the same update is retried, eventId stays the same, so the receiver can skip the duplicate. data is the shipment at that moment: the tracking number trackingNumber, the status status, the carrier substatus statusTrack, the shipment id routerId, the current summary summary, and the timeline detail. When the carrier returns an error object, data also includes error.

Every webhook Tracksuite sends includes a computed signature. Your server uses it to confirm the request came from Tracksuite. A third party or a malicious caller without the signing secret cannot produce a signature that passes the check. The signature is an HMAC of the raw request body, computed with SHA256 and written as a hex string in the X-Tracky-Signature header.

Sign in to the Tracksuite Portal, open Settings → API & Webhooks, and copy the signing secret. Tracksuite sends webhooks only after both the endpoint URL and the secret are set.

HMAC-SHA256 the raw request body with the signing secret, and encode the result as hex. Use that exact byte string. Do not parse the JSON and serialize it again, and do not sign X-Tracky-Timestamp. Either mistake makes the signature fail.

Read X-Tracky-Signature and compare it with the value computed above. This function computes the digest, then compares it. If the header is missing or the function returns false, return 401 and reject the request. After a valid signature, return 2xx within 5 seconds, before slower work.

import { createHmac, timingSafeEqual } from 'node:crypto'
function verifySignature(rawBody: string, signature: string): boolean {
const expected = createHmac('sha256', WEBHOOK_SECRET).update(rawBody).digest('hex')
if (expected.length !== signature.length) {
return false
}
return timingSafeEqual(Buffer.from(expected), Buffer.from(signature))
}