How a refund or replacement gets decided
The parcel is back and someone has opened the box. Now comes the only decision that actually costs money: does the customer get a refund, a replacement, or neither? This post is about how the system turns an inspection result into a clear proposal — with the reason spelled out — and hands it to a person to approve.
Key takeaways
- The decision runs on a recorded inspection — what the box actually contained — not on what the customer said they’d send.
- A staffer logs the condition and a photo or two; that result, stored in S3, is what the decision is built on.
- The decide step is plain code: it proposes full refund, partial refund, replacement, or reject — with the reason attached.
- The proposal goes to the returns desk with three buttons: Approve, Switch, or Reject. A human signs off before anything moves.
- Once approved, the outcome is recorded and the customer is told. No refund or replacement leaves on its own.
The only decision that costs money
Everything so far has been preparation. The parcel is back, someone has opened the box, and now the system reaches the only step that actually moves money or stock: refund, replace, or neither. This is where a lot of returns go wrong — refunded on the customer’s say-so before anyone looked in the box, or refunded and replaced because two people each handled half the return. The handler avoids both by making the decision depend on a recorded inspection, proposing exactly one outcome with its reason, and handing that to a person to approve.
The inspection
When the carrier scan from Part 4 resumes the execution, the system doesn’t decide anything yet — it emails the warehouse that a parcel for RMA-4821-7 has arrived and needs checking. The inspect step is itself a short task-token wait: a staffer opens the box, then submits what they found through a simple form behind the rmar-inspect Function URL — which item, what condition (matches the order, sealed, used, damaged, wrong item), and one or two photos that land in the photos/ prefix in S3. Submitting resumes the execution. The point is that the decision runs on what was actually in the box, recorded by a person, with photographic evidence kept — not on the customer’s description from two weeks earlier.
The decision, in plain code
The rmar-decide step takes the inspection result and the original reason and applies your policy as code to propose exactly one outcome:
- Full refund — the right item came back in a resaleable condition within the window, and the reason is a straightforward change of mind or a fault you accept. The proposal carries the amount and the reason: “received sealed, matches order line, within window — propose full refund of £79.”
- Partial refund — the item is back but not as it left: opened packaging, missing accessory, light wear. The policy sets the deduction, and the proposal shows the maths: “returned used, −20% restocking per policy — propose refund of £63.20.”
- Replacement — the customer wanted a different size or a like-for-like swap and the item is resaleable and in stock. The proposal names the replacement: “faulty on arrival, same SKU in stock — propose replacement, no charge.”
- Reject — what came back doesn’t support a refund: the wrong item, damage beyond what the reason explains, or a box that’s effectively empty. The proposal says why and proposes returning it to the customer.
Like eligibility, this is deterministic. The policy already defines what a used return is worth and when a swap beats a refund; the step just applies it and shows its working. A model is never asked “should we refund this?” — only, at most, to phrase the customer’s outcome email kindly once a human has approved it.
The approval
The proposal doesn’t execute itself. The execution parks on an await approval step — the same task-token pattern as the inbound wait — and the returns desk gets one email: the RMA, the inspection photos, the proposed outcome, and the reason, with three buttons behind the rmar-approve Function URL. Approve takes the proposal as-is. Switch overrides it — refund instead of replace, or the reverse, with a note — for the judgement calls a policy can’t fully capture. Reject declines it. A tap resumes the execution into rmar-resolve, which records the final outcome and reason to the audit log and emails the customer what’s happening. The refund itself is queued into your normal payment run, and a replacement into your normal fulfilment — the handler proposes and records; it never reaches into the money or the stock on its own.
Design rules for the decision
- Decide on the inspection, not the request. The recorded condition of what arrived is the only input that counts.
- One proposal, with its reason. Full refund, partial, replacement, or reject — never a menu, always a recommendation that shows its working.
- The policy does the maths. Restocking deductions and refund-versus-replace come from the written policy, applied as code.
- A human approves, switches, or rejects. The verdict is always a person’s; the system only ever proposes.
- Record before you act. The outcome and reason are logged, then the refund or replacement joins your normal runs — not before.