Modaltrans can push events to your own HTTPS endpoint as they happen, so you do not have to poll the API or wait for a file. When something happens we send one signed POST with a JSON body. You return a 2xx and the delivery is done. Anything else counts as a failure, and some failures are retried once.This page covers what is the same for every event type: how an endpoint is created, how to verify the signature, how to answer, what we retry and when an endpoint is switched off. What each event carries is described on its own page, starting with Tracking Webhooks.Setting it up#
Two sides are involved and it helps to be clear about who does what.The receiving side (your IT team, your software supplier, or your customer's) builds the address that accepts the deliveries and decides what that address is. Modaltrans never invents it.The Modaltrans side (a colleague with access to your Modaltrans settings) enters that address in Modaltrans, ticks the events and hands the signing secret back to the receiving side.Before the endpoint is created, the receiving address has to:be reachable from the public internet, and stay that way: no internal hostname, no private IP address, no VPN-only address. If the address later starts resolving to an internal address, deliveries stop and they are not retried;
answer over https with a valid certificate, since plain http is rejected when you save;
accept a POST with a JSON body and answer within 10 seconds, see Responding below.
1.
Go to Settings > Integration Settings > Webhook Endpoints and select New Webhook Endpoint.
2.
In Name, write who it belongs to, for example "Acme Ltd production". It is only a label for your own list.
3.
In Target URL, paste the address the receiving side gave you.
4.
In Customer, pick the customer whose shipments should be sent. Left empty it means All customers, so every shipment your account tracks is forwarded to that address. Empty is right for a system of your own, and wrong when the address belongs to one of your customers.
5.
Tick the events under Subscribed Events.
6.
Save. The endpoint page now shows the Secret. Send it to the receiving side the way you would send a password. Anyone who has it can forge a delivery.
7.
Select Send Test Event and have the receiving side confirm that the request arrived and the signature matched.
The target URL cannot be changed once the endpoint is saved. If the address changes, create a new endpoint and delete the old one; the new endpoint gets a new secret and an empty delivery list. The secret can be replaced at any time with Rotate Secret, and the old one stops working the moment you do, so agree on a time with the receiving side first.If you cannot see this screen, your Modaltrans user does not have access to the integration settings. Ask the colleague who administers your Modaltrans account to create the endpoint for you.Verifying the signature#
Every request carries these headers:| Header | Value |
|---|
X-Modaltrans-Signature | sha256= followed by the lowercase hex digest |
X-Modaltrans-Delivery | Delivery id, identical on every attempt of the same delivery |
X-Modaltrans-Event | The event name, for example sea_tracking_events/create |
Content-Type | Always application/json |
User-Agent | Modaltrans-Webhook/1.0 |
Your signing secret looks like whsec_ followed by 32 random bytes in base64. Use the whole string, prefix included, as the HMAC key; do not strip the prefix and do not base64-decode it.Compute HMAC-SHA256 over the entire raw request body and compare the result, prefixed with sha256=, to X-Modaltrans-Signature. Sign the bytes exactly as received, before any JSON parsing or re-serialisation; whitespace matters. Use a constant-time comparison, not ==.You can check your implementation offline against this vector:secret whsec_SxQbP1Q6TTh9jC1zBDuvWJH0RGKiKpYcO0mQ4ap9ZmI=
body {"message":"You shall not pass!"}
signature sha256=d55e674078099a696aebbcb1c4e0094a938fd7902667c8d06de366bcdfcc2741
The signature is computed at the moment we send, with the secret the endpoint holds right then. A retry or a resend after a rotation therefore carries the same body under a different signature, so verify against the current secret rather than a value you stored earlier.Reject any request whose signature does not match. When the secret is rotated the old one stops working immediately, so read it from configuration rather than hard-coding it.The signature carries no timestamp, so a request someone captured stays valid for as long as the secret does. Keep the receiving address on TLS, do not log the body where it can be read back, and de-duplicate on X-Modaltrans-Delivery.Responding#
Return a 2xx as soon as you have stored the body. Do not validate business rules, look up records or write to downstream systems before answering. If your processing fails after you have accepted the delivery, that is yours to retry; if you answer with an error we will resend the same body and you may end up processing it twice.A 2xx means "received", not "understood". Any other status is treated as a failure.We wait 10 seconds, and the response body counts. A 200 whose body trickles in after that is a timeout on our side, and the same delivery is sent again, so answer first, answer completely, and do the work afterwards.We do not keep your response body when the delivery succeeds. When it fails we store the first 1 KB so your administrator can see the error you returned. We still download whatever you send before truncating it, so keep error bodies short and do not put anything sensitive in them.Retries#
| Outcome | What we do |
|---|
2xx | Done |
5xx, 408, 429, timeout, connection failure, TLS failure | One retry, at least 5 minutes later |
Other 4xx | No retry, the delivery is marked failed |
3xx | No retry, give us the final URL instead |
A delivery gets two attempts: the first one, then one retry at least five minutes later. The wait is a floor, not a schedule, so do not time anything against it. If the second attempt also fails the delivery is marked failed and stays on the endpoint page, where your Modaltrans administrator can resend it by hand; a resend starts a fresh pair of attempts.Retries and manual resends reuse the stored body byte for byte, so X-Modaltrans-Delivery and eventID are identical on every attempt. The signature is the one exception: it is recomputed at send time, so it differs if the secret was rotated in between.When an endpoint is disabled#
Modaltrans keeps the time of the first failure in an unbroken run of failures. When a delivery fails again 24 hours or more after that first one, the endpoint is disabled and the colleague who created it is notified. One successful delivery in between clears the run, so an endpoint that works even once a day is never disabled this way, and an endpoint that fails and then receives no traffic at all stays active until the next failure.While an endpoint is disabled, new events are not queued for it. They are skipped, not stored, and they do not arrive later. A retry or a manual resend that was already on its way can still reach you. Someone has to set the endpoint back to active by hand once the problem is fixed, and the events missed in the meantime have to be asked for separately.Identifiers and ordering#
Two identifiers do different jobs:eventID identifies this message. A correction to something you already have arrives as an update event with a new eventID.
modaltrans.eventKey identifies the record as Modaltrans holds it. It stays the same while that record stays the same, so upsert on it. What can make it change is specific to the event type, and the event page says so.
X-Modaltrans-Delivery tells you whether two requests are the same attempt at the same message. It is stable across retries and manual resends, so use it to avoid double-processing, but do not treat a repeat as something to ignore silently: a resend usually means your administrator wants that event applied.Deliveries are not ordered, and eventCreatedDateTime is the moment we built the message, not the moment anything happened. A create and the update that follows it can carry the same value, so it settles nothing on its own. Apply messages in the order you receive them and let the last one win for a given eventKey.data is an array today and always holds one element. If we start grouping several events of one shipment into a single delivery, the shape will not change.Testing and troubleshooting#
Your Modaltrans administrator can press Send Test Event on the endpoint page at any time. The test delivery is signed and headed exactly like a real one and carries "test": true in the envelope, so you can drop it before it reaches your records. Its body has one field real payloads never have, modaltrans.sentBy, so do not reject it on a strict schema check.The same page lists the last ten deliveries with their status, the HTTP code you returned, how many attempts were made, the exact body we sent and, when the delivery failed, the beginning of the answer you gave. That list is the first place to look when something is missing. If the delivery is there and was accepted, the event left us and the problem is on the receiving side. If it is not there at all, the event was never forwarded, and the event page explains what we filter out. Any delivery on the list can be resent by hand, with the same body and the same delivery id. The list only holds the last ten, so on a busy endpoint a failed delivery scrolls off it within minutes and can no longer be resent from there. Modified at 2026-09-25 15:35:17