A shipment in Voxarel changes status dozens of times across its lifecycle. Each change matters to different people through different channels. The operations dashboard needs a real-time WebSocket update. The recipient needs a WhatsApp message. The sender needs an email. Compliance needs an immutable audit log entry. The AI prediction engine needs a CBM accumulation snapshot to improve its container forecasting.

Before Voxarel, the operations team refreshed their dashboard every few minutes. Customers called the office to ask "Where is my package?" There was no audit trail for status changes. The prediction engine did not exist.

The Fan-Out Architecture

When a status changes, the system writes to the database inside a transaction. Then, asynchronously and non-blocking, it fans out to five channels simultaneously. No channel blocks any other. A slow email delivery does not delay the WebSocket update. A WhatsApp API timeout does not prevent the audit log from recording.

The five channels:

  • WebSocket via Ably. The operations dashboard receives real-time updates without polling. When a driver marks a delivery complete in a parking garage and syncs 30 seconds later, the dashboard updates within milliseconds of the sync.
  • WhatsApp Business API. The recipient gets a templated message with the current status, expected delivery window, and a tracking link. Messages are pre-approved by WhatsApp for deliverability.
  • Email notifications. The sender receives a formatted email with status details, useful for corporate accounts that track shipments through email threads.
  • Immutable audit log. Every status change records the timestamp, the user who triggered it, the previous status, the new status, and the IP address. This log is append-only. Freight forwarding is a regulated industry; the audit trail is a compliance requirement.
  • AI prediction engine snapshot. Each status change contributes to the CBM (cubic meter) accumulation model that powers container fill predictions. The prediction engine uses 90 days of these snapshots to forecast when the next container booking should happen.

Why Asynchronous Fan-Out Matters

The synchronous alternative would mean every status update waits for all five channels to complete before returning a response. If the WhatsApp API takes 3 seconds, the driver's app waits 3 seconds. If the email server is down, the entire status update fails.

Asynchronous fan-out decouples the write from the distribution. The database transaction commits. The API returns 200. Then the fan-out processes each channel independently with its own retry logic, its own error handling, its own timeout budget.

In practice, the WebSocket update arrives in under 100 milliseconds. WhatsApp messages arrive within 1 to 5 seconds. Email within 10 to 30 seconds. The audit log writes synchronously within the transaction (too important to defer). The AI snapshot writes asynchronously with guaranteed delivery.

The Offline Complication

The fan-out gets interesting when the driver app is offline. A driver in a basement parking garage marks three deliveries complete, collects two COD payments, and photographs proof of delivery. All of this queues locally in the encrypted SQLite database.

When the driver reconnects 10 minutes later, the sync engine pushes all pending operations to the server. Each synced status change triggers the full fan-out. The operations dashboard shows three deliveries completing in rapid succession. The recipients get their WhatsApp messages. The audit log records the actual timestamps from the driver's device, not the sync timestamps.

The Lesson for Product Engineers

Real-time tracking systems are not about WebSockets. WebSockets are one channel. The real problem is fan-out: taking a single event and distributing it to every system and person that cares about it, without coupling those systems to each other or to the event source.

Build the write path (database transaction) and the distribution path (fan-out) as separate concerns. Let each channel have its own retry logic, its own failure mode, its own latency budget. The user who triggered the event gets a fast response. Everyone else gets notified on their own timeline.