Use case
Automating Post-Purchase Review Request Emails
Turn every order into social proof with well-timed, automated review requests.
Use case
Turn every order into social proof with well-timed, automated review requests.
Customer reviews are one of the highest-leverage assets an e-commerce business can build. They serve three distinct purposes that compound over time:
The problem is that most customers don't leave reviews unprompted. Industry data suggests that fewer than 5% of buyers will voluntarily write a review. A well-timed email nudge pushes that number to 15-20%. The math is straightforward: if you're not asking, you're leaving reviews on the table.
The single most important variable in review request emails is when you send them. Too early, and the customer hasn't received (or used) the product yet. Too late, and the excitement of a new purchase has faded.
The sweet spot for most physical products is 24-72 hours after delivery, not after purchase. This distinction matters. A customer who ordered on Monday but won't receive their package until Friday shouldn't get a review request on Tuesday. That's annoying at best, and at worst it signals that you're not paying attention to their experience.
For digital products and SaaS, the timing shifts. You want to ask after the customer has had enough time to experience value — usually 3-7 days after signup or first meaningful usage. Asking for a review before the user has accomplished anything is a waste of everyone's time.
This means your review request system needs to be event-driven: triggered by a delivery confirmation webhook, a shipment tracking status change, or a usage milestone — not by a fixed delay from order creation.
Some small shops start by manually emailing customers after orders. This works when you're doing 5-10 orders a day. It stops working at 50. It's impossible at 500. Manual outreach doesn't scale, and the inconsistency means you'll forget to ask half your customers — usually the ones who had the best experience and would have left glowing reviews.
Shopify, WooCommerce, and BigCommerce all offer some form of automated post-purchase email. Shopify's built-in review request, for example, can be enabled in your notification settings. WooCommerce has the follow-up emails extension.
The limitations are predictable: rigid timing (usually a fixed delay from order date, not delivery date), minimal customization, no A/B testing, and no conditional logic. You can't say "send the review request 24 hours after delivery, but only if the customer hasn't already contacted support about this order." These tools are better than nothing, but they're a blunt instrument.
Services like Yotpo, Judge.me, Trustpilot, and Stamped.io provide full-featured review collection and display. They handle timing, templating, reminders, and review display widgets. Many integrate directly with Shopify and WooCommerce.
The trade-offs are cost and lock-in. Yotpo's paid plans start around $79/month and scale with order volume. Your reviews live on their platform, and migrating away means losing display widgets, SEO markup, and sometimes the reviews themselves. If your needs are straightforward and you're on a supported e-commerce platform, these tools work well. If you need custom logic or you're building on a custom stack, you're paying a premium for features you can't use.
The classic approach: write a script that queries your database for orders delivered in the last 24-72 hours that haven't been sent a review request yet, then sends the emails. Run it via cron every hour.
# crontab -e
0 * * * * /usr/bin/python3 /app/scripts/send_review_requests.py
# send_review_requests.py
import requests
import psycopg2
from datetime import datetime, timedelta
conn = psycopg2.connect(DATABASE_URL)
cursor = conn.cursor()
cursor.execute("""
SELECT o.id, o.customer_email, o.customer_name, p.name as product_name
FROM orders o
JOIN products p ON o.product_id = p.id
WHERE o.delivered_at < NOW() - INTERVAL '24 hours'
AND o.delivered_at > NOW() - INTERVAL '72 hours'
AND o.review_requested_at IS NULL
AND o.status = 'delivered'
""")
for order_id, email, name, product in cursor.fetchall():
requests.post("https://api.mailgun.net/v3/yourdomain.com/messages", data={
"from": "[email protected]",
"to": email,
"subject": f"How's your {product}, {name}?",
"template": "review-request",
"v:customer_name": name,
"v:product_name": product,
"v:review_url": f"https://yourstore.com/review/{order_id}",
})
cursor.execute(
"UPDATE orders SET review_requested_at = %s WHERE id = %s",
(datetime.utcnow(), order_id)
)
conn.commit() This works, and for a single-server setup it's perfectly adequate. The problems emerge over time: if the script fails silently (the server reboots, the cron daemon stops, the database connection times out), nobody notices until someone asks "why aren't we getting reviews anymore?" Monitoring cron jobs is a solved problem, but it's one more thing to maintain. You also need to handle idempotency carefully — what happens if the script runs twice in quick succession?
A more robust approach is to enqueue a delayed job when the delivery event occurs. When your system receives a delivery confirmation webhook, it schedules a job to run 24 hours later:
# Python + Celery example
from celery import Celery
app = Celery('reviews', broker='redis://localhost:6379/0')
@app.task
def send_review_request(order_id):
order = Order.objects.get(id=order_id)
if order.review_requested_at or order.has_support_ticket:
return # Skip if already sent or customer has an open issue
send_email(
to=order.customer_email,
template="review-request",
context={"order": order},
)
order.review_requested_at = datetime.utcnow()
order.save()
# Called when delivery webhook arrives
def on_delivery_confirmed(order_id):
send_review_request.apply_async(
args=[order_id],
countdown=86400, # 24 hours in seconds
) This is event-driven, supports retries, and task queues like Celery provide built-in monitoring via Flower. The cost is infrastructure: you need a message broker (Redis or RabbitMQ), worker processes, and the operational knowledge to keep them healthy. For teams already running Celery or Sidekiq, adding a review request task is trivial. For teams that aren't, spinning up a task queue just for review emails is overkill.
HTTP job schedulers like Recuro take a different approach: instead of running your own queue infrastructure, you schedule an HTTP request to be made at a specific time. When the delivery webhook arrives, your backend makes a single API call to schedule the review request:
# When delivery webhook arrives
import requests
def on_delivery_confirmed(order_id, customer_email):
requests.post("https://api.recurohq.com/api/jobs", json={
"queue": "review-requests",
"url": "https://yourapp.com/webhooks/send-review-email",
"method": "POST",
"headers": {
"Authorization": "Bearer YOUR_INTERNAL_SECRET",
"Content-Type": "application/json"
},
"payload": {
"order_id": order_id,
"customer_email": customer_email
},
"delay": 86400 # 24 hours
}, headers={
"Authorization": "Bearer YOUR_RECURO_TOKEN"
})
Your app still has a /webhooks/send-review-email endpoint that does the actual
sending — that logic doesn't change. What changes is that you don't need to manage cron jobs,
task queue workers, or message brokers. The scheduler handles the "run this later" part, including
retries if your endpoint is temporarily down. Services like Recuro also provide execution logs and
failure alerts, so you know immediately if something breaks.
This approach works well for teams that want event-driven scheduling without the infrastructure overhead. The trade-off is an external dependency and per-request costs, though for most e-commerce volumes the free tiers are more than sufficient.
"Hi {first_name}, how's your {product_name}?" is table stakes. Better review request emails reference specific details: the color or variant they chose, how long they've had it, or what category it falls into. A customer who bought running shoes should get a different email than one who bought a desk lamp. The ask is the same, but the framing should match the product experience.
If a customer places three orders in a month, they should not receive three review request emails. Implement a per-customer cooldown — typically 30 days between review requests. Pick the highest-value or most-reviewed product to ask about. This requires tracking review requests at the customer level, not just the order level.
The review email should contain a direct link to a review form, ideally pre-populated with the product and order details. Every extra click between "open email" and "submit review" loses a significant chunk of respondents. Some brands embed a star rating directly in the email — the customer clicks a star, lands on a form with the rating pre-filled, and just needs to add text.
Review request emails must include an unsubscribe mechanism. Beyond legal compliance (CAN-SPAM, GDPR), it's practical: a customer who doesn't want review emails but can't opt out will either mark you as spam (hurting deliverability for everyone) or leave an angry review. Neither outcome is what you want.
Before sending a review request, check whether the customer has an open support ticket, has initiated a return, or has already left a review. Asking someone who's mid-dispute to review your product is a reliable way to get a one-star rating. Your review request logic should query against these conditions before sending.
review_requested_at timestamp or a unique constraint to prevent duplicates.The right solution depends on your current stack and order volume:
| Approach | Best for | Watch out for |
|---|---|---|
| Platform built-in | Small shops on Shopify/Woo, simple needs | Limited timing control, no conditional logic |
| Third-party review platform | Brands prioritizing review display & SEO | Cost scales with volume, vendor lock-in |
| Custom cron job | Small teams with server access, simple logic | Silent failures, manual monitoring |
| Task queue (Celery, Sidekiq) | Teams already running queue infrastructure | Operational overhead if you don't already have it |
| HTTP scheduler (Recuro, etc.) | Event-driven timing without infra overhead | External dependency, requires webhook endpoint |
None of these approaches is universally correct. The best choice is the one that fits your team's existing infrastructure and the complexity of your review request logic. Start simple, measure your review collection rate, and add sophistication only when the data justifies it.
Recuro handles scheduling, retries, alerts, and execution logs. 1,000 free requests to start.
No credit card required