# WISMO AI agents & synchronous tracking

URL: https://docs.lastscanned.com/wismo

Customer support workflows—especially **"Where Is My Order?" (WISMO)** AI agents, helpdesk bots, and order-status pages—are triggered when a shopper asks about a package right now.

`lastscanned` is designed for **synchronous, single-request tracking**: pass a tracking number to `GET /v1/track/{trackingNumber}` (or call the `track_package` MCP tool) and get the normalized status, estimated delivery, cross-border handoff number, and full scan history in one response across all [31 supported carriers](/carriers).

## 1. Synchronous `GET` vs. webhook pre-registration

Most legacy tracking aggregators are built around asynchronous webhook pipelines:

1. You `POST` a tracking number to register a tracker on their platform.
2. Their queue schedules an asynchronous background job.
3. You wait seconds or minutes for an inbound webhook before your app has any scan events to show the customer.

That model breaks down in interactive AI agents, customer support chat, and on-demand order lookup routes where a customer pastes a tracking number or asks *"Where is my order #10492?"* and expects an immediate answer.

| Requirement | Legacy webhook trackers (`POST` + webhook) | `lastscanned` (`GET /v1/track/{number}`) |
|---|---|---|
| First-time lookup latency | Queued (`PENDING` until async webhook fires) | Immediate live response in the same HTTP call |
| State required in your database | Must store tracker IDs, webhook secrets & state | Stateless—pass the raw tracking number on demand |
| Carrier credential setup | Often requires your own shipper OAuth keys | Zero carrier credentials required across 31 carriers |
| Billing on repeated status checks | Per-shipment registration or per-call fees | **Pro ($4.99/mo)**: 1,000 unique numbers included; unlimited re-polls all month |
| AI agent protocol support | Custom wrapper required | Built-in remote MCP server (`https://mcp.lastscanned.com`) |

## 2. Normalized status lifecycle (`status` enum)

Every carrier uses different internal event codes and phrasing. `lastscanned` normalizes both the top-level parcel state (`status`) and each individual scan event (`events[].status`) into a deterministic 7-value enum so your WISMO agent or support router can branch cleanly:

| `status` value | Lifecycle stage | Typical customer meaning | Recommended WISMO agent action |
|---|---|---|---|
| `label_created` | Pre-transit (`pre_transit`) | The shipper generated a label, but the carrier has not scanned the physical parcel yet. | Reassure the customer that the order is packed and awaiting carrier pickup; if it's a `92…` USPS consolidator label, check `?carrier=osm` or `?carrier=pitneybowes` for middle-mile sort scans. |
| `in_transit` | In transit | The parcel is moving through origin, linehaul, customs, or regional sort facilities. | Share the latest scan location (`events[0].location`), timestamp (`events[0].localTime`), and `estimatedDelivery` date. |
| `out_for_delivery` | Out for delivery | The parcel is on the local delivery vehicle for final-mile drop-off today. | Let the customer know delivery is expected today and highlight any delivery window in `estimatedDelivery` / `estimatedDeliveryAt`. |
| `delivered` | Delivered | The parcel was delivered (`deliveredAt`, `deliveredAtLocal`, and `signedBy` when available). | Confirm delivery time and city/state (`destination`). If the customer says it is missing, check cross-border handoffs (`message`) or wait 24 hours before opening a claim. |
| `exception` | Exception / delay | Address issue, customs hold, weather delay, business closed, or failed delivery attempt. | Surface `statusDescription` and `events[0].description` immediately; escalate to human support or prompt the customer to confirm their delivery address. |
| `returned` | Returned to sender | The parcel is being routed back or has been returned to the sender. | Trigger a reshipment or refund workflow and notify support with the return scan timestamp. |
| `unknown` | Unknown / expired | The carrier returned a response whose status text did not map to a standard stage, or the tracking number has expired from active status. | Fall back to displaying `statusDescription` and the latest entry in `events` verbatim. |

> **What if a brand-new label isn't in the carrier's system yet?** When a merchant prints a label that the carrier has not indexed yet, the API returns HTTP `404` with `error.code: "not_found"`. In a WISMO bot, treat `not_found` on orders created within the last 24–48 hours as pre-transit ("Your shipping label was just created and is waiting for the carrier's first scan").

## 3. Resolving cross-border handoffs & the `"USPS Currently Awaiting Package"` consolidator black hole

### A. Cross-border last-mile handoffs (`message`)

One of the most common WISMO failure modes happens on cross-border e-commerce shipments ([`yanwen`](/carriers/yanwen), [`cainiao`](/carriers/cainiao), [`yunexpress`](/carriers/yunexpress), [`4px`](/carriers/4px), [`cne`](/carriers/cne), [`sunyou`](/carriers/sunyou), [`ubi`](/carriers/ubi), [`asendia`](/carriers/asendia)). A shopper receives a tracking number like `YT2412345678901234`, `3A5V700123456`, or `AHOY7K2M9Q4T`, sees "Arrived at destination airport" or "Handed over to local courier," and asks support which company is actually delivering to their door.

When a cross-border carrier hands a parcel to a domestic last-mile carrier (such as **[USPS](/carriers/usps)**, **[UniUni](/carriers/uniuni)**, **[GOFO](/carriers/gofo)**, **[SpeedX](/carriers/speedx)**, **[OnTrac](/carriers/ontrac)**, **[Cirro](/carriers/cirro)**, **[Canada Post](/carriers/canadapost)**, **[Intelcom / Dragonfly](/carriers/intelcom)**, **Australia Post**, or **Royal Mail**), `lastscanned` automatically extracts the handoff information and populates the top-level `message` field:

```json
{
  "carrier": "yunexpress",
  "trackingNumber": "YT2412345678901234",
  "status": "in_transit",
  "statusDescription": "In transit",
  "service": "YunExpress Standard",
  "deliveredAt": null,
  "deliveredAtLocal": null,
  "signedBy": null,
  "destination": { "city": null, "state": null, "country": "US" },
  "estimatedDelivery": null,
  "estimatedDeliveryAt": null,
  "message": "Handed to UniUni as UUSC000019638703",
  "events": [
    {
      "timestamp": "2026-10-25T18:40:00Z",
      "localTime": "2026-10-25T14:40:00",
      "status": "in_transit",
      "description": "Arrived at local facility",
      "location": "LOS ANGELES, CA, US",
      "code": null
    }
  ]
}
```

1. **Always inspect `message`** on `yanwen`, `4px`, `cainiao`, `yunexpress`, `cne`, `sunyou`, `ubi`, and `asendia` responses.
2. **Surface the doorstep carrier and secondary tracking number** directly to the customer (for example, *"Your package cleared customs via YunExpress and was handed to UniUni for local delivery under tracking number `UUSC000019638703`"*).
3. **Optionally chain a second lookup**: if the customer wants the granular local doorstep scans, call `GET /v1/track/UUSC000019638703` (or `track_package`) with the secondary tracking number extracted from `message`.

### B. Solving `"USPS Currently Awaiting Package"` on `92…` consolidator labels (`?carrier=osm` / `?carrier=pitneybowes`)

The second major WISMO spike comes from domestic parcels shipped through postal consolidators like **[OSM Worldwide](/carriers/osm)** and **[Pitney Bowes](/carriers/pitneybowes)**. These parcels carry a standard 22-digit `92…` USPS IMpb barcode (or 30-digit `420` + ZIP + `92…`), so querying USPS directly returns `status: "label_created"` with *"Picked Up by Shipping Partner, USPS Awaiting Item"* (`"USPS Currently Awaiting Package"`) for 3–7 days while the consolidator trucks the parcel across the country.

In your WISMO agent:
- If a `92…` [USPS](/carriers/usps) lookup returns `status: "label_created"` and the scan text mentions `"OSM WORLDWIDE"`, `"PITNEY BOWES"`, or `"Shipping Partner"`, immediately query the exact same `92…` number with **`?carrier=osm`** or **`?carrier=pitneybowes`** (or pass `carrier: "osm"` / `"pitneybowes"` in MCP).
- You will get back the live middle-mile sort center arrivals/departures (e.g. `Glendale Heights, IL`, `Atlanta, GA`, `Monroe Twp, NJ`) and the consolidator's `estimatedDelivery` date so your AI agent can give the shopper a real transit update instead of repeating *"USPS is still waiting for your package."*

## 4. Copy-paste integration patterns

### Option A: Remote MCP server (zero code)

If your AI agent or support platform supports the Model Context Protocol (Streamable HTTP), point it directly at `https://mcp.lastscanned.com`. Your agent immediately gains `track_package`, `track_packages` (up to 100 numbers per call), and `list_carriers`.

```json tab="Standard MCP (Bearer Header)"
{
  "mcpServers": {
    "lastscanned": {
      "url": "https://mcp.lastscanned.com",
      "headers": {
        "Authorization": "Bearer lsk_live_YOUR_API_KEY"
      }
    }
  }
}
```

```text tab="URL-Path Auth (ChatGPT / Custom Connectors)"
https://mcp.lastscanned.com/lsk_live_YOUR_API_KEY
```

```bash tab="Claude Code CLI"
claude mcp add --transport http lastscanned https://mcp.lastscanned.com \
  --header "Authorization: Bearer lsk_live_YOUR_API_KEY"
```

### Option B: TypeScript / Next.js Route Handler & AI SDK tool

For custom support bots, Shopify/order portals, or serverless API routes, call `GET https://api.lastscanned.com/v1/track/{trackingNumber}` directly from your server:

```ts
export interface TrackingEvent {
  timestamp: string | null;
  localTime: string | null;
  status:
    | "label_created"
    | "in_transit"
    | "out_for_delivery"
    | "delivered"
    | "exception"
    | "returned"
    | "unknown";
  description: string;
  location: string | null;
  code: string | null;
}

export interface TrackingResponse {
  carrier: string;
  trackingNumber: string;
  status: TrackingEvent["status"];
  statusDescription: string;
  service: string | null;
  deliveredAt: string | null;
  deliveredAtLocal: string | null;
  signedBy: string | null;
  destination: {
    city: string | null;
    state: string | null;
    country: string | null;
  } | null;
  estimatedDelivery: string | null;
  estimatedDeliveryAt: string | null;
  message?: string;
  events: TrackingEvent[];
}

export async function getOrderTracking(
  trackingNumber: string,
  carrier?: string,
): Promise<TrackingResponse> {
  const url = new URL(
    `https://api.lastscanned.com/v1/track/${encodeURIComponent(trackingNumber.trim())}`,
  );
  if (carrier) url.searchParams.set("carrier", carrier);

  const res = await fetch(url, {
    headers: {
      Authorization: `Bearer ${process.env.LASTSCANNED_API_KEY}`,
    },
    // Fresh status on customer inquiry
    cache: "no-store",
  });

  if (!res.ok) {
    const err = await res.json().catch(() => null);
    throw new Error(err?.error?.message ?? `Tracking lookup failed (${res.status})`);
  }

  return res.json() as Promise<TrackingResponse>;
}
```

Because the **Pro plan ($4.99/month)** bills per **unique tracking number per billing period** (with 1,000 unique numbers included and $0.015 per extra unique number), your WISMO bot can call `getOrderTracking()` every time a customer refreshes their order page or asks for an update without paying per poll.
