How to Create a Return/Refund Request Form
Returns happen. The question is whether they create frustrated customers and support tickets, or a smooth self-service experience. A good return form collects the right information upfront, sets expectations, and routes requests to the right workflow automatically.
This guide covers building a complete return/refund request form. Order lookup, reason selection, photo evidence for damaged items, resolution options, and estimated refund amounts. Forms that handle returns properly reduce support load and keep customers coming back.
Order Identification
First, identify which order we're talking about. Order number and email let you look up the purchase and verify ownership.
// Order identification
form.addRow(row => {
row.addTextbox('orderNumber', {
label: 'Order Number',
placeholder: 'e.g., ORD-12345',
isRequired: true
});
});
form.addRow(row => {
row.addEmail('email', {
label: 'Email Used for Order',
isRequired: true
});
});
form.addRow(row => {
row.addTextPanel('orderHelp', {
label: 'Find your order number in the confirmation email or on your packing slip.'
});
});The help text tells customers where to find their order number. Small details like this reduce form abandonment - people don't give up looking for information.
Pro tip
If you can integrate with your order system, validate the order number in real-time. Show "Order found: Blue Widget, purchased Jan 15" to confirm they're returning the right item.
Return Reason
Why are they returning it? The reason determines what happens next - different reasons trigger different fields, policies, and workflows.
// Return reason selection
form.addRow(row => {
row.addDropdown('reason', {
label: 'Reason for Return',
options: [
{ id: 'wrong-item', name: 'Received wrong item' },
{ id: 'damaged', name: 'Item arrived damaged' },
{ id: 'defective', name: 'Item is defective' },
{ id: 'not-as-described', name: 'Not as described' },
{ id: 'no-longer-needed', name: 'No longer needed' },
{ id: 'wrong-size', name: 'Wrong size/fit' },
{ id: 'better-price', name: 'Found better price elsewhere' },
{ id: 'other', name: 'Other' }
],
isRequired: true
});
});
// Details for specific reasons
form.addRow(row => {
row.addTextarea('reasonDetails', {
label: 'Please describe the issue',
placeholder: 'What was wrong with the item?',
rows: 3,
isRequired: () => {
const reason = form.dropdown('reason')?.value();
return ['damaged', 'defective', 'not-as-described', 'other'].includes(reason ?? '');
},
isVisible: () => {
const reason = form.dropdown('reason')?.value();
return ['damaged', 'defective', 'not-as-described', 'other'].includes(reason ?? '');
}
});
});When the reason is damage, defect, or "not as described," the details field becomes visible and required. For simple reasons like "no longer needed," no explanation is necessary.
Resolution Options
What does the customer want - refund, exchange, store credit? Offering options gives customers control and can steer them toward outcomes that work for your business (like store credit with a bonus).
// What the customer wants
form.addRow(row => {
row.addRadioButton('resolution', {
label: 'Preferred Resolution',
options: [
{ id: 'refund', name: 'Full refund to original payment method' },
{ id: 'exchange', name: 'Exchange for different item/size' },
{ id: 'store-credit', name: 'Store credit (get 10% bonus)' },
{ id: 'replacement', name: 'Send replacement of same item' }
],
isRequired: true
});
});
// Exchange details
form.addRow(row => {
row.addTextbox('exchangeItem', {
label: 'What would you like instead?',
placeholder: 'Item name, size, color...',
isVisible: () => form.radioButton('resolution')?.value() === 'exchange',
isRequired: () => form.radioButton('resolution')?.value() === 'exchange'
});
});
// Replacement only for certain reasons
form.addRow(row => {
row.addTextPanel('replacementNote', {
label: 'Replacements available for damaged or defective items only.',
isVisible: () => {
const resolution = form.radioButton('resolution')?.value();
const reason = form.dropdown('reason')?.value();
return resolution === 'replacement' &&
!['damaged', 'defective', 'wrong-item'].includes(reason ?? '');
}
});
});Exchange shows an additional field for what they want instead. The replacement option shows a note if the return reason doesn't qualify - you can't get a replacement for "no longer needed."
Photo Evidence
For damaged or defective items, photos are essential. They verify the claim, help with carrier disputes, and document what went wrong.
// Photo evidence for damaged/defective items
const needsPhoto = form.computedValue(() => {
const reason = form.dropdown('reason')?.value();
return ['damaged', 'defective'].includes(reason ?? '');
});
form.addRow(row => {
row.addTextPanel('photoHeader', {
label: 'Please provide photos of the damage/defect',
isVisible: () => needsPhoto()
});
});
form.addRow(row => {
row.addTextbox('photoUrl1', {
label: 'Photo 1 URL',
placeholder: 'Link to photo (upload to Imgur, Google Drive, etc.)',
isVisible: () => needsPhoto(),
isRequired: () => needsPhoto()
});
});
form.addRow(row => {
row.addTextbox('photoUrl2', {
label: 'Photo 2 URL (optional)',
placeholder: 'Additional photo if needed',
isVisible: () => needsPhoto()
});
});
form.addRow(row => {
row.addTextPanel('photoTip', {
label: 'Clear photos help us process your request faster. Show the damage and any relevant labels.',
isVisible: () => needsPhoto()
});
});The photo fields only appear when the reason is damage or defect. At least one photo is required; a second is optional for complex issues. The tip encourages useful photos that actually help.
See more form examples in the gallery.
Item Condition
What condition is the item in? This affects refund eligibility and amount. Be upfront about restrictions.
// Item condition assessment
form.addRow(row => {
row.addRadioButton('condition', {
label: 'Item Condition',
options: [
{ id: 'unopened', name: 'Unopened, original packaging' },
{ id: 'like-new', name: 'Opened but unused, all tags attached' },
{ id: 'used', name: 'Used/worn' },
{ id: 'damaged-by-me', name: 'Damaged after receipt' }
],
isRequired: true
});
});
// Used/damaged items have restrictions
form.addRow(row => {
row.addTextPanel('conditionWarning', {
label: 'Used or damaged items may only be eligible for partial refund or store credit.',
isVisible: () => {
const condition = form.radioButton('condition')?.value();
return ['used', 'damaged-by-me'].includes(condition ?? '');
}
});
});The warning appears immediately when they select "used" or "damaged by me" - no surprises later. Honesty about policies builds trust even when the answer isn't what customers want to hear.
Return Method
How will they get the item back to you? Offer options that work for different situations.
// How to return the item
form.addRow(row => {
row.addRadioButton('returnMethod', {
label: 'Return Method',
options: [
{ id: 'mail', name: 'Ship it back (prepaid label provided)' },
{ id: 'dropoff', name: 'Drop off at store location' },
{ id: 'pickup', name: 'Schedule pickup from my address' }
],
defaultValue: 'mail'
});
});
// Store location selector
form.addRow(row => {
row.addDropdown('storeLocation', {
label: 'Select Store',
options: [
{ id: 'downtown', name: 'Downtown - 123 Main St' },
{ id: 'mall', name: 'Westfield Mall - Suite 456' },
{ id: 'suburb', name: 'Northside - 789 Oak Ave' }
],
isVisible: () => form.radioButton('returnMethod')?.value() === 'dropoff',
isRequired: () => form.radioButton('returnMethod')?.value() === 'dropoff'
});
});
// Pickup address
const pickupSection = form.addSubform('pickupAddress', {
title: 'Pickup Address',
isVisible: () => form.radioButton('returnMethod')?.value() === 'pickup'
});
pickupSection.addRow(row => {
row.addTextbox('street', { label: 'Street Address', isRequired: true });
});
pickupSection.addRow(row => {
row.addTextbox('city', { label: 'City', isRequired: true });
row.addTextbox('zip', { label: 'ZIP Code', isRequired: true });
});Each method shows different follow-up fields. Mail returns just need an address for the prepaid label. Store dropoff shows location options. Pickup collects their address.
Refund Estimation
Show customers what they'll get before they submit. No surprises, no "I thought I was getting a full refund" complaints.
// Show estimated refund
const originalPrice = 79.99;
const shippingPaid = 8.99;
const refundAmount = form.computedValue(() => {
const reason = form.dropdown('reason')?.value();
const condition = form.radioButton('condition')?.value();
const resolution = form.radioButton('resolution')?.value();
// Store credit bonus
if (resolution === 'store-credit') {
return (originalPrice * 1.10).toFixed(2);
}
// Full refund for our mistakes
if (['wrong-item', 'damaged', 'defective'].includes(reason ?? '')) {
return (originalPrice + shippingPaid).toFixed(2);
}
// Partial refund for used items
if (condition === 'used') {
return (originalPrice * 0.7).toFixed(2);
}
// Standard refund (no shipping)
return originalPrice.toFixed(2);
});
form.addRow(row => {
row.addPriceDisplay('estimate', {
label: 'Estimated Refund',
computedValue: () => parseFloat(refundAmount()),
prefix: () => {
const resolution = form.radioButton('resolution')?.value();
return resolution === 'store-credit' ? 'Store Credit: ' : 'Refund: ';
},
isVisible: () => !!form.radioButton('resolution')?.value()
});
});The estimate updates as they fill out the form. Store credit shows the 10% bonus. Returns for your mistakes include shipping refund. Used items show reduced amount. Transparency reduces disputes.
Complete Example
Here's a streamlined return form with the essential elements.
// Complete return/refund request form
form.addRow(row => {
row.addTextbox('orderNumber', {
label: 'Order Number',
placeholder: 'ORD-12345',
isRequired: true
});
row.addEmail('email', {
label: 'Email',
isRequired: true
});
});
form.addRow(row => {
row.addDropdown('reason', {
label: 'Reason for Return',
options: [
{ id: 'wrong-item', name: 'Wrong item received' },
{ id: 'damaged', name: 'Arrived damaged' },
{ id: 'defective', name: 'Defective' },
{ id: 'not-as-described', name: 'Not as described' },
{ id: 'wrong-size', name: 'Wrong size' },
{ id: 'no-longer-needed', name: 'No longer needed' }
],
isRequired: true
});
});
form.addRow(row => {
row.addTextarea('details', {
label: 'Details',
placeholder: 'Describe the issue...',
rows: 3,
isVisible: () => {
const reason = form.dropdown('reason')?.value();
return ['damaged', 'defective', 'not-as-described'].includes(reason ?? '');
}
});
});
form.addRow(row => {
row.addRadioButton('resolution', {
label: 'Preferred Resolution',
options: [
{ id: 'refund', name: 'Refund' },
{ id: 'exchange', name: 'Exchange' },
{ id: 'store-credit', name: 'Store Credit (+10%)' }
],
isRequired: true
});
});
form.addRow(row => {
row.addRadioButton('condition', {
label: 'Item Condition',
options: [
{ id: 'unopened', name: 'Unopened' },
{ id: 'like-new', name: 'Like new' },
{ id: 'used', name: 'Used' }
],
isRequired: true
});
});
form.addRow(row => {
row.addRadioButton('returnMethod', {
label: 'Return Method',
options: [
{ id: 'mail', name: 'Ship back' },
{ id: 'dropoff', name: 'Store dropoff' }
],
defaultValue: 'mail'
});
});Workflow Considerations
Auto-Approve Simple Returns
"No longer needed" with "unopened" condition and standard refund? Auto-approve and send the shipping label immediately. No human review needed for straightforward cases.
Flag for Review
High-value items, repeat returners, or unusual patterns should route to a human. The form data helps them make quick decisions.
Integration Points
Connect to your order system to validate orders, check return windows, and look up original payment method. Connect to shipping to generate labels automatically.
Best Practices
Check Return Window
Validate against purchase date. If they're outside the return window, say so immediately rather than letting them fill out the whole form.
Show Policy Summary
"Returns accepted within 30 days. Original packaging preferred. Refunds processed in 5-7 business days." Set expectations upfront.
Confirm Everything
Send detailed confirmation: what they're returning, why, what resolution they chose, what happens next, and timeline. Include return instructions or shipping label.
Make Status Trackable
Give them a return request number. Let them check status online. Send updates when you receive the item and process the refund.
Common Questions
How do I handle partial refunds?
Calculate refund amount based on condition and reason. Show the calculation to the customer before they submit. Be clear about what affects the refund amount - restocking fees, missing items, damage, etc.
What about items that can't be returned?
Check the item type against your non-returnable list (custom items, final sale, hygiene products). If non-returnable, show a message explaining why instead of the return form. Offer alternatives like warranty claims or exchanges.
How do I prevent return fraud?
Track return patterns per customer. Flag unusual activity (high return rate, expensive items, claims of damage). Require photos for damage claims. Verify order ownership with email match. For high-value returns, require additional verification.
Should I require login to submit returns?
Optional. Login makes order lookup automatic and prevents some fraud. But requiring login adds friction and some customers won't have accounts. Order number + email verification is a good middle ground.