A reliable WooCommerce email integration does more than replace a store’s default mail transport. It lets you use the order data WooCommerce already creates, decide exactly which customer event deserves an email, and send through a transactional provider without exposing credentials in a browser or storefront theme.

WooCommerce does not provide a native Volanea marketplace extension. The practical integration is WooCommerce’s built-in webhook system plus a small server-side endpoint that validates the webhook, maps the order payload, and calls Volanea’s REST email API. That approach is flexible: it works whether the receiving application is a WordPress-adjacent service, a Node server, a serverless function, or an existing backend.

What this integration does

This setup has three separate jobs:

  1. WooCommerce detects an order event. In this guide, the event is the native Order created webhook topic, represented as order.created in the webhook headers.
  2. Your middleware receives and verifies the order webhook. It checks that the request genuinely came from WooCommerce and extracts safe, expected fields such as the order ID, billing email, billing first name, total, currency, and items.
  3. The middleware sends an email through Volanea. The Volanea API key stays in a server-side environment variable, and the API call creates the transactional email.

That separation is important. WooCommerce webhooks are designed to notify another HTTP endpoint about store events. They do not offer a built-in field-transformation editor that can turn an order object into a provider-specific email request, nor should a secret sending key be placed in a storefront script.

The result can be an order-received note, a payment confirmation, a fulfillment update, a backorder message, a post-purchase onboarding email, or an internal operations alert. The same pattern applies to each, but the event and deduplication rules must change with the message’s purpose.

Why use a webhook relay rather than browser code

A customer’s checkout page is not a secure place to call an email API. JavaScript, page source, browser developer tools, network requests, and bundled configuration can all expose values sent to the client. A Volanea API key authorizes sending, so it belongs only in infrastructure you control.

A WooCommerce webhook relay also gives you a dependable boundary between store data and email data. It can validate signatures, reject malformed payloads, normalize product names, guard against duplicates, record a delivery decision, and return a fast HTTP success response to WooCommerce.

The data path

For an order.created workflow, the path is:

  • A shopper completes checkout and WooCommerce creates an order.
  • WooCommerce queues a webhook delivery to the Delivery URL you configured.
  • Your endpoint receives the JSON body and X-WC-Webhook-* headers.
  • The endpoint verifies the HMAC signature using the webhook secret.
  • It uses billing.email as the recipient and constructs a Volanea email request.
  • Volanea accepts the request and returns an API response.
  • Your endpoint stores the WooCommerce delivery ID and order/message decision, then returns a 2xx response.

The middleware is not optional merely because the email itself is simple. It is where credentials, validation, idempotency, and observability belong.

Choose the WooCommerce trigger carefully

WooCommerce’s Webhooks screen lets a store owner create webhooks for resource events. For this guide, create a webhook with the topic Order created. WooCommerce identifies the topic on delivery with the X-WC-Webhook-Topic: order.created header.

In the WordPress admin, the usual configuration location is WooCommerce > Settings > Advanced > Webhooks. Create a webhook, set its status to Active, select the Order created topic, add your HTTPS middleware URL as the Delivery URL, and set a long, random Secret. The secret is shared only by WooCommerce and your receiving service; it is not the Volanea API key.

What “Order created” actually means

An order-created event means WooCommerce created the order record. It does not universally mean that payment has settled, stock has shipped, or a manual review has passed. Depending on payment method and store configuration, the initial status may be pending, on-hold, processing, or another valid order status.

That distinction determines what email should be sent:

Message goalSuitable event strategyImportant condition
Order received acknowledgementorder.createdClearly state that payment or fulfillment may still be pending.
Payment-related messageOrder update webhook or an application eventSend only when the order status has reached the store’s approved paid state.
Shipment notificationFulfillment/shipping system eventInclude a tracking URL only after it exists.
Failed-payment recoveryOrder update workflowAvoid sending when a payment is simply delayed.
Internal new-order alertorder.createdSend to an internal mailbox, not the billing address.

If your business rule is “send only after payment,” do not treat order.created as a payment-confirmed trigger. Use a status-aware workflow instead. WooCommerce can emit order-update webhooks, but an update is broad: address edits, notes, payment changes, and status changes may all update an order. Your relay must compare the incoming status value against the precise statuses your store treats as eligible.

Understand the WooCommerce webhook payload

WooCommerce sends a JSON representation of the resource involved in the event. For an order webhook, it is an order object, not a purpose-built email payload. It contains considerably more information than an email normally needs, including customer address data, payment metadata, line items, taxes, shipping lines, coupons, and links.

A representative order.created payload has this shape. Values and optional fields vary by store, payment method, checkout fields, extensions, and WooCommerce version.

{
  "id": 1234,
  "parent_id": 0,
  "status": "processing",
  "currency": "USD",
  "version": "9.0.0",
  "prices_include_tax": false,
  "date_created": "2026-09-25T10:30:00",
  "date_modified": "2026-09-25T10:30:12",
  "discount_total": "0.00",
  "shipping_total": "8.00",
  "total": "48.00",
  "customer_id": 0,
  "payment_method": "stripe",
  "payment_method_title": "Credit Card",
  "transaction_id": "ch_example",
  "billing": {
    "first_name": "Avery",
    "last_name": "Ng",
    "company": "",
    "address_1": "100 Market Street",
    "address_2": "",
    "city": "San Francisco",
    "state": "CA",
    "postcode": "94105",
    "country": "US",
    "email": "avery@example.com",
    "phone": "555-0100"
  },
  "shipping": {
    "first_name": "Avery",
    "last_name": "Ng"
  },
  "line_items": [
    {
      "id": 77,
      "name": "Studio Mug",
      "product_id": 44,
      "variation_id": 0,
      "quantity": 2,
      "tax_class": "",
      "subtotal": "40.00",
      "subtotal_tax": "0.00",
      "total": "40.00",
      "total_tax": "0.00",
      "sku": "MUG-STUDIO",
      "price": 20
    }
  ],
  "shipping_lines": [],
  "fee_lines": [],
  "coupon_lines": [],
  "refunds": [],
  "payment_url": "https://store.example.com/checkout/order-pay/1234/",
  "is_editable": false,
  "needs_payment": false,
  "needs_processing": true
}

Treat this as untrusted input even after signature verification. Signature verification establishes that the request was made by a party holding the webhook secret; it does not guarantee that every optional field is present, formatted as expected, or suitable for a customer-facing message.

For example, guest orders can have customer_id: 0. Physical-product stores may have shipping fields, while virtual orders may not. A billing email can be absent or invalid if a store’s custom checkout process permits it. Product names are merchant-controlled text and should be HTML-escaped before insertion into an HTML template.

Create the secure receiving endpoint

The example below uses Node.js with Express. It receives the native WooCommerce webhook, verifies the X-WC-Webhook-Signature header against the exact raw request body, uses the WooCommerce delivery ID for idempotency, and sends a mapped transactional email through Volanea.

Install the dependencies:

npm install express

Set these environment variables on the server or in your platform’s encrypted secret manager. Do not commit them to a repository or place them in a WordPress theme, browser bundle, checkout block setting, or public environment variable.

WC_WEBHOOK_SECRET="a-long-random-webhook-secret"
VOLANEA_API_KEY="your-volanea-api-key"
VOLANEA_FROM="Orders <orders@updates.example.com>"
PORT="3000"

Here is a complete minimal endpoint. It uses POST https://api.volanea.com/v1/email/send, sends the Volanea API key in the server-side Authorization header, and maps WooCommerce order fields into the email request.

import crypto from "node:crypto";
import express from "express";

const app = express();

// In production, replace this in-memory Set with a database table or Redis.
// Store successful delivery IDs durably with a unique constraint.
const processedDeliveryIds = new Set();

function safeEqual(left, right) {
  const a = Buffer.from(left || "", "utf8");
  const b = Buffer.from(right || "", "utf8");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

function escapeHtml(value = "") {
  return String(value)
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#039;");
}

app.post("/webhooks/woocommerce/order-created", express.raw({ type: "application/json" }), async (req, res) => {
  const rawBody = req.body;
  const signature = req.get("X-WC-Webhook-Signature");
  const topic = req.get("X-WC-Webhook-Topic");
  const deliveryId = req.get("X-WC-Webhook-Delivery-ID");

  const expectedSignature = crypto
    .createHmac("sha256", process.env.WC_WEBHOOK_SECRET)
    .update(rawBody)
    .digest("base64");

  if (!safeEqual(signature, expectedSignature)) {
    return res.status(401).json({ error: "Invalid WooCommerce webhook signature" });
  }

  if (topic !== "order.created") {
    return res.status(202).json({ ignored: true, reason: "Unexpected webhook topic" });
  }

  if (!deliveryId) {
    return res.status(400).json({ error: "Missing WooCommerce delivery ID" });
  }

  // A duplicate delivery must not create a second customer email.
  if (processedDeliveryIds.has(deliveryId)) {
    return res.status(200).json({ duplicate: true });
  }

  let order;
  try {
    order = JSON.parse(rawBody.toString("utf8"));
  } catch {
    return res.status(400).json({ error: "Invalid JSON payload" });
  }

  const recipient = order.billing?.email?.trim();
  if (!recipient || !recipient.includes("@")) {
    // Record this condition internally; do not retry an order that cannot be addressed.
    processedDeliveryIds.add(deliveryId);
    return res.status(200).json({ skipped: true, reason: "No usable billing email" });
  }

  const firstName = escapeHtml(order.billing?.first_name || "there");
  const orderNumber = escapeHtml(order.number || order.id);
  const currency = escapeHtml(order.currency || "");
  const total = escapeHtml(order.total || "");
  const itemRows = (order.line_items || []).map((item) => {
    const name = escapeHtml(item.name || "Item");
    const quantity = escapeHtml(item.quantity || 0);
    const lineTotal = escapeHtml(item.total || "");
    return `<li>${name} × ${quantity} — ${currency} ${lineTotal}</li>`;
  }).join("");

  const emailPayload = {
    from: process.env.VOLANEA_FROM,
    to: [recipient],
    subject: `We received your order #${orderNumber}`,
    html: `
      <p>Hi ${firstName},</p>
      <p>We received your order <strong>#${orderNumber}</strong>.</p>
      <ul>${itemRows}</ul>
      <p>Order total: <strong>${currency} ${total}</strong></p>
      <p>We will send another update when your order progresses.</p>
    `,
    text: `Hi ${order.billing?.first_name || "there"},\n\nWe received your order #${order.number || order.id}.\nOrder total: ${order.currency || ""} ${order.total || ""}.`,
    headers: {
      "X-WooCommerce-Order-ID": String(order.id),
      "X-WooCommerce-Delivery-ID": deliveryId
    }
  };

  const volaneaResponse = await fetch("https://api.volanea.com/v1/email/send", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.VOLANEA_API_KEY}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify(emailPayload)
  });

  if (!volaneaResponse.ok) {
    const responseText = await volaneaResponse.text();
    console.error("Volanea send failed", volaneaResponse.status, responseText);
    // Return non-2xx so WooCommerce can treat this delivery as unsuccessful.
    return res.status(502).json({ error: "Email provider request failed" });
  }

  const result = await volaneaResponse.json();
  processedDeliveryIds.add(deliveryId);
  console.log("Order email accepted", { orderId: order.id, deliveryId, message: result });

  return res.status(200).json({ accepted: true });
});

app.listen(process.env.PORT || 3000, () => {
  console.log("WooCommerce webhook relay is listening");
});

The field mapping is intentionally visible in emailPayload:

  • order.billing.email becomes Volanea’s to recipient.
  • order.id or order.number becomes the order reference in the subject and body.
  • order.line_items becomes a sanitized HTML item list.
  • order.currency and order.total become the total shown to the customer.
  • X-WooCommerce-Order-ID and X-WooCommerce-Delivery-ID are retained as message metadata headers for troubleshooting.

Before deploying, compare the request fields with the current email API reference and setup guides. In particular, use a verified sender identity for from, and keep the sending domain aligned with the domain customers expect to see in their inbox.

Configure WooCommerce to deliver the event

Once the endpoint is deployed at a public HTTPS URL, configure the webhook in WooCommerce. A local http://localhost address cannot receive delivery from a hosted store. During development, use an authenticated tunnel or a staging environment, then change the Delivery URL to production before enabling the production webhook.

Use a distinct webhook per purpose where the operational ownership differs. For example, an order-received email relay and an internal warehouse alert can be separate endpoints or separate routes. That makes it easier to apply different authorization, templates, retention rules, and failure alerts.

Test with a real order

Place a low-value test order in a staging store or a production store configured with a safe test payment method. Then inspect:

  1. The WooCommerce webhook delivery log and response code.
  2. Your middleware logs, including the WooCommerce delivery ID and order ID.
  3. The Volanea API response and message identifier.
  4. The receiving mailbox, including spam placement, sender display name, HTML rendering, and plain-text fallback.

Do not test only by manually posting a simplified JSON object. A manually generated request can miss headers, signature handling, unexpected line-item shapes, tax fields, empty strings, or encoding details that occur in actual WooCommerce delivery.

Keep credentials and customer data safe

There are two secrets in this design, and they solve different problems.

The WooCommerce webhook secret verifies the inbound request. WooCommerce uses it to create the HMAC-SHA256 signature in X-WC-Webhook-Signature; the middleware uses the same secret to verify the raw body. If the signature does not match, return an authorization error and do not parse or process the order as a trusted event.

The Volanea API key authorizes the outbound send. It should be stored as an encrypted environment secret in the middleware host. It must never be embedded in checkout JavaScript, a public WordPress option exposed through REST, a front-end build variable, a client-side analytics tag, or a webhook URL query parameter.

Minimize what the email contains

An order email usually needs less information than the full webhook carries. Avoid putting full delivery addresses, phone numbers, payment method details, transaction IDs, or customer metadata into logs or email headers unless there is a compelling operational reason.

Use the order ID as the correlation key. If a support agent needs details, they can retrieve the order inside WooCommerce under appropriate access controls. This reduces the chance that sensitive data leaks through logs, observability tools, error trackers, or forwarded messages.

Also validate the recipient. A syntactically plausible string is not proof that an inbox exists, and a checkout typo turns an order update into a delivery failure. For high-value flows, check addresses at collection time with an address verification tool, then still treat provider delivery events and customer support reports as the final signal.

When this breaks: failures specific to this hop

A WooCommerce-to-middleware-to-email path has more failure points than a direct PHP mail call, but it also makes those points visible. Design for the following cases before a large promotion or seasonal peak.

WooCommerce retries can cause duplicate sends

A webhook sender may retry after a connection error, timeout, or non-2xx response. A particularly awkward case is when the middleware successfully submits the email to Volanea but loses its connection before it can return 200 to WooCommerce. WooCommerce may deliver the same event again even though the first email was accepted.

The example uses X-WC-Webhook-Delivery-ID as a first line of defense, but an in-memory Set disappears during a restart and does not work across multiple server instances. In production, persist delivery IDs in a database or Redis with an atomic unique insert. Mark the delivery as processed only after the Volanea send is known to be accepted, and decide how to recover records left in an in-progress state.

For stronger business-level protection, store a second idempotency key such as order:{id}:order-received:v1. That catches logically duplicate notifications generated through separate webhook deliveries. Do not use only an email address as a key: one customer can legitimately place multiple orders.

Webhook timeouts and slow rendering

WooCommerce needs a timely HTTP response from the Delivery URL. If your endpoint performs slow template rendering, waits on several external services, sends a large batch, or has a cold-start delay, delivery can time out and be retried.

A robust production design validates the signature, writes an idempotent job record, returns a 2xx response quickly, and processes the Volanea send in a durable queue. The queue worker can retry temporary provider errors with bounded backoff. The job record prevents both the webhook handler and worker from sending the same logical message twice.

Do not return 200 before persisting enough information to recover the send. Otherwise, WooCommerce considers the webhook delivered while your application has no durable record of work to do.

Some payload fields can be empty or extension-dependent

Core order payloads include billing and line-item data, but stores customize checkout and order data extensively. A virtual product may have no shipping data. A guest checkout has no registered customer ID. A product can have an empty SKU. A custom field added by a plugin may not be available in the webhook representation you expected.

Build templates with fallbacks. If billing.first_name is blank, use “there.” If no line items are present, do not generate an empty list; include the order number and link to the order status page if your flow supports one. If billing email is missing, record a skipped event and alert the store team rather than repeatedly sending an invalid API request.

A valid webhook is not automatically a valid email

The webhook signature confirms origin, not email eligibility. Your relay should reject unsupported topics, validate the event’s business state, and make recipient selection explicit. For customer mail, do not accept a recipient from arbitrary request parameters; derive it from the verified order payload or an approved internal lookup.

Finally, distinguish API acceptance from inbox placement. A successful Volanea API response means the sending service accepted the request. Authentication, domain reputation, recipient mailbox rules, content, and suppression status can still affect delivery. Configure sender authentication before relying on the workflow for critical receipts.

Improve the first version before scaling

The minimal relay is enough to prove the data flow. A production implementation should add a few operational features.

  • Durable idempotency: Use a database table with unique keys for delivery ID and logical notification key.
  • Structured logs: Log event type, order ID, delivery ID, provider message ID, response code, and a redacted recipient indicator rather than full payloads.
  • Versioned templates: Store a template version with each send so support can explain exactly what a customer received.
  • Environment separation: Use different sender domains, webhook secrets, endpoints, and Volanea API keys for staging and production.
  • Alerts: Notify an operator when webhook verification failures, skipped customer emails, or provider errors exceed a normal threshold.
  • Suppression-aware behavior: Respect provider suppression and bounce outcomes; do not keep retrying an address known to be undeliverable.

As volume grows, review costs as well as architecture. A workflow that sends one confirmation per order may later add receipts, fulfillment notices, return updates, review requests, and internal alerts. Understand transactional email sending costs before those additional event types become production defaults.

Alternatives and when to use them

A native WooCommerce webhook plus a relay is usually the cleanest option for developers because it gives complete control over mapping and authentication. It is not the only option.

Custom WordPress code

A WordPress developer can hook directly into WooCommerce actions, such as order-status transition hooks, and use WordPress’s server-side HTTP client to call Volanea. This removes the external webhook receiver, but it couples email availability to the WordPress request or background-job environment. It also requires careful secret management in server configuration and robust retry logic.

Use this when you own the WordPress codebase, can deploy a maintained custom plugin, and want the email decision to happen inside the store application. Never put the API key in a theme’s front-end assets or in code shared publicly.

Automation middleware

An automation platform can receive WooCommerce data, transform it, and call an HTTP endpoint. This can be useful for low-volume operational workflows and prototypes, especially when non-developers need to change routing.

The trade-off is another system holding customer data and provider credentials, plus potential limitations around payload transformation, retries, latency, observability, and idempotency. For receipts and other customer-critical transaction mail, a purpose-built relay often offers clearer control.

WooCommerce’s built-in emails

WooCommerce itself can send standard order emails using WordPress mail configuration and extensions. That is appropriate when the built-in templates and trigger rules meet your requirements. Choose the webhook relay when you need Volanea’s API workflow, custom sending logic, a separate application, or a message type WooCommerce does not natively model.

A practical deployment checklist

Before switching on this WooCommerce email integration, verify the following:

  1. Your Volanea sending domain and from address are configured and approved for the messages you plan to send.
  2. The webhook endpoint is public, HTTPS-only, and not protected by an interactive login page.
  3. The WooCommerce webhook secret and Volanea API key are distinct secrets stored server-side.
  4. Signature verification uses the exact raw request body, not reserialized JSON.
  5. The endpoint accepts only the expected webhook topic and validates the recipient before sending.
  6. A durable idempotency store prevents duplicate sends after retries, restarts, or horizontal scaling.
  7. Templates escape merchant-controlled product names and have a plain-text alternative.
  8. Staging and production use separate credentials and endpoints.
  9. You have tested successful, missing-email, invalid-signature, provider-error, and duplicate-delivery cases.
  10. Logs connect a WooCommerce order ID and webhook delivery ID to the provider response without retaining unnecessary personal data.

The key principle is simple: WooCommerce owns the commerce event, your relay owns the decision and transformation, and Volanea owns the transactional send. Keeping those responsibilities separate produces a workflow that is easier to secure, test, and evolve.

FAQ

Does Volanea have a native WooCommerce plugin?

No native WooCommerce marketplace app is required for this approach. Use WooCommerce’s built-in webhooks with a server-side relay that calls Volanea’s REST API.

Which WooCommerce trigger should I use for an order email?

Use the native Order created webhook when the message is an order-received acknowledgement. For payment confirmation or fulfillment updates, use an event strategy that verifies the appropriate order status or fulfillment state rather than assuming that creation means payment or shipment.

Where should the Volanea API key be stored?

Store it in your middleware host’s encrypted environment variables or secret manager. Never expose it in browser JavaScript, public site configuration, a webhook URL, or client-visible WordPress settings.

Why did a customer receive the same email twice?

A timeout or failed response can cause webhook redelivery even if the first provider request succeeded. Persist and enforce an idempotency key based on the WooCommerce delivery ID and, ideally, a logical order-and-message key.

Can I send an email if the billing email is missing?

Not a customer email. Treat a missing or unusable billing.email as a skipped event, log it for review, and return a deliberate response rather than repeatedly retrying a request that has no valid recipient.