If you need to send email from Magento through Volanea, do not look for a native Volanea Magento extension or a Marketplace install flow: there is not one. The reliable approach is a small server-side Magento module that observes an order event, maps the order into a Volanea REST request, and keeps the sending key out of anything customer-facing.

The Magento integration reality: choose the right outbound path

Magento is now used to describe more than one deployment model, and that distinction matters when planning an email integration.

Magento Open Source does not provide a generic, merchant-configured outbound HTTP automation builder that can take an order event and POST it directly to an arbitrary email API. It has an event/observer architecture for extensions, which is the correct server-side scripting layer for this job. A custom module can observe an event such as sales_order_place_after and make the Volanea API call from PHP.

Adobe Commerce has additional extensibility options. Its Webhooks capability is synchronous: Commerce calls an external service while the originating Commerce operation is still in progress. Adobe documents this for cases such as validation, taxes, stock checks, and other decisions that must immediately affect the request. That makes it a poor default for an order-confirmation email, because a slow email-provider call should not delay or fail checkout.

Adobe Commerce also supports asynchronous Adobe I/O Events. The order-placement example in Adobe’s eventing documentation uses plugin.magento.sales.api.order_management.place, then routes the event through Adobe I/O Events to an App Builder application. That is a solid enterprise architecture when your Commerce edition and Adobe App Builder setup already support it. The event consumer can retrieve the order and call Volanea asynchronously.

For most Magento installations, however, the practical integration is simpler:

  1. Magento creates the order.
  2. A server-side observer receives the order object from sales_order_place_after.
  3. The observer creates a narrowly scoped payload for an order-confirmation message.
  4. Magento POSTs that payload to https://api.volanea.com/v1/send.
  5. Volanea accepts the message for processing, while delivery and engagement remain separate downstream events.

This guide uses that direct, server-side observer route. It works with Magento 2 / Adobe Commerce codebases where you can deploy a custom module. It does not require exposing a Volanea key to the browser, installing a marketplace connector, or making checkout wait on a synchronous Adobe Commerce webhook.

The concrete trigger: sales_order_place_after

The trigger in this integration is Magento’s sales_order_place_after event. It fires after Magento has placed the order and provides an order object to observers.

That detail is important: Magento does not natively emit a pre-shaped JSON webhook body for this event in Magento Open Source. The observer receives a PHP order object, and your module deliberately creates the outbound JSON. This gives you control over what customer data leaves Magento and avoids passing a massive order object, internal notes, payment metadata, or other fields your email does not need.

For an order confirmation, the minimal fields are usually:

  • Magento increment ID, such as 100000123
  • customer email
  • customer first name
  • grand total and order currency
  • checkout date
  • store name and base URL
  • a safe order-details URL
  • visible order line items

Do not use an event that fires before order persistence for an email receipt. At that stage, the increment ID, customer record, payment result, or final totals may not be stable. Likewise, avoid sending an order email from low-level quote events: a cart can change many times and never become an order.

The direct observer route is particularly useful for an email that represents a completed business action: receipt, order acknowledgement, payment failure alert, fraud-review notification, or shipment notice. For status-based messages, use a separate observer tied to the specific state transition you intend to communicate. Do not turn every order save into an email trigger; Magento can save an order several times during fulfillment.

Recommended architecture for Magento and Volanea

Treat Magento as the source of truth for commerce data and Volanea as the sending system. The integration should have one explicit boundary between them.

What runs inside Magento

Your Magento module should do four jobs only:

  • subscribe to the Magento event;
  • extract and normalize message-safe order fields;
  • create a deterministic idempotency key;
  • send or queue a Volanea API request.

It should not make the storefront browser call Volanea. It should not place the secret in checkout JavaScript, a public GraphQL response, a Knockout UI component, or a client-visible .env file that gets bundled into front-end assets.

What Volanea handles

Volanea’s POST /v1/send endpoint accepts a transactional message request. The send endpoint supports an Idempotency-Key header, which is essential when a network failure leaves Magento uncertain whether Volanea received the request. Volanea accepts the send for processing; that acceptance is not the same thing as a delivered message in a recipient inbox.

Use Volanea’s sending-domain setup before you deploy. Your selected from address must belong to a verified domain. That prerequisite should be handled before order traffic reaches the integration, not discovered during a customer’s checkout.

For API details beyond this Magento-specific implementation, keep the engineering team aligned with the email API reference and setup guides.

Why the integration should be asynchronous in production

A synchronous HTTP request in an order-placement observer is easy to understand and useful for a first implementation, but it has a tradeoff: checkout or API order placement may wait for the remote request. A production store should normally put the send work onto Magento’s message queue or a durable outbox table, then let a consumer call Volanea after the order transaction is committed.

That architecture isolates checkout from transient DNS failures, API latency, provider maintenance, and rate limiting. The core mapping and headers shown below remain the same whether you call Volanea immediately or from a consumer. The durable queue changes when the API call happens, not what the message means.

Before you write code

Complete these prerequisites first. They reduce the chance that the first real order becomes a debugging exercise.

  1. Verify a sending domain in Volanea. Use an address such as orders@example.com, not a free consumer mailbox. Publish the DNS records Volanea provides and wait for domain verification.
  2. Create a restricted operational secret. Store the Volanea secret key in server-side Magento configuration or an environment-backed deployment secret. Do not hard-code it in a module repository.
  3. Choose a stable sender and reply-to policy. For example, use orders@example.com as the sender and support@example.com as the reply-to address. The customer’s own address belongs in to, never in from.
  4. Decide what message is authoritative. If Magento’s built-in order email remains enabled, customers can receive two confirmations. Either keep Magento’s built-in sender and use Volanea for other messages, or deliberately disable the overlapping Magento confirmation after thoroughly testing the Volanea replacement.
  5. Create a test order path. Use a non-production store view, a test API key if available, and a mailbox you control. Test guest checkout as well as logged-in checkout.

The distinction between a custom Volanea send and Magento’s own transactional email settings is operationally important. Sending a second receipt because two systems both react to the same placed order is not a deliverability win; it is a customer-experience defect.

Build the Magento observer module

A Magento module needs an event subscription and an observer class. The example below is intentionally narrow: it sends one order-confirmation message after an order is placed.

Create app/code/Acme/VolaneaEmail/etc/events.xml:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="sales_order_place_after">
        <observer name="acme_volanea_order_confirmation"
                  instance="Acme\VolaneaEmail\Observer\SendOrderConfirmation" />
    </event>
</config>

Magento calls this observer with an event payload containing the placed order. In PHP, the real inbound shape is accessed as follows:

$order = $observer->getEvent()->getOrder();

That object is Magento’s event payload. It is not a public HTTP body and should not be JSON-encoded wholesale. Instead, map only the fields the customer-facing email needs.

Create app/code/Acme/VolaneaEmail/Observer/SendOrderConfirmation.php:

<?php
declare(strict_types=1);

namespace Acme\VolaneaEmail\Observer;

use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\HTTP\Client\Curl;
use Magento\Store\Model\StoreManagerInterface;
use Psr\Log\LoggerInterface;

final class SendOrderConfirmation implements ObserverInterface
{
    public function __construct(
        private readonly Curl $curl,
        private readonly StoreManagerInterface $storeManager,
        private readonly LoggerInterface $logger,
        private readonly string $volaneaApiKey,
        private readonly string $fromEmail,
        private readonly string $fromName,
        private readonly string $replyToEmail
    ) {}

    public function execute(Observer $observer): void
    {
        /** @var \Magento\Sales\Model\Order $order */
        $order = $observer->getEvent()->getOrder();

        // A placed order can be guest or registered; email is the common recipient field.
        $recipientEmail = (string) $order->getCustomerEmail();
        if ($recipientEmail === '') {
            $this->logger->warning('Volanea order email skipped: order has no customer email.', [
                'order_id' => $order->getEntityId(),
            ]);
            return;
        }

        $store = $this->storeManager->getStore((int) $order->getStoreId());
        $incrementId = (string) $order->getIncrementId();
        $customerName = trim((string) $order->getCustomerFirstname() . ' ' . (string) $order->getCustomerLastname());
        $orderUrl = rtrim($store->getBaseUrl(), '/') . '/sales/order/view/order_id/' . (int) $order->getEntityId();

        $items = [];
        foreach ($order->getAllVisibleItems() as $item) {
            $items[] = [
                'name' => (string) $item->getName(),
                'sku' => (string) $item->getSku(),
                'qty' => (float) $item->getQtyOrdered(),
                'rowTotal' => number_format((float) $item->getRowTotalInclTax(), 2, '.', ''),
            ];
        }

        // This is the actual JSON body Magento sends to Volanea.
        $payload = [
            'from' => [
                'email' => $this->fromEmail,
                'name' => $this->fromName,
            ],
            'to' => [[
                'email' => $recipientEmail,
                'name' => $customerName !== '' ? $customerName : null,
            ]],
            'replyTo' => $this->replyToEmail,
            'subject' => sprintf('We received your order #%s', $incrementId),
            'html' => sprintf(
                '<h1>Thanks%s</h1><p>We received order <strong>#%s</strong>.</p><p>Total: <strong>%s %s</strong></p><p><a href="%s">View your order</a></p>',
                $customerName !== '' ? ', ' . htmlspecialchars($customerName, ENT_QUOTES, 'UTF-8') : '',
                htmlspecialchars($incrementId, ENT_QUOTES, 'UTF-8'),
                htmlspecialchars((string) $order->getOrderCurrencyCode(), ENT_QUOTES, 'UTF-8'),
                number_format((float) $order->getGrandTotal(), 2),
                htmlspecialchars($orderUrl, ENT_QUOTES, 'UTF-8')
            ),
            'text' => sprintf(
                'We received your order #%s. Total: %s %s. View your order: %s',
                $incrementId,
                (string) $order->getOrderCurrencyCode(),
                number_format((float) $order->getGrandTotal(), 2),
                $orderUrl
            ),
            'metadata' => [
                'magento_order_id' => (string) $order->getEntityId(),
                'magento_increment_id' => $incrementId,
                'store_id' => (string) $order->getStoreId(),
                'event' => 'sales_order_place_after',
            ],
        ];

        // One logical order-confirmation message gets one stable key.
        $idempotencyKey = 'magento-order-confirmation-' . (string) $order->getEntityId();

        $this->curl->addHeader('Authorization', 'Bearer ' . $this->volaneaApiKey);
        $this->curl->addHeader('Content-Type', 'application/json');
        $this->curl->addHeader('Idempotency-Key', $idempotencyKey);
        $this->curl->setTimeout(10);

        try {
            $this->curl->post(
                'https://api.volanea.com/v1/send',
                json_encode($payload, JSON_THROW_ON_ERROR)
            );

            $status = $this->curl->getStatus();
            if ($status < 200 || $status >= 300) {
                throw new \RuntimeException('Volanea returned HTTP ' . $status);
            }
        } catch (\Throwable $exception) {
            $this->logger->error('Volanea order email request failed.', [
                'order_id' => $order->getEntityId(),
                'increment_id' => $incrementId,
                'idempotency_key' => $idempotencyKey,
                'exception' => $exception->getMessage(),
            ]);

            // In production, persist or enqueue this failure for a worker retry.
            // Do not generate a new idempotency key when retrying this same email.
        }
    }
}

The code maps Magento data into a message rather than dumping the complete order. Here is the mapping in plain language:

Magento order fieldVolanea fieldWhy it is used
getCustomerEmail()to[0].emailRecipient address
customer first and last nameto[0].nameRecipient display name
configured server-side senderfrom.email and from.nameVerified sending identity
getIncrementId()subject, text, metadataCustomer-facing order number and traceability
getGrandTotal() and getOrderCurrencyCode()html and textReceipt summary
getAllVisibleItems()optional message content or template variablesLine-item list without hidden child items
Magento order entity IDmetadata and Idempotency-KeyInternal correlation and deduplication

The $items array is deliberately prepared but not inserted into the basic HTML example. For a polished production receipt, use a server-rendered Magento template or a stored Volanea template, and pass only vetted variables such as item name, quantity, price, order total, and URL. Never concatenate raw product names or customer-entered values into HTML without escaping them.

Store the Volanea API key on the Magento server

The Volanea API key authorizes sending email. Anyone who obtains it may be able to send messages from your verified domain, consume quota, create operational confusion, or damage sender reputation. Treat it as an application secret, not a configuration value for the storefront.

Good places for the secret

Use one of these server-side patterns:

  • an environment variable injected by your hosting or deployment system;
  • a secrets manager value loaded during deployment;
  • encrypted Magento Admin configuration, restricted to administrators, with the field masked;
  • a server-only env.php deployment value that is never committed to source control.

If you expose the key through Magento configuration, use a backend model or secret-injection mechanism appropriate to your deployment. The observer should receive the value through dependency injection or a configuration provider, not read it from a browser request.

Places the secret must not live

Do not place the key in:

  • .phtml, RequireJS, Knockout, React, Vue, or checkout JavaScript;
  • a public REST or GraphQL response;
  • CMS blocks, email-template variables, or page configuration;
  • a committed app/etc/env.php file in a public repository;
  • a Zapier or Make field mapping visible to users who should not have email-sending authority.

A browser cannot safely hold a Volanea secret. Even if your front end obfuscates it, the key can be recovered from browser developer tools, network requests, source maps, or a compromised session.

Use templates for maintainable order email

Inline HTML is useful to prove the integration, but it is not the ideal long-term way to maintain an order email. Order confirmations tend to acquire localization, tax language, promotional blocks, support contacts, dynamic line items, legal copy, and brand changes. Hard-coding all of that in an observer creates a deployment requirement for every content revision.

A better pattern is to create a Volanea template for the message and pass structured variables from Magento. The send API supports stored templates addressed by templateId, with variables used to render dynamic data.

The payload concept becomes:

{
  "from": {"email": "orders@example.com", "name": "Example Store"},
  "to": [{"email": "customer@example.net", "name": "Avery Morgan"}],
  "replyTo": "support@example.com",
  "templateId": "tpl_magento_order_confirmation",
  "variables": {
    "customerName": "Avery Morgan",
    "orderNumber": "100000123",
    "currency": "USD",
    "grandTotal": "84.50",
    "orderUrl": "https://store.example.com/sales/order/view/order_id/987",
    "items": [
      {"name": "Canvas Tote", "sku": "TOTE-01", "qty": 1, "rowTotal": "24.50"},
      {"name": "Travel Mug", "sku": "MUG-02", "qty": 2, "rowTotal": "60.00"}
    ]
  },
  "metadata": {
    "magento_order_id": "987",
    "magento_increment_id": "100000123"
  }
}

Keep subject-line ownership clear. If the Volanea template contains the subject, avoid setting a competing subject in the send payload unless your template configuration and API behavior explicitly support that override. Use one approach consistently in staging before releasing it.

For order emails, prefer an authenticated customer account link over a URL containing a reusable secret. Guest-order lookup flows should rely on Magento’s own secure mechanics rather than inventing a query string that exposes an order ID and email address.

When this breaks: retries, timeouts, and incomplete payloads

An integration is not production-ready because it succeeds once. The failure modes in the Magento-to-Volanea hop are predictable, and each needs a deliberate response.

Magento retries can create duplicate email

A request can reach Volanea successfully while Magento times out before receiving the response. If your code retries with a new request identity, Volanea has no reliable way to know that the second request represents the same order-confirmation email.

Use the same Idempotency-Key for every retry of one logical message. In the example, that key is based on the Magento order entity ID and the message type:

magento-order-confirmation-987

Do not generate a random UUID inside the retry loop. A random key is appropriate for a new send, but it defeats deduplication if it changes for a retry. If you later add separate messages for order confirmation, payment capture, shipment, and refund, include the message purpose in the stable key so those legitimate emails remain distinct.

For example:

magento-order-confirmation-987
magento-shipment-confirmation-987-shipment-332
magento-refund-confirmation-987-creditmemo-441

Webhook or remote-call timeouts should not block checkout

Adobe Commerce Webhooks are synchronous by design, and Adobe explicitly positions them for decisions that need to affect the current Commerce operation. Sending an email is normally not such a decision. Do not attach a live email request to a checkout-critical synchronous webhook and then fail an order because the email endpoint is slow.

For the observer route, use a short HTTP timeout and move failures into a durable retry path. The durable path can be Magento Message Queue, a cron-consumed database outbox table, or an external worker. Persist these fields before retrying:

  • Magento order ID and increment ID;
  • message type;
  • recipient email;
  • idempotency key;
  • serialized message payload or enough source data to rebuild it deterministically;
  • retry count, last error, and next-attempt time.

Retry transient failures such as DNS resolution errors, connection resets, timeouts, HTTP 429, and many HTTP 5xx responses. Do not blindly retry permanent failures such as malformed payloads, missing required content, an unverified sender domain, or an invalid recipient address. Those should create an actionable operational alert.

Fields can be absent depending on checkout and configuration

Not every Magento order contains every field you might expect. Guest customers may not have a customer ID. Some payment methods may leave invoice-related fields empty until later. An order can have no customer first or last name in unusual integrations. Store configuration can differ by website or store view. Custom checkout fields are not guaranteed to exist unless your own module persists them.

Build the payload around a required minimum. For an order confirmation, customer email and a stable order identifier are generally essential. A customer name is optional; the sample code omits it when empty. If a field cannot be trusted, do not put it in a subject line or destination address.

The same principle applies to line items. getAllVisibleItems() normally avoids configurable-product child rows, but custom product types and third-party extensions can change how items appear. Test bundles, grouped products, gift cards, downloadable products, and configurable products before using item data in a customer receipt.

A 2xx acceptance is not inbox delivery

A successful Volanea send response means the API accepted the request. It does not prove the recipient opened the email, that the receiving server accepted it, or that the message landed in the primary inbox. Delivery can still be affected by recipient suppression, mailbox policy, bounces, complaints, quota, or downstream provider handling.

Use Volanea send logs and email events for support investigations. Keep Magento’s order ID and increment ID in message metadata so support can correlate an order with its corresponding Volanea send record without searching by a customer’s full email address.

Test the full path before replacing customer-facing mail

Test the module in a non-production environment with a verified test sender or an approved staging domain. Do not limit testing to one logged-in customer and one simple product.

Use this checklist:

  • Place a guest checkout order.
  • Place a logged-in customer order.
  • Test a product type with options or child items.
  • Confirm the recipient, sender, reply-to, subject, order number, total, currency, and order link.
  • Confirm the email is not duplicated when the observer is invoked again.
  • Simulate a timeout after request submission and verify the retry reuses the same idempotency key.
  • Remove the customer name and confirm the send still works without producing malformed text.
  • Test an unverified from domain in a safe environment so the failure path is logged and queued correctly.
  • Confirm Magento’s native order-confirmation email is not sending an unintended duplicate.
  • Search Volanea’s send log using the Magento metadata for a support-friendly audit trail.

A test should also cover failure visibility. A message that silently fails is worse than a message that visibly fails: operations staff need enough context to retry safely, repair configuration, or contact a customer through another approved channel.

Adobe Commerce eventing and middleware alternatives

If you run Adobe Commerce and already use Adobe App Builder, the asynchronous Adobe I/O Events route can be preferable to running the HTTP client directly in Magento. Subscribe to the order-placement event, let Adobe I/O Events invoke a Runtime action, retrieve the authoritative order details through Commerce APIs as needed, and call Volanea from the action using a server-side secret.

That approach creates a stronger separation between storefront commerce code and integration code. It can also make it easier to centralize retries, observability, and multiple downstream actions. Adobe’s journaling model is worth considering when consumers need controlled batch processing or resilience against missed direct invocations.

Do not assume Zapier or Make can natively receive every Magento order event without additional work. Magento Open Source does not ship a general outbound webhook automation layer for this purpose. A middleware route still needs a trustworthy event source: typically the same small Magento observer posts a signed, minimal order event to your relay, and the relay or automation platform maps it to Volanea.

If you use middleware, keep the Volanea key in the middleware’s protected connection or secret store. Do not send the Volanea key as a Magento field value. The middleware should validate the incoming event, deduplicate by Magento order ID plus message type, and make the Volanea call with a stable idempotency key.

Operational guidance for reliable transactional email

The code is only one part of reliable email. Treat sending as an operational system.

Use a verified domain with clear mailbox ownership. Keep receipts and account notifications separate from promotional mail in both message purpose and customer expectations. Keep reply handling intentional: a reply-to inbox needs monitoring if customers are encouraged to use it.

Log correlation IDs without unnecessarily logging full HTML bodies, payment details, addresses, or unmasked email addresses. The order entity ID, increment ID, Volanea response identifier, idempotency key, response status, and error category are usually enough to diagnose most incidents.

Finally, define which system owns each message. Magento may own its native account and order emails. Volanea may own a custom order acknowledgement, a shipping notification, or a post-purchase sequence. An explicit ownership list prevents duplicate sends and makes migrations safer.

FAQ

Does Volanea have a native Magento extension?

No. This integration uses Magento’s server-side module and event system to call the Volanea REST API. Do not invent a Marketplace installation step or expect a Volanea configuration page to appear in Magento after installing an unrelated package.

What Magento event should send an order confirmation?

For a custom Magento module, sales_order_place_after is a practical trigger because it provides the placed order object. For Adobe Commerce eventing, Adobe documents plugin.magento.sales.api.order_management.place as an order-placement example. Avoid firing a customer email from cart or quote updates.

Can the browser call Volanea directly from Magento checkout?

No. The Volanea secret key must remain server-side because it authorizes email sending. Call Volanea from a Magento observer, queue consumer, App Builder action, or another protected backend service.

How do I prevent duplicate emails after a timeout?

Create one stable Idempotency-Key per logical email, persist it with the job, and reuse it for every retry. For example, use magento-order-confirmation-<order-entity-id> for the order-confirmation message.

Should I call Volanea during checkout or from a queue?

A queue or durable outbox is preferable for production. It prevents a temporary email API problem from extending checkout latency and gives you a controlled, observable retry path. The observer can create the outbox job after the order is placed, and a worker can make the Volanea request afterward.