Guide to Ship24 Webhooks: Features and Best Practices
Ship24 webhooks push parcel tracking updates to your system as couriers report them. Ship24 tracks shipments across many carriers through one API, and once you create a Tracker for a shipment, every new event the courier reports, such as in transit, out for delivery or delivered, can reach your order system or customer notifications straight away instead of on your next poll.
This guide covers how Ship24 webhooks are set up and authenticated, the tracking and proof-of-delivery messages and their shape, how grouping, ordering and retries behave, and the practices that keep a Ship24 receiver reliable.
What are Ship24 webhooks?
Ship24 sends webhook messages as soon as a valid Webhook URL is configured in your dashboard. From then on, it pushes any new tracking events found by your Trackers to that URL as JSON POST requests. Each message contains the entire event, so your system can use the data without calling the API.
Ship24 does not sign webhooks. It allocates a random Webhook Secret to your account and sends it with every request as Authorization: Bearer <secret>. Your endpoint compares the value with the one shown in your dashboard.
Ship24 webhook features
| Feature | Details |
|---|---|
| Configuration | Webhook URL in the Ship24 dashboard. Proof of Delivery needs a separate URL on the same page |
| Method and format | POST, JSON body |
| Authentication | Authorization: Bearer <Webhook Secret>, allocated per account |
| Signature and timestamp | None |
| Topics | tracking/events and tracking/pod, in metadata.topic |
| Grouping | One update per message by default. Grouping, on request, adds about 15 minutes of delay |
| Ordering | Not guaranteed |
| Success response | Any 2xx. The response body does not matter |
| Retries | Up to 20, with exponential backoff from a few seconds to a few hours |
| Resend | All messages for a tracker, through the API's resend webhooks endpoint |
| Testing | Test your integration in the dashboard sends dummy tracking events |
| Source IPs | 54.161.7.2. Dashboard test sends come from elsewhere |
Tracking and proof-of-delivery messages
Every message has a trackings array. By default on recent accounts it holds exactly one item, and that item's events array holds exactly one event. Here is Ship24's example, with the parts it elides marked:
{
"trackings": [
{
"metadata": {
"generatedAt": "2025-03-04T17:13:35.000Z",
"messageId": "356a7f93-3ce5-4b49-b560-156537283df9",
"topic": "tracking/events"
},
"tracker": {
"trackerId": "26148317-7502-d3ac-44a9-546d240ac0dd",
"trackingNumber": "9400115901047177598206",
"shipmentReference": "c6e4fef4-a816-b68f-4024-3b7e4c5a9f81",
"clientTrackerId": "3fa99515-3ca0-4901-85bb-056ee016799b",
"isSubscribed": true,
"createdAt": "2025-03-01T03:16:22.000Z"
},
"shipment": { "shipmentId": "f4f888d7-d140-423f-9a48-e0689d27e098", "...": "..." },
"events": [
{
"eventId": "ee8ebe96-4eae-4a91-9a99-8f3afa6a0f46",
"status": "Delivered to the addressee",
"occurrenceDatetime": "2025-03-04T17:12:57",
"...": "..."
}
]
}
]
}
metadata.messageId identifies the message, tracker identifies the Tracker and its tracking number, and occurrenceDatetime is when the event happened. Some fields belong to add-on options and are absent when you are not subscribed to them, so your parser must tolerate missing keys.
With the Proof of Delivery add-on, Ship24 asks the courier for a proof once a shipment is delivered, and sends one message with topic tracking/pod to your Proof of Delivery Webhook URL. Its data.status is found or unavailable. When found, data.content holds either text, such as a signatory name, or a downloadUrl for a PDF or image. The download URL is valid for 7 days. Proof-of-delivery messages are never grouped.
Inspect Ship24 webhooks as they arrive. Set your Webhook URL to a Hookdeck Inspect URL and use Test your integration to see a message's headers and body, with no account or setup required.
Setting up Ship24 webhooks
- In the Ship24 dashboard, set your Webhook URL to your HTTPS endpoint.
- If you subscribe to Proof of Delivery, set the Proof of Delivery Webhook URL on the same page. It can be the same endpoint or a different one. Without it, no proof-of-delivery messages are sent.
- Copy the Webhook Secret shown in the dashboard and store it server-side, for example as
SHIP24_WEBHOOK_SECRET. - Use Test your integration to send a message with dummy tracking events.
To route through Hookdeck, create a Hookdeck source with the Ship24 type and use its URL as the Webhook URL, and as the Proof of Delivery Webhook URL if you use it. The source has one field, Webhook Secret: paste the Webhook Secret from your Ship24 dashboard. Paste only the secret. Ship24 sends it as Bearer <secret> in the Authorization header, and Hookdeck matches the Bearer scheme itself. The source accepts POST only, which is the method Ship24 uses.
If you restrict inbound traffic by IP on your own endpoint, set it up after testing: dashboard test sends do not come from 54.161.7.2.
Securing Ship24 webhooks
Read the Authorization header, check that the scheme is Bearer (case-insensitively), and compare the rest with your Webhook Secret in constant time.
const crypto = require('crypto');
const express = require('express');
const WEBHOOK_SECRET = process.env.SHIP24_WEBHOOK_SECRET; // the secret only, no "Bearer "
function verifyBearer(header) {
if (!WEBHOOK_SECRET || typeof header !== 'string') return false;
const match = /^bearer (.+)$/i.exec(header);
if (!match) return false;
const received = Buffer.from(match[1], 'utf8');
const expected = Buffer.from(WEBHOOK_SECRET, 'utf8');
// timingSafeEqual throws on unequal lengths, so check first.
return received.length === expected.length && crypto.timingSafeEqual(received, expected);
}
const app = express();
app.post('/webhooks/ship24', express.raw({ type: '*/*' }), (req, res) => {
if (!verifyBearer(req.get('authorization'))) {
return res.sendStatus(401);
}
let message;
try {
message = JSON.parse(req.body.toString('utf8'));
} catch {
return res.sendStatus(400);
}
for (const tracking of message.trackings || []) {
// Route on tracking.metadata.topic; dedupe on tracking.metadata.messageId.
}
res.sendStatus(200);
});
The same check in Python:
import hmac
import os
WEBHOOK_SECRET = os.environ.get("SHIP24_WEBHOOK_SECRET", "") # the secret only
def verify_bearer(header) -> bool:
if not WEBHOOK_SECRET or not header:
return False
scheme, _, token = header.partition(" ")
if scheme.lower() != "bearer":
return False
# Compare bytes: compare_digest raises TypeError on a non-ASCII str.
return hmac.compare_digest(token.encode("utf-8"), WEBHOOK_SECRET.encode("utf-8"))
The Webhook Secret is static. It authenticates the sender, not the body, and with no timestamp a captured request can be replayed. Serve the endpoint over HTTPS and keep the secret out of logs. Allowlisting 54.161.7.2 adds a second check, but the secret is what authenticates a request.
Make Ship24 webhooks production-ready. Hookdeck Event Gateway checks the Webhook Secret, answers Ship24 straight away, and queues every tracking update durably before your service sees it.
Ship24 webhook limitations and pain points
Updates arrive out of order
The Problem: A shipment shows as in transit after it has been delivered.
Why It Happens: Ship24 does not guarantee order. A retried message can arrive after newer ones, and couriers can report events for one shipment at different times.
Workarounds:
- Before changing a shipment's status, compare the event's
occurrenceDatetimewith the latest one you already have, as Ship24 recommends, and ignore older events. - Store every event, but derive the current status from the latest by occurrence time.
How Hookdeck Can Help: Hookdeck keeps every message with its full body, so when a status looks wrong you can see the order things actually arrived in and replay them after fixing your logic.
Retries mean duplicates
The Problem: The same tracking event is processed twice, and a customer gets two "delivered" emails.
Why It Happens: When Ship24 does not receive a 2xx, it retries the message up to 20 times, with delays growing from a few seconds to a few hours.
Workarounds:
- Store each
metadata.messageId, or the event'seventId, before acting on it, and skip repeats. - Make notifications to customers idempotent per shipment and status.
How Hookdeck Can Help: Hookdeck deduplication can drop repeated messages before they reach your service, based on messageId or eventId.
Proof-of-delivery files expire
The Problem: A proof of delivery you meant to keep is gone when someone asks for it.
Why It Happens: The downloadUrl in a tracking/pod message is valid for 7 days, and proofs are only delivered by webhook, not through the tracking API.
Workarounds:
- Download the file and store it on your side as soon as the message arrives.
- If your service was down, use the API's resend webhooks endpoint for the tracker while the link is still valid.
How Hookdeck Can Help: Hookdeck stores every tracking/pod message and retries delivery to your service on a schedule you control, so a brief outage does not leave you without the link.
Grouped messages need different handling
The Problem: A handler that reads only trackings[0] drops updates.
Why It Happens: Accounts with grouping enabled can receive several updates, for several shipments, in one message, and Ship24 waits about 15 minutes to group them.
Workarounds:
- Always loop over
trackings, even on a default account. - Keep the default of one update per message unless the volume of requests is a real problem.
How Hookdeck Can Help: Hookdeck transformations can reshape a message before it reaches your service, and every original message is kept for inspection.
Best practices
Store the secret, not the header
Keep only the secret in your configuration and match the Bearer scheme in code, the same way the Hookdeck source does.
Tolerate missing fields
Add-on fields are absent when you are not subscribed to them. Use optional access throughout and never require a field the core payload does not guarantee.
Make handlers idempotent
Key stored events on eventId and messages on messageId. See our guide to webhook idempotency.
Acknowledge fast and process asynchronously
Verify, store, return 200, and do the work afterwards. See why to process webhooks asynchronously.
Conclusion
Ship24 pushes tracking updates and proofs of delivery as JSON POST requests with your account's Webhook Secret in an Authorization: Bearer header and no signature. Check the scheme and compare the secret in constant time over HTTPS, loop over trackings, compare occurrenceDatetime before changing status, deduplicate retried messages, and store proof-of-delivery files before their links expire.
Hookdeck Event Gateway checks the Webhook Secret, answers Ship24 immediately and keeps every message until your service has processed it, so an outage on your side does not mean missed deliveries.
Get started with Hookdeck for free and handle Ship24 webhooks reliably in minutes.