Receive tracking event
Download OpenAPIWhen an event you subscribed to occurs, TimeToCargo sends a POST request to your callback_url.
Return 200 OK as soon as you receive the event. If processing takes time, enqueue the event, send the response, and then process it asynchronously. TimeToCargo waits up to 10 seconds for a response.
Delivery succeeds with any 2xx status. TimeToCargo retries after 408, 409, 425, 429, and 5xx statuses, as well as timeouts and DNS or network errors. It does not retry after 400, 401, 403, 404, or 422. After a 429 response, TimeToCargo honors the Retry-After header when present. Each delivery can have up to five attempts.
To verify the source of the request, use the webhook secret and the x-timetocargo-timestamp and x-timetocargo-signature headers. The signature is an HMAC SHA-256 digest of timestamp + '.' + rawBody, prefixed with sha256=. Use the unchanged request body as rawBody. Signature verification is optional, but recommended.
Node.js / Express
import crypto from 'node:crypto';import express from 'express';const app = express();const secret = process.env.TIMETOCARGO_WEBHOOK_SECRET;if (!secret) { throw new Error('TIMETOCARGO_WEBHOOK_SECRET is not set');}function queueWebhook(payload: unknown): void { // Enqueue the event, save it to your database, // or pass it to another service for processing. console.log('Webhook received from TimeToCargo:', payload);}app.post('/webhooks/timetocargo', express.raw({ type: 'application/json' }), (req, res) => { const timestamp = req.header('x-timetocargo-timestamp'); const signature = req.header('x-timetocargo-signature'); if (!timestamp || !signature) { return res.sendStatus(401); } const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(timestamp).update('.').update(req.body).digest('hex'); const signatureBuffer = Buffer.from(signature); const expectedBuffer = Buffer.from(expected); if (signatureBuffer.length !== expectedBuffer.length || !crypto.timingSafeEqual(signatureBuffer, expectedBuffer)) { return res.sendStatus(401); } const payload = JSON.parse(req.body.toString('utf8')); queueWebhook(payload); return res.sendStatus(200);});app.listen(3000, () => { console.log('Webhook server is running on port 3000');});Python / FastAPI
import hmacimport hashlibimport osfrom fastapi import FastAPI, Request, HTTPExceptionapp = FastAPI()secret = os.environ["TIMETOCARGO_WEBHOOK_SECRET"].encode()def enqueue_webhook(payload: dict) -> None: # Enqueue the event, save it to your database, # or pass it to another service for processing. print("Webhook received from TimeToCargo:", payload)@app.post("/webhooks/timetocargo")async def timetocargo_webhook(request: Request): body = await request.body() timestamp = request.headers.get("x-timetocargo-timestamp") signature = request.headers.get("x-timetocargo-signature") if not timestamp or not signature: raise HTTPException(status_code=401) digest = hmac.new( secret, timestamp.encode() + b"." + body, hashlib.sha256, ).hexdigest() expected = f"sha256={digest}" if not hmac.compare_digest(signature, expected): raise HTTPException(status_code=401) payload = await request.json() enqueue_webhook(payload) return {"ok": True}Header Parameters
HMAC SHA-256 signature of timestamp + '.' + rawBody, prefixed with sha256=. Use the unchanged request body as rawBody.
Unix timestamp used to generate the signature
Unique event ID matching payload.id
uuidUnique delivery attempt ID
uuidRequest Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
Example Requests
/trackingWebhookDelivery