> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Last-mile logistics notifications with driver privacy

> Compose the full delivery loop for parcel operators: masked driver-recipient sessions, SMS dispatch and ETA updates, MMS proof of delivery, WhatsApp reschedule flows, failed-delivery keyword handling, and delivery monitoring.

# Last-mile logistics notifications with driver privacy

A parcel operation texts a recipient many times per order: dispatch confirmed, courier assigned, ETA approaching, out for delivery, arrived, delivered, or failed and rescheduled. This guide assembles those sends into one loop on Devotel Orbit while keeping the driver's and the recipient's real phone numbers private for the whole delivery window.

The reader is a parcel carrier integration lead. Your order system (dispatch software or TMS) stays the system of record for shipment state; Orbit provides the messaging and privacy primitives that consume your events. Every capability below links to the page that documents it field by field. This guide is the composition, in the order a shipment actually happens: dispatch, track, reschedule, proof of delivery.

## 1. Reference architecture: shipment events to Orbit surfaces

Emit one event per shipment state change from your TMS, and map each event to a single Orbit surface:

| Shipment event | Orbit surface | Output to the recipient |
| - | - | - |
| `order.created` | [Number masking](https://docs.orbit.devotel.io/numbers/masking.md) session create | Driver and recipient bound behind one proxy number |
| `dispatch.confirmed` | SMS send | Dispatch confirmation with tracking link |
| `eta.updated` | SMS send | Revised window, sent only on meaningful change |
| `out_for_delivery` | MMS send | Courier or parcel photo with the ETA text |
| `reschedule.requested` | WhatsApp Flow | Interactive window picker the recipient fills in |
| `delivery.failed` | Keyword rules and Inbox | HELP handling and an agent ticket when a human is needed |
| `delivery.completed` | Masking session close | Proxy number released, pairing redacted |

The same loop as a sequence:

```text theme={null}
order.created      -> create masking session (driver, recipient, TTL = delivery window)
dispatch.confirmed -> SMS: dispatched, tracking short link or QR
eta.updated        -> SMS: new window (throttled to real changes)
out_for_delivery   -> MMS: photo + ETA
reschedule         -> WhatsApp Flow form -> flow completion webhook -> TMS re-slots
delivery.failed    -> keyword rules -> inbox ticket when an agent must act
delivery.completed -> DELETE session -> proxy number returns to the pool
```

Two rules keep the architecture clean:

* The TMS decides state; Orbit surfaces only consume events. A message is never the source of truth for whether a delivery happened.
* One masking session, one sender pool, and one flow per order. Stable keys (order id, stop id) ride in every event so each surface can correlate back to the same delivery.

## 2. Driver-recipient privacy: one masking session per delivery window

When a courier is assigned, create a proxy session binding the driver and the recipient for that delivery. Neither party ever sees the other's real number; both see only the proxy number.

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/proxy/sessions \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "participant_a": "+14155550100",
    "participant_b": "+14155550399",
    "ttl_minutes": 240
  }'
```

Size `ttl_minutes` to the delivery window plus a buffer for gate codes, lobbies, and a failed first attempt. The field defaults to 60 and accepts 1 to 1440; the [TTL picker](https://docs.orbit.devotel.io/guides/number-masking-guide.md) and the [session walkthrough](https://docs.orbit.devotel.io/guides/number-masking-privacy-sessions.md) cover sizing and the full create-route-terminate-verify sequence, including a worked courier example.

TTL and expiry behavior to encode in your completion handler:

* Expiry is enforced on access: past `expires_at`, the session stops accepting traffic even before the scheduled sweeper marks it `expired` and releases the number.
* Close explicitly on `delivery.completed` or `delivery.failed` terminal states so the number frees immediately. A `409` on close means the session is already `closed` or `expired`; treat it as success, not as a retry.
* Create one session per delivery. Reusing a number from an earlier session is the classic leak: the old pairing stays reachable from the new order's messages.

Unmask audit. The real pair exists only in your create call and your TMS. Orbit's message records show the proxy number as sender and recipient, and each forwarded message carries the session id so masked traffic stays attributable in your conversation view. At close the pairing is redacted: neither party can re-contact the other, and stored threads keep showing the proxy number only. If your data policy also covers stored bodies, pair the session with [message history redaction](https://docs.orbit.devotel.io/guides/search-message-history.md). Any operator view of real numbers belongs in your own systems under your own access controls; keep participant identity out of message bodies and flow context.

## 3. Dispatch and ETA over SMS, photo over MMS

Send dispatch and ETA updates through your operational sender pool, as set up in the [order-dispatch notifications workflow](https://docs.orbit.devotel.io/guides/order-dispatch-notifications-workflow.md). Keep operational traffic on its own pool so delivery notifications are not queued behind campaign volume, and keep the pool's senders on the tenant's approved sender configuration.

ETA discipline matters more than ETA frequency. Send an update when the window moves by a meaningful amount (say, fifteen minutes or more), not on every GPS ping; a recipient who gets nine near-identical ETA texts treats the tenth as spam. Include the order's tracking link in the dispatch message, created with your tenant's [short links](https://docs.orbit.devotel.io/guides/short-links-and-click-tracking.md) and [QR tools](https://docs.orbit.devotel.io/guides/qr-tools.md) so clicks are measurable and the body stays within one SMS segment where possible.

When the courier scans out for delivery, upgrade the channel: attach a parcel or van photo with the ETA text using the [MMS send path](https://docs.orbit.devotel.io/guides/mms-send-and-receive.md). A photo of the recipient's own parcel on the doorstep doubles as the last pre-POD touchpoint and cuts "where is it" replies.

```text theme={null}
Dispatch SMS:  "Devotel Courier: order 58211 dispatched. Track: https://o.devotel.io/t/58211"
ETA SMS:       "Order 58211: courier now arriving 2-4 pm. Same link tracks it live."
Out-for-delivery MMS: [photo] "Order 58211 is out for delivery with Ana, arriving 2-4 pm."
```

Recipients reply to these messages, so decide the reply path before launch: either the reply lands in the masked driver thread (section 2) or it lands in your keyword and inbox handling (section 5). Route one way per sender, not both.

## 4. Recipient reschedule via WhatsApp Flow

For recipients on WhatsApp, replace "reply with a day that suits you" with a structured form. Build the window picker as a [WhatsApp Flow](https://docs.orbit.devotel.io/guides/whatsapp/whatsapp-flows.md): date choices from your TMS's available slots, a time-window select, and a safe-to-leave note field. Open the flow from a WhatsApp message sent when `delivery.failed` or `reschedule.offered` fires, and keep the SMS tracking link as the fallback for recipients who are not on WhatsApp.

Handle flow completion in a webhook: when the recipient submits the form, the completion event carries the chosen window back to your endpoint. Verify the webhook signature before you act on it, write the new slot to the TMS, and confirm the re-slot to the recipient on their preferred channel. Two cautions from the consent model:

* WhatsApp consent is separate from SMS consent. A recipient who opted in for delivery SMS is not automatically opted in for WhatsApp; send the flow only to numbers whose tenant records show WhatsApp consent.
* A reschedule is operational, not marketing. The flow's follow-up messages are delivery notifications and should be classified that way in your tenant's compliance settings (section 7).

## 5. Failed-delivery loop: keywords and inbox handoff

A failed first attempt generates the most inbound volume a delivery operation sees. Configure [keyword rules](https://docs.orbit.devotel.io/guides/settings-keyword-rules-console.md) for the replies you can answer mechanically: RESCHEDULE opens the WhatsApp Flow (or returns the reschedule link by SMS), TRACK returns the tracking short link, HELP returns your support contact. Mandatory opt-out and help keywords behave per your tenant's [opt-out rules](https://docs.orbit.devotel.io/guides/opt-out-rules.md); never override them with a custom reply.

Whatever the keywords cannot resolve becomes a ticket. Hand the thread to your support team with the [inbox ticket workflow](https://docs.orbit.devotel.io/guides/inbox-tickets-workflow.md): the masked conversation, the order id, and the delivery history arrive as one ticket an agent can act on without asking the recipient to repeat anything. Set the handoff condition narrowly (for example, two consecutive failed attempts plus an unanswered reschedule offer) so the inbox carries exceptions, not routine traffic.

## 6. Ops monitoring: delivery log, DLRs, and the stuck order

Watch delivery the same way you watch campaigns: from the delivery log and from [DLR outcomes monitoring](https://docs.orbit.devotel.io/guides/dlr-outcomes-monitoring.md). For a last-mile tenant the outcomes that matter are delivered-vs-pending on ETA sends (a pending spike means a sender or route problem on the day's peak window) and failure reasons on dispatch sends (invalid number vs. carrier rejection, which route to different fixes in your TMS data).

When an order stops moving, walk it through the [stuck-order case study](https://docs.orbit.devotel.io/guides/interactions-stuck-order-case.md). It shows where an order can stall between surfaces (event never ingested, message suppressed, session expired before the attempt) and which log answers each question. Pair it with the masking drift check: `GET /api/v1/proxy/sessions?status=active` reconciled against open orders catches sessions your completion handler forgot to close.

## 7. Cost control: quiet hours and frequency caps

Delivery notifications are high volume, so two tenant controls decide both cost and deliverability:

* **Quiet hours** defer sends outside the recipient's permitted window. For a delivery operation that is a per-tenant judgment: a 6 am dispatched text deferred to 8 am is fine; the same deferral applied to a courier-at-the-door call-out would break the delivery. Configure the window in your tenant's quiet hours settings and read [send gating and quiet hours](https://docs.orbit.devotel.io/concepts/send-gating-and-quiet-hours.md) for how deferral applies per channel.
* **Frequency caps** bound sends per recipient per period. Set caps with the operational class in mind: [frequency caps](https://docs.orbit.devotel.io/guides/frequency-caps.md) and the [campaign limits and quiet hours guide](https://docs.orbit.devotel.io/guides/campaign-limits-quiet-hours.md) explain how caps and quiet hours interplay, and [marketing classification](https://docs.orbit.devotel.io/concepts/email-quiet-hours-and-marketing-classification.md) explains why operational traffic must not be counted as marketing. A cap tuned for promotional volume will happily block a third ETA update on a rescheduled day; classify delivery traffic as operational and let marketing sends respect the marketing caps.

Both are tenant-owned controls with tenant defaults; set them per market rather than assuming one global posture.

## Troubleshooting

| Symptom | Check |
| - | - |
| Recipient sees the driver's real number | Confirm the send went to the proxy number, not `participant_a`; outbound sends addressed to the proxy resolve to the session |
| Driver and recipient cannot connect late in the window | Session expired: compare `expires_at` against the delivery window and raise `ttl_minutes` |
| ETA texts arrive at night | Tenant quiet-hours window is deferring operational sends; adjust the window or the operational classification |
| Reschedule form never opens | Recipient lacks WhatsApp consent, or the flow message used a channel the recipient has no address on |
| DLRs show delivered but the recipient reports nothing | Check suppression and the sender pool assignment in the order-dispatch workflow; verify with the delivery log, not the send response |

## Next steps

* [Mask two parties' numbers with a proxy session](https://docs.orbit.devotel.io/guides/number-masking-privacy-sessions.md) — the full session lifecycle with a worked delivery example.
* [Order-dispatch notifications workflow](https://docs.orbit.devotel.io/guides/order-dispatch-notifications-workflow.md) — sender pools and dispatch send setup.
* [MMS send and receive](https://docs.orbit.devotel.io/guides/mms-send-and-receive.md) — attach photos to delivery messages.
* [WhatsApp Flows](https://docs.orbit.devotel.io/guides/whatsapp/whatsapp-flows.md) — build the reschedule form.
* [DLR outcomes monitoring](https://docs.orbit.devotel.io/guides/dlr-outcomes-monitoring.md) — watch delivery of every operational send.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.