Ultimate Sack × ElmwoodCustomer Service Platform
Roadmap

Returns and customer service platform

One front door for every customer request, one set of policies everything reads from, and one place the numbers live. Returns first, because they are the worst of it.

Phases are ordered by dependency, not by calendar. Nothing here carries a date.

What this actually is

Returns are the first thing it does, not the reason it exists.

This is a customer service portal for Ultimate Sack. Returns and refunds are the first process running on it because they are painful, measurable, and touch every system at once. They are not the point.

The point is what it gives back. Returns currently take Fletcher's time, attention and patience, and his attention is the scarcest thing in the business. Put this on autopilot and that time goes somewhere with a far better return. The savings are small. The reclaimed bandwidth is not.

And once policies live in one place, real connections feed it, and every decision is logged with what it cost, the next process is cheap. Warranty claims, wholesale requests, review recovery, whatever comes next: the connections, the policy engine and the ticket layer already exist and already carry the data.

Judge the returns work on returns. Judge the platform on what the second process costs to add.

The problem, stated plainly

Why this is being built at all.

Customer service reaches Ultimate Sack through at least five doors: email, a contact form, Instagram, live chat, and two marketplaces. Nothing routes. Nothing is tagged. No structured reason is ever captured for a return, so no root-cause work is possible.

That shows up as tickets aging past two weeks, refunds Amazon issues automatically when a return sits unactioned for 48 hours, and a returns policy that contradicts itself on the live site.

Principles

Five rules the build is held to. Each one settles an argument before it happens.

01
Policy is data, not a document

It lives here, versioned, and publishes outward. Nothing is maintained in two places.

02
Grade condition from facts

The form asks whether the vacuum bag is sealed, never whether the customer considers it used.

03
Never pay freight for something worth less than the freight

Refund and let them keep it. Cheaper for us, better for them.

04
Measure before changing

Every metric gets a baseline before the thing it measures is rebuilt.

05
Fast beats generous

Customers tolerate a clear rule answered today. They do not tolerate ambiguity answered in eighteen days.

How to read this

Phases run in dependency order, not on a calendar. Inside each one, every item carries its own status so finished work stays visible instead of disappearing.

Done
Built and working in the code right now. It does not mean customers can see it. Publishing outward is phase two.
In progress
Being worked on right now.
Fletcher
Waiting on a decision only Fletcher can make. Store credit rate, warranty term, salvage percentages, cost per SKU. No amount of building moves these.
Access
Waiting on a key, a scope, or a paid feature. Someone has to switch something on before the work can even start.
Blocked
Cannot start until earlier work lands. Nothing outside the team is holding it up, but it still cannot move.
To do
Not started and nothing is stopping it. This is the queue.

Where the build stands

Every item across all nine phases, counted by status.

Done
45
of 88 items, built and working
Waiting on Fletcher
7
Decisions only he can make
Waiting on access
2
Keys, scopes, paid features
Open queue
32
28 ready to start, 4 blocked

What "done" means here

An adversarial audit of this roadmap checked 41 items marked done and overturned four of them. One had been breaking the rule that foam never comes back, on live orders, for as long as the rule had existed. So the bar moved.

Done
Done means checked against live data by someone trying to break it. Not written, not passing its own test, and never on the strength of a note in a handover file. An item that cannot be reproduced against real orders goes back to the queue. That is how the classifier bug surfaced after weeks of being counted as finished.

What 1.0 means

1.0 is the point Redo gets switched off. Not a feature count. Eight things have to be true at once, and until all eight are, we are running two systems instead of one.

1
Applied everywhere2 of 7 · 1 blockedTo do1.0

A policy that exists in one place and not the others is worse than no policy. Storefront pages, checkout footer, order and shipping confirmation emails, Amazon return settings, handling times, and the reply macros the team uses. The scope that blocked this is granted now, so this is ordinary work rather than a waiting game.

Delivers · One promise, said the same way everywhere a customer can read it.
Implementation checklist exists
27 checks defined across the four policies, each one tickable with a date.
Done
Permission to publish
write_legal_policies granted. This was the blocker and it is gone.
Done
Publish the four policies to the storefront
First real write this system will do. Runs in dry run first.
To do
27 checks still unticked
Nothing has been published outward yet. This is the actual work of the phase.
To do
Reply macros not written
The team is still answering from memory, which is where inconsistency comes from.
To do
Confirmation emails not updated
Order and shipping emails still carry the old promise.
To do
Amazon settings stay manual
Return settings and handling times cannot be automated until SP-API is connected.
Blocked
2 done1 blocked4 to do
0
Form matches policy exactly11 of 16In progress1.0

Every branch in the form has to trace back to a line in a policy document. If the form offers something the policy does not, or the policy promises something the engine will not do, we have two sources of truth and neither can be trusted.

Delivers · A form you can read the policy off, and a policy you can read the form off.
One question at a time
Stepped flow rather than a wall of fields, so nobody has to read anything twice.
Done
Questions change with the product
A cover has no vacuum bag and no foam. Asking those makes the form look broken.
Done
The right product, finally
The classifier matched the word cover in the title, and every filled product is titled Removable Washable Cover. 14,950 line units a year were wrong. It reads the SKU now.
Done
Blankets exist
BKT is 3,319 units a year and the system had no concept of them. It was asking blanket buyers whether the vacuum bag was sealed.
Done
Reasons filtered by condition and product
A sealed box cannot be firmer than expected. Only honest answers are offered.
Done
Condition graded A to E from facts
Graded off whether the bag is sealed and whether the foam came out, never off customer opinion.
Done
Undelivered orders cannot submit
Shows live tracking instead. You cannot return something that has not arrived.
Done
The reason decides what could fix it
The engine ranked on cost alone, so a cover swap won every foam case including too big, which a cover does not fix.
Done
A real repair beats a cheaper payout
Within 1.5x. A payout that leaves someone holding the same problem just books a second ticket.
Done
Foam top-up added as a fix
For a sack that arrived underfilled or too soft. Cheapest real fix in the system at about $28.
Done
Cover swap removed for liners
A liner is the inner bag. There was never a cover to swap.
Done
Customer-view preview
Play back exactly what a customer sees, step by step, and change the answers to see where each one lands.
In progress
Never show the outcome until every answer is in
The strong version of no backspacing. Reveal an offer mid-flow and the form becomes something you can fish in.
To do
Answers lock forward-only
Once submitted a step cannot be reopened and edited.
To do
Photos captured before the decision
Timestamped and tied to the request, so the grade can be checked afterward.
To do
Branch-by-branch audit against final policy text
Cannot start until the policies are final in phase one.
To do
11 done1 in progress4 to do
0
Automations we actually trust6 of 10To do1.0

Everything was read-only until today. The app now holds 34 write scopes, which is the difference between a very good simulation and a system. The guards went in before the power did: nothing can pay twice, nothing can spend past a ceiling, and nothing leaves the machine while dry run is on.

Delivers · The system does what it decided, and cannot do it twice.
Write access granted
72 scopes, 34 of them write. Orders, returns, gift cards, policies, products, themes.
Done
Idempotency on every write
The key comes from the business fact, not the request, so one approved refund stays one refund however many times it is submitted.
Done
Dry run is the default
Every write is fully computed and fully logged and nothing is sent. The record holds the exact request that would go, so what gets reviewed is what later executes.
Done
Spend ceiling
500 dollars of outbound exposure a day, about 12x the modeled average. It measures the hole, not our cost, so a gift card counts at face value.
Done
Every attempt is on the ledger
Executed or not, blocked or not. data/writes.json, readable at /api/writes.
Done
Decide and do are separate
Resolving a ticket records the decision. Executing it is a second, guarded step.
Done
Prove each write path in dry run against real records
Refund, partial refund, gift card, order tagging. Read the request each one would send before any of them are armed.
To do
Turn dry run off, deliberately
One constant. It should be a decision with a person behind it, not a default.
To do
Return label creation through ShipStation
The API can buy labels. Every call spends money, so it goes behind the same guard.
To do
Transactional email for outcome messages
The customer never hears the answer today.
To do
6 done4 to do
1
All traffic through the form1 of 6 · 2 on access · 1 blockedTo do1.0

Every door a customer can currently use: email, contact form, chat, Instagram, Facebook, and both marketplaces. Each one either points at the form or feeds the same queue. This is the step that actually replaces Redo rather than sitting alongside it.

Delivers · One front door, and Redo can be switched off.
Permission to edit the storefront
write_themes granted. Redirecting the existing returns links is now possible.
Done
Audit every entry point that exists today
Nobody has written down the full list yet, which makes closing them guesswork.
To do
Theme edits to redirect existing links
Every returns link on the storefront currently points at Redo. Customer-visible, so it gets asked about before it ships.
To do
Email ingestion for Amazon buyer messages
Amazon has no read API for buyer messages. They arrive as email to an anonymized alias.
Access
Chat widget replacement
Redo runs chat today, including AI deflection. Switching Redo off means replacing that.
Access
Redo cannot be switched off before this
This phase is the definition of 1.0. Everything earlier is preparation for it.
Blocked
1 done2 access1 blocked2 to do
1
Measured, not guessed8 of 11 · 1 on Fletcher · 1 blockedTo do1.0

Refund dollars per revenue dollar as the headline, live from the day traffic starts. Plus the form funnel: started, completed, abandoned and where. Plus outcomes: what the system handled alone, what needed a person, how long each took, and what each cost.

Delivers · A baseline that begins when traffic does, so every later change can be proved.
Live Shopify dashboard
Pulls orders, refunds, products and policies directly. No exports.
Done
Return rate measured
3.36 percent. 243 returns against 7,239 orders over the trailing twelve months.
Done
Reason mix measured
Too big is 42 percent of all returns. Appearance complaints together are another 47 percent.
Done
Return timing measured
Median four days from delivery. 97 percent inside thirty days. That is what makes the window a non-event.
Done
Coverage attach and revenue measured
Including the finding that raising coverage from $3 to $5 cut revenue per order by 18 percent.
Done
Cancels and returns no longer half-counted
Two thresholds left a dead band at 8 to 14 days that silently dropped 186 refunds, 17.7 percent of them.
Done
Every reason rolls up to a lever
Was 11 of 22 reason strings, so ordered the wrong size never counted toward sizing, the top cause at 42 percent. Now 22 of 22, each pointing at an owner.
Done
Causes priced by the outcome we chose
It was pricing every ticket at its cheapest option, so a declined request scored as free.
Done
Form funnel not instrumented
Started, completed, abandoned and where. None of it is captured yet.
To do
Cost per SKU
Same blocker as phase one. Without it cost per return stays modeled rather than actual.
Fletcher
Response and resolution times need live traffic
There is nothing to measure until the form is the front door.
Blocked
8 done1 fletcher1 blocked1 to do
0
Easy enough that nobody complains6 of 10In progress1.0

A customer should finish this on a phone in under a minute without reading anything twice, and never hit a dead end. A person working the queue should be able to clear a day in a couple of minutes without being trained first.

Delivers · Nobody has to be taught how to use it.
Navigation that works on both
Persistent rail with a collapse on desktop, drawer on mobile. Rebuilt after the first version could not be closed.
Done
Inbox reads like an inbox
Full-width rows, order number as the key, 24-hour countdown going yellow then orange then red.
Done
A ticket is one decision, not a document
Seven facts on one strip, one recommendation with its cost, one alternative, everything else folded away. Enter approves, 2 takes the alternative, s skips, and it moves to the next by itself.
Done
Running cost, and whether it is getting worse
Last 30 days against the 30 before, on top of the processed list, which starts collapsed.
Done
Open notes are clickable instead of highlighted
The red overlay was a second layer sitting behind the text and it drifted as you scrolled. The notes jump you to the word now.
Done
Plain American English everywhere
Swept the whole app and all four policies.
Done
Customer-view preview in the form builder
So the person writing the rules can see what the rules actually say to a customer.
In progress
Mobile pass on the form
The console is done. The customer-facing form is not.
To do
Every dead end needs a next step
Including the declines. A no with nowhere to go becomes an email to Fletcher.
To do
Worked examples still quote the old cost model
The decision tree and the scenario write-ups were computed when recovered units were credited at retail. Some figures are several times off.
To do
6 done1 in progress3 to do
1
Analytics that change a decision2 of 5 · 1 blockedTo do1.0

The point of the reason codes is to fix causes, not count complaints. The analytics page has to say which cause costs the most and which lever moves it, well enough that an experiment is worth running.

Delivers · A page that tells you what to go fix, ranked by dollars.
Every cause names an owner and a fix
Sizing points at the listing, quality points at the supplier, fulfillment points at operations, no-fault points at the cart.
Done
Sizing is now visibly the biggest line
It was hidden while half the reason vocabulary fell into Other.
Done
Rank causes by dollars on the page itself
The rollup computes it. The page does not lead with it.
To do
Experiment framework
Specified, not built. Needed to prove a listing change actually moved the return rate.
To do
Needs live reason codes
The measured mix came from Shopify history. Ongoing measurement needs the form running.
Blocked
2 done1 blocked2 to do
0
After 1.00 of 6To doLater

Amazon SP-API. Walmart and Wayfair. Cover recovery batching. The unified inbox with full email reply. Supabase, GitHub and hosting move here too. They are how it runs, not what it does, and moving early means paying migration on every iteration.

Delivers · The next process, on infrastructure that is already paid for.
Amazon SP-API
Needs Fletcher to register the developer account. Owner only, about fifteen minutes.
To do
Walmart and Wayfair
Deliberately out of scope until 1.0 holds.
To do
Cover recovery batching
Returned covers accumulate. Worth handling in batches rather than one at a time.
To do
Unified inbox with full email reply
Returns is 20 tickets a month. There are 27,000 closed tickets. This is where the project stops being about returns.
To do
The product pages that cause the returns
Too big is 42 percent of every return and it is a listing problem being paid for at the returns desk. Customer-visible and close to Whitney, so it gets agreed before it starts.
To do
GitHub, Supabase and hosting
Runs on one laptop with JSON files today. All three have free tiers that cover this volume.
To do
6 to do

What is actually connected

Two live connections, one manual, two not started. Worth being precise about this, because "data flowing in" is easy to overstate.

ConnectionStateWhat it gives us
Shopify Admin APILive, read onlyOrders, refunds, products, policies, themes. Cannot write yet.
ShipStation v2Live, read onlyDelivery state on 2,217 shipments. Tracking endpoint is behind a paid tier.
RedoManualBilling and ticket history by CSV export. No live connection.
Amazon SP-APINot connectedNeeds Fletcher to register the developer account, owner only.
Walmart, WayfairNot connectedDeliberately out of scope until the MVP holds.

What gets measured

Honest status per metric. "Not yet" means exactly that, and names where the number will come from.

MetricStatusSource
Return rate and cancellation rateMeasuredShopify
Coverage attach and revenueMeasuredShopify
Return mix by product typeMeasuredShopify
Shipping cost and recoveryStaticShipStation export
Cost per return by SKUPartialNeeds cost per SKU
First response timeNot yetRedo history, then Phase 4
Time to resolutionNot yetRedo history, then Phase 4
Contact reason mixNot yetPhase 2 form
Amazon SLA breachesNot yetAmazon reports

Open items

Carried deliberately. Each is either waiting on a decision or on data only Fletcher has.

Where this eventually lives

Right now it runs on one laptop with JSON files for storage. The end state is GitHub for code history and rollback, Supabase for the database, and Cloudflare or Vercel for hosting. All three have free tiers that comfortably cover this volume, so cost is not the blocker. It is deliberately last, because every iteration we make now would otherwise cost a migration.

Amazon SP-API private app

A private Selling Partner API app on the Ultimate Sack seller account. It gives us structured return reason codes on the channel where we currently have none, reimbursement and SAFE-T data to size the 48-hour auto-refund leak, return status for the SLA timer, and order lookup so an Amazon customer gets the same treatment in our form as a Shopify one. Buyer messages cannot be read over the API and arrive as email to an anonymised alias, so conversations come in by email ingestion while everything around them comes from the API. Amazon restricts developer registration to the primary account holder, so Fletcher does one fifteen-minute session to register and mint the refresh token; Jake needs a secondary user, not his login. A public Appstore app is a separate and much heavier path, parked.

Cover recovery loop

Covers are 27% of returned units, $10 to $15 to ship back, washable and separately saleable. The one retrieval path that clearly pays. Open question is where they land once Ohio closes.

Coverage pricing

The $3 era earned $0.86 per order against $0.02 at $5, because Redo's cost per covered order rose faster than the price did. Price is set in Redo's configuration, not editable in Shopify.

Live ShipStation connection

Shipping figures come from a static export and go stale.

Redo historical pull

Ticket corpus and return reason codes, needed once for the first response time baseline.

Cost per SKU

Required to finish the retrieval thresholds. Only Fletcher has it.

v20
Read-only preview · real engine, real Shopify numbers · nothing here can be changed