AI Agent for Order Support: Tracking, Returns, Exchanges, and Modifications
Build an AI agent that handles the complete order support lifecycle — from tracking shipments and processing returns to managing exchanges and order modifications — reducing support ticket volume significantly.
The Order Support Challenge
Order-related inquiries account for 40 to 60 percent of all e-commerce customer support tickets. "Where is my order?", "I want to return this", and "Can I change my shipping address?" are repetitive, high-volume questions that follow predictable patterns. An AI agent can handle most of these autonomously while escalating edge cases to human agents.
Designing the Order Lookup System
The foundation of an order support agent is reliable order retrieval. The agent needs to look up orders by order number, email address, or phone number and present the current status clearly.
from agents import Agent, Runner, function_tool
from datetime import datetime, timedelta
from enum import Enum
class OrderStatus(str, Enum):
PROCESSING = "processing"
SHIPPED = "shipped"
DELIVERED = "delivered"
RETURN_REQUESTED = "return_requested"
RETURN_COMPLETED = "return_completed"
CANCELLED = "cancelled"
# Simulated order database
ORDERS_DB = {
"ORD-10042": {
"customer_email": "alex@example.com",
"items": [
{"sku": "SKU-001", "name": "Merino Wool Jacket", "qty": 1,
"price": 189.99, "returnable": True}
],
"status": OrderStatus.SHIPPED,
"tracking": "1Z999AA10123456784",
"carrier": "UPS",
"ordered_at": "2026-03-10",
"shipped_at": "2026-03-12",
"estimated_delivery": "2026-03-18",
"shipping_address": "123 Main St, Portland, OR 97201",
},
}
@function_tool
def lookup_order(order_id: str) -> str:
"""Look up an order by its order ID."""
order = ORDERS_DB.get(order_id.upper())
if not order:
return f"No order found with ID {order_id}. Please verify the order number."
return (
f"Order {order_id}: Status={order['status'].value}, "
f"Items={[i['name'] for i in order['items']]}, "
f"Carrier={order['carrier']}, Tracking={order['tracking']}, "
f"Est. Delivery={order['estimated_delivery']}"
)
@function_tool
def get_tracking_details(tracking_number: str) -> str:
"""Get real-time tracking details for a shipment."""
# In production, call carrier API (UPS, FedEx, USPS)
return (
f"Tracking {tracking_number}: "
f"Mar 12 - Picked up, Portland OR | "
f"Mar 14 - In transit, Sacramento CA | "
f"Mar 16 - Out for delivery, San Francisco CA"
)
Building the Return and Exchange Logic
Returns require careful validation: Is the item within the return window? Is it in a returnable category? Has the customer already initiated a return for this item?
RETURN_WINDOW_DAYS = 30
NON_RETURNABLE = ["underwear", "swimwear", "customized"]
@function_tool
def initiate_return(order_id: str, item_sku: str, reason: str) -> str:
"""Initiate a return for a specific item in an order."""
order = ORDERS_DB.get(order_id.upper())
if not order:
return "Order not found."
if order["status"] not in (OrderStatus.DELIVERED, OrderStatus.SHIPPED):
return "Returns can only be initiated for shipped or delivered orders."
# Check return window
order_date = datetime.strptime(order["ordered_at"], "%Y-%m-%d")
if (datetime.now() - order_date).days > RETURN_WINDOW_DAYS:
return f"Return window of {RETURN_WINDOW_DAYS} days has expired."
item = next((i for i in order["items"] if i["sku"] == item_sku), None)
if not item:
return f"Item {item_sku} not found in order {order_id}."
if not item.get("returnable", True):
return f"{item['name']} is not eligible for return."
return_id = f"RET-{order_id}-{item_sku}"
return (
f"Return {return_id} initiated for {item['name']}. "
f"Reason: {reason}. A prepaid return label has been emailed. "
f"Refund of ${item['price']:.2f} will be processed within "
f"5-7 business days after we receive the item."
)
@function_tool
def initiate_exchange(order_id: str, item_sku: str,
new_sku: str, reason: str) -> str:
"""Exchange an item for a different variant."""
order = ORDERS_DB.get(order_id.upper())
if not order:
return "Order not found."
item = next((i for i in order["items"] if i["sku"] == item_sku), None)
if not item:
return f"Item {item_sku} not found in this order."
exchange_id = f"EXC-{order_id}-{item_sku}"
return (
f"Exchange {exchange_id} created. Returning {item['name']} "
f"for {new_sku}. Ship the original item back using the prepaid "
f"label sent to your email. The replacement ships once we "
f"receive your return."
)
Order Modification Tool
Customers frequently want to change shipping addresses or cancel orders before shipment. The agent should check whether modifications are still possible.
See AI Voice Agents Handle Real Calls
Book a free demo or calculate how much you can save with AI voice automation.
@function_tool
def modify_order(order_id: str, modification_type: str,
new_value: str) -> str:
"""Modify an order (address change, cancellation) if still possible."""
order = ORDERS_DB.get(order_id.upper())
if not order:
return "Order not found."
if order["status"] in (OrderStatus.SHIPPED, OrderStatus.DELIVERED):
return (
"This order has already shipped. Address changes are no "
"longer possible. You may initiate a return after delivery."
)
if modification_type == "cancel":
return f"Order {order_id} has been cancelled. Refund processing in 3-5 days."
elif modification_type == "address":
return f"Shipping address updated to: {new_value}"
else:
return f"Modification type '{modification_type}' is not supported."
Assembling the Order Support Agent
order_agent = Agent(
name="Order Support Agent",
instructions="""You are a customer service agent for an online retailer.
Help customers with order tracking, returns, exchanges, and modifications.
Rules:
- Always verify the order exists before taking any action
- Explain return policies clearly before processing returns
- Confirm the customer's intent before making changes
- If an order cannot be modified, explain why and offer alternatives
- Provide tracking links when available
- Escalate to a human agent if the customer is upset or the issue
is outside your capabilities""",
tools=[lookup_order, get_tracking_details, initiate_return,
initiate_exchange, modify_order],
)
result = Runner.run_sync(order_agent, "Where is my order ORD-10042?")
print(result.final_output)
FAQ
How do I connect the agent to real carrier tracking APIs?
Most carriers provide REST APIs. UPS offers the Tracking API, FedEx has Track API v1, and USPS provides the Web Tools API. Wrap each carrier's API in a unified tracking tool that accepts a tracking number and carrier name, normalizes the response into a common format (timestamp, location, status), and returns it. Cache responses for 15 minutes to reduce API calls.
What happens when a customer wants to return an item bought with a promotion?
Build promo-aware return logic that calculates the actual paid amount after discounts. If the returned item triggers a threshold change (for example, "buy 2 get 10% off" and the customer returns one), recalculate the order total and issue a partial refund reflecting the adjusted discount. Document this policy clearly in the agent's instructions.
How should the agent handle abusive or frustrated customers?
Include a sentiment detection step in the agent loop. If the customer uses aggressive language or repeats the same complaint more than twice, the agent should acknowledge their frustration, apologize, and offer to transfer the conversation to a human supervisor. Never argue or become defensive in automated responses.
#OrderManagement #CustomerSupportAI #ReturnsProcessing #ECommerce #RetailAI #AgenticAI #LearnAI #AIEngineering
CallSphere Team
Expert insights on AI voice agents and customer communication automation.
Try CallSphere AI Voice Agents
See how AI voice agents work for your industry. Live demo available -- no signup required.