Returns Resolution Agent Blueprint

Retail's AI demos tend to end at the checkout, which is a pity, because the expensive part of retail starts after the sale. A return is a small negotiation with money on both sides: the customer's refund, the retailer's recovered stock, a policy that has to be applied the same way on a Tuesday as on Black Friday, and a minority of requests that are not honest. Put a fluent model in charge of that negotiation and it will be charming, inconsistent, and occasionally generous with money that is not its own. This blueprint takes the opposite position, and holds it everywhere: the model talks, the code decides.
This open blueprint, the Returns Resolution Agent, runs on Amazon Bedrock AgentCore and handles a return from request to receipt. Eligibility is computed from a versioned policy pack. The refund is a derived ledger. Disposition — where the returned item actually goes — is read off a matrix. Fraud is scored in code. Refunds move only through approval gates and an idempotent payments port. The model's single job is drafting the message the customer reads, and even that draft is verified figure by figure before it may exist. Follow one pair of trail boots through it.
Day sixty of sixty
The boots were delivered on August 27 and the return was requested on October 26 — the last day of the footwear window, exactly. The policy pack is data: windows by category (sixty days for apparel and footwear, ninety for gear, thirty for electronics), excluded categories, a final-sale exclusion, a bar on customer-damaged goods. The engine's decision records the pack version and the clauses that fired. The boots pass on the window's final day; their companion case in the sample feed, requested one day later, fails on the same clause. Both outcomes are graded, because an edge a system gets right by luck is an edge it will get wrong under pressure.
Nothing about that decision involves the model. It also produces the paper trail an auditor wants: not "approved" but the clause list, replayable against the pack as it stood that day.
The refund is a ledger, not a sentence
Eligibility says whether money moves. The computation says how much, and it is built like a ledger rather than an estimate: an item credit of $145.00 for the boots, a restocking deduction of $14.50 — the pack's ten percent for the customer's declared like-new condition — and a shipping deduction of zero, because the pack makes footwear returns free. Refund total: $130.50. Every line carries its derivation: the pinned input it came from, the rule, the policy reference. The whole computation is content-addressed and re-performed by a verifier that detects any altered line, because a refund figure nobody can re-derive is just a number someone typed.
The ledger also shows its gap, in the open. The order carries no tender breakdown, so the allocation of the refund across payment methods is recorded as not computable, naming the missing input — the total is final, but where the money lands is not invented. That pattern recurs throughout the system: a missing input is a named state, never a quiet zero.

Where the boots go next is read off a matrix
A refunded item is not finished; it has to go somewhere, and the somewhere is margin. Disposition is a versioned matrix over category, declared condition and age band — thirty-six cells, each naming a disposition from a closed vocabulary (restock, refurbish, outlet, liquidate, return to vendor, recycle, hold for inspection) and the channel it flows through. The boots, like-new and in season, read restock via the store. When a cell's channel is closed, the decision falls to the matrix's stated next cell and records the hop; a fact set that matches no cell comes back not decidable, naming the missing dimension, rather than defaulting to whatever someone usually does. A pair of used headphones in the same feed lands on hold for inspection, and the decision names what the inspection must establish — a functional test result and a data-wipe status — before anything else may happen to it.
A score can block the money
Fraud is scored by a second pack, in code, from stored records only: return velocity, the share of a customer's orders that come back, wardrobing patterns, carrier weight against the product master's weight, payment-card churn. Each signal shows its inputs and its contribution; a signal without enough history says insufficient data instead of guessing. The sample feed's serial returner — eight orders, all eight returned, four payment cards — scores 95 and lands in the review band, which blocks the refund path outright.
The block has exactly one door: a named fraud-review decision, with a coded outcome and a reason from the pack's vocabulary. In the sample, a reviewer clears the case with the reason recorded — and here is the detail that tells you the system is honest: the score artefact still reads 95, still in the review band. The decision lifted the block. It did not rewrite the arithmetic.
Two approvals, one receipt
Refunds execute under thresholds the pack sets: automatic up to $50, one named approver above that, a second approver — a different person, enforced when the act is recorded — above $150. The boots' $130.50 takes one approval; the $189 case in the feed takes two, and its first execution attempt is refused, in so many words, because only one approver is on record. An approval pins the exact computation it approves, so approving yesterday's figures cannot authorise today's.
Execution runs through the payments port under a mandatory idempotency key. Retrying the boots' refund with the same key returns the original receipt and the movement count stays at one; retrying under a new key is refused as a duplicate. The port's only implementation in the blueprint is a local double, labelled as such on every receipt it produces — it moves no real money, and says so. The gates, the idempotency contract and the failure handling are the deliverable; a production deployment binds its real payments provider behind the same port.
Then the warehouse disagrees
The boots come back, and the warehouse grades them used, not like-new. This is the moment most returns systems handle with an email and an argument. Here it triggers a cascade, all of it mechanical: the disposition is re-decided on the graded facts — restock becomes outlet — as a new linked decision that supersedes the declared one without editing it. The refund is re-priced under the used rate: $116.00 instead of $130.50. The $14.50 difference is recorded as an open recovery position, a receivable on the record — and no clawback is executed, because reaching back into a customer's account is a business decision with a human owner, not a cascade step. The customer summary is re-issued through the same figure verifier, quoting the new numbers and the reason they changed.
Even the label keeps its own small honesty: on another case in the feed, the carrier charged $8.50 for a label against a $6.95 shipping deduction, and the $1.55 variance sits on the label's record as a variance — not absorbed, not rounded away, not hidden in a footnote.

The seams, stated
The carrier, like payments, is a labelled local double — nothing really ships. Customer history is held as memory with limits: event-shaped summaries of closed cases, retrievable by exact customer identity only, every read written to an access log, and records past the retention window excluded and counted as expired rather than silently deleted. The AgentCore services around the agent — Gateway for the carrier tools, Policy for access, Memory for that history — are ports, live where wired and doubles where not, and the customer-facing words are the only place a model is allowed to speak, always downstream of the verifier.
Work with us
If your returns operation runs on agent discretion — refunds negotiated chat by chat, disposition decided at the warehouse door, fraud spotted when someone happens to notice — the alternative is buildable: policy as data, refunds as ledgers, every block and approval a recorded act. Bring the problem through Trida AI's intake and we will scope what a returns agent inside your operation should, and should not, be allowed to touch.
Get the Code on GitHub: github.com/tridaai/blueprint-agentcore-returns-resolution
