Agent skill
Ship24 Webhooks Skill
Receive and authenticate Ship24 parcel tracking webhooks. Use when setting up a Ship24 webhook endpoint, checking the Authorization: Bearer webhook secret, debugging 401s, or handling tracking/events and tracking/pod (proof of delivery) messages.
Install this skill
npx skills add hookdeck/webhook-skills --skill ship24-webhooks
Ship24 is a multi-carrier parcel tracking API. When a tracker you follow gets a new tracking event (or a proof of delivery), Ship24 POSTs a JSON message to your Webhook URL. Webhooks are not signed. Ship24 allocates a random Webhook Secret to your account and sends it on every request as Authorization: Bearer <webhook secret>; your endpoint authenticates the sender by comparing it with the secret shown in your Ship24 dashboard.
When to Use This Skill
- How do I receive Ship24 webhooks?
- How do I verify the
Authorization: Bearerheader on a Ship24 webhook? - Does Ship24 sign webhook payloads? (No: use the shared Webhook Secret)
- How do I handle
tracking/eventsandtracking/podmessages? - How do I deduplicate Ship24 messages by
messageId? - Why does my Ship24 webhook endpoint return 401?
Setup Note
Paste the Webhook Secret from your Ship24 dashboard.
Store just the secret (without Bearer ) as SHIP24_WEBHOOK_SECRET. See references/setup.md.
Verification (core)
const crypto = require('crypto');
function verifyShip24Request(headers, configured = process.env.SHIP24_WEBHOOK_SECRET) {
if (!configured) return false; // never accept when unconfigured
const secret = Buffer.from(configured.replace(/^bearer /i, ''), 'utf8'); // bare secret
if (!secret.length) return false; // a configured value of just "Bearer " must not match
const header = headers['authorization'];
if (typeof header !== 'string' || header.slice(0, 7).toLowerCase() !== 'bearer ') return false;
const received = Buffer.from(header.slice(7), 'latin1'); // scheme is case-insensitive
return secret.length > 0 && received.length === secret.length &&
crypto.timingSafeEqual(received, secret);
}
- Header:
Authorization: Bearer <secret>. TheBearerscheme matches in any case (RFC 9110); the secret itself is compared exactly, never trimmed. - Missing header, wrong scheme or mismatch:
401. Body that isn't{ "trackings": [...] }:400. - Python:
hmac.compare_digeston bytes (encode both sides;strraises on non-ASCII). - Read the body raw (
express.raw({ type: '*/*' })) so any content type works, and parse it only after the check.
What this does and doesn't give you. A static shared credential authenticates the sender, not the body, and gives no replay protection (no signature, no timestamp). Anyone who captures one request, or learns the secret, can send you arbitrary tracking data. Use HTTPS only and rotate the Webhook Secret if it leaks.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Message Shape and Topics
The body is { "trackings": [ ... ] }. Each item has metadata (generatedAt, messageId, topic), tracker, shipment, events and statistics. Loop over trackings[] (it holds one item unless you raised "Maximum number of updates per webhook message") and dispatch on metadata.topic:
metadata.topic | Sent When | Webhook URL |
|---|---|---|
tracking/events | A tracker has a new tracking event | Webhook URL |
tracking/pod | A proof of delivery was retrieved or is unavailable (Proof of Delivery add-on) | Proof of Delivery Webhook URL (can be the same endpoint) |
- Dedupe on
metadata.messageId. Ship24 retries a message that didn't get a 2xx, so you can see the samemessageIdtwice. - Ordering isn't guaranteed. Compare each event's
occurrenceDatetimewith what you already stored before changing a shipment's status. - Respond with any
2xx. Otherwise the request is retried up to 20 times with exponential backoff (seconds to hours). Proof of delivery messages always contain exactly one tracking.
Environment Variables
SHIP24_WEBHOOK_SECRET=your_webhook_secret # Ship24 dashboard > Webhook Secret (without "Bearer ")
Local Development
Ship24 can't reach localhost. Tunnel with the Hookdeck CLI (no account required):
npx hookdeck-cli listen 3000 ship24 --path /webhooks/ship24
Use the printed URL as the Webhook URL in your Ship24 dashboard.
Resources
- references/overview.md - What Ship24 webhooks are, topics and payload shape
- references/setup.md - Configure the Webhook URL and find the Webhook Secret
- references/verification.md - Bearer secret check details, gotchas, debugging 401s
- examples/ - Runnable Express, Next.js and FastAPI handlers with tests