Shopify, 3PL & Custom ERP Package Tracking & Webhooks Guide (2026)

Shopify merchants, third-party logistics providers (3PLs), and custom ERP teams typically lose thousands of dollars per year—and hundreds of milliseconds of storefront load time—to legacy post-purchase tracking apps like AfterShip, 17TRACK, and ParcelPanel.

Those apps inject multi-script tracking widgets into your storefront theme, lock REST API and webhook access behind $59–$199/month tiers, and still fail to resolve domestic 92… USPS consolidator gaps (OSM Worldwide, Pitney Bowes) or China cross-border last-mile handoffs (Yanwen, 4PX, Cainiao, YunExpress $\rightarrow$ UniUni, SpeedX, GOFO, OnTrac).

With lastscanned, you can power your Shopify order-status page, 3PL warehouse management system (WMS), Klaviyo/Postscript flows, and Gorgias/Zendesk WISMO tickets using a single synchronous REST API and push Watches & Webhooks across 542 carriers for $4.99/month (1,000 unique tracking numbers included, $0.015 per additional number, unlimited re-polls and scheduled watch checks).


Architecture Comparison: Storefront Tracking Apps vs. lastscanned

DimensionLegacy Storefront Apps (AfterShip / 17TRACK / ParcelPanel)lastscanned REST API + Watches + <lastscanned-widget>
Storefront JavaScript impactHeavy third-party scripts, iframes, and promotional upsell bannersZero third-party trackers; optional <lastscanned-widget> is a single lightweight Web Component (/embed.js)
Order fulfillment triggerRequires async tracker registration; initial API response has events: []POST /v1/watches returns the live normalized TrackingResult immediately and schedules automatic webhook updates
Webhook payload formatProprietary per vendorNative lastscanned JSON (tracking.updated, watch.ended) or drop-in 17TRACK v2 format ("format": "17track")
92… "USPS Currently Awaiting Package"Stuck in pre-shipment for 3–7 days until USPS local scanPass ?carrier=osm or ?carrier=pitneybowes on 92… numbers to surface live middle-mile sort scans
Cross-border last-mile barcode swapCustomer sees stagnant origin waybill after customs clearanceAutomatically extracts destination carrier & local barcode into message (e.g., "Handed to UniUni as UUS5BM1269395824723")
Monthly cost at 1,000 orders/mo$59 – $119/month$4.99/month (Pro) or $0.02/lookup via keyless HTTP 402 Stripe MPP

1. Managing Watches & Webhooks in the Web UI Dashboard (No Code Required)

Before writing any code—or for operations teams managing exception shipments by hand—you can run your entire watch and webhook workflow directly in the Web UI Dashboard:

  • Dashboard Watches (app.lastscanned.com/watches): Paste up to 100 tracking numbers at once (with an optional carrier override and custom label stored in metadata.label). Each number is looked up immediately, metered under your signed-in account, and displayed in a filterable list (All, Active, Ended) showing its current status, latest scan, and next scheduled check time. Click any watch to inspect its full scan timeline or stop watching with one click.
  • Dashboard Webhooks (app.lastscanned.com/webhooks): Set or rotate your webhook endpoint URL and signing secret (whsec_...), switch between native lastscanned JSON and drop-in 17TRACK v2 format ("format": "17track"), send a live webhook.test ping, and inspect or retry any webhook delivery from the past 30 days.

2. Triggering a Watch via API When Shopify Creates a Fulfillment (orders/fulfilled)

When your Shopify store or 3PL WMS fulfills an order (orders/fulfilled or fulfillments/create webhook), register the tracking number with POST https://api.lastscanned.com/v1/watches and attach the Shopify order ID and customer email in metadata:

// Handler for Shopify's `fulfillments/create` webhook
export async function onShopifyFulfillmentCreate(shopifyFulfillment: {
  order_id: number;
  tracking_number: string;
  tracking_company?: string;
   email?: string;
}) {
  const res = await fetch("https://api.lastscanned.com/v1/watches", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.LASTSCANNED_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      trackingNumber: shopifyFulfillment.tracking_number,
      metadata: {
        shopifyOrderId: String(shopifyFulfillment.order_id),
        customerEmail: shopifyFulfillment.email ?? "",
      },
    }),
  });

  const watch = await res.json();
  // watch.id     -> "wat_109060ade9f9a48732117f7a"
  // watch.result -> immediate live TrackingResult (carrier, status, estimatedDelivery, events)
  return watch;
}

Because POST /v1/watches performs an immediate live lookup and returns watch.result in the HTTP response, your database has the normalized carrier slug (ups, usps, fedex, uniuni, speedx, veho, gofo, ontrac, yanwen, 4px, cainiao), status, and estimatedDelivery on the very first call.


3. Receiving Signed Status Webhooks (tracking.updated & watch.ended)

Configure your webhook receiver once via PUT /v1/webhook (or on the Webhooks dashboard):

curl -X PUT https://api.lastscanned.com/v1/webhook \
  -H "Authorization: Bearer $LASTSCANNED_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://api.yourbrand.com/webhooks/lastscanned"}'

lastscanned automatically re-checks active watches on an adaptive schedule—every 15 minutes when out_for_delivery, every 30 minutes on exception, every 1 hour when in_transit, and every 2 hours on label_created—at zero additional cost. Whenever the status, newest scan, or estimatedDelivery changes, your endpoint receives a signed POST:

import crypto from "node:crypto";

export async function handleLastscannedWebhook(rawBody: string, signatureHeader: string) {
  const { t, v1 } = Object.fromEntries(signatureHeader.split(",").map((p) => p.split("=")));
  const expected = crypto
    .createHmac("sha256", process.env.LASTSCANNED_WEBHOOK_SECRET!)
    .update(`${t}.${rawBody}`)
    .digest("hex");

  if (!crypto.timingSafeEqual(Buffer.from(v1, "hex"), Buffer.from(expected, "hex"))) {
    throw new Error("Invalid Lastscanned-Signature");
  }

  const event = JSON.parse(rawBody);
  if (event.type === "tracking.updated") {
    const { watch, previousStatus, result } = event.data;
    // Example: trigger Klaviyo / Postscript SMS when status transitions to `out_for_delivery` or `exception`
    if (previousStatus !== result.status && result.status === "out_for_delivery") {
      await notifyCustomerOutForDelivery(watch.metadata.shopifyOrderId, result);
    }
  }
}

Migrating from an existing 17TRACK integration? Pass "format": "17track" to PUT /v1/webhook and lastscanned will send TRACKING_UPDATED / TRACKING_STOPPED payloads with 17TRACK's sign header so your existing Shopify/ERP webhook handler works with zero code changes. See Migrating from 17TRACK.


4. Resolving Cross-Border Handoffs & 92… USPS Consolidator Parcels

For brands shipping from overseas factories (Yanwen, 4PX, Cainiao, YunExpress) or using domestic consolidators (OSM Worldwide, Pitney Bowes):

  1. Automatic Last-Mile Handoff Extraction: When Yanwen (UL494089685YP), 4PX (4PX3002639757438CN), Cainiao (LP00836938654367), or YunExpress (YT2621200705470145) hands a parcel to UniUni (UUS5BM1269395824723), SpeedX (SPXCLT138701431240), GOFO (GFUS01074503026309), or USPS, result.message surfaces "Handed to UniUni as UUS5BM1269395824723". You can immediately create a second watch on the local courier barcode so the buyer sees real-time out-for-delivery driver scans.
  2. Eliminating "USPS Currently Awaiting Package" Tickets: When a 926129… or 927489… barcode returns "Pre-Shipment: Picked Up by Shipping Partner, USPS Awaiting Item" on USPS, query GET /v1/track/9274899998486353272191?carrier=osm or GET /v1/track/9261290980949654107812?carrier=pitneybowes to show the buyer the exact middle-mile sort facility and departure timestamp.

5. Embedding a Clean, Zero-Bloat Order Lookup Widget

If you want a self-serve tracking lookup page on your Shopify storefront or help center without loading third-party ad scripts, use the Tracking Widget Generator to drop <lastscanned-widget> onto any page:

<script src="https://lastscanned.com/embed.js" defer></script>
<lastscanned-widget
  theme="light"
  accent="#10b981"
  placeholder="Enter your tracking number (e.g. 1Z54V8630394427740)"
></lastscanned-widget>

On this page