Skip to main content

Overview

Three things can happen after a customer receives an order, and Spree models each on its own: Keeping them separate matters most for claims. Without a record for “it arrived smashed”, merchants end up either creating a manual order or opening a return they immediately mark received — which records the wrong thing and makes damage reporting impossible.

Statuses

Each step is its own API call, because each one needs information the last one didn’t have — what actually turned up, how much to refund, what to send instead.

Customer self-service

Customers can open a return or a claim on their own order and follow its progress. Approving, receiving and refunding stay with the merchant.
Claim reasons are records each store owns. New stores are seeded with Arrived damaged, Never arrived, Wrong item sent, Missing item from order and Item not as described, and merchants can add, rename or remove their own.

Processing a return

Receiving takes the quantities the warehouse actually counted, because partial and damaged returns are normal rather than exceptional. A customer says three items are coming; two arrive; one of those can’t be sold again. Only resellable goods go back into stock. Leave items out to receive everything as requested. Refunds default to whatever the return is still owed, and can go back to the original payment method or to store credit. A returned item is worth what the customer paid for it after discounts, not its list price, so a free gift sent back refunds nothing. Refunding a return that is owed nothing completes it with no money moved and no refund email; an amount of zero is refused on any return the customer is still owed money on.

Tax on returns

A return gives back the tax the customer paid on what came back. A 25.00shirtsoldwith1025.00 shirt sold with 10% tax added on top refunds 27.50, and on a store whose prices include VAT the VAT inside the price goes back with it. Delivery and its tax are not refunded. Each return line shows its worth the way an order line does: pre_tax_amount before tax, included_tax_total and additional_tax_total beside it, and refund_amount with the tax included. The return’s refund_tax_total says how much of refund_total is tax, and expand=return_line_items.tax_lines (Admin API) lists the tax given back rate by rate. The store’s tax provider works this out when the return opens, and again just before the money goes back, for the units the warehouse counted. Refunding less than the return is worth — keeping a restocking fee, say — gives the tax back in the same proportion as the goods. If the tax service cannot answer, the return is not opened (or not refunded) and the customer can try again. Claims and exchanges follow the same rules.

Where the goods come back to

A return is received at a stock location. By default that is wherever the goods shipped from, which is right until a merchant inspects and restocks returns at one processing centre — so locations carry a returns_enabled flag, and a return routes to one that accepts them: the seller’s for a seller’s goods, the operator’s own otherwise. Pass stock_location_id when opening the return to choose explicitly.

Prepaid return labels

A return can carry its own postage. The label is bought against the same carrier account the outbound parcel shipped on, and the shipment is booked in reverse — from the customer’s address back to the return’s stock location. Buying one mints a delivery on the return, so the inbound parcel is tracked the same way an outbound one is. A delivery reporting arrival never receives the return: arrival is not inspection, and what actually turned up is still counted by hand.
Customers download the label from the storefront, so a return request can end with “print this and drop it off” rather than an email exchange. See shipping labels for how postage records work in general.

Resolving a claim

What to do about a claim is decided when you resolve it, not when the customer opens it — merchants usually decide once they’ve seen the photos.
A replacement creates a new fulfillment on the original order, so the customer doesn’t have to place a second one. The replacement is filed under the order line it replaces, but it doesn’t count toward that line’s quantity. Editing the line later, from a claim or an exchange, adds or removes only the goods the customer paid for and leaves the replacement where it is. Adding a fulfillment for that line works the same way: it takes only the goods the customer paid for, and the replacement stays in its own fulfillment. If a replacement’s fulfillment is canceled, for example because the courier lost the booking, send the goods again by naming that fulfillment as the source of a new one. The new fulfillment takes everything the canceled one held and reserves its stock again. It is priced like any new fulfillment, so pass cost: 0 to carry over the canceled fulfillment’s delivery cost instead, which keeps a free replacement free.
A claim never refunds more than the customer paid for the claimed items after discounts, tax included. Each claim line reports that ceiling as paid_amount, and the refund_amount you enter for a line includes its tax. The tax goes back in proportion to the money: refunding half of what a line is worth gives back half its tax, and resolving with a replacement alone gives none back.

Exchanges

An exchange works like a return, but ends by sending different items instead of refunding:

Settling the difference

An exchange credits what came back and sells the replacement, each with its tax:
  • original_price is what the customer paid for the items coming back, after discounts and with their tax.
  • new_variant_price is the replacement, with its own tax. It keeps the deal the customer had: an item bought at 10% off is replaced at 10% off its price, so swapping a size costs nothing.
  • price_difference is the second less the first.
When you fulfill, a cheaper replacement refunds the difference, to store credit or the original payment. A dearer one adds the difference to the order as an exchange fee, so the order shows a balance due. Spree never charges a stored card for it; take the payment through the order’s payments.

Reporting

Because each one is its own record, you can query them directly:

Return policy

Spree ships one built-in rule: a return window, counted from when the order was placed and set per market (30 days by default). Whether a customer may open a return is store policy, and it’s checked when the return is created — so you can hold customers to that window while letting staff make an exception for a good customer, and vary the rule by market where local law requires it. Set the return window per market, or express a more specific rule in your own application. See Configuration and Services & Workflows.

Events

Each step publishes an event — return.received, return.refunded, exchange.fulfilled, claim.resolved — which also reach webhooks. Returns and claims also update the order’s payment status, so a refunded order reflects it without any manual bookkeeping.