If you need to send email from Ninja Forms reliably, do not make a form submission depend on WordPress’s local mail() configuration. A webhook-to-API pattern lets Ninja Forms collect the submission while Volanea handles the transactional send through authenticated email infrastructure.
There is no native Volanea action or app inside Ninja Forms. The practical integration is therefore a small, explicit workflow: Ninja Forms sends a webhook to an endpoint you control, that endpoint validates and normalizes the submission, and it sends the resulting message to Volanea’s REST API.
That distinction matters. It keeps your Volanea API key out of browser code and out of publicly visible form settings, gives you a place to apply anti-abuse rules, and makes the email content predictable. It also avoids treating a WordPress form notification as if it were a full transactional-email system.
What this integration does
Ninja Forms is responsible for gathering and validating the fields shown to a visitor. Its Webhooks add-on can send a submission to an HTTP endpoint after the form is processed. Your endpoint then decides whether a message should be sent and, if it should, creates a Volanea email request.
The completed flow looks like this:
- A visitor submits a Ninja Forms form on WordPress.
- Ninja Forms completes its normal field validation and runs its configured actions.
- The Webhooks action sends a
POSTrequest to an endpoint you control. - Your endpoint checks a shared webhook secret, validates field values, and builds the email.
- Your endpoint makes an authenticated
POSTrequest to the Volanea email API. - Volanea accepts the message for delivery and returns a send result that you log.
This guide uses a contact-request example. A customer submits their name, email address, subject, and message; the business receives an internal notification. The same approach works for quote requests, appointment alerts, support routing, download follow-ups, and other events where a form should start a transactional message.
It is intentionally not an approach for sending a newsletter to a list. A form-triggered confirmation or notification is transactional when it is tied to the submission. Ongoing promotional mail needs consent, suppression handling, and campaign workflows that are outside the scope of a basic form notification.
Why use a webhook instead of WordPress SMTP alone?
A WordPress SMTP plugin can improve delivery compared with PHP mail(), and it can be a good fit for simple administrator notifications. But a webhook plus an email API gives an application more control when form email becomes operationally important.
With the webhook pattern, the form, business rules, and sender are separate. You can reject suspicious requests before they become messages, select a recipient based on the form category, add a CRM reference to the subject, and retain structured send logs without editing a WordPress mail transport globally.
The main operational advantages
A REST request is explicit. The code specifies the sender, recipient, subject, HTML, text version, and headers rather than relying on whichever plugin last configured wp_mail().
That has useful consequences:
- Credential isolation: the Volanea API key stays on your server, not in a page, browser request, or Ninja Forms field.
- Clear retries: your receiver can distinguish an invalid form payload from a temporary API failure.
- Structured content: you can generate an HTML message and a plain-text fallback from validated values.
- Routing control: a “Sales” selection can notify sales while a “Support” selection can notify support.
- Auditability: log the submission identifier and provider response ID without storing more personal data than necessary.
- Deliverability discipline: use a verified sending domain and a stable, recognizable sender address rather than arbitrary visitor-provided From addresses.
The trade-off is that you must maintain a small endpoint. For many teams, that is preferable to granting a WordPress site broad mail-sending authority or embedding a long-lived email credential in a plugin configuration.
What you need before you start
Set up the email side before adding the form action. An unverified sender or a poorly chosen From address can make a technically correct webhook look broken when the real issue is sender authentication.
You need the following:
- A WordPress site with Ninja Forms installed.
- Ninja Forms’ Webhooks add-on, since the core form plugin does not turn arbitrary submissions into server-to-server HTTP requests by itself.
- A public HTTPS endpoint you control. This can be a small PHP endpoint, a serverless function, or an application route.
- A Volanea account, an API key created for server-side use, and a verified sending domain.
- A sender address on that verified domain, such as
forms@example.com. - A destination inbox controlled by your team, such as
inquiries@example.com. - A randomly generated webhook secret shared only by Ninja Forms and your receiver.
For API authentication, sender verification, and the current request schema, use the Volanea API reference and setup guides as the source of truth. Keep API keys in environment variables or server-side secrets, never in WordPress page content, frontend JavaScript, or a Git repository.
Choose the sender correctly
The form visitor’s email address should usually be used as Reply-To, not as the email’s From address. Sending “from” jane@gmail.com through your own email infrastructure can fail alignment checks and looks like impersonation to receiving systems.
Use a stable sender on your domain instead:
From: Website forms <forms@example.com>
Reply-To: Jane Doe <jane@example.net>
To: Sales team <inquiries@example.com>
That arrangement gives your team a one-click reply path while preserving an authenticated From domain. It also prevents visitor input from controlling a security-sensitive sender identity.
Understand Ninja Forms webhook data before mapping it
One important detail is easy to miss: Ninja Forms Webhooks does not give every integration an immutable, universal business payload that all sites must use. The action is configured around the request body and mapped field values. The precise field keys and values depend on your form and on the mappings you create.
That is good news. Instead of writing code that depends on internal field IDs or labels that an editor may later change, define a small, stable JSON contract for your receiver. Use field keys in Ninja Forms, not visual labels, as your stable reference points.
For this guide, create fields with these keys:
| Form label | Ninja Forms field key | Purpose |
|---|---|---|
| Name | name | Visitor’s name |
email | Reply-to address | |
| Subject | subject | Human-readable request subject |
| Message | message | Request details |
| Department | department | Optional routing choice |
A webhook request body configured for the receiver can then look like this:
{
"event": "ninja_forms_submission",
"form_id": "12",
"submission_id": "9831",
"name": "Jane Doe",
"email": "jane@example.net",
"subject": "Request a product demo",
"message": "We have 25 users and would like to discuss onboarding.",
"department": "sales"
}
The values in this example are not a claim that every Ninja Forms installation emits those exact keys automatically. They are the deliberate JSON contract you map in the Webhooks action. That is more robust than making your email service depend on a plugin’s broader internal submission structure.
Why a compact payload is better
Avoid forwarding every field and every piece of WordPress context to a third party by default. A contact form may include consent checkboxes, hidden anti-spam values, attachments, page URLs, tracking data, or fields that should not appear in an email.
Send only the data the receiver needs. It reduces accidental exposure, keeps logs smaller, and gives you a useful review point whenever someone adds a new form field.
A good minimum payload includes an event name, form identifier, optional submission identifier, and the fields required to build the message. Include an opaque correlation ID if your site has one, but do not put API keys, passwords, or WordPress nonces in the payload.
Configure the Ninja Forms Webhooks action
Open the relevant form in Ninja Forms and add a Webhooks action under the form’s Emails & Actions area. The exact screen wording can vary by Ninja Forms and add-on version, but the essential settings are the same: a POST request, a receiver URL, JSON request data, and an authentication value your receiver can validate.
Use an HTTPS URL such as:
https://example.com/wp-json/site-forms/v1/contact-email
If you run the receiver outside WordPress, it might instead be:
https://forms-api.example.com/ninja/contact
Set the request method to POST and send JSON. Map each request property to the corresponding Ninja Forms field key. In other words, map the receiver property email to the Ninja Forms field whose key is email, not merely to a label that could be renamed from “Email” to “Work email.”
If your Webhooks action supports custom request headers, set a header such as:
X-Form-Webhook-Secret: a-long-random-value
Use a unique randomly generated value, not a memorable word. If the action version available to you cannot add a custom header, include a webhook_secret property in the JSON body and compare it with a server-side environment variable. A header is preferable because it is less likely to be copied into logs or email content, but either method is materially better than accepting anonymous requests.
Recommended action order
Keep Ninja Forms’ normal confirmation or success-message action independent of the webhook. A visitor should see a confirmation only after Ninja Forms accepts their submission, but you should decide whether a downstream email failure must be visible to that visitor.
For an internal lead alert, it is usually better not to expose email-provider errors to the visitor. Record the failure and alert your team instead. For a required transactional receipt, consider a queue or retry mechanism so a short API outage does not silently lose the message.
Also keep any existing Ninja Forms Email action disabled for this notification while testing the webhook path. Otherwise you may receive duplicate messages and mistake that duplication for a Volanea issue.
Build a secure webhook receiver in WordPress
You can host the receiver in a separate application, but a small WordPress REST route is convenient when the site already runs PHP. The following example registers a route, validates a shared header secret, validates the expected JSON fields, and sends an internal notification through Volanea.
Put this in a small site-specific plugin or an mu-plugin. Do not put it in a theme’s functions.php if you expect to change themes.
<?php
/**
* Plugin Name: Site Forms to Volanea
*/
add_action('rest_api_init', function () {
register_rest_route('site-forms/v1', '/contact-email', [
'methods' => 'POST',
'callback' => 'site_forms_send_volanea_email',
'permission_callback' => '__return_true',
]);
});
function site_forms_send_volanea_email(WP_REST_Request $request) {
$expected_secret = getenv('NINJA_FORMS_WEBHOOK_SECRET');
$provided_secret = $request->get_header('x-form-webhook-secret');
if (!$expected_secret || !hash_equals($expected_secret, $provided_secret)) {
return new WP_REST_Response(['error' => 'unauthorized'], 401);
}
$payload = $request->get_json_params();
if (!is_array($payload) || ($payload['event'] ?? '') !== 'ninja_forms_submission') {
return new WP_REST_Response(['error' => 'invalid event'], 400);
}
$name = sanitize_text_field($payload['name'] ?? '');
$email = sanitize_email($payload['email'] ?? '');
$subject = sanitize_text_field($payload['subject'] ?? '');
$message = sanitize_textarea_field($payload['message'] ?? '');
$department = sanitize_key($payload['department'] ?? '');
if ($name === '' || !is_email($email) || $subject === '' || $message === '') {
return new WP_REST_Response(['error' => 'invalid form data'], 422);
}
$recipients = [
'sales' => 'sales@example.com',
'support' => 'support@example.com',
];
$to_email = $recipients[$department] ?? 'inquiries@example.com';
$html = sprintf(
'<h2>New website enquiry</h2><p><strong>Name:</strong> %s</p><p><strong>Email:</strong> %s</p><p><strong>Department:</strong> %s</p><p><strong>Message:</strong><br>%s</p>',
esc_html($name),
esc_html($email),
esc_html($department ?: 'general'),
nl2br(esc_html($message))
);
$text = "New website enquiry\n\n"
. "Name: {$name}\n"
. "Email: {$email}\n"
. "Department: " . ($department ?: 'general') . "\n\n"
. "Message:\n{$message}";
$volanea_response = wp_remote_post('https://api.volanea.com/v1/emails', [
'timeout' => 15,
'headers' => [
'Authorization' => 'Bearer ' . getenv('VOLANEA_API_KEY'),
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'body' => wp_json_encode([
'from' => [
'email' => 'forms@example.com',
'name' => 'Website forms',
],
'to' => [[
'email' => $to_email,
]],
'reply_to' => [[
'email' => $email,
'name' => $name,
]],
'subject' => '[Website] ' . $subject,
'html' => $html,
'text' => $text,
]),
]);
if (is_wp_error($volanea_response)) {
error_log('Volanea request failed: ' . $volanea_response->get_error_message());
return new WP_REST_Response(['error' => 'email send failed'], 502);
}
$status = wp_remote_retrieve_response_code($volanea_response);
$body = wp_remote_retrieve_body($volanea_response);
if ($status < 200 || $status >= 300) {
error_log('Volanea API error (' . $status . '): ' . $body);
return new WP_REST_Response(['error' => 'email was not accepted'], 502);
}
return new WP_REST_Response([
'accepted' => true,
'submission_id' => sanitize_text_field($payload['submission_id'] ?? ''),
], 202);
}
The request body above uses the Volanea email endpoint and nested address objects. Before deploying, compare the endpoint version and supported message fields with the current Volanea documentation, especially if your account uses a newer API version. Do not add unverified fields simply because another provider uses them.
Store secrets outside the plugin file
The example reads values from environment variables:
NINJA_FORMS_WEBHOOK_SECRET=replace-with-a-long-random-secret
VOLANEA_API_KEY=replace-with-your-server-side-api-key
Your host may expose environment variables through its dashboard, deployment configuration, or secrets manager. On a managed WordPress host without environment-variable support, use a server-side configuration file outside the web root or an approved secret-management mechanism. Avoid storing the API key in a Ninja Forms hidden field, WordPress option visible to low-privilege users, or committed plugin source.
How the Volanea API request turns the form into email
The receiver makes a normal authenticated JSON request. Stripped down to its essentials, the request is:
curl --request POST 'https://api.volanea.com/v1/emails' \
--header 'Authorization: Bearer YOUR_VOLANEA_API_KEY' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
"from": {
"email": "forms@example.com",
"name": "Website forms"
},
"to": [{"email": "inquiries@example.com"}],
"reply_to": [{"email": "jane@example.net", "name": "Jane Doe"}],
"subject": "[Website] Request a product demo",
"html": "<h2>New website enquiry</h2><p>...</p>",
"text": "New website enquiry"
}'
The from address must be authorized for your Volanea account and should belong to a domain you have verified. The to address is the internal mailbox receiving the notification. reply_to is the visitor’s address, so your staff can reply without copying it from the message body.
Always generate both html and text. HTML provides a useful layout, while text improves readability in plain-text clients and gives receiving systems a meaningful alternative representation. Escape every visitor-provided value before inserting it into HTML; the esc_html() and nl2br() sequence in the WordPress example does that.
Do not let a visitor control to, from, BCC recipients, API keys, tags, or arbitrary custom headers. A contact form with a user-controlled recipient becomes an open relay risk. A visitor-controlled From address creates authentication and spoofing problems.
Test the workflow in layers
Testing only from the public form makes diagnosis harder. Test each layer separately so you know whether a problem is in Ninja Forms, the webhook route, or the send request.
1. Test the Volanea request directly
Start with the curl command using a verified sender and an inbox you control. Confirm that Volanea accepts the request and that the message arrives. If it is accepted but not visible in the inbox, inspect spam and the provider’s delivery events before changing form settings.
2. Test the webhook receiver with curl
Send the exact JSON contract to your WordPress endpoint:
curl --request POST 'https://example.com/wp-json/site-forms/v1/contact-email' \
--header 'Content-Type: application/json' \
--header 'X-Form-Webhook-Secret: YOUR_SHARED_SECRET' \
--data '{
"event":"ninja_forms_submission",
"form_id":"12",
"submission_id":"9831",
"name":"Jane Doe",
"email":"jane@example.net",
"subject":"Request a product demo",
"message":"Please contact me.",
"department":"sales"
}'
A successful response should be a 202 JSON response with accepted: true. A 401 means the webhook secret did not match. A 422 means a required field was missing or invalid. A 502 means the receiver could not get a successful acceptance from the email API.
3. Submit the real Ninja Forms form
Once the direct receiver test works, submit the form with a real test address. Check that the mapped values arrive in the email exactly as expected. Test each department route, long messages, apostrophes, non-ASCII names, line breaks, and an invalid email address.
4. Check duplicates and retries
If you receive two notifications, look for both a Ninja Forms Email action and the webhook action. If you later add retries, use the submission_id as an idempotency key in your own database or cache. Otherwise a timeout can cause a retry even though the first send was accepted.
A simple approach is to store a short-lived record keyed by form_id:submission_id before sending. If the same event arrives again, return a successful response without creating another message. For a high-value workflow, persist that record in a database and store the Volanea response identifier alongside it.
Deliverability and abuse controls for form-triggered email
Transactional APIs improve the sending path, but they do not make a public form safe by themselves. A bot can still submit thousands of valid-looking entries, turn your inbox into noise, and consume sending volume.
Use layered protection:
- Enable Ninja Forms’ available anti-spam controls, such as a honeypot or CAPTCHA appropriate to your privacy requirements.
- Add rate limiting at the CDN, web application firewall, or webhook receiver.
- Require an expected form ID and event name; do not accept every JSON request sent to the route.
- Validate the visitor email with
is_email()before placing it in Reply-To. - Limit message length and reject unexpectedly large bodies.
- Allow-list route destinations in code rather than accepting a recipient from form input.
- Log enough metadata for investigation, but avoid logging full message content indefinitely.
For forms that send a receipt or confirmation directly to the visitor, consider an additional confirmation step for high-risk actions. For example, a product download can usually send immediately, while a password reset should be initiated only by a known account flow with its own token and expiry rules.
Authentication is not optional
Set up the domain authentication records requested by Volanea before production sending. Domain authentication helps recipients evaluate whether messages claiming to be from your domain are authorized. It also gives your messages a more consistent identity than using a generic or visitor-supplied sender.
Keep the visible From address consistent across form notifications. Frequent changes to sender domains, display names, or unrelated Reply-To domains make troubleshooting and mailbox filtering harder. If multiple teams need separate routing, use stable addresses such as forms@example.com and support@example.com on the same authenticated organizational domain.
Common mistakes and how to avoid them
The most common mistake is treating the webhook URL as secret. URLs leak through logs, browser history in some test setups, configuration exports, and support screenshots. Authenticate every request with a separate secret and rotate it when needed.
Another common problem is sending raw form data directly into HTML. A visitor can submit angle brackets, misleading markup, or URLs. Escape all data for its output context, generate the message server-side, and keep a plain-text version.
Teams also sometimes use the visitor’s address as the From address because it makes replying easy. Use Reply-To instead. This small change is one of the most important deliverability decisions in the whole integration.
Finally, do not make the customer experience depend on an inbox notification. A successful form confirmation means the site accepted the request; it should not promise that a person has read it. Send your internal alert, monitor failures, and establish an operational process for following up.
When SMTP may still be the simpler choice
Not every Ninja Forms notification needs a custom webhook receiver. If the only requirement is sending a small number of administrative messages from WordPress, an SMTP configuration can be simpler to operate. It uses Ninja Forms’ normal Email action and routes WordPress mail through an authenticated provider.
Choose the REST approach when you need field-based routing, an isolated API credential, send-level logging, custom validation, integration with other systems, or consistent behavior independent of WordPress’s global mail settings. Choose SMTP when you need the lowest implementation effort and do not need application-level message logic.
The two models can coexist, but define ownership clearly. Avoid having both send the same notification. For teams evaluating sending volume, domain features, and expected costs before choosing an architecture, review transactional email pricing and sending costs.
A production checklist
Before enabling the workflow for visitors, verify all of the following:
- The Volanea sending domain and From address are verified.
- The API key is stored server-side and has only the access it needs.
- The webhook route requires HTTPS and validates a high-entropy shared secret.
- The receiver accepts only the expected event and form ID.
- User input is validated and HTML-escaped.
- Reply-To, not From, uses the visitor’s email address.
- Recipient addresses are chosen in server-side code.
- HTML and text email bodies are both supplied.
- Logs capture failures and correlation IDs without unnecessarily retaining sensitive content.
- Anti-spam and rate limiting are enabled before public launch.
- Duplicate-delivery behavior is defined for webhook retries.
- Test submissions have been checked in major mailbox providers and spam folders.
This architecture is small enough for a single WordPress site but has the right boundaries for growth. The form remains a form, the webhook is a controlled event handoff, and Volanea remains the transactional delivery layer.
FAQ
Can Ninja Forms send directly to Volanea without custom code?
Not through a native Volanea Ninja Forms action. Use Ninja Forms’ Webhooks add-on to call a receiver you control, then have that receiver call the Volanea REST API. This keeps the API key out of the form configuration.
Should I put my Volanea API key in the Ninja Forms webhook settings?
No. Treat the API key as a server-side secret. Ninja Forms should send only a webhook secret to your endpoint; the endpoint adds the Volanea authorization header when it makes the API request.
What should the From address be for a Ninja Forms notification?
Use an address on your verified domain, such as forms@example.com. Put the visitor’s submitted email address in Reply-To so staff can reply to them without using an unauthenticated From address.
Why did the form submit successfully but no email arrive?
The form and email send are separate stages. Check the Ninja Forms webhook action, your endpoint logs, the endpoint’s HTTP response, Volanea API acceptance response, domain verification, and the receiving mailbox’s spam or filtering rules.
Can this pattern send an auto-reply to the person who filled out the form?
Yes, but validate the submitted address, rate-limit the form, and use a carefully scoped transactional message. For public forms, do not let arbitrary fields control the sender, recipient list, or email headers.