Send transactional email from BigCommerce reliably by treating the store event as a trigger—not as the complete email payload. BigCommerce sends a lightweight webhook notification, then your server fetches the order details and uses Volanea’s REST API to deliver the message.

There is no native Volanea app or one-click BigCommerce integration in this setup. The honest architecture is a webhook-to-API workflow: BigCommerce notifies an HTTPS endpoint you control, that endpoint retrieves the relevant order from BigCommerce, and it calls Volanea with the recipient, subject, plain-text fallback, and HTML email.

That extra server-side step is useful rather than inconvenient. It keeps your Volanea secret key out of browser code and webhook configuration, gives you a place to prevent duplicate receipts, and lets you decide exactly which BigCommerce events deserve customer email.

What this BigCommerce transactional email integration does

The example in this guide sends an order-confirmation email after BigCommerce creates an order. The same pattern also works for shipment notifications, cancelled-order notices, back-in-stock alerts, payment follow-ups, customer-service messages, and internal operational alerts.

The complete flow is:

  1. A shopper places an order in BigCommerce.
  2. BigCommerce POSTs a store/order/created webhook notification to your HTTPS endpoint.
  3. Your endpoint checks a shared secret header and validates the expected event shape.
  4. Your service fetches the full order record from BigCommerce’s Orders API.
  5. Your service builds safe email content from the fetched order fields.
  6. Your service POSTs the email to Volanea’s POST /v1/send endpoint.
  7. The same deterministic idempotency key is sent on retries, so one order event maps to one logical email send.

The critical detail is step four. BigCommerce’s webhook callback is deliberately lightweight. For an order-created event, it identifies the store and order, but it does not contain the billing email, totals, products, shipping destination, or order line items needed for a useful receipt. Fetch the authoritative order data before you send.

Why a webhook receiver is the right pattern

A direct call from BigCommerce to a generic email endpoint would be tempting, but it would create several problems. The data available in the notification is not enough to produce a proper receipt. More importantly, placing a sending API key in an automation configuration or a client-visible context expands the chance of credential exposure.

A dedicated receiver gives you a controlled boundary between commerce events and outbound email. It can apply business rules, request fresh order data, escape shopper-controlled text, choose a sender identity, add observability, and make retries safe.

The notification is not the order

BigCommerce documents webhook callbacks as a lightweight description of an event. A store/order/created callback has this shape:

{
  "scope": "store/order/created",
  "store_id": "1025646",
  "data": {
    "type": "order",
    "id": 250
  },
  "hash": "dd70c0976e06b67aaf671e73f49dcb79230ebf9d",
  "created_at": 1561479335,
  "producer": "stores/{store_hash}"
}

The fields matter for different reasons:

  • scope tells you which event caused the callback. Treat it as a routing field, not customer content.
  • store_id identifies the BigCommerce store numerically.
  • data.id is the order ID to retrieve from the Orders API.
  • hash is a SHA-1 hash of the JSON-encoded data payload and is useful for event identity and duplicate detection.
  • created_at is the event time as a Unix timestamp.
  • producer follows the stores/store_hash pattern.

Do not mistake hash for a signed authorization header. It is not a replacement for authenticating the request. In this guide, authentication comes from a custom header configured on the webhook subscription, plus normal HTTPS transport. For higher-risk integrations, put the endpoint behind a gateway, restrict incoming traffic where practical, and keep a durable event ledger.

Why retrieve the order after the event

The full BigCommerce order resource includes the billing email, customer name, currency, monetary totals, order status, and references to products and shipping information. Your handler can call:

GET https://api.bigcommerce.com/stores/{store_hash}/v2/orders/{order_id}

with an X-Auth-Token header. That endpoint returns the order’s billing_address.email along with data such as id, currency_code, total_inc_tax, status, and products references.

For a simple confirmation, the billing email and total may be enough. For an itemized receipt, make a second request to the order-products endpoint or use the appropriate order detail your implementation requires. Keep that enrichment inside your service rather than attempting to infer it from the webhook callback.

Prepare the credentials and sender identity

You need three server-side values before deploying the handler:

BIGCOMMERCE_STORE_HASH=abc123def
BIGCOMMERCE_ACCESS_TOKEN=...
BIGCOMMERCE_WEBHOOK_SECRET=replace-with-a-long-random-value
VOLANEA_API_KEY=sk_...
ORDER_FROM_EMAIL=orders@updates.example.com

BIGCOMMERCE_ACCESS_TOKEN needs permission to read the order data your handler fetches. Grant the narrowest access needed for the workflow. A receiver that only reads orders and sends a receipt does not need catalog write access, customer write access, or administrative credentials.

VOLANEA_API_KEY is a secret server credential. Keep it in your deployment platform’s secret store or environment-variable system. Never put it in storefront JavaScript, a theme file, a public Git repository, or a BigCommerce webhook destination URL.

The ORDER_FROM_EMAIL value should use a sending domain you control and have authenticated for Volanea. Authentication is not cosmetic: it is part of establishing a consistent sender identity for receipts and other mail customers expect. Review the Volanea API reference and setup guides before sending production traffic.

Choose the right sender and reply path

Order confirmations should come from a stable, recognizable address, such as orders@updates.example.com. Avoid using a different sender per event type unless you have a clear operational reason; customers build trust through recognizable sender names and domains.

If customers should reply to a support inbox, configure the appropriate reply handling in your sending configuration or include a clearly monitored contact address in the message. Never direct replies to an address nobody reviews. Transactional messages often generate replies about address changes, cancellations, order corrections, and delivery issues.

Keep secrets separate by environment

Use distinct BigCommerce stores, Volanea keys, and sender domains or subdomains for development, staging, and production whenever possible. A test order should never send a customer-facing production receipt to a real shopper because somebody reused production credentials locally.

At minimum, add a development-only recipient allowlist. A simple rule—send only to your own test addresses outside production—prevents accidental mail while you test webhook parsing and templates.

Create the BigCommerce order-created webhook

BigCommerce creates a webhook subscription through its REST API. The request below subscribes to order creation and sends callbacks to a public HTTPS URL that you host:

curl --request POST \
  "https://api.bigcommerce.com/stores/${BIGCOMMERCE_STORE_HASH}/v3/hooks" \
  --header "X-Auth-Token: ${BIGCOMMERCE_ACCESS_TOKEN}" \
  --header "Content-Type: application/json" \
  --header "Accept: application/json" \
  --data '{
    "scope": "store/order/created",
    "destination": "https://email.example.com/webhooks/bigcommerce/orders",
    "is_active": true,
    "headers": {
      "X-Webhook-Secret": "replace-with-the-same-long-random-value"
    }
  }'

Replace the URL with your deployed receiver and use the exact secret stored in BIGCOMMERCE_WEBHOOK_SECRET. BigCommerce requires an HTTPS destination and documents port 443 for HTTPS webhook destinations; do not build a production endpoint around a development tunnel or a custom port.

The headers object is useful because it lets your receiver reject routine internet traffic that happens to find the endpoint. It is still important to store the secret safely, rotate it when required, and avoid logging it.

Select the event intentionally

store/order/created is suitable for a basic order-confirmation message because it fires when an order is created. It is not automatically the right event for every email.

For example:

  • Use an order-created event for an acknowledgement that the store received the order.
  • Use a status-updated event only when you have explicit rules for which statuses should send customer mail.
  • Use a fulfillment or shipment-related event for dispatch messaging when the associated data is available and verified.
  • Avoid sending a new receipt for every update, including internal notes, edits, or status changes that a customer should not see.

An event name should not substitute for business logic. The handler should examine the fetched order and make a deliberate decision about whether an email is appropriate.

Expect retries and deactivation behavior

Webhook delivery is not a once-only guarantee. Network failures, deployment restarts, timeouts, and non-2xx responses can cause callbacks to be attempted again. Your code must be able to receive the same order event more than once without creating duplicate customer mail.

BigCommerce also documents that a webhook subscription can be deactivated after 90 days of inactivity. Add webhook health checks and alerts to your operations routine. A silent integration failure is particularly expensive for transactional email because shoppers may assume their order failed when they simply did not receive confirmation.

Build a secure Node.js webhook receiver

The following Express application is a practical reference implementation. It verifies the custom header, checks the event, fetches the BigCommerce order, renders basic safe content, and sends through Volanea.

It uses Node.js built-in fetch, available in current Node.js releases. Install Express first:

npm install express

Save this as server.mjs:

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

const app = express();
app.use(express.json({ limit: "256kb" }));

const {
  BIGCOMMERCE_STORE_HASH,
  BIGCOMMERCE_ACCESS_TOKEN,
  BIGCOMMERCE_WEBHOOK_SECRET,
  VOLANEA_API_KEY,
  ORDER_FROM_EMAIL
} = process.env;

for (const name of [
  "BIGCOMMERCE_STORE_HASH",
  "BIGCOMMERCE_ACCESS_TOKEN",
  "BIGCOMMERCE_WEBHOOK_SECRET",
  "VOLANEA_API_KEY",
  "ORDER_FROM_EMAIL"
]) {
  if (!process.env[name]) throw new Error(`Missing required environment variable: ${name}`);
}

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

function formatMoney(amount, currency) {
  const numericAmount = Number(amount);
  if (!Number.isFinite(numericAmount)) return `${amount} ${currency}`;

  try {
    return new Intl.NumberFormat("en-US", {
      style: "currency",
      currency: currency || "USD"
    }).format(numericAmount);
  } catch {
    return `${numericAmount.toFixed(2)} ${currency || "USD"}`;
  }
}

function secretMatches(received) {
  const expected = Buffer.from(BIGCOMMERCE_WEBHOOK_SECRET);
  const actual = Buffer.from(received || "");
  return actual.length === expected.length && crypto.timingSafeEqual(actual, expected);
}

async function getOrder(orderId) {
  const response = await fetch(
    `https://api.bigcommerce.com/stores/${BIGCOMMERCE_STORE_HASH}/v2/orders/${orderId}`,
    {
      headers: {
        "Accept": "application/json",
        "X-Auth-Token": BIGCOMMERCE_ACCESS_TOKEN
      }
    }
  );

  if (!response.ok) {
    throw new Error(`BigCommerce order lookup failed: ${response.status} ${await response.text()}`);
  }

  return response.json();
}

async function sendWithVolanea({ to, subject, text, html, idempotencyKey }) {
  const response = await fetch("https://api.volanea.com/v1/send", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${VOLANEA_API_KEY}`,
      "Content-Type": "application/json",
      "Idempotency-Key": idempotencyKey
    },
    body: JSON.stringify({
      from: ORDER_FROM_EMAIL,
      to: [to],
      subject,
      text,
      html
    })
  });

  const responseText = await response.text();
  if (!response.ok) {
    throw new Error(`Volanea send failed: ${response.status} ${responseText}`);
  }

  return responseText ? JSON.parse(responseText) : null;
}

app.post("/webhooks/bigcommerce/orders", async (req, res) => {
  if (!secretMatches(req.get("X-Webhook-Secret"))) {
    return res.status(401).json({ error: "Unauthorized" });
  }

  const event = req.body;

  if (event?.scope !== "store/order/created" || event?.data?.type !== "order" || !event?.data?.id) {
    return res.status(204).end();
  }

  try {
    const order = await getOrder(event.data.id);
    const recipient = order?.billing_address?.email;

    if (!recipient) {
      console.error("Order has no billing email", { orderId: event.data.id });
      return res.status(204).end();
    }

    const firstName = order.billing_address.first_name || "there";
    const orderNumber = order.id;
    const total = formatMoney(order.total_inc_tax, order.currency_code);
    const subject = `We received your order #${orderNumber}`;

    const text = [
      `Hi ${firstName},`,
      "",
      `Thanks for your order. We received order #${orderNumber}.`,
      `Order total: ${total}`,
      "",
      "We will send another update when your order is ready."
    ].join("\n");

    const html = `
      <p>Hi ${escapeHtml(firstName)},</p>
      <p>Thanks for your order. We received <strong>order #${escapeHtml(orderNumber)}</strong>.</p>
      <p><strong>Order total:</strong> ${escapeHtml(total)}</p>
      <p>We will send another update when your order is ready.</p>
    `;

    await sendWithVolanea({
      to: recipient,
      subject,
      text,
      html,
      idempotencyKey: `bigcommerce-order-created-${BIGCOMMERCE_STORE_HASH}-${orderNumber}`
    });

    return res.status(200).json({ ok: true });
  } catch (error) {
    console.error("BigCommerce order email webhook failed", {
      message: error.message,
      scope: event?.scope,
      orderId: event?.data?.id,
      eventHash: event?.hash
    });

    return res.status(500).json({ error: "Temporary processing failure" });
  }
});

app.listen(process.env.PORT || 3000, () => {
  console.log("Webhook receiver listening");
});

This code intentionally performs the email send before returning 200. If Volanea or the BigCommerce lookup fails, it returns 500, which allows the upstream delivery mechanism to retry. The idempotency key ensures that a retry of the same logical order-created event is not treated as a new email request by Volanea.

Understand the Volanea send request

The email request in the example is:

POST https://api.volanea.com/v1/send
Authorization: Bearer sk_...
Content-Type: application/json
Idempotency-Key: bigcommerce-order-created-abc123def-250
{
  "from": "orders@updates.example.com",
  "to": ["shopper@example.com"],
  "subject": "We received your order #250",
  "text": "Hi Jane,\n\nThanks for your order...",
  "html": "<p>Hi Jane,</p><p>Thanks for your order...</p>"
}

Volanea’s single-message endpoint accepts one recipient or up to 50 recipients in a send request. For an order confirmation, use a single customer recipient. Do not put unrelated shoppers into one recipient list: it exposes addresses and turns a personal transaction message into an unsafe bulk send.

Always include text and HTML

HTML enables your branded receipt layout, but a plain-text version remains important. It serves recipients who prefer text-only rendering, supports accessibility and assistive workflows, and provides a graceful fallback for clients that strip or restrict HTML.

Keep both versions semantically equivalent. The total, order reference, next step, and support expectation should agree exactly. If the HTML contains a shipping disclaimer or cancellation instruction, the text version should contain it too.

Why the idempotency key matters

A webhook is an asynchronous distributed-system boundary. Your code may send an email successfully but lose the network connection before it receives the success response. If you blindly retry without an idempotency key, the shopper can receive two receipts.

Use a stable key derived from the business event, not from the current timestamp or a randomly generated UUID. In this example, the key combines the store hash, order number, and the event purpose:

bigcommerce-order-created-{store_hash}-{order_id}

That value remains the same across retries for one order confirmation while staying distinct from a later shipment email for the same order. A shipment message could use a separate namespace such as bigcommerce-order-shipped-{store_hash}-{order_id}-{shipment_id}.

Make the confirmation email useful, not just automatic

A receipt that says only “Thanks for your order” may technically work, but it shifts support work back to your team. A useful transactional message reduces uncertainty at the moment customers are most likely to check their inbox.

At a minimum, include:

  • The order number customers can quote to support.
  • A clear statement that the store received the order, without promising fulfillment before payment and inventory checks complete.
  • The total and currency as stored on the order.
  • The next expected update, such as a shipment notice.
  • A recognizable sender name and a practical support route.

For an itemized message, fetch the order products and render a compact table of product name, quantity, unit price, and line total. Keep product names escaped before inserting them into HTML. Product titles, customer names, custom form values, and messages are data—not trusted markup.

Avoid premature promises

“Your order has shipped” is a fulfillment claim, not an order-created claim. Sending it at the wrong lifecycle stage creates support tickets and weakens trust even if the receipt itself arrives promptly.

Likewise, avoid saying payment is fully captured unless the fetched order state and your payment flow confirm it. A carefully worded confirmation such as “We received your order and will send another update when it is ready” is accurate for more store configurations.

Separate transactional and marketing intent

Order confirmation email is transactional because it communicates about a shopper’s purchase. Do not quietly turn it into a promotional campaign with unrelated offers, excessive cross-sells, or marketing content that overrides the receipt.

If you add a modest product recommendation or loyalty message, ensure it does not obscure the essential order information. Marketing consent, campaign segmentation, and unsubscribe handling should remain separate from the core operational email path.

Production safeguards for reliable delivery

The sample application is intentionally compact. A production implementation should add durable state, monitoring, and operational limits around it.

Store processed events durably

The Idempotency-Key header protects the Volanea send operation, but a database-backed event table provides broader protection and troubleshooting value. Store at least the incoming hash, scope, order ID, received time, processing result, Volanea response identifier where available, and last error.

A typical processing design is:

  1. Insert or claim the incoming event using a unique key such as the webhook hash plus scope.
  2. If it was already processed successfully, return a 2xx response without sending again.
  3. Fetch the order and apply business rules.
  4. Send to Volanea with the matching deterministic idempotency key.
  5. Mark the event successful only after the send call returns successfully.
  6. Record failures with enough context for a safe replay.

This lets you answer support questions such as “Was a receipt attempted for order 250?” without relying on raw application logs.

Acknowledge fast, but do not lose work

For high order volume, receiving a webhook and performing several external API calls synchronously can make the endpoint slow. The more resilient pattern is to validate and persist the event quickly, return a 2xx response, and enqueue processing in a background worker.

That design needs a durable queue and idempotent worker behavior. Do not acknowledge an event before it is safely persisted; otherwise, a process crash after the response can lose the notification permanently.

For low-to-moderate volume, the synchronous example is a reasonable starting point because it is easy to understand and BigCommerce can retry on a failure response. As volume grows, move the send work behind a queue without changing the fundamental event-to-order-to-email model.

Log identifiers, not sensitive payloads

Log the order ID, webhook hash, event scope, delivery status, and Volanea request outcome. Avoid writing access tokens, API keys, addresses, full order objects, customer messages, or raw HTML into ordinary logs.

If your support team needs traceability, use a controlled event record with retention rules and access controls. Transactional email is customer communication, which means the integration often handles personal data even when its job appears simple.

Test the BigCommerce-to-Volanea flow safely

Do not make a real customer order your first integration test. Test each boundary independently, then test the complete workflow in a sandbox or development store.

Test the receiver with the real callback shape

Run the service locally and use a temporary HTTPS tunnel only for development. Then POST a representative payload to confirm parsing and header validation:

curl --request POST "http://localhost:3000/webhooks/bigcommerce/orders" \
  --header "Content-Type: application/json" \
  --header "X-Webhook-Secret: replace-with-a-long-random-value" \
  --data '{
    "scope": "store/order/created",
    "store_id": "1025646",
    "data": { "type": "order", "id": 250 },
    "hash": "dd70c0976e06b67aaf671e73f49dcb79230ebf9d",
    "created_at": 1561479335,
    "producer": "stores/abc123def"
  }'

The handler will attempt to fetch order 250, so use an order ID that exists in the store associated with your credentials. During development, point ORDER_FROM_EMAIL at a verified test sender and restrict recipients to test addresses.

Use a deliberate test checklist

Before enabling the webhook for production orders, verify all of the following:

  • The receiver returns 401 when the custom header is missing or incorrect.
  • A malformed payload does not cause an unhandled exception.
  • A valid event fetches the intended order from the intended store.
  • The email recipient is read from the fetched order, not from webhook input.
  • The message renders correctly in a desktop client and a mobile client.
  • The text version contains the order number and total.
  • Replaying the same event does not produce another customer email.
  • A temporary Volanea failure results in a retryable failure path rather than silent loss.
  • Application logs contain event identifiers but not secrets or full customer data.

Check delivery separately from API acceptance

A successful API response means the send request was accepted for processing; it does not guarantee that a recipient saw the message in the inbox. Monitor delivery events, bounces, complaints, and suppression outcomes as part of the operating workflow.

If expected mail is not arriving, first check sender-domain authentication, recipient address accuracy, suppression status, and the exact event timeline. Avoid repeatedly resending a receipt as a first response to a delivery question—duplicate transactional email can create confusion and damage sender reputation.

Common implementation mistakes

The most frequent failures are architectural rather than syntax errors. Avoiding them early makes the integration easier to maintain.

Sending directly from the webhook payload

The callback lacks the details required for a personalized receipt. It contains the order ID, not the shopper’s billing email or purchase details. Retrieve the order first.

Putting the Volanea key in a browser or automation URL

A sending key belongs in server-side secrets. Treat it like a payment or database credential: anyone who obtains it may be able to send as your authenticated domain.

Returning success after a failed send

If the BigCommerce lookup or Volanea call fails and your endpoint returns 200 anyway, BigCommerce has no reason to retry. Return an error only when work was not safely persisted or completed, and make the replay path idempotent.

Using a new idempotency key on every attempt

A timestamp or random UUID defeats retry protection. Derive the key from a stable event purpose and order identity so a network retry is recognized as the same operation.

Rendering unescaped store data into HTML

Names, item titles, form fields, and addresses can contain characters that change HTML structure. Escape dynamic values before interpolation. Better still, use a well-tested template system with automatic escaping for complex receipt layouts.

Triggering every notification from order status updates

A status update can occur for many operational reasons. Write explicit rules for statuses that warrant customer communication, and use distinct idempotency keys for each approved message type.

Extend the pattern beyond order confirmation

Once the basic receiver works, you can reuse the same architecture for the rest of the purchase lifecycle. The event listener remains small; your business rules and templates become the configurable layer.

Useful extensions include:

  • A shipment email that includes carrier and tracking data only after fulfillment confirms it.
  • A cancellation or refund communication that reflects the actual order state and support policy.
  • An internal alert to operations when high-value orders, suspicious orders, or manual-review states appear.
  • A post-purchase follow-up scheduled after delivery, subject to your marketing and consent requirements.
  • A customer-service notification when a shopper adds a message or requests an order change.

Keep each message type distinct in three places: the triggering rule, the template/content, and the idempotency key. That separation prevents a later expansion from accidentally turning an order update into a duplicate confirmation.

Conclusion

To send transactional email from BigCommerce using Volanea, do not look for an app-install flow that does not exist. Use BigCommerce webhooks as the event source, retrieve the full order through the Orders API, and send the resulting message through Volanea’s REST endpoint from a server environment you control.

This approach is transparent, secure, and extensible. It also solves the hard parts that matter after the first successful test: correctly handling BigCommerce’s lightweight webhook payload, protecting credentials, avoiding duplicate receipts, rendering safe customer data, and observing delivery over time.

FAQ

Does Volanea have a native BigCommerce app integration?

No. This guide uses a custom integration pattern: BigCommerce sends a webhook to your HTTPS endpoint, and your server calls Volanea’s REST API. There is no claim of an installable native Volanea BigCommerce app.

What payload does BigCommerce send for an order-created webhook?

The callback includes scope, store_id, data, hash, created_at, and producer. For store/order/created, data includes the order type and order ID. Fetch the full order separately because the callback does not include the customer email or complete order information.

Why should I use an idempotency key when sending the email?

Webhook delivery and HTTP calls can be retried after timeouts or temporary failures. A deterministic Idempotency-Key lets Volanea treat repeated attempts for the same order email as one logical send instead of delivering duplicate receipts.

Can I send an itemized order receipt?

Yes. Fetch the order’s line items from BigCommerce, then render product name, quantity, and price into both HTML and text email bodies. Escape all dynamic values before adding them to HTML.

Can I send shipment and refund emails with the same setup?

Yes. Subscribe to the relevant BigCommerce event, retrieve the current authoritative resource data, apply explicit business rules, and use a separate template and idempotency key for each message type.