Order confirmation email for online stores
Updated
The order confirmation is the most opened email an online store sends. Customers use it as their receipt, their reference number and their proof of the delivery address.
What it must contain
- Order number and date
- Items, quantities and prices
- Totals, tax and shipping
- Delivery address and method
- A way to contact you or view the order
When it fires
When the order is created and payment is confirmed. For pay-later methods, send it at creation and say payment is pending.
Implementation (Node.js 18+)
Merge tags substitute single values, so render the line items into HTML yourself:
const esc = (s) => String(s).replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c]));
export async function sendOrderConfirmation(order) {
const rows = order.items
.map((i) => `<tr><td>${esc(i.name)}</td><td>${i.qty}</td><td>${esc(i.price)}</td></tr>`)
.join("");
const res = await fetch("https://api.inboxili.com/api/v1/transactional/send", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.INBOXILI_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
to: order.email,
from_email: "orders@yourdomain.com",
from_name: "Acme Store",
subject: "Order {{order_number}} confirmed",
html_body:
"<h1>Thanks for your order</h1><p>Order {{order_number}}</p>" +
"<table><tr><th>Item</th><th>Qty</th><th>Price</th></tr>{{rows}}</table>" +
"<p><strong>Total {{total}}</strong></p><p>Shipping to: {{address}}</p>",
template_data: {
order_number: order.number,
rows, // already escaped above
total: order.total,
address: esc(order.shippingAddress),
},
}),
});
if (!res.ok) throw new Error(`Inboxili ${res.status}: ${(await res.text()).slice(0, 200)}`);
return (await res.json()).message_id;
}
Escaping matters. Item names and addresses are user-controlled text going into HTML.
Sample email
Subject: Order 10482 confirmed Thanks for your order. 2 × Linen shirt, 1 × Canvas tote. Total $112.00. Shipping to: 12 Market St.
Things that go wrong
- Unescaped user text in item names or addresses.
- Sending before stock reservation succeeds.
- Hundreds of confirmations during a sale hitting the 120 requests per minute key limit. Queue and pace them.
- Missing text part. Provide
text_body; remember merge tags are not applied to it.
Delivery events
Use bounced to flag orders whose confirmation did not arrive, so support can reach the customer another way before they contact you.
Deliverability notes
Keep marketing banners out of the confirmation. Cross-sell belongs in a separate, clearly promotional message sent from campaigns.
Frequently asked questions
- Can I send a list of items with merge tags?
- Merge tags substitute plain values, so build the items table as an HTML string in your code and pass it as one value. Values are converted to text and placed into the template as-is, so escape any user-supplied text first.
Send an order confirmation
Create a workspace, verify a domain, and make your first API call.