Send email from TYPO3 reliably by adding a small custom finisher to TYPO3’s Form Framework and calling Volanea’s REST API from the server. This approach works without a native TYPO3 marketplace extension, keeps your API key private, and gives you control over validation, field mapping, retries, and delivery diagnostics.

The right TYPO3 integration model

Volanea does not ship a native TYPO3 app, marketplace listing, or installable TYPO3 extension. That is not a blocker, but it does determine the correct implementation path: use TYPO3’s server-side extension APIs rather than looking for an install-and-connect flow.

TYPO3 has two relevant outbound-integration capabilities:

  • Its webhooks system extension can send outgoing HTTP webhooks for supported backend events, such as page modifications and file events.
  • Its Form Framework supports custom finishers, which run after a form has been successfully submitted and validated.

For transactional email triggered by a website contact form, registration form, quote request, or support request, a custom Form finisher is the better fit. TYPO3’s standard outgoing webhooks are useful for events such as a page being created or a file changing, but a public form submission is handled by the Form Framework’s finisher pipeline.

The concrete trigger in this guide is therefore: a visitor successfully submits a TYPO3 EXT:form form, validation passes, and TYPO3 executes the configured VolaneaEmail finisher. Finishers execute in the order in which they appear in the form definition. If your form uses a Redirect finisher, place the Volanea finisher before it, because Redirect terminates later finishers.

This is a direct server-to-server integration:

  1. A visitor submits a TYPO3 form.
  2. TYPO3 validates and property-maps the submitted fields.
  3. The custom finisher receives the validated formValues array.
  4. The finisher maps those values into Volanea’s POST /v1/send JSON payload.
  5. Volanea accepts the transactional email for sending and returns an API response.
  6. TYPO3 either continues to its confirmation or redirect finisher, or logs an integration failure for operators to investigate.

That design has two important benefits. First, the Volanea secret key never reaches the browser. Second, the recipient, subject, and message are created only after TYPO3 has applied the form’s server-side validators.

What TYPO3 actually gives your finisher

A TYPO3 custom finisher extends TYPO3\CMS\Form\Domain\Finishers\AbstractFinisher. When TYPO3 invokes it, the finisher context exposes the submitted values through:

$this->finisherContext->getFormValues()

Those values are the submitted form data after validation and property mapping. TYPO3 does not wrap a form submission in a generic, prebuilt outbound webhook envelope for this use case. Instead, your finisher receives an associative PHP array whose keys are the identifiers you gave the form elements.

For a contact form with name, email, company, and message elements, the practical payload shape available to your finisher is:

[
    'name' => 'Avery Morgan',
    'email' => 'avery@example.com',
    'company' => 'Northwind Studio',
    'message' => 'Could we discuss an implementation timeline?'
]

The exact keys are not universal. They come from your form definition. A form with an element identifier of emailAddress produces an emailAddress key, not an email key. This is why explicit mapping matters: it prevents a future editor rename from silently sending blank fields or routing mail to the wrong recipient.

The Form Framework can support multi-step forms, conditional variants, checkboxes, select values, and file uploads. Do not assume every field exists on every successful submission. A field rendered only for one form variant, for example, may be absent or empty for another. Treat optional fields as optional in PHP, and validate the fields required to send the particular email.

Prerequisites before you send production email

Set up the sending side before adding code. A correct API request is not enough if the sender domain is not ready for mail delivery.

1. Verify a Volanea sending domain

Add the domain or sending subdomain that will appear in the From address, then install the DNS records Volanea provides. The precise records depend on the domain configuration, so copy the values from your Volanea domain setup rather than guessing DNS hostnames or record values.

For a site that sends website messages, a clear sender might be:

Website Messages <website@example.com>

Use a sender address on a domain you control. Avoid using the visitor’s address in From; doing so can break DMARC alignment and makes replies confusing. Put the visitor’s address in Reply-To only if your sending policy and the API payload you use support that field. In the example below, the notification is sent to your internal inbox and includes the visitor’s address in the body, which works even if you choose not to set a reply-to address.

2. Create a server-side Volanea secret key

The Volanea send endpoint uses a secret API key, identified by the sk_ or sk_test_ prefix. Use a test key while building the integration, then replace it with a live secret key when the sending domain and form behavior have been tested.

Keep the key outside the public web root and outside client-delivered configuration. Do not put it in:

  • TypoScript rendered into a page.
  • JavaScript, a frontend build variable, or a public JSON configuration endpoint.
  • A form YAML file stored where non-developers can download or edit it.
  • A Git repository, even a private one, unless it is encrypted with an approved secrets workflow.

Store VOLANEA_API_KEY in the environment of the PHP-FPM, Apache, container, or hosting deployment that runs TYPO3. The custom finisher reads it with getenv(), so the value is available only while TYPO3 executes server-side PHP.

3. Confirm outbound HTTPS access

The TYPO3 server must be able to make an HTTPS request to https://api.volanea.com. TYPO3 ships with Guzzle and provides TYPO3\CMS\Core\Http\RequestFactory as its HTTP wrapper, so a separate HTTP library is not needed for this integration.

If your host restricts outbound connections, allow HTTPS on port 443 to the Volanea API. Do not work around a firewall restriction by moving the secret key into frontend JavaScript. Fix the server’s egress configuration instead.

For endpoint details, authentication behavior, and current request fields, consult the email API reference and setup guides before deploying changes.

Build the custom Volanea email finisher

The following implementation sends a notification to an internal recipient when a contact form is submitted. It deliberately creates both HTML and plain-text content, escapes visitor-controlled values before inserting them into HTML, and sends a deterministic idempotency key.

The idempotency key in this example is based on the submitted values. That is useful for protecting against a repeated execution of the same form data, but it is not a substitute for a durable submission record in a high-volume workflow. A stronger production pattern is covered later.

Create this class in your site package:

<?php

declare(strict_types=1);

namespace Vendor\SitePackage\Domain\Finishers;

use TYPO3\CMS\Core\Http\RequestFactory;
use TYPO3\CMS\Form\Domain\Finishers\AbstractFinisher;

final class VolaneaEmailFinisher extends AbstractFinisher
{
    protected $defaultOptions = [
        'recipient' => '',
        'from' => '',
        'subject' => 'New website enquiry',
    ];

    public function __construct(
        private readonly RequestFactory $requestFactory,
    ) {
        parent::__construct();
    }

    protected function executeInternal(): void
    {
        $values = $this->finisherContext->getFormValues();

        // These keys must match your TYPO3 form-element identifiers.
        $name = trim((string)($values['name'] ?? ''));
        $email = trim((string)($values['email'] ?? ''));
        $company = trim((string)($values['company'] ?? ''));
        $message = trim((string)($values['message'] ?? ''));

        $recipient = trim((string)$this->parseOption('recipient'));
        $from = trim((string)$this->parseOption('from'));
        $subject = trim((string)$this->parseOption('subject'));
        $apiKey = getenv('VOLANEA_API_KEY') ?: '';

        if ($apiKey === '' || $recipient === '' || $from === '') {
            throw new \RuntimeException('Volanea email configuration is incomplete.');
        }

        if ($name === '' || $email === '' || $message === '') {
            throw new \RuntimeException('Required contact-form values are missing.');
        }

        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new \RuntimeException('The submitted email address is invalid.');
        }

        $html = sprintf(
            '<h1>New website enquiry</h1>'
            . '<p><strong>Name:</strong> %s</p>'
            . '<p><strong>Email:</strong> %s</p>'
            . '<p><strong>Company:</strong> %s</p>'
            . '<p><strong>Message:</strong></p><p>%s</p>',
            htmlspecialchars($name, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'),
            htmlspecialchars($email, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'),
            htmlspecialchars($company !== '' ? $company : '—', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'),
            nl2br(htmlspecialchars($message, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')),
        );

        $text = "New website enquiry\n\n"
            . "Name: {$name}\n"
            . "Email: {$email}\n"
            . "Company: " . ($company !== '' ? $company : '—') . "\n\n"
            . "Message:\n{$message}\n";

        // Field mapping: TYPO3 form values become the Volanea send payload.
        $payload = [
            'from' => $from,
            'to' => $recipient,
            'subject' => $subject,
            'html' => $html,
            'text' => $text,
        ];

        // Stable for the same logical content; replace with a persisted outbox ID
        // when implementing automatic retries or high-value workflows.
        $idempotencyKey = hash(
            'sha256',
            'typo3-contact|' . json_encode($values, JSON_THROW_ON_ERROR)
        );

        $response = $this->requestFactory->request(
            'https://api.volanea.com/v1/send',
            'POST',
            [
                'headers' => [
                    'Authorization' => 'Bearer ' . $apiKey,
                    'Content-Type' => 'application/json',
                    'Idempotency-Key' => $idempotencyKey,
                ],
                'body' => json_encode($payload, JSON_THROW_ON_ERROR),
                'timeout' => 10,
                'connect_timeout' => 3,
            ],
        );

        $statusCode = $response->getStatusCode();
        if ($statusCode < 200 || $statusCode >= 300) {
            throw new \RuntimeException(
                'Volanea send failed with HTTP status ' . $statusCode
            );
        }
    }
}

The important mapping occurs in the $payload array:

TYPO3 sourceVolanea fieldPurpose
configured finisher optionfromVerified sender identity
configured finisher optiontoInternal recipient for the notification
configured finisher optionsubjectNotification subject line
name, email, company, messagehtmlEscaped HTML email body
name, email, company, messagetextPlain-text alternative

The visitor’s email address is not used as the API recipient in this notification example. If you are building an acknowledgement email instead, map the validated $email value to to, use a verified from address, and ensure the public form cannot be abused to send arbitrary messages to arbitrary third parties.

Register the finisher with TYPO3 Form

Next, tell the standard Form prototype that the VolaneaEmail identifier maps to your PHP class. Add this to your site package’s form configuration, for example EXT:site_package/Configuration/Form/CustomFormSetup.yaml:

prototypes:
  standard:
    finishersDefinition:
      VolaneaEmail:
        implementationClassName: 'Vendor\SitePackage\Domain\Finishers\VolaneaEmailFinisher'
        options:
          recipient: 'sales@example.com'
          from: 'Website Messages <website@example.com>'
          subject: 'New website enquiry'

Then add the finisher to the relevant form definition. This example assumes a YAML form definition with fields named name, email, company, and message:

identifier: contact-form
label: 'Contact us'
type: Form
prototype: standard

finishers:
  - identifier: VolaneaEmail
    options:
      recipient: 'sales@example.com'
      from: 'Website Messages <website@example.com>'
      subject: 'New contact-form enquiry'
  - identifier: Confirmation
    options:
      message: 'Thanks — we will get back to you shortly.'
  - identifier: Redirect
    options:
      pageUid: 42

The execution order is intentional. Volanea sends the email first, TYPO3 then prepares the confirmation behavior, and Redirect remains last. If you put the redirect first, TYPO3 will not execute the email finisher after it.

Keep recipient and sender settings as finisher configuration rather than submitted fields. A visitor should never be able to choose the internal recipient by altering browser data. Similarly, do not permit a public subject field to become the actual email subject without a fixed prefix and strict length and character controls.

Secure the API key and the form boundary

The integration has two trust boundaries: browser to TYPO3, and TYPO3 to Volanea. The API key belongs only on the second boundary.

Where the key should live

Set the key as an environment variable in your deployment environment:

VOLANEA_API_KEY=sk_live_replace_with_your_secret_key

The exact configuration mechanism depends on hosting. In a container, it may be a container secret. On a managed server, it may be a protected environment-variable setting for the PHP application. In a CI/CD deployment, it should be injected by the deployment platform’s secrets manager.

The finisher reads the value only during server-side execution:

$apiKey = getenv('VOLANEA_API_KEY') ?: '';

That is materially safer than adding the secret to a YAML form definition. Form definitions can be maintained by integrators and may be stored under locations that are easier to export, inspect, or accidentally commit. The sender, recipient, and subject may be suitable configuration values; a live API key is not.

Validate before constructing email content

TYPO3 validation should reject malformed required fields before finishers run. The PHP code still validates the email address and checks required values because integrations should not rely on a single layer of validation.

Also treat the message field as untrusted content. The sample uses htmlspecialchars() before injecting submitted text into the HTML body. Without escaping, a visitor could place markup into a form field and alter the email’s rendered content.

For a public acknowledgement email, use an allowlisted template and fixed subject. Do not turn a general website form into an open email relay by accepting arbitrary recipient addresses, subjects, HTML, or attachments from the browser.

When this breaks: direct TYPO3-to-Volanea troubleshooting

A direct API call is simple, but the failure modes are specific. Build observability around the handoff instead of assuming that a green form confirmation guarantees an email was accepted.

A timeout can mean the message was accepted

The most important edge case is an ambiguous timeout. TYPO3 may lose the API response after Volanea has accepted the send request. If your code blindly retries with a new request identity, one form submission can produce two messages.

Use Volanea’s Idempotency-Key header on every logical send. The key must remain the same when retrying the same event. It must not be a new random UUID generated each time PHP retries a request.

The sample hashes the submitted form values, which gives repeated identical content a stable key. For a production contact form, build a more robust outbox design:

  1. Create a database row with a server-generated submission UUID before calling Volanea.
  2. Store the mapped payload, status, timestamps, and that UUID.
  3. Use the UUID as the Idempotency-Key.
  4. Mark the row accepted only after a successful API response.
  5. Retry pending rows with the same UUID and payload.

This preserves one logical send across PHP errors, queue retries, operator replays, and transient network failures. It also lets you distinguish a genuine second enquiry with identical wording from a retry of the first enquiry.

TYPO3 retries are not a delivery strategy by themselves

A standard synchronous form finisher runs during the visitor’s HTTP request. TYPO3 does not turn your API call into a durable email job merely because the code uses an HTTP client. If you throw an exception, the visitor may see an error; if your hosting restarts a worker, the state of the send is not automatically durable.

For low-volume contact forms, a synchronous call with idempotency and error logging can be sufficient. For order confirmations, account-security messages, or lead flows where no enquiry may be lost, write to a durable outbox and send from a scheduled TYPO3 command or queue worker. Return the form success page after the submission is safely stored, not only after a remote API round trip succeeds.

Missing fields are usually a form-definition problem

TYPO3 Form values reflect the fields that were actually submitted after validation and property mapping. Fields may be absent because of conditional variants, multi-step form paths, renamed element identifiers, or forms that use different layouts for different site languages.

There is no safe assumption that a field such as company, phone, or consent is always present. Use null coalescing defaults for optional values and fail deliberately for fields your email cannot function without, such as the recipient email in an acknowledgement flow.

Do not frame this as a “plan” issue. TYPO3’s Form Framework and Volanea’s single-message send endpoint do not make standard contact-form fields appear based on a plan tier. If an expected value is missing, inspect the actual form definition and log only field names or a redacted diagnostic—not the full personal-data payload.

HTTP errors need different responses

A 401 or 403 response usually points to a missing, invalid, test-versus-live, or insufficiently authorized API key. Confirm the deployment environment loaded VOLANEA_API_KEY; do not print the key in a TYPO3 exception, log file, or backend message.

A 400-range validation response usually means the request shape is wrong: a sender has not been verified, a required API field is missing, an address is invalid, or JSON was malformed. Log the status code and a safely redacted response summary, then compare the payload fields with the current Volanea API reference.

A 500-range response, network failure, DNS failure, or connection timeout may be temporary. Queue or retry it only with the same durable idempotency key. Repeated immediate retries during a provider outage can create load without improving the chance of delivery.

The form succeeds but mail is not visible

Separate API acceptance from inbox placement. Start by checking the Volanea send log and response data to establish whether the message was accepted. Then check sender-domain verification, recipient mailbox spam or quarantine folders, and the message content.

Use a verified domain and a stable From identity. Include useful plain text as well as HTML. Avoid surprising subjects, excessive link tracking for a simple form notification, or user-controlled HTML. These choices do not replace proper domain authentication, but they make contact-form mail easier to inspect and less likely to look suspicious.

Use templates and campaigns carefully

A TYPO3 form notification is normally transactional mail: it exists because a visitor asked a question, created an account, requested a quote, or completed another action. Send that message with the transactional send endpoint, not by adding the visitor to a promotional campaign without clear consent.

You can still use Volanea templates for stable acknowledgement layouts and brand consistency. The integration choice depends on what varies:

  • Use a direct html and text payload for a small internal notification where the body is assembled from form values.
  • Use a stored template when you need a consistent customer acknowledgement, localization, design review, or reusable variables.
  • Use campaigns only for permission-based bulk communications, not as a replacement for a one-to-one form receipt.

If your form collects marketing consent, record it explicitly and separately from the operational request. A visitor who submits a support message has not automatically granted permission for future promotional mail.

Test the complete path before launch

A useful integration test is more than “the PHP class did not crash.” Test each link in the chain with a controlled recipient inbox.

  1. Submit the TYPO3 form with valid required values.
  2. Confirm the success or confirmation behavior appears only after validation succeeds.
  3. Check that the internal recipient receives one message with the correct mapped values.
  4. Submit malformed data and confirm TYPO3 blocks the finisher.
  5. Test an optional field left empty and verify the email remains readable.
  6. Temporarily point the API call at a controlled failure scenario or use a test key to confirm error logging and visitor behavior.
  7. Submit the same test payload twice and verify how your idempotency strategy behaves.
  8. Inspect the Volanea send log to confirm the server-side API request, rather than the browser, created the message.

Keep test and live credentials separate. A sk_test_ key and a live key represent different environments, so a successful test request does not prove that the production sender domain, production key, or production recipient rules are correctly configured.

Alternatives for non-form TYPO3 events

The custom finisher approach is specifically for Form Framework submissions. If the event you care about is a TYPO3 backend event—such as a page created, page updated, file added, file updated, or file removed—TYPO3’s outgoing Webhooks extension may be a better starting point.

In that model, TYPO3 sends a webhook to middleware you operate. The middleware validates the webhook, maps the event payload into a Volanea send request, and holds the Volanea secret key. This is useful when the event is not a form submission or when you want a central integration layer shared by TYPO3 and other systems.

Do not point a generic TYPO3 webhook directly at Volanea’s send endpoint unless its payload exactly matches Volanea’s required send schema and authentication requirements. TYPO3 webhook event payloads describe TYPO3 events; Volanea’s endpoint expects an email payload. Middleware is the adaptation layer between those two contracts.

Automation platforms can also act as middleware, but evaluate their retry behavior, secret storage, payload transformation, and idempotency support before using one for sensitive or high-volume transactional mail. A direct custom finisher is often easier to audit because the mapping, timeout, and failure behavior live in version-controlled PHP.

Conclusion

The dependable way to send email from TYPO3 with Volanea is not a marketplace plugin—it is a server-side custom Form finisher. The trigger is a successful, validated TYPO3 Form submission; the finisher reads TYPO3’s formValues, explicitly maps them to Volanea’s POST /v1/send payload, and authenticates with a server-only secret key.

Start with the synchronous implementation for a straightforward contact form. Then add durable outbox storage and stable idempotency keys when a duplicate message or a lost submission would create a real business problem. That progression keeps the first version small without treating reliability as an afterthought.

FAQ

Does Volanea have a native TYPO3 extension?

No. Volanea does not provide a native TYPO3 marketplace app or plugin for this integration. Use TYPO3’s custom Form finisher capability for form-triggered email, or use TYPO3 outgoing webhooks plus middleware for supported backend events.

What starts the Volanea email send in TYPO3?

In this guide, the trigger is a successful EXT:form submission. TYPO3 validates the form, then executes the configured VolaneaEmail custom finisher as part of the finisher sequence.

Can I put the Volanea key in a TYPO3 form YAML file?

No. Keep the secret key in a server-side environment variable or deployment secrets manager. Form YAML, frontend configuration, and browser code are inappropriate places for a live email API key.

How do I prevent duplicate emails after a timeout?

Send an Idempotency-Key header and reuse the same key for retries of the same logical submission. For important workflows, persist a server-generated submission ID in a durable outbox table and use that ID as the idempotency key.

Can I send an acknowledgement to the person who filled out the form?

Yes. Map the validated email form value to Volanea’s to field, use a verified sender address for from, and use a fixed template or carefully constrained content. Do not allow a public visitor to choose arbitrary recipients or arbitrary email HTML.