Relay API
Authentication
← API Reference
๐Ÿ”’ Confidential & Proprietary. Prepared for the authorized integration team. Contains confidential technical information of Last Mile Solutions โ€” do not redistribute.

Authenticating to Relay

Every Relay request carries a per-partner API key, and every request except public tracking is signed with an HMAC-SHA256 signature over a timestamped canonical string. Signing proves the request body was not altered in transit and binds it to a moment in time, defeating tampering and replay. This page is the practical guide; the wire contract lives in the API Reference.

Header: X-Api-Key Signature: HMAC-SHA256 Skew window: 300s Every POST: Idempotency-Key

Base URLs & environments

Keys are environment-scoped: a sandbox key authenticates only against the sandbox host, a production key only against production. Keys and signing secrets never cross environments, and sandbox never touches live carriers.

Sandbox
https://sandbox.relay.lastmile.us/v2
Production
https://api.relay.lastmile.us/v2

1Per-partner API keys

Each partner is issued an API key presented on every request in the X-Api-Key header. The key identifies the partner and selects that partner's rate limits, scopes, and IP allowlist.

2HMAC request signing

Beyond the API key, every request (except public tracking, ยง6) is signed. Three headers participate:

HeaderValue
X-Api-KeyThe partner API key.
X-Relay-TimestampCurrent time in unix seconds.
X-Relay-SignatureHMAC-SHA256(signing_secret, timestamp + "." + raw_body), hex-encoded.
Canonical signing string: the timestamp, a literal ., then the exact raw request body as sent on the wire. For bodyless requests the body is the empty string, so the signed string is timestamp + ".". The server recomputes the HMAC over the raw received bytes and compares in constant time.
Signing a request โ€” Python
import time, hmac, hashlib, json, requests

API_KEY        = "pk_sandbox_..."          # X-Api-Key
SIGNING_SECRET = b"whsec_..."              # per-partner signing secret (bytes)
BASE           = "https://sandbox.relay.lastmile.us/v2"

# Serialize the body ONCE and send the exact bytes you signed.
body = json.dumps({"direction": "return", "fulfillment": "print_label",
                   "order_id": "A-10042"}, separators=(",", ":")).encode()

ts  = str(int(time.time()))
msg = ts.encode() + b"." + body                     # timestamp + "." + raw_body
sig = hmac.new(SIGNING_SECRET, msg, hashlib.sha256).hexdigest()

resp = requests.post(BASE + "/shipments", data=body, headers={
    "X-Api-Key":         API_KEY,
    "X-Relay-Timestamp": ts,
    "X-Relay-Signature": sig,
    "Idempotency-Key":   "11111111-2222-3333-4444-555555555555",
    "Content-Type":      "application/json",
})
Sign and send the same bytes. If you re-serialize the JSON for the request (different key order or spacing), the server's HMAC over the received bytes won't match and you'll get invalid_signature.

3Timestamp skew & replay protection

Relay rejects any request whose X-Relay-Timestamp differs from server time by more than 300 seconds (invalid_signature, details.reason = timestamp_skew). Because the timestamp is inside the signed string, a captured request cannot be replayed after the window, and a fresh timestamp cannot be forged without the signing secret.

Signature / key failure envelope
{ "error": { "code": "invalid_signature",
            "message": "signature mismatch",
            "details": { "reason": "timestamp_skew" } } }

Signing-secret handling

4Idempotency

Every POST accepts an Idempotency-Key. Relay fingerprints the request and its response, keyed per partner and per endpoint:

Idempotency makes network retries safe without opening a replay hole, complementing the timestamp-bound signature.

5Verifying webhooks

Outbound webhook bodies are signed with the per-webhook secret returned when the endpoint is registered, using the same scheme:

X-Relay-Signature = HMAC-SHA256(webhook_secret, timestamp + "." + raw_webhook_body)

On every delivery you must:

Each webhook has its own secret, so rotating or deleting one endpoint's secret never affects another.

6Public tracking (the one exception)

GET /v2/tracking/{tracking} is intentionally lighter so it can back customer-facing tracking pages: it requires the API key only and no signature. It exposes only shipment status and scan history for that tracking number โ€” never full addresses, pricing, partner metadata, or other shipments. Tracking numbers are high-entropy and unguessable.

7Rate limiting & IP allowlisting

Each partner key is limited (default 100 requests/minute per environment). Exceeding it returns 429 with rate_limited and a Retry-After header โ€” a clear, machine-readable backoff signal. Limits can be raised per partner by arrangement.

A partner may also provide a list of source IPs/CIDRs. When set, Relay rejects that key's requests from any other address before doing any work. Allowlisting is recommended for production keys.

Integration checklist