Webhooks and signature verification
Both APIs push events to your endpoint. Verify every delivery before you process it: check the signature with a constant-time comparison, reject stale timestamps, and deduplicate by delivery id.
TravelDistro webhooks
- X-TD-Webhook-Signature
- sha256=<hex>, HMAC-SHA256 of {TIMESTAMP}.{RAW_BODY} keyed by the endpoint secret
- X-TD-Webhook-Timestamp
- Unix seconds; reject if older than 300 s
- X-TD-Webhook-Event
- Event type, e.g. transfer.confirmed
- X-TD-Webhook-Id
- Delivery id, stable across retries; deduplicate by it
- Retries
- 2 retries with exponential backoff, then dead-lettered
Transfer events
- transfer.confirmed
- transfer.updated
- transfer.driver_assigned (driver details arrive here)
- transfer.driver_unassigned
- transfer.cancelled
Verification in Node.js
import crypto from "node:crypto";
// TravelDistro webhook: X-TD-Webhook-Signature = "sha256=" + HMAC-SHA256(`${timestamp}.${rawBody}`, endpointSecret)
export function verifyTravelDistroWebhook(headers: Record<string, string>, rawBody: string, endpointSecret: string): boolean {
const timestamp = headers["x-td-webhook-timestamp"];
const signature = headers["x-td-webhook-signature"] ?? "";
if (!timestamp || Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false; // stale or replayed
const expected = "sha256=" + crypto.createHmac("sha256", endpointSecret).update(`${timestamp}.${rawBody}`).digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(signature);
return a.length === b.length && crypto.timingSafeEqual(a, b); // constant-time compare
}
// Deduplicate by X-TD-Webhook-Id: the same id is reused on every retry of one delivery.Hotel API by Safaryar Holidays
- X-Webhook-Signature
- HMAC signature of the delivery
- Events
- booking.created, booking.cancelled, booking.prebook_confirmed, booking.prebook_rejected
- Retries
- None; respond within 10 seconds
Endpoint checklist
- Read the raw body before any JSON parsing; the signature covers the exact bytes.
- Return 2xx quickly and process asynchronously.
- Store processed delivery ids for at least the retry window.
- Treat the endpoint secret like the API secret: server-side only.
Licensed travel agency? Apply for API access.
Get API access