Skip to content
Learn Agentic AI
Learn Agentic AI11 min read4 views

Returns and Refund AI Agent: Automating Complex Support Workflows

Build an AI agent that handles returns and refunds by enforcing company policies, managing approval workflows, processing partial refunds, and handling exceptions with proper escalation for edge cases.

Why Returns Are Harder Than They Look

Returns and refunds seem straightforward until you account for the real-world complexity: time-window policies, restocking fees, partial refunds for bundles, different rules for digital versus physical products, and exceptions for VIP customers or defective items. A returns agent must encode all of this business logic while remaining conversational and helpful.

The key insight is separating policy evaluation from the conversation. The agent talks to the customer; a policy engine determines what is allowed.

Policy Engine

The policy engine evaluates return eligibility based on structured rules. It takes the order details and return reason and produces a clear allow/deny decision with explanations.

flowchart TD
    START["Returns and Refund AI Agent: Automating Complex S…"] --> A
    A["Why Returns Are Harder Than They Look"]
    A --> B
    B["Policy Engine"]
    B --> C
    C["Approval Workflow"]
    C --> D
    D["The Returns Agent"]
    D --> E
    E["FAQ"]
    E --> DONE["Key Takeaways"]
    style START fill:#4f46e5,stroke:#4338ca,color:#fff
    style DONE fill:#059669,stroke:#047857,color:#fff
from dataclasses import dataclass
from datetime import datetime, timedelta
from enum import Enum
from typing import Optional

class ReturnReason(Enum):
    DEFECTIVE = "defective"
    WRONG_ITEM = "wrong_item"
    NOT_AS_DESCRIBED = "not_as_described"
    CHANGED_MIND = "changed_mind"
    DUPLICATE_ORDER = "duplicate_order"

class RefundType(Enum):
    FULL = "full"
    PARTIAL = "partial"
    STORE_CREDIT = "store_credit"
    DENIED = "denied"

@dataclass
class ReturnPolicy:
    return_window_days: int = 30
    restocking_fee_pct: float = 0.15
    free_return_reasons: list = None
    digital_returnable: bool = False

    def __post_init__(self):
        if self.free_return_reasons is None:
            self.free_return_reasons = [
                ReturnReason.DEFECTIVE,
                ReturnReason.WRONG_ITEM,
            ]

@dataclass
class ReturnEvaluation:
    eligible: bool
    refund_type: RefundType
    refund_amount: float
    restocking_fee: float
    reason: str
    requires_approval: bool

class PolicyEngine:
    def __init__(self, policy: ReturnPolicy):
        self.policy = policy

    def evaluate(
        self,
        order_date: datetime,
        order_total: float,
        return_reason: ReturnReason,
        is_digital: bool = False,
        is_vip: bool = False,
    ) -> ReturnEvaluation:
        days_since_order = (datetime.utcnow() - order_date).days

        # Digital products
        if is_digital and not self.policy.digital_returnable:
            if return_reason not in (
                ReturnReason.DEFECTIVE,
                ReturnReason.WRONG_ITEM,
            ):
                return ReturnEvaluation(
                    eligible=False,
                    refund_type=RefundType.DENIED,
                    refund_amount=0,
                    restocking_fee=0,
                    reason="Digital products are non-refundable",
                    requires_approval=False,
                )

        # Return window check
        window = self.policy.return_window_days
        if is_vip:
            window = int(window * 1.5)

        if days_since_order > window:
            if return_reason == ReturnReason.DEFECTIVE:
                return ReturnEvaluation(
                    eligible=True,
                    refund_type=RefundType.FULL,
                    refund_amount=order_total,
                    restocking_fee=0,
                    reason="Defective items eligible outside return window",
                    requires_approval=True,
                )
            return ReturnEvaluation(
                eligible=False,
                refund_type=RefundType.DENIED,
                refund_amount=0,
                restocking_fee=0,
                reason=f"Return window of {window} days has passed",
                requires_approval=False,
            )

        # Calculate refund
        if return_reason in self.policy.free_return_reasons:
            return ReturnEvaluation(
                eligible=True,
                refund_type=RefundType.FULL,
                refund_amount=order_total,
                restocking_fee=0,
                reason="Full refund for defective/wrong item",
                requires_approval=False,
            )

        # Changed mind — restocking fee applies
        fee = order_total * self.policy.restocking_fee_pct
        return ReturnEvaluation(
            eligible=True,
            refund_type=RefundType.PARTIAL,
            refund_amount=order_total - fee,
            restocking_fee=fee,
            reason=f"Refund minus {self.policy.restocking_fee_pct*100:.0f}% restocking fee",
            requires_approval=False,
        )

Approval Workflow

Some returns require manager approval — high-value orders, out-of-window exceptions, or repeated returns from the same customer. The approval workflow queues these cases and tracks their resolution.

See AI Voice Agents Handle Real Calls

Book a free demo or calculate how much you can save with AI voice automation.

from enum import Enum as PyEnum
import uuid

class ApprovalStatus(PyEnum):
    PENDING = "pending"
    APPROVED = "approved"
    DENIED = "denied"

@dataclass
class ApprovalRequest:
    id: str
    order_id: str
    evaluation: ReturnEvaluation
    customer_id: str
    status: ApprovalStatus = ApprovalStatus.PENDING
    reviewer: Optional[str] = None
    notes: str = ""

class ApprovalWorkflow:
    def __init__(self):
        self.pending: dict[str, ApprovalRequest] = {}

    def submit(
        self, order_id: str, customer_id: str, evaluation: ReturnEvaluation
    ) -> ApprovalRequest:
        request = ApprovalRequest(
            id=str(uuid.uuid4()),
            order_id=order_id,
            evaluation=evaluation,
            customer_id=customer_id,
        )
        self.pending[request.id] = request
        return request

    def approve(self, request_id: str, reviewer: str, notes: str = ""):
        req = self.pending.get(request_id)
        if req:
            req.status = ApprovalStatus.APPROVED
            req.reviewer = reviewer
            req.notes = notes

    def deny(self, request_id: str, reviewer: str, notes: str = ""):
        req = self.pending.get(request_id)
        if req:
            req.status = ApprovalStatus.DENIED
            req.reviewer = reviewer
            req.notes = notes

The Returns Agent

The conversational agent collects information from the customer, evaluates the policy, and either processes the return immediately or submits it for approval.

from agents import Agent, function_tool

policy_engine = PolicyEngine(ReturnPolicy())
approval_workflow = ApprovalWorkflow()

@function_tool
def evaluate_return(
    order_id: str,
    order_date: str,
    order_total: float,
    return_reason: str,
    is_digital: bool,
    is_vip: bool,
) -> str:
    """Evaluate whether a return is eligible per company policy."""
    reason_enum = ReturnReason(return_reason)
    dt = datetime.fromisoformat(order_date)

    result = policy_engine.evaluate(
        order_date=dt,
        order_total=order_total,
        return_reason=reason_enum,
        is_digital=is_digital,
        is_vip=is_vip,
    )

    if result.requires_approval:
        req = approval_workflow.submit(order_id, "customer", result)
        return (
            f"Return requires approval. Request ID: {req.id}. "
            f"Reason: {result.reason}. "
            f"Estimated refund: ${result.refund_amount:.2f}"
        )

    if result.eligible:
        return (
            f"Return approved. Refund: ${result.refund_amount:.2f}. "
            f"Restocking fee: ${result.restocking_fee:.2f}. "
            f"Reason: {result.reason}"
        )

    return f"Return denied. Reason: {result.reason}"

returns_agent = Agent(
    name="Returns Agent",
    instructions="""You handle return and refund requests.

Collect these details before evaluating:
1. Order ID
2. Reason for return (defective, wrong_item, not_as_described,
   changed_mind, duplicate_order)
3. Confirm the item and order details

Use evaluate_return to check eligibility. Explain the result
clearly to the customer. If a restocking fee applies, explain
why. If approval is needed, tell the customer their request
has been submitted and they will hear back within 24 hours.

Never override policy decisions. Never promise outcomes you
cannot guarantee.""",
    tools=[evaluate_return],
)

FAQ

How do I handle partial refunds for bundled products?

Break the bundle into individual item prices and apply the return policy to each item separately. If the customer returns only one item from a three-item bundle, refund that item's prorated price minus any bundle discount they received. Store individual item prices at order time to make this calculation possible.

Should the AI agent have the authority to issue refunds directly?

For standard, policy-compliant returns below a dollar threshold (e.g., under $100), yes — the agent should process automatically. For high-value orders, out-of-window exceptions, or repeated returns, require human approval. This balances speed with financial risk management.

How do I prevent return fraud?

Track return frequency per customer and flag accounts that exceed a threshold (e.g., more than three returns in 90 days). The policy engine should consider return history as an input. For flagged accounts, route all returns to human review regardless of the policy evaluation.


#Returns #RefundAutomation #WorkflowAutomation #PolicyEnforcement #AIAgents #AgenticAI #LearnAI #AIEngineering

Share
C

Written by

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.

Related Articles You May Like

Use Cases

Automating Client Document Collection: How AI Agents Chase Missing Tax Documents and Reduce Filing Delays

See how AI agents automate tax document collection — chasing missing W-2s, 1099s, and receipts via calls and texts to eliminate the #1 CPA bottleneck.

Use Cases

Returns and Exchanges Create Avoidable Tickets: Use Chat and Voice Agents to Pre-Handle the Workflow

Many return and exchange contacts should never become full support tickets. Learn how AI chat and voice agents automate policy checks, labels, and next steps.

Learn Agentic AI

API Design for AI Agent Tool Functions: Best Practices and Anti-Patterns

How to design tool functions that LLMs can use effectively with clear naming, enum parameters, structured responses, informative error messages, and documentation.

Learn Agentic AI

Computer Use in GPT-5.4: Building AI Agents That Navigate Desktop Applications

Technical guide to GPT-5.4's computer use capabilities for building AI agents that interact with desktop UIs, browser automation, and real-world application workflows.

Learn Agentic AI

Prompt Engineering for AI Agents: System Prompts, Tool Descriptions, and Few-Shot Patterns

Agent-specific prompt engineering techniques: crafting effective system prompts, writing clear tool descriptions for function calling, and few-shot examples that improve complex task performance.

Learn Agentic AI

AI Agents for IT Helpdesk: L1 Automation, Ticket Routing, and Knowledge Base Integration

Build IT helpdesk AI agents with multi-agent architecture for triage, device, network, and security issues. RAG-powered knowledge base, automated ticket creation, routing, and escalation.