If you need to send email from PrestaShop with Volanea, the reliable route is a small server-side module that listens for a PrestaShop order hook and calls Volanea’s REST API. PrestaShop core does not provide a native outbound-webhook builder for this job, so the integration belongs in trusted PHP code running with your store.

This distinction matters. A storefront order is not just another browser event: it contains customer data, payment-adjacent context, and a state transition that can be retried or reached more than once. The integration should therefore run after PrestaShop has created the order, keep the Volanea API key outside public JavaScript and theme settings, and make duplicate prevention part of the design.

This guide uses PrestaShop’s actionValidateOrder hook as the trigger. It is a good fit for an order-received email because the hook runs when PrestaShop validates and creates an order through its payment flow. The module receives PrestaShop objects rather than an outbound JSON webhook payload, maps the relevant order and customer fields, and submits a transactional message to Volanea from the server.

What PrestaShop offers for outbound email integrations

PrestaShop has a mature module and hook system. Hooks are extension points in the application lifecycle: a module registers for a named hook, and PrestaShop calls the module’s PHP method when that event occurs. This is not the same thing as a SaaS automation canvas or a native HTTP webhook subscription.

For this particular use case, that gives you two important facts:

  • PrestaShop core can invoke your module during an order lifecycle event.
  • Your module can make a server-side HTTP request with PHP, including a call to Volanea’s API.

What core does not do is let an administrator create a generic “order created → POST this JSON to this URL” outbound webhook from the back office. Its webservice feature is primarily an API through which an external system reads or writes PrestaShop resources; it is not a general outbound event-delivery service.

That means there is no native Volanea marketplace app to install and no standard “paste webhook URL” flow to rely on. The direct approach is a custom module. This can be a very small private module maintained in your repository, or work completed by a PrestaShop developer as part of your store deployment.

Why a module is preferable to a storefront script

A browser-side script cannot safely hold an email API credential. Anyone can inspect JavaScript delivered to a shopper, examine network requests, or copy configuration embedded in a theme. More fundamentally, a browser tab can close before the request finishes and should not be trusted to decide whether an order-related message has been sent.

A module runs on the PHP server, has access to the order objects only after PrestaShop has built them, and can use a secret injected into the hosting environment. It also creates a clear place to add logging, idempotency, queueing, and error handling later.

The no-code alternative: middleware, not a core webhook

If your team cannot deploy PHP code, use a middleware service only if the connector you choose genuinely supports your PrestaShop version and desired order event. In practice, a middleware route usually needs either a third-party PrestaShop connector/module or polling through PrestaShop’s webservice API. It is not a replacement for a core outbound webhook because core does not emit one.

Polling can be acceptable for lower-urgency workflows such as a daily follow-up, but it is a weaker option for a receipt or order-confirmation message. It introduces delay, must track which orders have already been processed, and may see orders before payment or fulfillment state is final. For immediate transactional mail, the hook-based module is the more direct design.

The PrestaShop trigger: actionValidateOrder

The concrete trigger in this integration is PrestaShop’s actionValidateOrder hook. A module subscribes to that hook during installation, and PrestaShop calls hookActionValidateOrder() when an order is validated.

The hook payload is not a JSON document sent over the network. It is a PHP associative array whose values are PrestaShop objects. For this hook, the documented context includes these keys:

  • cart: a Cart object for the purchased cart.
  • order: the newly created Order object.
  • customer: the Customer who placed the order.
  • currency: a Currency object used for the order.
  • orderStatus: the initial OrderState object.

That object-based payload is useful because you can map an authoritative email address from $params['customer']->email, use $params['order']->reference for a human-friendly reference, and inspect the initial state before deciding which email should go out.

Do not confuse order validation with every possible order-status change. If the message should be sent only when an order reaches a later status, such as shipped or payment accepted, use an order-status hook appropriate to that transition instead. actionValidateOrder is best for a “we received your order” or “your order was created” message, not a shipment notification.

Define the message semantics before writing code

The words in the email determine the safe trigger. A message that says “We received your order” can be tied to validation. A message that says “Your payment has cleared” must be tied to the payment state you actually use. A message that says “Your order has shipped” should be driven by the shipping transition, not inferred from order creation.

This avoids a common transactional-email problem: technically successful delivery of a factually incorrect message. A customer who sees a payment-confirmation email before a payment is accepted is not helped by a high deliverability rate.

For this guide, the message is intentionally conservative: an order receipt that references the order reference and total. Customize the copy and trigger only after checking how your payment modules set order states.

Data mapping from a PrestaShop order to Volanea

A transactional API needs a recipient, sender identity, subject, and content. The mapping below keeps the email narrow: it sends only the customer email and information needed for a useful order receipt. It does not attach a complete address, payment details, or every line-item field merely because the module can access them.

PrestaShop sourceVolanea email fieldReason
$params['customer']->emailtoThe recipient address for the order receipt.
Verified environment valuefromA sender on a domain authenticated in Volanea.
$order->referencesubject and HTMLCustomer-friendly order identifier.
$order->total_paid_tax_inclHTMLTotal amount, formatted in store context.
$params['orderStatus']->nameHTML, optionallyInitial order-state label, when appropriate for your copy.
Store-generated event keyidempotency headerPrevents a retry from creating a second message.

Use a sender address on a domain you have verified and authenticated with Volanea. The address is not simply a cosmetic From value: receiving servers evaluate domain authentication and alignment when deciding how to treat your mail. Before deploying, complete the sender-domain steps in the email API setup guides and send to a controlled test inbox.

Keep template data safe

Values such as an order reference and customer name can contain characters that should not be inserted into HTML unescaped. In PHP, use htmlspecialchars() for content placed inside an HTML text node. This is not only a security habit; it also prevents malformed order references or names from breaking the message markup.

Avoid using request parameters from the storefront as direct email fields. Take recipient and order information from the saved PrestaShop objects passed to the hook. That ensures the email corresponds to the created order rather than to an arbitrary request value.

Build the server-side PrestaShop module

A minimal module has a module class, an installation method that registers the hook, and the hook handler. Put it in your project’s modules/ directory as part of your deployment process, then install it from the PrestaShop back office as a custom module. It is not a marketplace integration and should be version-controlled like the rest of your store code.

The following example shows the real shape of the actionValidateOrder context and the field mapping performed before the API request. It uses PHP cURL because it is available on many PrestaShop PHP environments; your team may instead standardize on the HTTP client already used by your application.

<?php

if (!defined('_PS_VERSION_')) {
    exit;
}

class VolaneaOrderReceipt extends Module
{
    public function __construct()
    {
        $this->name = 'volaneaorderreceipt';
        $this->tab = 'administration';
        $this->version = '1.0.0';
        $this->author = 'Your team';
        $this->need_instance = 0;

        parent::__construct();
        $this->displayName = 'Volanea order receipt';
        $this->description = 'Sends an order receipt through Volanea after order validation.';
    }

    public function install()
    {
        return parent::install()
            && $this->registerHook('actionValidateOrder');
    }

    // PrestaShop calls this with:
    // [
    //   'cart' => Cart,
    //   'order' => Order,
    //   'customer' => Customer,
    //   'currency' => Currency,
    //   'orderStatus' => OrderState,
    // ]
    public function hookActionValidateOrder(array $params)
    {
        /** @var Order $order */
        $order = $params['order'];
        /** @var Customer $customer */
        $customer = $params['customer'];
        /** @var Currency $currency */
        $currency = $params['currency'];

        if (!Validate::isEmail($customer->email)) {
            PrestaShopLogger::addLog('Volanea: no valid customer email for order ' . (int) $order->id, 2);
            return;
        }

        // Keep this in PHP-FPM/Apache environment configuration, not theme JS or a form field.
        $apiKey = getenv('VOLANEA_API_KEY');
        $from = getenv('VOLANEA_FROM_EMAIL'); // Example: Orders <orders@shop.example>

        if (!$apiKey || !$from) {
            PrestaShopLogger::addLog('Volanea: missing server-side email configuration', 3);
            return;
        }

        $reference = htmlspecialchars((string) $order->reference, ENT_QUOTES, 'UTF-8');
        $total = Tools::displayPrice((float) $order->total_paid_tax_incl, $currency);

        // Map PrestaShop objects into the Volanea REST email payload.
        $emailPayload = [
            'from' => $from,
            'to' => [$customer->email],
            'subject' => 'We received order ' . $order->reference,
            'html' => '<h1>Thanks for your order</h1>'
                . '<p>Your order reference is <strong>' . $reference . '</strong>.</p>'
                . '<p>Order total: ' . htmlspecialchars($total, ENT_QUOTES, 'UTF-8') . '</p>',
        ];

        // A stable event key makes repeated handling of this order detectable downstream.
        $idempotencyKey = 'prestashop-order-receipt-' . (int) $order->id;

        $ch = curl_init('https://api.volanea.com/v1/emails');
        curl_setopt_array($ch, [
            CURLOPT_POST => true,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_CONNECTTIMEOUT => 3,
            CURLOPT_TIMEOUT => 10,
            CURLOPT_HTTPHEADER => [
                'Authorization: Bearer ' . $apiKey,
                'Content-Type: application/json',
                'Idempotency-Key: ' . $idempotencyKey,
            ],
            CURLOPT_POSTFIELDS => json_encode($emailPayload, JSON_UNESCAPED_SLASHES),
        ]);

        $response = curl_exec($ch);
        $status = (int) curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
        $error = curl_error($ch);
        curl_close($ch);

        if ($response === false || $status < 200 || $status >= 300) {
            PrestaShopLogger::addLog(
                'Volanea send failed for order ' . (int) $order->id
                . '; HTTP ' . $status . '; ' . $error,
                3
            );
            // Do not throw here: an email transport outage should not undo an already-created order.
        }
    }
}

The API endpoint, authorization header, and email schema in production should match the version shown in Volanea’s current reference. Keep this module’s request code close to your deployment configuration so a deliberate API upgrade is reviewed and tested rather than silently copied from an old example.

Store the Volanea API key outside public configuration

The Volanea API key belongs in server-side secret management. For a conventional PrestaShop deployment, that commonly means an environment variable made available to the PHP-FPM pool, Apache virtual host, container runtime, or a dedicated secret manager injected at deploy time.

In the example, getenv('VOLANEA_API_KEY') reads that secret at runtime. The sender identity is also read from VOLANEA_FROM_EMAIL, allowing staging and production to use different verified senders without editing module source.

Do not put the key in any of these locations:

  • A .js file, theme template, or browser-readable environment variable.
  • A hidden checkout field or a client-side tag-manager variable.
  • A repository commit, even in a private repository.
  • An email template, order note, or debug log.
  • A screen recording or support ticket containing full request headers.

A module configuration form in the back office is server-side rather than publicly delivered to shoppers, but it is still usually a less robust choice for a long-lived API secret. Configuration values may be stored in the application database, included in database backups, visible to broad administrator roles, or copied across staging environments. Prefer the hosting platform’s secret mechanism and restrict access to it.

Rotate safely

Plan for key rotation before an incident forces it. Create a new key, update the server secret, deploy or reload the PHP runtime as needed, send a controlled test order, then revoke the old key. Keep the module from logging the Authorization header or full request body.

Also separate environments. A staging store should have a staging API key and a safe recipient policy, such as routing tests only to internal addresses. Otherwise, a developer’s test order can email a real customer from a non-production storefront.

Avoid blocking checkout and design for delivery

The shortest implementation makes the API request directly in hookActionValidateOrder. It is easy to understand, but it puts an external network dependency in the order-validation path. A slow or unavailable endpoint can add latency at the moment a shopper completes checkout.

The example caps the connection phase at three seconds and the full request at ten seconds, and it does not throw an exception after the order exists. That is a baseline safety measure, not a complete reliability architecture.

For a high-volume store, use an outbox pattern. At order validation, write a small record to your own module table: order ID, event type, recipient, normalized payload, attempt count, status, and idempotency key. A scheduled worker then sends pending rows to Volanea. This makes checkout quick and creates a durable audit trail.

A practical outbox lifecycle

A robust worker can use the following state progression:

  1. Insert one pending row for order_id + event_type in the same general processing path as the event.
  2. Claim the row with a lock or atomic status update so two workers cannot send it concurrently.
  3. Submit the request to Volanea with a stable idempotency key.
  4. Mark the row sent only after a successful API response, retaining the provider message identifier when available.
  5. For transient failures, schedule a later attempt with exponential backoff; for permanent failures such as an invalid recipient, mark the row failed for review.

A database-level unique constraint on the event identity is especially valuable. It protects you even if two PHP processes encounter the same hook event or a worker crashes between steps.

When this breaks: failures specific to this integration hop

Every integration has a failure boundary. Here, the hop is PrestaShop’s order hook → your module/worker → Volanea REST API. Treat the possible outcomes as more nuanced than simply “success” or “failure.”

Duplicate sends after retries

PrestaShop core does not operate a generic outbound-webhook delivery service that automatically retries HTTP requests. A custom module or worker decides whether and how to retry. However, duplicate sends can still happen: an administrator may re-run a process, two workers may pick up the same row, or a network connection may fail after Volanea accepted the request but before your code received the response.

That last case is why idempotency is not optional for order-triggered mail. Use a deterministic key such as prestashop-order-receipt-{order-id} for a message that should happen once per order. Do not generate a random key for each attempt; random keys make retries indistinguishable from new sends.

Your own outbox table should also enforce uniqueness for the event. API-side idempotency and application-side uniqueness solve different parts of the problem and work best together.

Timeouts and checkout impact

There is no PrestaShop core webhook timeout setting for this flow because it is not a core outbound webhook. The timeout is whichever limit your PHP HTTP client sets. Without an explicit timeout, a stalled connection can consume PHP workers and make checkout feel slow.

Keep connect and total timeouts bounded. More importantly, do not retry aggressively inside the checkout request. One immediate retry can double the shopper-facing delay and still leave you uncertain whether the first attempt reached the provider. Queue the retry when possible, preserve the idempotency key, and let a worker handle backoff.

If you use a third-party connector or automation platform instead of this module, that service may impose its own inbound webhook timeout and retry behavior. Read its documentation for the exact thresholds; do not assume its retry policy matches PrestaShop’s, because PrestaShop is not the HTTP sender in that architecture.

Missing fields and incompatible assumptions

There are no PrestaShop “standard plans” that selectively omit fields from this hook payload in the way a SaaS webhook product might offer plan-gated payloads. The more realistic causes of missing data are a custom checkout module, a PrestaShop version difference, an order created outside the usual payment flow, or a module that changes the order lifecycle.

Defend against this by checking required values before constructing a message. Recipient email, order ID, and order reference are reasonable minimums. If your copy depends on a carrier, tracking number, invoice, VAT number, or an accepted payment state, verify that data exists at the specific hook you chose rather than assuming it is available at order validation.

Log a non-sensitive reason when data is missing, and route it to a review queue instead of sending a partial or misleading email. Never substitute an arbitrary address or a blank reference just to keep an automation green.

Authentication and sender failures

An API request can reach Volanea yet still fail because the key is invalid, the sender domain is not verified, or the From identity is not permitted. These are configuration failures, not retry candidates. Repeatedly retrying them only creates noise and can delay discovery of the underlying issue.

Alert on authentication and sender-configuration responses separately from network errors. A production-ready integration should make it easy to answer: was this order event created, was it attempted, did Volanea accept it, and which message identifier corresponds to it?

Test the integration before enabling it for customers

Start with a local or staging store and a sender domain configured for that environment. Create an order through the same payment path used in production, then inspect the module log and the Volanea activity for the accepted request.

Test more than the happy path:

  • Place an ordinary order with a valid customer email.
  • Use a customer record with an invalid or absent email and confirm no API call is made.
  • Temporarily use an invalid API key and confirm the order still completes while the failure is logged.
  • Simulate a slow API endpoint or blocked outbound connection to observe checkout latency.
  • Invoke the same event twice and confirm the idempotency key prevents a second receipt.
  • Create orders using each payment module and confirm the selected trigger’s state is correct.
  • Check the rendered email in major mailbox providers for subject, sender, reference, and price formatting.

Keep an eye on localization as well. Tools::displayPrice() uses the currency object, but your email copy, tax wording, number conventions, and legal disclosures may need to vary by customer language and country. An initial implementation can send a single language if that is your store policy; just make the choice deliberate.

Operational guidance for transactional email

An order receipt is transactional mail. Its recipient and content arise from an order relationship, not from a marketing subscription. Keep that purpose clear in the subject line, sender name, and template. Do not turn a receipt into a heavily promotional campaign unless you have separately handled consent and local legal requirements.

Use a recognizable sender such as Orders <orders@your-domain> and a reply-capable address where appropriate. Customers often reply to receipts with address corrections, cancellation questions, or payment concerns. A no-reply address may reduce operational work in the short term while creating a worse support experience.

Monitor both application behavior and mailbox outcomes. Application logs tell you whether your module requested a send. Provider activity tells you whether the API accepted it. Delivery, bounce, and complaint signals tell you whether recipient infrastructure accepted the message. These are separate stages, and conflating them makes incident diagnosis difficult.

As volume grows, calculate sending costs and operational requirements from actual transactional volume, not only campaign volume. Order messages, password resets, payment notices, and support notifications can all contribute. Review transactional email pricing when choosing the plan and capacity that match your expected event rate.

Conclusion

To send email from PrestaShop with Volanea, build the integration around a real PrestaShop server-side lifecycle hook rather than a nonexistent core outbound-webhook setting. actionValidateOrder provides a clear order-created trigger and a useful object payload; a private module maps that data to an API request while the API key remains in server-side secret storage.

The first version can be compact, but the production version should be deliberate about idempotency, timeouts, queueing, sender authentication, and auditability. That is what turns an order email from a best-effort HTTP call into dependable customer communication.

FAQ

Does PrestaShop have a native outbound webhook for new orders?

PrestaShop core provides hooks for modules, including actionValidateOrder, but not a generic back-office outbound-webhook builder for posting order JSON to arbitrary URLs. A custom server-side module is the direct integration route.

Which PrestaShop event should send an order receipt?

Use actionValidateOrder for a receipt that says the store received or created the order. Use a later order-status transition hook if the email claims that payment was accepted, the order shipped, or another later milestone occurred.

Where should the Volanea API key be stored?

Store it in server-side secret management, such as a PHP-FPM environment variable, container secret, or hosting secret store. Do not expose it in JavaScript, theme files, checkout fields, repositories, or logs.

Can a retry create duplicate order emails?

Yes. A timeout can occur after the provider accepted a request, leaving your application unsure of the result. Use one deterministic idempotency key per order-email event and enforce a unique event record in your own outbox table.

Should the Volanea API call run during checkout?

It can for a small implementation, with strict timeouts and no checkout-breaking exception. For higher reliability or volume, write an outbox record during the hook and send it asynchronously from a worker so external API latency does not slow checkout.