Generated from: “Show a real run of the agent handling "refund my order", turn by turn.”
View the Markdown
```meta
title: Refund run — "refund my order"
subtitle: One real episode of the support agent, turn by turn, from the first message to the refund receipt.
tag: TRACE
```
The support agent takes a customer message, calls tools, and either fixes the
problem or hands it to a human. This document records one episode as it ran on
2026-09-11. Customer Priya Nair wrote "refund my order" and nothing more. The
agent had to find the order, confirm policy, and pay the money back.
Order states are defined in `order-lifecycle#order-states`; this run ends in
`REFUNDED`.
```callout
tone: note
title: Assumptions
body: "The transcript is reconstructed from the agent log for session ses_7f2c91. Customer name, order ids, and amounts are realistic placeholders, not production data. Refund policy: full refund within 30 days of delivery for a defective item. No return shipment is required when the item price is under 100 USD. The payment gateway settles refunds in 5 to 10 business days; the agent reports the gateway timeline, never a guess."
```
## The loop this run executes
Every turn below is one pass through the same loop. The agent stops when the
customer has a refund id in hand or a ticket number for a human. It never
issues a refund without a matched order and a policy check that returns
`eligible`.
```agentloop
id: refund-loop
agent:
name: Support agent
model: claude-sonnet-4-6
note: "Resolves order problems inside policy; escalates everything else."
env: Customer chat
tools:
- { name: get_customer, desc: "Look up the customer by chat session" }
- { name: list_orders, desc: "Recent orders for a customer id" }
- { name: get_order, desc: "One order with items, status, and delivery date" }
- { name: check_refund_policy, desc: "Eligibility for an order and a reason code" }
- { name: issue_refund, desc: "Refund through the payment gateway" }
- { name: set_order_status, desc: "Move an order to a new status" }
- { name: create_ticket, desc: "Escalate to the human support queue" }
memory:
- conversation history
- customer profile
- refund policy version 2026-08
stop: refund id sent to the customer, or ticket created
```
## The run, turn by turn
The message "refund my order" names no order. Priya has two orders in the last
30 days, so the first assistant turn asks rather than guesses. The second
question, the reason, is the one input the policy check cannot infer. Nine
turns and five tool calls later the customer holds refund id `rf_2Kd9Qm`.
```trace
id: refund-run
title: Session ses_7f2c91 — 2026-09-11 14:02 UTC
turns:
- role: system
text: "Support agent v14. Policy version 2026-08. Customer is authenticated via app session."
- role: user
text: refund my order
- role: assistant
thinking: "No order id in the message. Identify the customer first, then see how many orders could be meant before asking anything."
tool: get_customer
args: '{ "session_id": "ses_7f2c91" }'
- role: tool
tool: get_customer
result: '{ "customer_id": "cus_81A4", "name": "Priya Nair", "email": "priya.nair@example.com" }'
- role: tool
tool: list_orders
args: '{ "customer_id": "cus_81A4", "days": 30 }'
result: '[ { "order_id": "ord_48213", "total": "86.40 USD", "status": "DELIVERED", "delivered_at": "2026-09-08", "summary": "Wireless earbuds" }, { "order_id": "ord_48377", "total": "23.00 USD", "status": "SHIPPED", "summary": "USB-C cable, 2 m" } ]'
- role: assistant
thinking: "Two candidates. The delivered one is the likely target, but a refund is irreversible — confirm instead of assuming."
text: "I can help with that. I see two recent orders: the wireless earbuds (ord_48213, delivered 8 Sep) and a USB-C cable (ord_48377, still in transit). Which one do you want refunded?"
- role: user
text: the earbuds. the left one is dead
- role: assistant
thinking: "Order matched and a reason given. Map 'dead' to reason code DEFECTIVE and let the policy tool decide; do not compute eligibility myself."
tool: get_order
args: '{ "order_id": "ord_48213" }'
- role: tool
tool: get_order
result: '{ "order_id": "ord_48213", "status": "DELIVERED", "delivered_at": "2026-09-08", "items": [ { "sku": "EB-220", "name": "Wireless earbuds", "qty": 1, "price": "79.00 USD" } ], "shipping": "7.40 USD", "total": "86.40 USD", "payment_id": "pay_9X3bT" }'
- role: tool
tool: check_refund_policy
args: '{ "order_id": "ord_48213", "reason": "DEFECTIVE" }'
result: '{ "eligible": true, "amount": "86.40 USD", "return_required": false, "rule": "defective-under-100-no-return", "days_since_delivery": 3 }'
- role: assistant
text: "Sorry about the earbuds. You are eligible for a full refund of 86.40 USD, and you do not need to send them back. Shall I issue it to the original card now?"
- role: user
text: yes please
- role: assistant
thinking: "Explicit consent received. Issue the refund first; only after the gateway accepts it move the order status, so a gateway failure leaves the order untouched."
tool: issue_refund
args: '{ "payment_id": "pay_9X3bT", "amount": "86.40 USD", "reason": "DEFECTIVE", "order_id": "ord_48213" }'
- role: tool
tool: issue_refund
result: '{ "refund_id": "rf_2Kd9Qm", "status": "pending", "settles_in": "5-10 business days" }'
- role: tool
tool: set_order_status
args: '{ "order_id": "ord_48213", "status": "REFUNDED", "refund_id": "rf_2Kd9Qm" }'
result: '{ "ok": true, "previous": "DELIVERED", "current": "REFUNDED" }'
- role: assistant
text: "Done. Refund rf_2Kd9Qm for 86.40 USD is on its way to your original card and should appear within 5 to 10 business days. Keep or recycle the earbuds — no return needed. Anything else?"
- role: user
text: no, thanks
- role: system
text: "Stop condition met: refund id delivered. Session closed after 4 min 12 s, 5 tool calls, 0 escalations."
```
## What the tools called
Each tool in the trace is a thin wrapper over one backend service. The order
of calls matters. The refund reaches the gateway before the order status
changes. A gateway rejection therefore leaves `ord_48213` in `DELIVERED`, and
the agent escalates instead of reporting a refund that never happened.
```sequence
id: refund-calls
actors:
- { id: Agent, name: Support agent }
- { id: CRM, name: Customer service }
- { id: Orders, name: Orders service }
- { id: Policy, name: Policy service }
- { id: Gateway, name: Payment gateway, external: true }
messages:
- Agent -> +CRM: get_customer(ses_7f2c91)
- CRM --> -Agent: cus_81A4
- Agent -> +Orders: list_orders(cus_81A4, 30d)
- Orders --> -Agent: ord_48213, ord_48377
- Agent -> +Orders: get_order(ord_48213)
- Orders --> -Agent: DELIVERED, pay_9X3bT
- Agent -> +Policy: check_refund_policy(ord_48213, DEFECTIVE)
- Policy --> -Agent: "eligible, 86.40 USD, no return"
- Agent -> +Gateway: issue_refund(pay_9X3bT, 86.40 USD)
- alt: gateway accepts
- Gateway --> -Agent: rf_2Kd9Qm pending
- Agent -> +Orders: set_order_status(ord_48213, REFUNDED)
- Orders --> -Agent: ok
- else: gateway rejects
- Gateway -x-> Agent: declined
- Agent -> CRM: create_ticket(ord_48213)
- end
foot:
- { label: Tool calls, value: "5" }
- { label: Wall time, value: "4 min 12 s" }
```
## Where the time went
Customer typing accounts for most of the wall time; the agent itself spent
under two seconds waiting on tools. The policy check is the slowest call
because it reads the order and the rule set in one request.
```table
columns: [Turn, Tool, Latency, Outcome]
rows:
- [3, get_customer, 41 ms, { v: cus_81A4 matched, tone: pos }]
- [4, list_orders, 118 ms, { v: 2 candidates — asked the customer, tone: warn }]
- [8, get_order, 64 ms, { v: DELIVERED 3 days ago, tone: pos }]
- [9, check_refund_policy, 402 ms, { v: eligible — no return, tone: pos }]
- [13, issue_refund, 871 ms, { v: rf_2Kd9Qm pending, tone: pos }]
- [14, set_order_status, 52 ms, { v: DELIVERED → REFUNDED, tone: pos }]
note: "Latency is server-side per call. Turn numbers count every entry in the trace, including tool results."
```