The returns module manages the post-sale lifecycle: a customer asks to return something, the platform issues a return-merchandise-authorisation number (RMA), the customer ships the item back, staff inspects it, and a refund is processed. Each step is its own endpoint so the workflow can be wired into automations or external systems.
Customer-initiated returns
/storefront/returns/requestJWT/storefront/returns/my-returnsJWT/storefront/returns/rma/:rmaNumberJWT/storefront/returns/rma/:rmaNumber/shipJWT/storefront/returns/rma/:rmaNumber/cancelJWTrequest creates the RMA. The body specifies the order, line items being returned, reason, and whether the customer wants a refund or exchange.
const rma = await fetch('/api/storefront/returns/request', {
method: 'POST',
headers: { orgid: ORG_ID, Authorization: `Bearer ${jwt}` },
body: JSON.stringify({
orderNumber: 'ord-12345',
items: [
{ sku: 'SNK-AIR-1-9-BLK', quantity: 1, reason: 'wrong-size', condition: 'unused' },
],
resolutionType: 'refund',
notes: 'Need size 10 instead.',
}),
}).then(r => r.json());
// { rmaNumber: 'RMA-2026-04-25-0001', status: 'pending', returnLabel: '...' }
If the org has free-return-shipping enabled, the response includes a return label. Otherwise the customer is responsible for postage.
ship is called by the customer after dropping the parcel at a carrier — they enter the tracking number, RMA moves to in-transit. cancel aborts before the parcel ships.
Staff workflow
/storefront/returnsJWT/storefront/returns/statsJWT/storefront/returns/order/:orderNumberJWTStaff list and filter. Stats break down RMAs by status, reason, refund total — what a returns dashboard renders.
Approval
/storefront/returns/:rmaNumber/approveJWT/storefront/returns/:rmaNumber/rejectJWTSome orgs auto-approve all returns; others require staff approval before issuing the label. Approval is a no-op if request already auto-approved.
Inspection
/storefront/returns/:rmaNumber/receiveJWT/storefront/returns/:rmaNumber/inspectJWTreceive is called when the parcel arrives at the warehouse — moves the RMA to received. inspect records the inspection outcome:
await fetch(`/api/storefront/returns/${rmaNumber}/inspect`, {
method: 'PUT',
headers: { orgid: ORG_ID, Authorization: `Bearer ${jwt}` },
body: JSON.stringify({
items: [
{
sku: 'SNK-AIR-1-9-BLK',
condition: 'as-received',
accepted: true,
restockable: true,
},
],
notes: 'Tags still attached, returning to stock.',
}),
});
If restockable: true, the inventory module automatically receives the unit back via POST /storefront/inventory/return.
Completion
/storefront/returns/:rmaNumber/completeJWT/storefront/returns/:rmaNumber/notesJWT/storefront/returns/:rmaNumber/cancelJWTcomplete triggers the refund: the storefront's payment service refunds the original payment method (Stripe, PayPal, gift card portion to gift card balance — see Gift cards). The RMA moves to completed and the customer receives a confirmation email.
notes adds a timestamped note to the RMA — used for staff comments and customer-visible status updates.
Refund types
The resolution types supported:
| Type | What happens at completion |
|---|---|
refund | Refund original payment method |
exchange | Issue new order for replacement, no refund |
store-credit | Issue gift card for the refund amount |
partial-refund | Refund a fixed amount (used for damaged-but-keep flows) |
For exchange flows, the new order's payment intent reuses the inspection result — if the customer is upgrading, charge the difference; if downgrading, refund it.
Stats and reporting
The stats endpoint returns:
- Total RMAs by status
- Total refund amount this period
- Top reasons for return
- Mean time-to-resolution
- Restock rate (% of returned items returned to inventory)
These feed quality and merchandising decisions — high return rates on a SKU usually mean a sizing issue or a description mismatch.
The RMA workflow integrates with the logistics module. When you generate a return label, it's a real shipment record — track it the same way you track outbound shipments. The customer's "where's my return?" page is the same component as "where's my order?".