- A property buyer submits a cart for approval.
- Rejecting the entire purchase is easy.
- The buyer must rebuild the cart, reselect the property, recreate the GL allocation, choose delivery again and explain the context to another approver.
- For a property-procurement platform built on Medusa, we implemented a revision lifecycle instead.
The client problem
A property buyer submits a cart for approval. The approver spots the problem: the requested refrigerator is not the approved model, the delivery method is wrong or the allocation needs clarification.
Rejecting the entire purchase is easy. It is also wasteful.
The buyer must rebuild the cart, reselect the property, recreate the GL allocation, choose delivery again and explain the context to another approver. The organisation loses the connection between the original request, the correction and the final decision.
For a property-procurement platform built on Medusa, we implemented a revision lifecycle instead. An approver can request a specific change. The buyer edits the existing purchasing context, describes what changed and resubmits it. The platform recalculates the approval decision, preserves the history and serialises competing decisions.
The client did not need a bigger rejection button. They needed approvals to support a real conversation about a purchase.
Approval is not always yes or no
A binary approval model assumes the request is either perfect or beyond repair.
Real purchasing decisions are more nuanced. An approver may agree with the need but disagree with one product, quantity, delivery choice or accounting split. The fastest path to a compliant order is often a correction, not a restart.
Without a revision state, teams usually improvise:
- reject the request and ask the buyer to remember the changes;
- approve it and coordinate a correction outside the platform;
- exchange notes in email while the original cart remains unchanged;
- create a second request and lose the relationship to the first.
Each workaround weakens the control the approval process was meant to provide.
We introduced revision_requested as a first-class state between pending and resubmitted. It records that the approver has not approved the current request but expects the buyer to correct it.
That distinction keeps work moving without treating an unresolved issue as either a final rejection or an approval.
Make the approver explain the requested change
A revision request without a reason simply moves uncertainty back to the buyer.
The approver therefore submits notes describing what needs attention. The route verifies that the request belongs to the approver's organisation, is still pending and falls within the person's property and role scope.
The resulting record includes the notes, the requesting actor, the time and an incremented revision number. The buyer receives a notification carrying the request identity and the approver's explanation.
This changes the interface from “declined” to an actionable task.
The notes do not need to become an elaborate specification. They need to answer the buyer's immediate question: what must change before this purchase can be reconsidered?
Because the request remains identifiable, the approver later sees the response in the same lifecycle rather than comparing an unfamiliar new cart with a forgotten earlier request.
Let the buyer revise the actual purchase context
The object under approval is not merely a total.
It includes cart lines, quantities, selected property, seller-specific shipping, payment intent and line-level accounting allocations. A useful revision flow must allow the buyer to correct that context without detaching it from the approval request.
The existing cart remains the workspace for a cart-based approval. The buyer can replace an item, change quantity or repair another allowed aspect of the request. Before resubmission, shipping guards revalidate the seller and delivery-method consistency expected by checkout.
This matters in a multi-vendor marketplace. Removing one item can change seller participation. Replacing a product can change stock location or delivery eligibility. A revision should not preserve an old shipping choice merely because it was valid for the previous cart.
Accounting context also stays connected to the lines. The approval history captures line metadata in the cart snapshot, and later checkout gates still validate the allocation rules required by the property.
Revision therefore means “edit and re-evaluate the purchase”, not “change a status while everything else remains assumed valid”.
Require the buyer to say what changed
When a buyer resubmits a request in revision_requested, the platform requires a revision response.
An empty message is rejected. A useful response might say that the requested appliance was replaced with the approved model, the quantity was reduced or the allocation was corrected.
This creates a concise handoff back to the approver. They do not have to compare every field manually to discover the buyer's intention.
The response is not used as authority. The system still reads the current cart and recomputes totals, policy signals and routing. The note explains the change; the cart proves the new commercial request.
This separation is important. Free text is valuable for human context, but it should never be able to declare that a threshold no longer applies or that a delivery method is valid.
The buyer's explanation and the platform's recalculation work together: one makes the revision understandable, the other keeps it enforceable.
Recalculate approval instead of reusing the old answer
A revised cart may no longer require the same approval path.
Replacing a product can lower the total below the automatic threshold. Correcting a GL allocation can change the relevant budget signal. Increasing a quantity can escalate the request to a higher responsibility level.
On resubmission, the platform resolves the approval decision again from the current cart.
If approval is no longer required, the buyer can proceed toward completion without waiting for an unnecessary second decision. The existing request remains available as the historical explanation of why the cart was revised.
If approval is still required, the same request identity is reused and returned to pending with its new amount, reason, budget summary and required approval level. A request originally routed to a Master can, for example, return to a Supervisor after the buyer reduces it into that delegated range.
This keeps governance precise. The organisation approves the purchase that exists now, not the purchase that existed before the correction.
Preserve a durable revision history
The approval record appends lifecycle events rather than retaining only its latest status.
The history can include submitted, revision requested, resubmitted, approved and rejected events. Each event records its time, actor, role, revision number, note, requested amount and currency. For cart-backed requests, it can also include a snapshot of the current items, quantities, prices, totals and line metadata.
That history answers practical questions:
- What did the buyer originally request?
- Who asked for a change?
- What reason did they give?
- What did the buyer say they changed?
- How did the amount and cart differ on resubmission?
- Who made the eventual decision?
The implementation appends events without mutating the previous array value, and tests cover this behaviour. We describe it as a durable revision history, not as a cryptographically immutable ledger.
For the business, the important outcome is continuity. A later reviewer can understand how the purchase reached its final form without searching through detached carts and emails.
Stop two approvers from deciding at once
Approval requests can be visible to more than one authorised person.
If two approvers open the same pending request, one may approve while another requests a revision seconds later. Checking the status in the browser does not prevent both actions from reaching the backend with the same stale view.
The decision workflows therefore acquire a transaction-scoped advisory lock keyed by the approval request before reloading and changing it. The lock has a bounded wait. Contention is returned as a conflict telling the later approver that somebody else is deciding and that they should retry.
Inside the lock, the route reads the current state again. If the first action already moved the request out of pending, the second action cannot apply its outdated decision.
Focused tests cover lock acquisition, bounded contention and error mapping.
This is a narrow but important guarantee: competing approval decisions for the same request are serialised. It does not imply that the entire checkout is globally exactly-once.
Prevent approval and checkout from creating competing outcomes
Approval cannot live as an isolated dashboard feature. It must meet checkout at the point where an order would otherwise be created.
The cart-completion route looks for the active approval context. A request that still needs approval returns that state instead of completing the cart. A resubmitted request is recorded before the platform decides whether checkout may continue.
When the current revision falls below the threshold, completion can proceed using the revised cart. When it still requires approval, the route returns the updated pending request rather than also creating an order.
The shipping guard runs again so seller-specific methods remain consistent with the revised lines. Payment method, quote context and accounting allocations retain their own validations rather than being assumed valid merely because an approver clicked approve.
This is how the platform avoids two parallel truths: an approval request saying “wait” and a commerce workflow already producing the order.
The approval decision becomes part of the checkout contract, not a notification beside it.
Keep revision distinct from final rejection
Sometimes the purchase should not continue.
An organisation may reject the need itself, prohibit the supplier or conclude that no acceptable correction exists. That is different from asking the buyer to replace one line or clarify one allocation.
The lifecycle preserves that distinction. Revision requested is actionable and revisable. Rejected is a final decision for that request. Approved permits the order path subject to the remaining commerce checks.
Clear states improve both reporting and behaviour. Operators can count requests awaiting buyer changes separately from decisions that ended. Buyers know whether they should edit or stop. Approvers do not have to misuse rejection as a temporary inbox.
This also makes reminders more meaningful. A revision request can prompt the buyer responsible for the cart; a pending request can prompt the current approver; a rejected request should not keep circulating as unfinished work.
Why Medusa was the right foundation
Medusa already manages carts, line items, shipping methods, payment sessions and order creation. The client needed those commerce objects to participate in a property-aware approval conversation.
We added the approval domain, organisational routing, revision history and concurrency control without creating a parallel purchasing system. The buyer corrects the same commerce context that will later become the order. Checkout reevaluates it before completion.
That is the practical value of choosing an extensible commerce engine.
A rigid platform might offer a single manual-payment or draft-order approval switch. The client's process needed more: role and property routing, budget signals, revision, resubmission and a guarded transition back into checkout.
Medusa gave us the transactional foundation. We could add the precise governance model the organisation needed around it.
A practical approval-revision checklist
Before adding revisions to an approval flow, define:
- Is revision a distinct state from rejection?
- Must the approver explain the requested change?
- Can only an in-scope approver request revision?
- Does the buyer edit the actual cart or a detached copy?
- Must the buyer describe what changed?
- Are totals, budgets and routing recalculated on resubmission?
- Can a revised cart proceed directly if approval is no longer required?
- Does the same request identity retain the lifecycle history?
- Are cart snapshots stored in consistent currency units?
- Are seller-specific shipping choices revalidated?
- Are concurrent decisions serialised and rechecked?
- Can checkout create an order while approval is still pending?
These questions determine whether revision reduces bureaucracy or merely creates another ambiguous status.
The broader lesson
The client did not need buyers to restart every imperfect purchase.
They needed approvers to identify a correctable problem, buyers to respond in the same purchasing journey and the platform to reconsider the revised request under current rules.
We connected revision notes, cart changes, routing recalculation, durable history and decision locking to Medusa checkout. The organisation retained control without forcing every correction through a brand-new order attempt.
Good approval software does not only stop purchases. It helps legitimate purchases become approvable.
