Order Confirmation Emails: Templates and Examples

Editorial Note: We may earn a commission when you visit links on our website.
Order Confirmation Emails

An order confirmation email is the one message every customer actually goes looking for. It’s also the easiest one to break without anyone telling you.

Most ecommerce platforms generate one by default. The default is usually a bare receipt, and it fails silently when your sending domain isn’t authenticated.

In this guide, you’ll learn what to put in an order confirmation email, with 6 examples and 4 copy-paste templates.

You’ll also learn how to send them over an API or SMTP, and how to fix confirmations that never arrive. Let’s get started.

What is an order confirmation email?

An order confirmation email is an automatic message that confirms you’ve received and accepted a customer’s order. It goes out within seconds of checkout and summarizes what they bought, what they paid, and what happens next.

It’s a transactional email, not a marketing email. The customer triggered it by buying something, so you don’t need their marketing consent to send it.

Order confirmations sit at the front of a 3-email sequence. Each one answers a different question.

The 3-email post-purchase sequence

Is an order confirmation email the same as a receipt?

No. A receipt proves payment, while a confirmation proves the order exists and is being worked on. Most stores combine both in one email, which is fine, but they answer different questions.

Order confirmationReceipt
AnswersDid my order go through?What did I pay?
SentRight after checkoutAfter payment clears
Must includeOrder number, items, next stepsAmount, tax, payment method
LegallyNot required in the USOften required for tax records

If you sell in the EU or UK, check your local consumer rules. Some require written confirmation of a distance sale.

What should an order confirmation email include?

Every order confirmation email needs 8 elements. Miss any of them, and you’ll field questions the email should have answered.

ElementWhy it mattersExample
Order numberThe reference for every future support messageOrder #SL-48211
Itemized summaryLets the customer verify what they actually bought2 × Ceramic Mug, Slate
Price breakdownPrevents “why was I charged more” ticketsSubtotal, tax, shipping, total
Payment methodConfirms which card was used, last 4 digits onlyVisa ending 4429
Shipping addressTheir last chance to catch a typo14 Oak Lane, Austin, TX
Delivery windowCuts “where is my order” messagesArrives Aug 8 to Aug 12
Support linkGives them a route that isn’t a reply-allContact support
Returns policy linkAnswers the second question every buyer has30-day returns
Anatomy of an order confirmation email

Two rules about what goes where.

Put the order number and summary at the top. Customers scan on mobile and most never reach the footer.

Never include full card numbers or the CVV. Last 4 digits is the standard, and anything more is a liability you don’t need.

Warning: Don’t email a full payment card number, expiry date, or CVV. Storing or transmitting that data puts you outside PCI DSS compliance.

What makes a good order confirmation email subject line?

The best order confirmation email subject lines lead with the order number so the email is searchable months later. Customers find old orders by searching their inbox, and they search for a number.

Keep it under roughly 50 characters. Mobile clients truncate longer subject lines, and the order number is what gets cut.

Here are 10 subject lines that work:

  1. Order #48211 confirmed
  2. We got your order, Sarah (#48211)
  3. Your Northside Goods order #48211
  4. Order #48211: thanks, we’re on it
  5. Confirmed: order #48211 is being packed
  6. Your order’s in. Here are the details (#48211)
  7. Order #48211 received, arriving Aug 12
  8. Thanks for your order, #48211
  9. Order confirmed. What happens next (#48211)
  10. #48211 is confirmed and paid

Skip words like “free”, “act now”, and rows of exclamation marks. Promotional language in a transactional email raises your spam-filter risk for no gain.

Pro Tip: Put the customer’s first name and the order number in the same subject line. It reads personal and stays searchable.

Emoji are brand-dependent, not a universal win. A homeware store can open with a box emoji. A pharmacy shouldn’t.

What do the best order confirmation emails look like?

The best order confirmation emails match the thing being bought. A physical parcel, a downloadable file, and a delayed preorder each need a different email.

Below are 6 scenarios and the changes in each. Copy the pattern that matches what you sell.

1. What does a standard retail confirmation need?

The default case, and the one every template is built for. Lead with the order number, then the itemized table, then the delivery window.

What to copy: a single clear delivery date range in the top third of the email. It’s the one thing customers look for.

Standard retail order confirmation email example

2. What changes for a digital product or download?

Nothing ships, so access replaces the delivery window. Put the download link or license key above the price breakdown.

What to copy: state how long the download link stays valid. Expired links are the top support ticket for digital sellers.

Note: Send digital access in the confirmation itself. Making the customer log in to find their file adds a step and a support ticket.

3. What should a subscription confirmation say?

The customer needs to know this will happen again. Name the renewal date and the amount in the confirmation, not just in the terms.

What to copy: one line stating the next charge date and how to cancel. It reduces chargebacks, which cost more than churn.

4. How do you confirm a preorder or backordered item?

The order is confirmed but the stock isn’t. Say so in the subject line, not buried in paragraph 3.

What to copy: an honest expected ship date and a promise to email if it slips. Vague language here converts into refund requests.

Preorder confirmation email example

5. How do you handle a multi-seller order?

One order splits into several shipments from different sellers. Group items by seller and give each group its own delivery window.

What to copy: a per-shipment breakdown. One combined delivery date for items arriving 2 weeks apart guarantees a complaint.

6. What does a service or appointment confirmation include?

Nothing ships at all, so the confirmation carries a time and a place. Add a calendar file so the appointment lands in their calendar.

What to copy: attach an .ics file and restate the cancellation window. Both cut no-shows.

How to write an order confirmation email

Start with a template, then replace the dynamic fields with your platform’s variables. Below are 4 templates you can paste and edit.

Merge field names differ by platform. WooCommerce uses {order_number}, Shopify uses {{ order_name }}, and a custom app uses whatever you define.

Simple order confirmation email template

Use this when you sell one type of physical product and ship it yourself.

Subject: Order #{{order_number}} confirmed

Hi {{first_name}},

Thanks for your order. We've got it and we're packing it now.

Order #{{order_number}}
Placed {{order_date}}

{{items_table}}

Subtotal   {{subtotal}}
Shipping   {{shipping_cost}}
Tax        {{tax}}
Total      {{order_total}}

Paid with {{payment_method_last4}}

Shipping to:
{{shipping_address}}

Arriving {{delivery_window}}

Need to change something? Reply to this email within 2 hours
and we'll sort it out before it ships.

{{store_name}}
{{support_url}} | {{returns_url}}

Order confirmation template with shipping details

Use this when delivery timing is the customer’s main concern.

Subject: Order #{{order_number}} received, arriving {{delivery_date}}

Hi {{first_name}},

Your order is confirmed and moving.

Order #{{order_number}}      Placed {{order_date}}
Delivery estimate            {{delivery_window}}
Shipping method              {{shipping_method}}

{{items_table}}

Total {{order_total}} (paid with {{payment_method_last4}})

Shipping to:
{{shipping_address}}

We'll email a tracking number the moment it leaves our warehouse.
Usually that's within 1 business day.

Questions about delivery? {{support_url}}

Digital product confirmation template

Use this for downloads, licenses, and course access.

Subject: Your {{product_name}} download is ready (#{{order_number}})

Hi {{first_name}},

Your order's confirmed and your files are ready.

Download {{product_name}}: {{download_url}}
Your license key: {{license_key}}

The link works for {{link_expiry_days}} days and {{download_limit}}
downloads. Need it again after that? Just reply to this email.

Order #{{order_number}}
Total {{order_total}}, paid with {{payment_method_last4}}

Trouble opening the file? {{support_url}}

What should a backordered item template say?

Use this when you’ve taken payment but can’t ship yet.

Subject: Order #{{order_number}} confirmed, ships {{expected_ship_date}}

Hi {{first_name}},

Your order's confirmed and paid. One item is on backorder,
so here's exactly what's happening.

{{items_table}}

{{backordered_item}} is expected back in stock
{{expected_ship_date}}. We'll ship your full order then.

Want the in-stock items now instead? Reply and we'll split
the shipment at no extra cost.

Order #{{order_number}}
Total {{order_total}}

If {{expected_ship_date}} changes, we'll email you the same day.

Pro Tip: Write your templates in plain text first, then style them. If the message doesn’t work as plain text, no amount of HTML will save it.

How to send order confirmation emails automatically

Order confirmation emails should be triggered once a user makes a purchase. Ecommerce platforms like WooCommerce and Shopify automatically handle this. So you’ll only need to worry about email deliverability.

Your web host’s default mail function isn’t built for this and will hurt your delivery rate.

Here’s why that matters. Shared hosting sends from an IP shared with hundreds of other sites. And mail() gives you no authentication, no logs, and no bounce data.

An email service handles authentication, retries, and delivery logs. When a confirmation goes missing, you can see what happened.

What do you need before you start?

You’ll need 4 things:

  1. A SendLayer account (the free trial covers 200 emails)
  2. Your sending domain authorized in SendLayer
  3. Your API key, from Settings » API Keys
  4. Node.js 18+ or PHP 8.1+, depending on which example you follow
  • 200 Free Emails
  • Easy Setup
  • 5 Star Support

Before proceeding, make sure to authorize your sending domain. This step is important for proving domain ownership and improving email deliverability.

How do you build the email template?

Build the HTML once, then inject order data at send time. Keep it to a single column and inline your CSS, because email clients strip blocks.

<!-- order-confirmation.html -->
<table width="100%" cellpadding="0" cellspacing="0" style="background:#f5f5f5;padding:24px 0;">
  <tr>
    <td align="center">
      <table width="600" cellpadding="0" cellspacing="0"
             style="background:#ffffff;font-family:Arial,sans-serif;color:#222;">
        <tr>
          <td style="padding:24px;">
            <h1 style="font-size:20px;margin:0 0 8px;">Order #{{orderNumber}} confirmed</h1>
            <p style="font-size:15px;line-height:1.5;margin:0 0 20px;">
              Hi {{firstName}}, thanks for your order. We're packing it now.
            </p>

            <!-- Order summary. Rows are injected by the send script. -->
            <table width="100%" cellpadding="8" cellspacing="0"
                   style="border-collapse:collapse;font-size:14px;">
              {{itemRows}}
              <tr>
                <td style="border-top:2px solid #222;"><strong>Total</strong></td>
                <td style="border-top:2px solid #222;" align="right">
                  <strong>{{orderTotal}}</strong>
                </td>
              </tr>
            </table>

            <p style="font-size:14px;line-height:1.5;margin:20px 0 0;">
              Shipping to:<br>{{shippingAddress}}
            </p>
            <p style="font-size:14px;margin:12px 0 0;">
              Arriving <strong>{{deliveryWindow}}</strong>
            </p>
            <p style="font-size:13px;color:#666;margin:24px 0 0;">
              Questions? <a href="{{supportUrl}}" style="color:#0b63c5;">Contact support</a>
            </p>
          </td>
        </tr>
      </table>
    </td>
  </tr>
</table>

Use a single-column layout at 600px wide. It’s the width that renders reliably across Outlook, Gmail, and Apple Mail.

How to send an order confirmation with an email API

Call the API from your checkout handler once the payment succeeds. This Node.js example uses SendLayer’s SDK.

Install it first:

npm install sendlayer

Then build and send the confirmation:

// send-confirmation.js
import { SendLayer } from 'sendlayer';
import fs from 'node:fs';

// Never hardcode the key. Load it from the environment.
const sendlayer = new SendLayer(process.env.SENDLAYER_API_KEY);

const template = fs.readFileSync('./order-confirmation.html', 'utf8');

First, we import the SendLayer client and Node’s fs module for file operations. You’ll need to initialize the SDK client with your SendLayer API key.

Next, the HTML email template is read from disk into memory using readFileSync.

// send-confirmation.js
import { SendLayer } from 'sendlayer';
import fs from 'node:fs';

// Never hardcode the key. Load it from the environment.
const sendlayer = new SendLayer(process.env.SENDLAYER_API_KEY);

const template = fs.readFileSync('./order-confirmation.html', 'utf8');

function money(cents) {
  return `$${(cents / 100).toFixed(2)}`;
}

// Escape order data before it goes into HTML.
function esc(value) {
  return String(value).replace(/[&<>"']/g, (c) => ({
    '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;',
  }[c]));
}

function renderItems(items) {
  return items.map((item) => `
    <tr>
      <td style="border-bottom:1px solid #eee;">
        ${esc(item.quantity)} &times; ${esc(item.name)}
      </td>
      <td style="border-bottom:1px solid #eee;" align="right">
        ${money(item.unitPriceCents * item.quantity)}
      </td>
    </tr>`).join('');
}

export async function sendOrderConfirmation(order) {
  const html = template
    .replace('{{itemRows}}', renderItems(order.items))
    .replace(/{{orderNumber}}/g, esc(order.number))
    .replace('{{firstName}}', esc(order.customer.firstName))
    .replace('{{orderTotal}}', money(order.totalCents))
    .replace('{{shippingAddress}}', esc(order.shippingAddress).replace(/\n/g, '<br>'))
    .replace('{{deliveryWindow}}', esc(order.deliveryWindow))
    .replace('{{supportUrl}}', esc(order.supportUrl));

  try {
    const response = await sendlayer.Emails.send({
      from: { name: 'Northside Goods', email: '[email protected]' },
      to: { name: order.customer.fullName, email: order.customer.email },
      subject: `Order #${order.number} confirmed`,
      html,
      // Plain-text fallback for clients that don't render HTML.
      text: `Order #${order.number} confirmed. Total ${money(order.totalCents)}.`,
    });
    return { sent: true, id: response?.MessageID ?? null };
  } catch (error) {
    // Log and queue a retry. Never block the checkout response on email.
    console.error(`Confirmation failed for order ${order.number}:`, error.message);
    return { sent: false, error: error.message };
  }
}

The money function converts cent values into formatted dollar strings. The esc function sanitizes user input by replacing special HTML characters with their escaped equivalents, preventing XSS attacks when injecting data into the template.

function renderItems(items) {
  return items.map((item) => `
    <tr>
      <td style="border-bottom:1px solid #eee;">
        ${esc(item.quantity)} &times; ${esc(item.name)}
      </td>
      <td style="border-bottom:1px solid #eee;" align="right">
        ${money(item.unitPriceCents * item.quantity)}
      </td>
    </tr>`).join('');
}

The renderItems function maps each order item into an HTML table row, displaying the quantity, product name, and calculated line total. All values are sanitized with esc() before being inserted into the markup.

export async function sendOrderConfirmation(order) {
  const html = template
    .replace('{{itemRows}}', renderItems(order.items))
    .replace(/{{orderNumber}}/g, esc(order.number))
    .replace('{{firstName}}', esc(order.customer.firstName))
    .replace('{{orderTotal}}', money(order.totalCents))
    .replace('{{shippingAddress}}', esc(order.shippingAddress).replace(/\n/g, '<br>'))
    .replace('{{deliveryWindow}}', esc(order.deliveryWindow))
    .replace('{{supportUrl}}', esc(order.supportUrl));

  try {
    const response = await sendlayer.Emails.send({
      from: { name: 'Northside Goods', email: '[email protected]' },
      to: { name: order.customer.fullName, email: order.customer.email },
      subject: `Order #${order.number} confirmed`,
      html,
      // Plain-text fallback for clients that don't render HTML.
      text: `Order #${order.number} confirmed. Total ${money(order.totalCents)}.`,
    });
    return { sent: true, id: response?.MessageID ?? null };
  } catch (error) {
    // Log and queue a retry. Never block the checkout response on email.
    console.error(`Confirmation failed for order ${order.number}:`, error.message);
    return { sent: false, error: error.message };
  }
}

The sendOrderConfirmation function populates the HTML template by replacing placeholders like {{orderNumber}} and {{firstName}} with sanitized order data. It uses a regex for {{orderNumber}} since it appears multiple times in the template.

The email is then sent via sendlayer.Emails.send() with sender details, recipient information, subject line, the rendered HTML, and a plain-text fallback for email clients that don’t support HTML.

The API key comes from process.env, so it never reaches your repo. The esc() helper escapes order data before it is rendered as HTML. That blocks injection through a malicious product name. And the catch block logs a failed send instead of breaking checkout.

Important: Never make your checkout response wait on the email send. Queue it, then return the order confirmation page immediately.

The example above uses the Node.js SDK provided by SendLayer. If you’re using a different programming language or framework, here’s a cURL example:

curl --request POST \
  --url https://console.sendlayer.com/api/v1/email \
  --header "Authorization: Bearer $SENDLAYER_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
    "From": { "name": "Northside Goods", "email": "[email protected]" },
    "To": [ { "name": "Sarah Chen", "email": "[email protected]" } ],
    "Subject": "Order #48211 confirmed",
    "ContentType": "HTML",
    "HTMLContent": "<h1>Order #48211 confirmed</h1><p>Thanks for your order.</p>"
  }'

How to fix confirmations in WooCommerce or Shopify

Both platforms send confirmations by default, so the usual problem is delivery, not configuration. Route their mail through an authenticated provider instead of the server’s built-in mailer.

For WooCommerce, order emails live under WooCommerce » Settings » Emails. Check that Processing order is enabled and the recipient and sender addresses are right.

WooCommerce email notification settings

Then install an SMTP plugin so WordPress stops using wp_mail() defaults. Our guide on customizing WooCommerce emails covers the template side.

For Shopify, notification templates sit under Settings » Notifications. Shopify sends from its own infrastructure, so add your domain there and complete the DNS records it asks for.

Want to route it through your own provider instead? Our guide on sending email from Shopify with SMTP covers the setup.

Why am I not getting order confirmation emails?

Order confirmation emails usually go missing for 1 of 5 reasons, and only 2 are on the customer’s side. Work through them in order.

Sender-side causes:

  1. Your domain isn’t authenticated. No SPF or DKIM means inboxes can’t verify you, so mail goes to spam or gets rejected.
  2. You’re sending through your web host. Shared hosting IPs carry other people’s spam reputation.
  3. The send silently failed. Without logs, a rejected message looks identical to a delivered one.

Recipient-side causes:

  1. A typo at checkout. The address doesn’t exist, so the message hard bounces.
  2. It landed in spam or a promotions tab. Common when the confirmation reads like an ad.
Flowchart for diagnosing a missing order confirmation email

Frequently Asked Questions

These are answers to some of the most common questions we receive about order confirmation emails.

How soon should an order confirmation email be sent?

Within seconds of a successful payment. Customers watch their inbox right after checkout, and a delay past a few minutes reads as a failed order. Trigger the send from your payment success handler, not from a scheduled job.

Is an order confirmation email legally required?

Not in the US. CAN-SPAM treats confirmations as transactional messages, so it only requires accurate header and routing information. Some EU and UK distance-selling rules do require written confirmation, so check your local rules.

Can I include marketing content in an order confirmation email?

Yes, but keep it small and keep it low. Once promotional content becomes the message’s primary purpose, it’s a commercial email and CAN-SPAM’s full rules apply. One recommendation block below the order details is safe.

What’s the difference between an order confirmation and a shipping confirmation?

An order confirmation says you received the order. A shipping confirmation says the parcel has left and includes tracking. They’re separate emails sent at different times, and combining them means the customer waits days for any acknowledgment.

How do I resend an order confirmation email?

Most platforms have a resend action on the order record. In WooCommerce, open the order and pick the email under Order actions. In Shopify, use Resend email on the order page. If you built the flow yourself, keep the rendered email or its data so you can send it again.

That’s it! You now know what belongs in an order confirmation email and how to send one that actually arrives.

Next, make sure the rest of your store’s email is just as reliable. Read our guide on transactional email to see which messages you should be sending and how they differ.

  • 200 Free Emails
  • Easy Setup
  • 5 Star Support

Ready to send your emails in the fastest and most reliable way? Get started today with the most user-friendly and powerful SMTP email delivery service. SendLayer Business includes 5,000 emails a month with premium support.

author avatar
David Ozokoye
David is a technical writer at SendLayer. He tests and documents new features and updates to SendLayer's email services. Away from the computer, he enjoys playing video games and roller skating.