Skip to content
Learn Agentic AI
Learn Agentic AI10 min read10 views

Building an Order Tracking Agent: Real-Time Status Updates via AI

Build an AI agent that looks up order status in real time, formats tracking updates conversationally, sends proactive notifications, and integrates with shipping carrier APIs for end-to-end visibility.

Order Tracking Is the Most Common Support Query

Across e-commerce, "where is my order" accounts for 25-40% of all support contacts. It is also one of the easiest queries to automate — the information exists in structured databases and carrier APIs. An order tracking agent that reliably answers these questions eliminates the single largest category of support volume.

Defining the Order Lookup Tool

The agent needs a tool that queries your order database by order ID or customer email. Using the OpenAI Agents SDK tool pattern, we define this as a function the agent can call.

flowchart TD
    USER(["User message"])
    LLM["LLM call<br/>with tools schema"]
    DECIDE{"Model wants<br/>to call a tool?"}
    EXEC["Execute tool<br/>sandboxed runtime"]
    RESULT["Append tool_result<br/>to messages"]
    GUARD{"Output passes<br/>guardrails?"}
    DONE(["Final reply"])
    BLOCK(["Refuse and log"])
    USER --> LLM --> DECIDE
    DECIDE -->|Yes| EXEC --> RESULT --> LLM
    DECIDE -->|No| GUARD
    GUARD -->|Yes| DONE
    GUARD -->|No| BLOCK
    style LLM fill:#4f46e5,stroke:#4338ca,color:#fff
    style EXEC fill:#ede9fe,stroke:#7c3aed,color:#1e1b4b
    style GUARD fill:#f59e0b,stroke:#d97706,color:#1f2937
    style DONE fill:#059669,stroke:#047857,color:#fff
    style BLOCK fill:#dc2626,stroke:#b91c1c,color:#fff
from dataclasses import dataclass
from datetime import datetime
from typing import Optional
from agents import function_tool

@dataclass
class OrderStatus:
    order_id: str
    status: str
    items: list[dict]
    shipping_carrier: Optional[str]
    tracking_number: Optional[str]
    estimated_delivery: Optional[str]
    last_update: str
    events: list[dict]

# Simulated database lookup
ORDER_DB = {
    "ORD-12345": OrderStatus(
        order_id="ORD-12345",
        status="in_transit",
        items=[
            {"name": "Wireless Headphones", "qty": 1, "price": 79.99}
        ],
        shipping_carrier="UPS",
        tracking_number="1Z999AA10123456784",
        estimated_delivery="2026-03-19",
        last_update="2026-03-16T14:30:00Z",
        events=[
            {"time": "2026-03-14T10:00:00Z", "description": "Order placed"},
            {"time": "2026-03-15T08:00:00Z", "description": "Shipped from warehouse"},
            {"time": "2026-03-16T14:30:00Z", "description": "In transit - Chicago, IL"},
        ],
    ),
}

@function_tool
def lookup_order(order_id: str) -> str:
    """Look up order status by order ID."""
    order = ORDER_DB.get(order_id.upper())
    if not order:
        return f"No order found with ID {order_id}"
    return (
        f"Order {order.order_id}: {order.status}\n"
        f"Items: {', '.join(i['name'] for i in order.items)}\n"
        f"Carrier: {order.shipping_carrier}\n"
        f"Tracking: {order.tracking_number}\n"
        f"Estimated delivery: {order.estimated_delivery}\n"
        f"Last update: {order.events[-1]['description']} "
        f"({order.events[-1]['time']})"
    )

@function_tool
def lookup_orders_by_email(email: str) -> str:
    """Look up all orders for a customer email."""
    # In production, query the database by customer email
    matching = [
        o for o in ORDER_DB.values()
        # Simplified — real implementation queries by email
    ]
    if not matching:
        return f"No orders found for {email}"
    return "\n---\n".join(
        f"{o.order_id}: {o.status} (ETA: {o.estimated_delivery})"
        for o in matching
    )

Carrier API Integration

For real-time tracking beyond what your internal database stores, integrate directly with carrier APIs. This gives the agent up-to-the-minute location data.

Hear it before you finish reading

Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.

Try Live Demo →
import httpx

class CarrierTracker:
    def __init__(self):
        self.carriers = {
            "UPS": "https://api.ups.com/track/v1",
            "FedEx": "https://apis.fedex.com/track/v1",
            "USPS": "https://api.usps.com/tracking/v3",
        }

    async def get_tracking(
        self, carrier: str, tracking_number: str
    ) -> dict:
        base_url = self.carriers.get(carrier)
        if not base_url:
            return {"error": f"Unsupported carrier: {carrier}"}

        async with httpx.AsyncClient() as client:
            response = await client.get(
                f"{base_url}/shipments/{tracking_number}",
                headers={"Authorization": "Bearer <token>"},
                timeout=10.0,
            )
            if response.status_code == 200:
                return response.json()
            return {
                "error": f"Tracking lookup failed: {response.status_code}"
            }

    def format_tracking_response(self, data: dict) -> str:
        if "error" in data:
            return data["error"]
        events = data.get("events", [])
        lines = []
        for event in events[-5:]:
            lines.append(
                f"  {event['timestamp']}: {event['description']} "
                f"- {event.get('location', 'N/A')}"
            )
        return "\n".join(lines)

Building the Agent

The agent combines the lookup tools with instructions that tell it how to handle various order status scenarios.

from agents import Agent, Runner

order_agent = Agent(
    name="Order Tracking Agent",
    instructions="""You are an order tracking specialist. Help customers
check their order status.

When a customer asks about an order:
1. Ask for their order ID if not provided
2. Use lookup_order to get the status
3. Present the information clearly and conversationally
4. If the package is delayed, acknowledge the delay and provide
   the updated estimate
5. If delivered, confirm delivery and ask if they received it

Never reveal internal system details. Format dates in a
human-friendly way (e.g., "Wednesday, March 19th").""",
    tools=[lookup_order, lookup_orders_by_email],
)

async def handle_order_query(user_message: str) -> str:
    result = await Runner.run(order_agent, user_message)
    return result.final_output

Proactive Update Notifications

Beyond reactive lookups, a production order agent sends proactive updates when shipment status changes. This reduces inbound "where is my order" volume significantly.

from datetime import timedelta

class ProactiveNotifier:
    def __init__(self, carrier_tracker: CarrierTracker):
        self.tracker = carrier_tracker
        self.notified_events: set[str] = set()

    async def check_and_notify(self, order: OrderStatus) -> Optional[str]:
        if not order.tracking_number or not order.shipping_carrier:
            return None

        data = await self.tracker.get_tracking(
            order.shipping_carrier, order.tracking_number
        )
        if "error" in data:
            return None

        latest_event = data.get("events", [{}])[-1]
        event_key = (
            f"{order.order_id}:{latest_event.get('id', '')}"
        )

        if event_key in self.notified_events:
            return None

        self.notified_events.add(event_key)
        status = latest_event.get("description", "Updated")
        return (
            f"Update on your order {order.order_id}: {status}. "
            f"Estimated delivery: {order.estimated_delivery}."
        )

FAQ

How do I authenticate the customer before showing order details?

Require the customer to verify their email or phone number associated with the order before returning any details. Never show order information based solely on an order ID, as these can be guessed or shared. Use a one-time code or session-based authentication for chat channels.

Still reading? Stop comparing — try CallSphere live.

CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.

What if the carrier API is down or slow?

Always fall back to your internal database status when the carrier API is unavailable. Set a 5-second timeout on carrier requests and return the last known status with a note that live tracking is temporarily unavailable. Customers prefer a slightly stale answer over no answer.

How should the agent handle lost packages?

If the tracking shows "delivered" but the customer says they did not receive it, the agent should collect details (delivery date, address confirmation) and escalate to a human agent with authority to issue replacements or refunds. Lost package resolution typically requires judgment calls the AI should not make autonomously.


#OrderTracking #CustomerSupport #ToolUse #APIIntegration #AIAgents #AgenticAI #LearnAI #AIEngineering

Share

Try CallSphere AI Voice Agents

See how AI voice agents work for your industry. Live demo available -- no signup required.

Related Articles You May Like