Key takeaways
- Acknowledge with a 2xx response quickly and do the work afterwards, in a queue.
- Expect duplicates and out-of-order events; make processing idempotent.
- Verify the signature over the exact bytes received, and reject old timestamps.
- Reconcile: look up messages that never reach a final status.
Why webhooks beat polling
Asking the API for the status of every message wastes calls and adds delay. With a webhook the provider calls you when a status changes, so your system can react in seconds: show a code as delivered, trigger a fallback or update an order.
What can go wrong
- Duplicates: delivery is usually at least once, so the same event may arrive more than once
- Order: a later status can arrive before an earlier one
- Delay: a status can arrive minutes or hours after sending, or not at all
- Downtime: if your endpoint is down, events are retried for a limited time and then dropped
Design rules for the endpoint
- Return a 2xx status as soon as the request is stored; do the processing in a queue
- Key every update on the message ID and the status, so a repeat changes nothing
- Never move a message backwards: once it is delivered or failed, ignore an earlier status
- Keep the raw payload for a while for debugging
- Make the endpoint cheap and fast; a slow endpoint looks like a failing one
Verify what you receive
Anyone can call a public URL, so check that the request came from your provider. A common method is a signature: the sender computes a keyed hash over a timestamp and the raw body, and sends it in a header. You compute the same value with your secret and compare.
Compute it over the exact bytes received, not a re-serialised copy, compare in constant time, and reject timestamps that are too old so a captured request cannot be replayed.
Reconcile the gaps
Run a job that looks for messages that have had no final status for longer than your normal delay, and ask the API for their status. It catches events that were lost and shows you whether a country or route is quiet.
How ZafeConnect fits
ZafeConnect sends a POST to your webhook URL when a message status changes, with the headers X-ZafeConnect-Event, X-ZafeConnect-Timestamp and X-ZafeConnect-Signature. The signature is a hex HMAC-SHA256 over the timestamp, a dot and the raw body. Return any 2xx to acknowledge; ZafeConnect retries up to 5 times if your endpoint does not. The developer documentation has the details.