If you need to send email from Joomla after editorial content is created or updated, the dependable route is a small server-side Joomla plugin that listens for a real Joomla event and calls the Volanea REST API. Joomla does not include a general no-code outbound-webhook feature for content events, so there is no native marketplace-style Volanea connector to install.

This guide uses Joomla’s onContentAfterSave plugin event. That event runs after a Joomla article has been saved, giving your extension access to the saved article and the context in which it was saved. The plugin then turns the article data into an email payload and sends it from your server to Volanea.

The result is useful for editorial alerts, approval notifications, subscriber digests, internal handoffs, and workflows such as “email the review team when a new article is submitted.” It is not an excuse to use the CMS save lifecycle as a bulk campaign engine: one editor action can be retried, repeated, or performed in batches, so the integration needs explicit safeguards.

What Joomla can and cannot do natively

Joomla has a mature plugin-event system and a server-side HTTP client, both of which are appropriate building blocks for this integration. A custom plugin can subscribe to content events, read the saved article’s fields, and make an HTTPS request using Joomla’s HTTP facilities.

What Joomla does not provide as a standard content-authoring feature is a universal outbound webhook configuration where an administrator picks Article saved, enters a URL, maps arbitrary JSON fields, and stores API authentication. Joomla’s workflows are designed to manage content stages and transitions inside Joomla; they are not a general HTTP automation product. Likewise, Joomla’s Web Services API is primarily an inbound API for clients that want to work with Joomla data, not a prebuilt outbound automation channel.

That distinction matters. There is no native Volanea Joomla app, no official “install and connect” flow, and no browser-side JavaScript integration that should contain a sending key. The direct approach is a custom extension running in Joomla’s PHP process.

For sites where custom extension work is not an option, use a middleware service that Joomla can safely call from a bespoke endpoint, or have a developer create a narrow Joomla plugin that posts only the required event data to your automation endpoint. Middleware can be useful when non-developers need to alter routing, but it adds another delivery hop, another credential store, and another retry policy. For high-value transactional messages, a direct server-to-server Volanea request is generally easier to observe and reason about.

The concrete trigger: Joomla’s article save event

The trigger in this implementation is Joomla’s content plugin event named onContentAfterSave. For core articles, the plugin checks for the context com_content.article. The event fires after an article record has been saved; it is not limited to publication, first creation, or a particular workflow stage.

That last point is important. An article can be saved as unpublished, saved repeatedly during editing, saved through the administrator interface, or saved by code. If you blindly send every time onContentAfterSave runs, editors may create a surprising number of email messages.

The traditional event method receives these arguments:

  • $context: a string identifying what was saved. For a core Joomla article, use com_content.article.
  • $article: the saved article object, which includes values such as id, title, alias, state, introtext, fulltext, and created_by.
  • $isNew: true when Joomla created the record and false when it updated an existing one.
  • $data: the submitted data array supplied during the save operation.

Joomla does not serialize these arguments into an outbound JSON webhook payload. They are PHP values passed inside the Joomla application. Your plugin is responsible for deciding what information is safe and useful, then constructing the JSON body sent to Volanea.

For an editorial notification, a sensible first rule is: send once when a newly created article is already published. In the core content table, a published item has state equal to 1. This rule will not send when an editor creates a draft; that is intentional. If your real business event is “the article entered a review stage,” build that logic around the workflow transition your site uses rather than treating any save as an approval.

Choose an email policy before writing code

The HTTP request is the easy part. The difficult part is stating precisely which CMS action represents a message-worthy event. Write that policy down before implementation, because it determines duplicate prevention, recipient choice, and test cases.

Common policies include:

  1. New published article: Send a message only if $isNew is true and the saved article’s state is 1. This is the example used below.
  2. First publication: Send when an existing draft changes from unpublished to published. This requires comparing the prior state with the new state; $isNew alone is insufficient.
  3. Workflow transition: Send when a content item reaches a defined workflow stage. Use your site’s workflow configuration and extension event model, rather than assuming the numeric article state represents every editorial stage.
  4. Contact-form submission: Use a purpose-built form extension’s documented server-side event or handler. A Joomla article event will not tell you that a visitor submitted an unrelated form.
  5. User account event: Use the relevant user plugin event for account creation or profile changes, not a content event.

Avoid a policy based solely on the article title, category name, or an editor-entered checkbox unless that is an explicit business requirement. Such signals are easy to change accidentally and difficult to audit later.

Also decide who receives the message. The example stores one operational recipient in the plugin configuration. That is appropriate for an internal editorial mailbox. For customer-facing delivery, derive an address only from a validated, permissioned data source, and never use an arbitrary request parameter as the destination. Before adding a user-provided recipient to a production send path, validate the address with an email address verification tool and enforce the consent rules that apply to your use case.

Build a Joomla content plugin, not a client-side script

Create a conventional Joomla content plugin. In Joomla 4 and Joomla 5, extensions are normally packaged with a manifest and use the current extension structure; the exact packaging details depend on the Joomla version and your project’s extension conventions. The key implementation detail is that the logic runs on the Joomla server under a content plugin.

At minimum, give the plugin configuration fields for:

  • a Volanea API key;
  • a verified Volanea sender address;
  • the internal notification recipient;
  • an enabled/disabled switch;
  • optionally, a category allowlist and a send mode such as new published only.

Use the plugin’s parameters for configuration that administrators need to change. Joomla stores extension parameters server-side in its database configuration; they are not automatically emitted to a site visitor’s browser. Restrict access to the plugin configuration using Joomla administrator permissions, because anyone who can read or alter the key can send mail through your account.

Do not put the Volanea key in any of the following places:

  • JavaScript delivered to a public page;
  • a module setting rendered into HTML;
  • a public JSON endpoint;
  • a Git repository or example configuration file;
  • a URL query string;
  • a browser automation rule.

For stronger operational separation, have the plugin read a deployment-provided environment variable or secret manager value instead of storing the API key in Joomla’s database. That approach reduces exposure in database exports and lets operations rotate a secret without an administrator editing the CMS. Whichever approach you use, the crucial property is the same: the key is available only to trusted server-side PHP code.

Field mapping: from a Joomla article to an email

A CMS article is not an email. Treat the conversion as an explicit mapping rather than passing a whole Joomla object into a JSON encoder. Whole-object sending can leak author identifiers, internal parameters, custom-field values, revision details, or content that was never intended for a notification.

Here is a practical mapping for a new-published-article alert:

Joomla valueVolanea email fieldWhy it is mapped this way
Configured plugin senderfromThe sender should be a verified address or domain identity in Volanea.
Configured editorial mailboxtoThis is a controlled operational recipient, not a visitor-supplied value.
$article->titlesubject and HTML headingIt gives the receiving team immediate context.
$article->idSubject and event metadataIt makes duplicate troubleshooting and CMS lookup easier.
$article->introtextHTML and plain-text bodyIt provides a short article preview.
$article->alias plus site URLArticle URLIt lets a recipient open the saved item directly.
$isNew and $article->stateSending decision onlyThey should control whether mail is sent, not normally be exposed to recipients.

The body should escape content before inserting it into HTML. Joomla introductory text can itself contain HTML, but an internal email template should not blindly embed editor-provided markup if you do not want external images, unexpected layout, or unsafe links in the email. The example below uses strip_tags() for a compact text preview, then HTML-escapes that preview.

Use a stable, human-readable subject such as New published article #123: Example title. Including the record ID gives support staff a concrete reference when an editor says “the alert was duplicated” or “the wrong story was sent.”

Working server-side REST call example

The following code shows the complete event-to-email transformation inside a Joomla content plugin method. It uses Joomla’s HTTP client and posts JSON to Volanea’s REST sending endpoint. The API key is read from server-side plugin parameters; it is never exposed to the browser.

Before deploying, create and verify the sender identity in Volanea, use an API key authorized for sending, and confirm the endpoint and available message fields against the Volanea REST API reference. The request below uses the standard Authorization: Bearer header and an email payload containing from, to, subject, html, and text.

<?php
// plugins/content/volanea/src/Extension/Volanea.php

declared('_JEXEC') or die;

namespace Joomla\Plugin\Content\Volanea\Extension;

use Joomla\CMS\Http\HttpFactory;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Uri\Uri;

final class Volanea extends CMSPlugin
{
    public function onContentAfterSave(
        $context,
        &$article,
        $isNew,
        $data = []
    ): bool {
        // Only react to core Joomla articles.
        if ($context !== 'com_content.article') {
            return true;
        }

        // Policy: notify once for a newly created, published article.
        if (!$isNew || (int) $article->state !== 1) {
            return true;
        }

        $apiKey = trim((string) $this->params->get('volanea_api_key'));
        $from   = trim((string) $this->params->get('from_email'));
        $to     = trim((string) $this->params->get('notification_email'));

        if ($apiKey === '' || $from === '' || $to === '') {
            // Log a server-side configuration error in a production plugin.
            return true;
        }

        $articleId = (int) $article->id;
        $title = trim((string) $article->title);
        $preview = trim(strip_tags((string) $article->introtext));
        $preview = mb_substr($preview, 0, 1200);

        // Adjust this route if your site uses a different article URL pattern.
        $articleUrl = Uri::root() . 'index.php?option=com_content&view=article&id='
            . $articleId;

        $safeTitle = htmlspecialchars($title, ENT_QUOTES, 'UTF-8');
        $safePreview = nl2br(htmlspecialchars($preview, ENT_QUOTES, 'UTF-8'));
        $safeUrl = htmlspecialchars($articleUrl, ENT_QUOTES, 'UTF-8');

        // Explicit Joomla-to-Volanea field mapping.
        $payload = [
            'from'    => $from,
            'to'      => [$to],
            'subject' => "New published article #{$articleId}: {$title}",
            'html'    => "<h1>{$safeTitle}</h1>"
                . "<p><strong>Joomla article ID:</strong> {$articleId}</p>"
                . "<p>{$safePreview}</p>"
                . "<p><a href=\"{$safeUrl}\">Open the article</a></p>",
            'text'    => "New published article #{$articleId}: {$title}\n\n"
                . $preview . "\n\nOpen: " . $articleUrl,
        ];

        $headers = [
            'Authorization' => 'Bearer ' . $apiKey,
            'Content-Type'  => 'application/json',
            'Accept'        => 'application/json',
        ];

        try {
            $http = HttpFactory::getHttp();
            $response = $http->post(
                'https://api.volanea.com/v1/send',
                json_encode($payload, JSON_THROW_ON_ERROR),
                $headers
            );

            if ($response->code < 200 || $response->code >= 300) {
                // Record $response->code and a redacted response body in your log.
                // Do not log Authorization headers or the API key.
            }
        } catch (\Throwable $exception) {
            // Log the exception server-side. Do not throw here unless you intend
            // to make the Joomla article save fail when email delivery fails.
        }

        return true;
    }
}

The code deliberately returns true whether Volanea accepts the message or not. That keeps a temporary email-provider outage from preventing an editor from saving content. For many content sites, preserving the CMS action is the correct trade-off. If the email is a legally or commercially mandatory part of the operation, do not merely change the return value and hope for the best; use a durable outbox design described later in this guide.

The to field is an array in this example, making the recipient shape explicit even for one address. If your Volanea account uses a different recipient object format or supports additional fields such as reply-to, tags, template identifiers, scheduled sends, or idempotency headers, add only the documented fields your use case needs.

Authenticate safely and verify the sending identity

An API key identifies and authorizes the caller. A sender address identifies the visible origin of the message. They solve different problems, and both must be configured correctly.

Store the Volanea API key in the Joomla plugin’s server-side parameters or, preferably, inject it from protected runtime configuration. Store the sender address separately. This makes it possible to rotate a key while keeping the From identity stable, or switch a notification stream from one approved sender to another without a code release.

The sender must be an identity Volanea accepts for your account. In most production deployments that means verifying the sending domain and publishing the DNS records Volanea provides. Do not guess at DNS record names or reuse values from another provider: use the records displayed for your Volanea domain setup. Domain authentication improves alignment and makes recipients more likely to accept the mail as expected.

A few operational rules are worth making explicit:

  • Create a dedicated restricted API key for the Joomla integration where your account’s access model allows it.
  • Never include the key in screenshots, support tickets, debug dumps, or exception messages.
  • Rotate the key immediately if it reaches source control or a public log.
  • Use HTTPS for the Joomla site and for the outbound API connection.
  • Keep the from address aligned with the domain identity configured in Volanea.
  • Set a sensible Reply-To only when replies should go to a monitored mailbox.

Authentication failures usually appear as an HTTP 401 or 403 response. Sender-verification failures usually appear as a validation-style 4xx response. Treat those as configuration issues, not transient events to retry hundreds of times.

Test the integration without emailing everyone

Test in layers. First, install the plugin in a non-production Joomla environment and configure a sender and recipient that your team controls. Create a new article, set it to published, and save it. Confirm that the plugin receives com_content.article, that $isNew is true, and that the article state is 1.

Second, inspect the server-side logs. Log a correlation value such as the Joomla article ID, the HTTP status, and Volanea’s returned message identifier if the API response contains one. Redact recipient addresses where required by your logging policy, and never log the authorization header.

Third, inspect the delivered message itself. Check the From address, subject, text alternative, HTML rendering, and article link. Test titles with quotes, ampersands, Unicode characters, and long text. Those inputs reveal whether your escaping and encoding are correct.

Then test the negative paths:

  • Save an unpublished new article and confirm no email is sent.
  • Edit an existing published article and confirm no second email is sent under the example policy.
  • Save a non-article content type and confirm the context guard prevents delivery.
  • Remove the API key in a test environment and confirm the article still saves while the configuration problem is logged.
  • Simulate a failed outbound request and confirm your error reporting does not reveal secrets.

Finally, test deliverability from a real receiving mailbox, not just an API success response. A 2xx response tells you Volanea accepted the request; it does not guarantee that a mailbox provider placed the message in the inbox. Verify sender-domain authentication, content quality, recipient consent, and any suppression behavior for the intended stream.

When this breaks: retries, timeouts, and incomplete fields

This integration has a specific failure boundary: Joomla saves an article, then your plugin performs an outbound HTTP request to Volanea. That boundary can fail after one system has completed work but before the other system can report the result back.

A timeout can produce a duplicate send

Suppose Joomla posts the payload, Volanea accepts and queues the message, but the network connection times out before Joomla receives the response. From Joomla’s perspective, the operation failed. If your plugin or hosting stack retries the request, Volanea may receive a second valid send request and deliver two messages.

The same risk exists if an administrator saves again after seeing an error, if a reverse proxy retries a request, or if a custom queue retries an ambiguous timeout. Do not equate “no successful HTTP response” with “Volanea definitely did not receive the message.”

Mitigate this with idempotency. If the Volanea endpoint supports an idempotency key, create a stable key such as joomla-article-published-123 and send it using the documented field or header. The same event must generate the same key on every retry. If the API does not provide idempotency, create a local delivery ledger keyed by an event type and article ID, and record accepted sends before allowing subsequent attempts.

Joomla save retries and repeated editor actions

Joomla’s onContentAfterSave event runs whenever qualifying code saves the article. The example avoids most accidental repeats by requiring $isNew, but it does not solve every business policy. If you need first-publication behavior, persist a marker such as a custom field or separate integration table after a successful accepted request. Do not use only a PHP static variable; it disappears after the request ends and cannot protect against another web request.

A robust local record might store the Joomla article ID, event type, payload hash, intended recipient group, Volanea message ID, attempt count, accepted timestamp, and last error category. This gives support staff evidence to answer whether an email was attempted, accepted, or deliberately suppressed.

Missing payload fields on some plans or content types

Not every Joomla site collects the same fields, and not every workflow makes every field available in the same way. Core articles normally have a title and article ID, but introtext can be empty. A custom content extension may use a different $context, a different table object, and a different field model entirely. Guest-authored content may lack information you expected from a user profile.

Treat optional fields as optional. In the example, an empty introtext becomes an empty preview rather than a PHP error. Require only the fields the email absolutely needs, such as a recipient, sender, subject source, and record identifier. If a custom field is used to select recipients or templates, validate that it exists and has an allowed value before sending.

The phrase “some plans” also matters if your Joomla site uses a third-party form, CRM, membership, or workflow extension. Their webhook, API, or automation capabilities can differ by edition and subscription. Do not assume an event or field exists because another Joomla extension offers it. Confirm the exact extension event and payload in that product’s documentation before using it as a trigger.

Permanent versus transient failures

Retry only errors that are plausibly temporary: connection failures, gateway failures, or documented rate-limit responses. Use bounded exponential backoff with jitter, and cap the number of attempts. Retrying an invalid API key, an unverified sender, malformed JSON, or an invalid recipient will not repair the configuration and can create noise.

For a high-volume or business-critical process, do not make the HTTP call directly inside the article save request. Write an outbox job to your database after the save, process it with a controlled worker or scheduled task, and mark it complete only after a confirmed outcome. The editor gets a fast save, retries are observable, and duplicate controls have a durable home.

Deliverability and content considerations

A working API call is only the beginning of reliable email. A message triggered by Joomla is still subject to sender authentication, mailbox-provider filtering, recipient engagement, and the quality of the content you generate.

Use a sender domain dedicated to mail you control and authenticate it with the DNS records provided in Volanea. Keep transactional or operational notifications distinct from broad promotional campaigns where possible. An editorial notification to an internal team can be concise and utilitarian; a subscriber alert should be permission-based, recognizable, and have the compliance features appropriate to its purpose.

The article preview deserves special attention. CMS HTML can contain tracking images, embedded media, relative links, layout classes, and markup that email clients handle poorly. A plain-text excerpt plus one absolute URL is often more robust than reproducing an entire article in an email. It also makes the message smaller and reduces the chance that a malformed content block ruins the template.

Use both html and text. The HTML version provides a readable branded layout, while the text version supports recipients and systems that do not render HTML. Keep their meaning equivalent: a user should be able to understand what happened and open the article from either alternative.

Alternatives to an article-save integration

A Joomla content plugin is the right tool only when the event originates from article persistence. Choose a different integration boundary when the underlying business event differs.

For a public contact form, integrate in the form extension’s documented server-side submission lifecycle. That gives you access to the actual submitted values, validation result, and consent fields. Do not create an article just to reuse onContentAfterSave.

For account messages, use the Joomla user lifecycle or your membership extension’s event model. For order receipts, send from the commerce system at the point where payment or order status changes. For a nightly roundup, query approved content on a schedule and build one controlled campaign rather than sending a message every time an editor presses Save.

Middleware is a valid alternative where an organization needs visual routing and transformation. The minimal Joomla plugin would post a deliberately small JSON object—article ID, title, state, and canonical URL—to a protected middleware webhook. The middleware would then make the Volanea API call. However, preserve the same security and reliability principles: authenticate the Joomla-to-middleware call, avoid client-side secrets, make events idempotent, and identify which system owns retrying each hop.

A production checklist

Before enabling this on a live Joomla site, review the whole path rather than only the PHP method.

  • Confirm the trigger policy: new item, first publication, workflow transition, or a different event.
  • Restrict the plugin to com_content.article unless you have mapped another context deliberately.
  • Verify the Volanea sender identity and use the exact DNS values Volanea provides.
  • Keep the Volanea API key server-side and restrict who can change plugin parameters.
  • Validate and control recipients; do not pass arbitrary visitor input to to.
  • Escape article-derived values before placing them in HTML email.
  • Include a plain-text alternative and an absolute link to the Joomla record.
  • Log article ID, event type, status, and a provider message identifier where available, but redact secrets.
  • Define idempotency and retry rules before enabling automatic retries.
  • Use a durable outbox or queue if lost or duplicate notifications would have material consequences.
  • Test saves, edits, drafts, failures, and delivery in a controlled mailbox.

The practical lesson is simple: sending email from Joomla is not a plugin-installation exercise when the trigger is a CMS lifecycle event. It is a server-side integration. Joomla supplies the event and HTTP building blocks; Volanea supplies the email delivery API; your implementation supplies the business rule, payload mapping, credential handling, and duplicate protection.

FAQ

Does Volanea have a native Joomla plugin?

No. This integration uses a custom Joomla content plugin and the Volanea REST API. Joomla does not provide a built-in Volanea marketplace connector or a generic no-code content webhook configuration.

What starts the email in this example?

The Joomla content plugin event onContentAfterSave starts the logic. The example limits it to the com_content.article context and sends only when a newly created article has published state 1.

Where should the Volanea API key be stored?

Keep it in server-side Joomla plugin parameters with tightly restricted administrator access, or inject it from protected environment configuration or a secret manager. Never expose it in page JavaScript, HTML, public configuration, or source control.

Can an article save send the same message twice?

Yes. Repeated saves, retry behavior, and timeouts after provider acceptance can all create duplicates. Use a documented idempotency mechanism where available or persist a local delivery ledger keyed to the Joomla event.

Should a failed Volanea request stop Joomla from saving an article?

Usually no for editorial notifications: log and retry through a controlled process so editors can continue working. If delivery is mandatory, use a durable outbox workflow rather than relying on one synchronous request during the save operation.