---
title: "Building a Shipping and Tracking Agent: Real-Time Package Status and Delivery Coordination"
description: "Create an AI agent that aggregates tracking data from multiple carriers, predicts ETAs, handles delivery exceptions, and provides real-time package status through a conversational interface."
canonical: https://callsphere.ai/blog/building-shipping-tracking-agent-real-time-package-status-delivery
category: "Learn Agentic AI"
tags: ["Shipping", "Package Tracking", "Logistics AI", "Carrier APIs", "Python"]
author: "CallSphere Team"
published: 2026-03-17T00:00:00.000Z
updated: 2026-05-07T01:30:57.556Z
---

# Building a Shipping and Tracking Agent: Real-Time Package Status and Delivery Coordination

> Create an AI agent that aggregates tracking data from multiple carriers, predicts ETAs, handles delivery exceptions, and provides real-time package status through a conversational interface.

## Why Shipping Needs Intelligent Tracking Agents

E-commerce businesses ship through multiple carriers: FedEx for overnight, UPS for ground, USPS for lightweight parcels, and regional carriers for last-mile. Customers ask "where is my package?" constantly, and support teams spend hours copying tracking numbers between carrier websites.

An AI shipping agent aggregates tracking data from every carrier into a single interface. It normalizes status updates into a consistent format, predicts delivery windows based on historical transit data, and proactively flags exceptions like weather delays or failed delivery attempts before customers even ask.

## Modeling Shipment Data

Define a consistent shipment model that normalizes data across carriers:

```mermaid
flowchart LR
    INPUT(["User intent"])
    PARSE["Parse plus
classify"]
    PLAN["Plan and tool
selection"]
    AGENT["Agent loop
LLM plus tools"]
    GUARD{"Guardrails
and policy"}
    EXEC["Execute and
verify result"]
    OBS[("Trace and metrics")]
    OUT(["Outcome plus
next action"])
    INPUT --> PARSE --> PLAN --> AGENT --> GUARD
    GUARD -->|Pass| EXEC --> OUT
    GUARD -->|Fail| AGENT
    AGENT --> OBS
    style AGENT fill:#4f46e5,stroke:#4338ca,color:#fff
    style GUARD fill:#f59e0b,stroke:#d97706,color:#1f2937
    style OBS fill:#ede9fe,stroke:#7c3aed,color:#1e1b4b
    style OUT fill:#059669,stroke:#047857,color:#fff
```

```python
from dataclasses import dataclass
from datetime import datetime
from enum import Enum
from typing import Optional

class ShipmentStatus(str, Enum):
    LABEL_CREATED = "label_created"
    PICKED_UP = "picked_up"
    IN_TRANSIT = "in_transit"
    OUT_FOR_DELIVERY = "out_for_delivery"
    DELIVERED = "delivered"
    EXCEPTION = "exception"
    RETURNED = "returned"

class Carrier(str, Enum):
    FEDEX = "fedex"
    UPS = "ups"
    USPS = "usps"
    DHL = "dhl"

@dataclass
class TrackingEvent:
    timestamp: datetime
    location: str
    description: str
    status: ShipmentStatus

@dataclass
class Shipment:
    tracking_number: str
    carrier: Carrier
    origin: str
    destination: str
    status: ShipmentStatus
    estimated_delivery: Optional[datetime]
    events: list[TrackingEvent]
    order_id: Optional[str] = None
    weight_lbs: Optional[float] = None
```

## Building the Multi-Carrier Tracking Tool

The tracking tool queries the appropriate carrier API based on the tracking number format:

```python
from agents import function_tool

SHIPMENTS_DB: dict[str, Shipment] = {
    "1Z999AA10123456784": Shipment(
        tracking_number="1Z999AA10123456784",
        carrier=Carrier.UPS,
        origin="Los Angeles, CA",
        destination="New York, NY",
        status=ShipmentStatus.IN_TRANSIT,
        estimated_delivery=datetime(2026, 3, 19, 17, 0),
        events=[
            TrackingEvent(datetime(2026, 3, 15, 8, 30), "Los Angeles, CA",
                          "Picked up", ShipmentStatus.PICKED_UP),
            TrackingEvent(datetime(2026, 3, 16, 14, 0), "Phoenix, AZ",
                          "In transit - departed facility", ShipmentStatus.IN_TRANSIT),
            TrackingEvent(datetime(2026, 3, 17, 6, 15), "Dallas, TX",
                          "In transit - arrived at hub", ShipmentStatus.IN_TRANSIT),
        ],
    ),
    "9400111899223100012": Shipment(
        tracking_number="9400111899223100012",
        carrier=Carrier.USPS,
        origin="Seattle, WA",
        destination="Portland, OR",
        status=ShipmentStatus.EXCEPTION,
        estimated_delivery=None,
        events=[
            TrackingEvent(datetime(2026, 3, 14, 10, 0), "Seattle, WA",
                          "Accepted at USPS origin", ShipmentStatus.PICKED_UP),
            TrackingEvent(datetime(2026, 3, 16, 9, 0), "Portland, OR",
                          "Delivery attempted - no access", ShipmentStatus.EXCEPTION),
        ],
    ),
}

@function_tool
def track_package(tracking_number: str) -> str:
    """Track a package by tracking number across all supported carriers."""
    shipment = SHIPMENTS_DB.get(tracking_number)
    if not shipment:
        return f"No shipment found for tracking number {tracking_number}."

    eta = (shipment.estimated_delivery.strftime("%B %d, %Y %I:%M %p")
           if shipment.estimated_delivery else "Unknown")

    lines = [
        f"Carrier: {shipment.carrier.value.upper()}",
        f"Route: {shipment.origin} -> {shipment.destination}",
        f"Status: {shipment.status.value.replace('_', ' ').title()}",
        f"Estimated Delivery: {eta}",
        "",
        "Tracking History:",
    ]
    for event in reversed(shipment.events):
        lines.append(
            f"  {event.timestamp.strftime('%m/%d %H:%M')} | "
            f"{event.location} | {event.description}"
        )
    return "\n".join(lines)
```

## Order Lookup Tool

Customers often know their order ID but not the tracking number. This tool bridges that gap:

```python
ORDER_TO_TRACKING = {
    "ORD-50001": ["1Z999AA10123456784"],
    "ORD-50002": ["9400111899223100012"],
    "ORD-50003": ["1Z999AA10123456784", "9400111899223100012"],
}

@function_tool
def lookup_order_shipments(order_id: str) -> str:
    """Look up all tracking numbers associated with an order ID."""
    tracking_numbers = ORDER_TO_TRACKING.get(order_id.upper())
    if not tracking_numbers:
        return f"No shipments found for order {order_id}."

    lines = [f"Order {order_id} has {len(tracking_numbers)} shipment(s):"]
    for tn in tracking_numbers:
        shipment = SHIPMENTS_DB.get(tn)
        if shipment:
            lines.append(
                f"  {tn} ({shipment.carrier.value.upper()}) - "
                f"{shipment.status.value.replace('_', ' ').title()}"
            )
    return "\n".join(lines)
```

## Exception Handling Tool

When a shipment hits an exception, the agent needs tools to resolve it:

```python
@function_tool
def report_delivery_exception(
    tracking_number: str,
    resolution: str,
    new_delivery_instructions: Optional[str] = None,
) -> str:
    """Report or resolve a delivery exception for a shipment."""
    shipment = SHIPMENTS_DB.get(tracking_number)
    if not shipment:
        return "Shipment not found."

    if shipment.status != ShipmentStatus.EXCEPTION:
        return f"Shipment is not in exception status. Current: {shipment.status.value}"

    valid_resolutions = ["reattempt", "hold_at_facility", "redirect", "return_to_sender"]
    if resolution not in valid_resolutions:
        return f"Invalid resolution. Choose from: {', '.join(valid_resolutions)}"

    # In production, this calls the carrier API
    return (
        f"Exception resolution submitted for {tracking_number}: {resolution}. "
        f"{'Instructions: ' + new_delivery_instructions if new_delivery_instructions else ''}"
        f"Carrier will process within 2 hours."
    )
```

## Assembling the Shipping Agent

```python
from agents import Agent, Runner

shipping_agent = Agent(
    name="Shipping Tracker",
    instructions="""You are a shipping and delivery assistant. Help customers:
    1. Track packages by tracking number or order ID
    2. Explain delivery status and estimated arrival times
    3. Handle delivery exceptions by offering resolution options
    Always explain carrier-specific terminology in plain language.""",
    tools=[track_package, lookup_order_shipments, report_delivery_exception],
)

result = Runner.run_sync(
    shipping_agent,
    "My order ORD-50002 was supposed to arrive yesterday. What happened?"
)
print(result.final_output)
```

The agent will look up the order, find the USPS tracking number, see the exception status, and proactively offer resolution options.

## FAQ

### How do I integrate with real carrier APIs?

Use carrier-specific SDKs or aggregate APIs like ShipEngine, EasyPost, or Shippo. These services normalize tracking data across carriers into a single API. Authenticate with API keys, poll for updates every 15 minutes, or use webhooks for real-time push notifications when status changes occur.

### How do I predict accurate delivery times beyond the carrier estimate?

Build a simple model that tracks actual transit times for common origin-destination pairs. Store historical delivery data and compute rolling averages. Factor in day-of-week patterns (Monday shipments take longer), weather disruptions, and holiday slowdowns. Even a basic lookup table outperforms carrier estimates for repeat lanes.

### What about international shipments with customs delays?

Add customs status as a tracking event type. International carriers like DHL and FedEx International provide customs clearance milestones through their APIs. The agent should explain common hold reasons (incomplete commercial invoice, restricted items, duties owed) and guide customers on providing required documentation.

---

#Shipping #PackageTracking #LogisticsAI #CarrierAPIs #Python #AgenticAI #LearnAI #AIEngineering

---

Source: https://callsphere.ai/blog/building-shipping-tracking-agent-real-time-package-status-delivery
