RelayRelayAPI Reference
🔒 Confidential & Proprietary. Prepared for the authorized integration team. Contains confidential commercial and operational information of Last Mile Solutions — do not redistribute.

Relay — returns & shipping coordination

Relay is Last Mile Solutions' returns & shipping coordination network. Quote a return, ingest the order with its marketplace context, hand the customer a print-at-home label or a QR code, and follow the parcel through possession, sort, carrier injection, and delivery — over one unified, versioned API.

Returns are the primary flow. Service is configured per market. Money is integer cents; timestamps are ISO-8601 UTC; every POST accepts an Idempotency-Key. A request body that is absent, malformed, or not a JSON object is 422 validation_error with details.field: "body" — a client-side mistake is never reported as a 5xx.

Your key may be scoped. Most keys carry * and reach everything below. A narrower key is confined to grants of the form <resource>:<action> — the resource is the first path segment after /v2/ (locations, shipments, rate-cards; the /v2/partners/… family is all partners), the action is read for GET and write for everything else. <resource>:write grants <resource>:read as well, not the reverse. An endpoint outside your grants answers 403 insufficient_scope, naming the scope you would have needed in details.required_scope — a different refusal from 403 forbidden, which means your source address is outside the allowlist pinned to your key. GET /v2/ and GET /v2/health answer on any key regardless of scope.

You see your own endpoints, not everyone's. Most of this reference is the shared API and is the same for every partner. A few endpoints under /v2/partners/… are written around one partner's own payload dialect, and each is listed only for the partner it belongs to — so this page never says who else we onboard, or what their requests look like. If yours is missing, open the reference from the link in your welcome email, or sign in on /docs/flow with your API key.

Auth: X-Api-Key + HMAC signature Scopes: enforced IDs: shp_ pkp_ rate_ adj_ whk_ qr_ loc_ evt_ tnd_ dsc_ clm_ doc_ lbl_ Release 2.226.1 Base path /v2 Sandbox online · Production online

Three programs, one API

Relay runs three drop-off programs across the Package Hub national mail-store network. They differ in who owns the parcel and where it is headed — not in the calls you make. One credential, one base path, one set of objects serves all three.

ProgramdirectionWhat happens
ReturnsreturnA consumer drops a return at a Package Hub store and Relay carries it back to the shipper. This is the primary flow, and the one the rest of this reference uses for its examples.
Platform drop-offoutboundA seller on an online platform uses Package Hub as the first mile for an outbound customer shipment. Same ingest, same lifecycle, opposite direction of travel.
RetailoutboundA walk-in customer presents a package at the counter: price it with GET /service-options, ingest it with POST /shipments, and take possession with POST /scan-events/accept. The store collects payment; Relay records the shipment.

Nothing about the program is baked into the credential or the path. Set direction per shipment, and use the free-form metadata object to carry your own program, channel, or campaign tags — Relay echoes it back verbatim on every read.

The five carrier scenarios

Every shipment resolves to a service level, and each service level names exactly one operating scenario: who selects the final-mile carrier, what class of carrier that is, and how the parcel leaves the LMS facility. Four metadata fields — carrier_selection, carrier_class, handoff, scenario — travel with every rate, service option, rate card, and shipment, so you never have to infer the model from the service-level name.

Scenarioservice_levelcarrier_selectioncarrier_classhandoff
1areach_alternativelmsalternativeinject
1breach_coveragelmscoverageinject
1ccorelmscoreinject
2client_carrier_injectclientclient_selectedinject
3client_carrier_collectclientclient_selectedcollect
  • 1a · reach_alternative — LMS sweeps, sorts, and injects to an alternative-network carrier. The most aggressive rates, with partial ZIP coverage.
  • 1b · reach_coverage — the same flow to a coverage carrier with 100% US ZIP coverage.
  • 1c · core — the same flow into the LMS Core network, for regional, ZIP-defined lanes.
  • 2 · client_carrier_inject — you select the carrier; LMS picks up, sorts, and injects at that carrier's facility.
  • 3 · client_carrier_collect — you select the carrier; LMS picks up, sorts, and makes the parcels ready, and the carrier collects them at the LMS facility.

Each launched market runs exactly one of these — GET /markets tells you which, and whether that market's carrier is chosen by LMS or by the marketplace. GET /service-options prices every scenario for a lane in a single call, including the local same-day drop-off cutoff.

Service-level rename

Responses always emit the current IDs below. The prior ID is echoed alongside them as legacy_service_level for one release, and legacy IDs remain accepted on input anywhere you send a service_level — rate-card and transit-time filters, and inline shipment creation — so nothing in flight breaks.

Legacy IDCurrent ID
relay_select_saverreach_alternative
relay_select_nationalreach_coverage
relay_corecore
pickup_sort_injectclient_carrier_inject
pickup_sort_collectclient_carrier_collect

The parcel lifecycle

Relay reports a parcel's whole journey, not just its endpoints. Every step below is a shipment status, a webhook event of the same name, and a tracking event on GET /tracking/{tracking}.

createdlabel_readyready_for_pickuppicked_upat_sort_centerin_possessionsortedinjected|made_readyin_transitout_for_deliverydelivered
delivery failed:undeliverable|unclaimedreturn_to_sender|disposed|out_for_delivery
terminal & exception:cancelledexpiredrejectedexception
StatusWhat it means
createdIngested and priced. Push-ahead orders carry an expires_at and auto-expire if never possessed.
label_readyThe printable label is minted, or the QR is staged with Package Hub.
ready_for_pickupScanned at a Package Hub counter — either a pre-applied label accepted at the counter, or a QR scanned, printed, and applied on site.
picked_upCollected from the store by an LMS driver.
at_sort_centerArrived at the LMS sort facility. Stamps milestones.sortScanned.
in_possessionPossession scan — at the store for a QR walk-in, or at the sort facility on the standard ladder. Stamps milestones.possessionScanned.
sortedSorted and segmented to the carrier staging area.
injectedHanded into the receiving carrier's network (handoff: inject).
made_readyStaged and signed for at the LMS facility for carrier collection (handoff: collect).
in_transitMoving with the carrier.
out_for_deliveryOn the delivery vehicle. Stamps milestones.driverScanned.
deliveredDelivered. Stamps milestones.deliveryScanned and publishes pod / podGrade.
rejectedRefused at the counter, with a reason code from the closed reject set.
expiredA push-ahead label or QR was never presented and its expires_at window elapsed. Stamps expired_at and emits shipment.expired. Terminal.
undeliverableDelivery failed and the parcel is off the delivery leg, awaiting its onward instruction.
return_to_senderThe return leg has started — the parcel is on its way back to the sender. Terminal.
unclaimedNobody collected it. Stamps unclaimed_since, and an advisory dispose_after 30 days later.
disposedDestroyed or liquidated. Terminal.

held is a flag, not a status: POST /shipments/{id}/hold and /release pause and resume a pre-injection parcel without moving it off the ladder.

The last four are the sad paths off the ladder, and they are reached by one call — POST /shipments/{id}/disposition, carrying rts, unclaimed, dispose, or redeliver plus a reason. A redeliver rejoins the ladder at out_for_delivery and completes normally; rts takes two calls, because a return leg cannot begin while the parcel is still out with the delivery carrier. See the endpoint for the full rule.

Drop-off, tender, and acceptance

  1. Quote and ingest. Price the lane with GET /service-options or POST /rates, then ingest with POST /shipmentsfulfillment: print_label for a print-at-home label, or qr_code for a code the customer shows at the counter.
  2. Amend before drop-off. PATCH /shipments/{id} re-rates a parcel that hasn't been scanned yet; a price or routing change mints a fresh label and supersedes the old one. POST /shipments/{id}/label/reprint and POST /shipments/{id}/qr/reissue cover a lost printout or a lost code. Once the parcel is scanned at a store, the shipment is locked.
  3. At the counter. A QR is presented to POST /scan-events/walk-in, which validates it, produces the label on site, and takes possession. A pre-applied label is accepted with POST /scan-events/accept. A package the store cannot take is refused with POST /scan-events/reject. If the weight or dimensions differ from what was declared, POST /discrepancies records declared-vs-observed up front and links the resulting price adjustment.
  4. Collection and sort. The driver collects the store's parcels; the day's drop-offs close onto a manifest (GET /manifests, POST /manifests/{id}/close). The parcels arrive at the sort facility, are possession-scanned, and are sorted to the carrier staging area.
  5. Handoff. POST /tenders stages a batch of parcels at a facility for a named receiving carrier. POST /tenders/{id}/accept is that carrier's proof of receipt — who accepted, when, and how many pieces — and advances every member shipment to injected or made_ready. A piece-count mismatch records a short/over discrepancy on the tender for reconciliation without blocking the handoff.
  6. Delivery. The parcel moves to in_transit, out_for_delivery, and delivered. Proof of delivery is at GET /shipments/{id}/pod; GET /tracking/{tracking} is the customer-facing view and needs only an API key. Reweigh adjustments are disputable, and lost, damaged, or misdelivered parcels are claimable.

In sandbox, POST /test/shipments/{id}/advance walks a shipment one step down this ladder at a time so you can exercise every status and every webhook without waiting on a real parcel. POST /test/shipments/{id}/rewind is its inverse — it puts the shipment back to a rung you name so you can run the same steps again, and emits nothing while it does, so what your endpoints see is the ladder replayed rather than a parcel travelling backwards.

Last Mile SolutionsPowered by Ellie
Relay API · Release 2.226.1 · Base path /v2 · © Last Mile Solutions. Confidential & Proprietary — for the intended recipient only; not for redistribution. This reference is self-contained and works offline.