Send email from Gravity Forms without relying on fragile WordPress mail delivery by using Gravity Forms’ Webhooks Add-On as the trigger and Volanea’s REST API as the delivery layer. This is not a native Gravity Forms plugin integration: the dependable pattern is a submitted form entry, an outbound webhook, a small server-side relay, and a transactional email request.
Gravity Forms can already send its own notification emails, but that is a different job from sending application-grade transactional mail through a dedicated email API. A notification is useful for a quick admin alert. A webhook-driven flow gives you clearer field mapping, a protected API key, safer retry behavior, delivery observability, and room to grow into confirmations, ticket acknowledgements, quote requests, or internal routing.
This guide uses the Gravity Forms Webhooks Add-On. It is an Elite Add-On, so it is not available with every Gravity Forms license. If you have a Pro license instead, the official Zapier Add-On is available, but a webhook relay is usually the more direct and controllable route when you need to call an email API yourself.
What starts the email in Gravity Forms?
The concrete trigger is a successful form submission. Gravity Forms saves the submitted values as an entry, then processes active add-on feeds associated with that form. A Webhooks feed makes an HTTP request when the form is submitted.
That distinction matters. The trigger is not a changed contact record, a workflow stage, or a background campaign event. It is a form submission that creates an entry in your WordPress site’s Gravity Forms data.
For example, imagine a “Request a demo” form with these fields:
- Field 1: Name
- Field 2: Email
- Field 3: Company
- Field 4: Message
- Field 5: Consent checkbox
When a visitor submits it, Gravity Forms creates an entry. The Webhooks feed then sends selected values from that entry to your relay endpoint. The relay validates the request, builds the email content, and calls POST https://api.volanea.com/v1/send.
Use this architecture when the message needs to be a true transactional response to a form action: a confirmation to the submitter, an internal alert to a sales team, or both. Do not use it to quietly add people to a marketing list without the appropriate consent and list-management process.
The recommended architecture: Gravity Forms, relay, Volanea
The safe flow has four parts:
- A visitor submits a Gravity Forms form.
- The Gravity Forms Webhooks Add-On posts selected entry fields as JSON to a relay you control.
- The relay verifies that request, validates the field values, and creates a stable idempotency key from the entry.
- The relay sends the resulting transactional email through Volanea’s REST API.
Visitor
-> Gravity Forms form submission
-> Gravity Forms entry created
-> Webhooks feed POSTs JSON
-> Your server-side relay
-> POST https://api.volanea.com/v1/send
-> Volanea queues and delivers the email
The relay is intentional, not unnecessary plumbing. It prevents the Volanea API key from being placed in client-side JavaScript, page source, a public form, or a third-party browser request. It also gives you one place to normalize input, escape HTML, reject malformed email addresses, log outcomes, and make retries safe.
A direct request from Gravity Forms to Volanea can be technically possible because the Webhooks Add-On supports JSON requests and custom headers. But it puts a long-lived email provider credential inside a WordPress feed configuration. That configuration is stored server-side, not exposed to normal form visitors, but it is still available to WordPress users and systems with sufficient administrative access. A relay lets the WordPress side hold only a narrowly scoped shared secret while the Volanea API key stays in your deployment’s environment-secret store.
Prerequisites before you configure the form
Before building the feed, prepare the sending side. You need a verified sender domain in Volanea and an API key with only the permissions required to send mail. Review the REST API reference and setup guides for the current sending endpoint, authentication details, and sender-domain setup.
You also need:
- A Gravity Forms form that collects the fields required for your message.
- The Gravity Forms Webhooks Add-On and an eligible Gravity Forms license.
- A publicly reachable HTTPS endpoint that you control, such as an Express route, serverless function, or Cloudflare Worker.
- A secret used only to authenticate Gravity Forms to your relay.
- A protected environment variable named something like
VOLANEA_API_KEYon the relay host. - A verified sender address, such as
forms@example.com.
Keep the form narrow. A confirmation email usually needs the submitter’s name and email address; an internal notification can include the rest of the entry. Sending every raw field by default is risky because forms often collect data that should not appear in inboxes, such as internal notes, uploaded file URLs, consent records, payment-related values, or hidden fields.
Choose field IDs before creating your feed
Gravity Forms field labels can change. Field IDs are the stable identifiers that matter when mapping entry data. In the form editor, select each field and note its field ID.
For the examples below, use this deliberate mapping:
| Form value | Gravity Forms field ID | Webhook JSON property |
|---|---|---|
| Name | 1 | name |
| 2 | email | |
| Company | 3 | company |
| Message | 4 | message |
| Marketing consent | 5 | consent |
| Entry ID | built-in merge tag | entry_id |
| Form ID | built-in merge tag | form_id |
The field numbers are examples. Replace them with your actual field IDs. If you use a multi-part Name field, Gravity Forms may expose individual inputs such as 1.3 and 1.6; map the specific parts you need or map the full formatted field value intentionally.
Configure the Gravity Forms Webhooks feed
In WordPress, open the form you want to connect. Go to Form Settings → Webhooks, then add a new feed. The Webhooks Add-On supports GET, POST, PUT, PATCH, and DELETE; use POST here. It also supports FORM and JSON request formats; use JSON because the relay expects a JSON object.
Configure the feed with these values:
| Webhooks feed setting | Value |
|---|---|
| Name | Volanea form email relay |
| Request URL | https://forms.example.com/hooks/gravity-forms |
| Request Method | POST |
| Request Format | JSON |
| Request Body | Select Fields |
| Webhook Condition | Optional; enable only if some submissions should send mail |
For request headers, add a static secret that the relay will check:
| Header | Value |
|---|---|
X-Gravity-Forms-Secret | a long random secret stored only in WordPress and your relay environment |
Do not put the Volanea API key in a field on the public form, a hidden form field, browser JavaScript, a URL parameter, or the webhook body. Those are all the wrong places for a provider credential. If you use the relay described here, the Volanea key lives only in the relay’s server-side environment variables.
Create the selected-field payload
Choose Select Fields, then add one Field Values row for each JSON property. Gravity Forms lets you choose a form field, entry meta, or a custom value that supports merge tags.
Use this mapping:
| Key | Value source |
|---|---|
entry_id | Custom Value: {entry_id} |
form_id | Custom Value: {form_id} |
name | Form field: Name |
email | Form field: Email |
company | Form field: Company |
message | Form field: Message |
consent | Form field: Marketing consent |
After Gravity Forms replaces the merge tags and field values, the relay receives a JSON request shaped like this:
{
"entry_id": "1842",
"form_id": "7",
"name": "Jordan Lee",
"email": "jordan@example.net",
"company": "Northwind Analytics",
"message": "We need help sending account notifications.",
"consent": "Yes"
}
This selected-field payload is preferable to the All Fields option for email automation. All Fields sends the full unformatted entry and uses Gravity Forms field/input IDs as keys. That can be useful for a generic integration, but it makes the receiving code depend on numeric keys and risks transmitting values the email flow does not need.
Add conditional logic where it belongs
A feed can run only when defined conditions match. Use that feature if the form has a field such as “Request type” and only one option needs an email.
For example:
- Send a sales acknowledgement only when
Request typeisDemo. - Send a support receipt only when
Request typeisSupport. - Do not treat a consent checkbox as a condition for a transactional receipt; a person can receive a requested receipt even if they do not opt into marketing.
Conditional logic reduces accidental sends, but it is not a replacement for validation in the relay. The server must still validate the received payload because a webhook endpoint is an integration boundary.
Build the secure relay that calls Volanea
The following Node.js and Express example accepts the Gravity Forms JSON payload, verifies the shared secret, validates the required fields, escapes user content before inserting it into HTML, and sends a transactional email through Volanea.
Set these environment variables on the server running the relay:
VOLANEA_API_KEY=replace-with-a-server-side-volanea-api-key
GRAVITY_FORMS_WEBHOOK_SECRET=replace-with-a-long-random-shared-secret
EMAIL_FROM=forms@example.com
SALES_INBOX=sales@example.com
Install Express:
npm install express
Then create server.mjs:
import express from "express";
const app = express();
app.use(express.json({ limit: "100kb" }));
const {
VOLANEA_API_KEY,
GRAVITY_FORMS_WEBHOOK_SECRET,
EMAIL_FROM,
SALES_INBOX,
} = process.env;
if (!VOLANEA_API_KEY || !GRAVITY_FORMS_WEBHOOK_SECRET || !EMAIL_FROM || !SALES_INBOX) {
throw new Error("Missing required environment variables");
}
function escapeHtml(value = "") {
return String(value)
.replaceAll("&", "&")
.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll('"', """)
.replaceAll("'", "'");
}
function text(value = "") {
return String(value).trim();
}
app.post("/hooks/gravity-forms", async (req, res) => {
const suppliedSecret = req.get("X-Gravity-Forms-Secret");
if (suppliedSecret !== GRAVITY_FORMS_WEBHOOK_SECRET) {
return res.status(401).json({ error: "Unauthorized webhook request" });
}
// This is the JSON shape produced by the selected-field webhook feed.
const { entry_id, form_id, name, email, company, message } = req.body ?? {};
const entryId = text(entry_id);
const formId = text(form_id);
const contactName = text(name);
const contactEmail = text(email).toLowerCase();
const companyName = text(company);
const enquiry = text(message);
if (!entryId || !formId || !contactName || !contactEmail) {
return res.status(400).json({
error: "Missing required Gravity Forms values",
required: ["entry_id", "form_id", "name", "email"],
});
}
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(contactEmail)) {
return res.status(422).json({ error: "Invalid email address" });
}
const safeName = escapeHtml(contactName);
const safeCompany = escapeHtml(companyName || "Not provided");
const safeMessage = escapeHtml(enquiry || "No message provided").replaceAll("\n", "<br>");
// A stable key makes a repeated attempt for this specific entry safe.
const idempotencyKey = `gravity-forms:${formId}:entry:${entryId}:sales-alert:v1`;
const volaneaResponse = await fetch("https://api.volanea.com/v1/send", {
method: "POST",
headers: {
"Authorization": `Bearer ${VOLANEA_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": idempotencyKey,
},
body: JSON.stringify({
from: EMAIL_FROM,
to: [SALES_INBOX],
replyTo: contactEmail,
subject: `New form enquiry from ${contactName}`,
html: `
<h1>New form enquiry</h1>
<p><strong>Name:</strong> ${safeName}</p>
<p><strong>Email:</strong> ${escapeHtml(contactEmail)}</p>
<p><strong>Company:</strong> ${safeCompany}</p>
<p><strong>Message:</strong><br>${safeMessage}</p>
<p><small>Gravity Forms entry ${escapeHtml(entryId)} from form ${escapeHtml(formId)}</small></p>
`,
text: [
"New form enquiry",
`Name: ${contactName}`,
`Email: ${contactEmail}`,
`Company: ${companyName || "Not provided"}`,
`Message: ${enquiry || "No message provided"}`,
`Gravity Forms entry ${entryId} from form ${formId}`,
].join("\n"),
}),
});
const responseBody = await volaneaResponse.text();
if (!volaneaResponse.ok) {
console.error("Volanea send failed", {
status: volaneaResponse.status,
entryId,
responseBody,
});
// A non-2xx response tells Gravity Forms that this webhook request failed.
return res.status(502).json({
error: "Email provider request failed",
providerStatus: volaneaResponse.status,
});
}
console.info("Volanea send accepted", { entryId, formId, responseBody });
return res.status(202).json({
accepted: true,
entry_id: entryId,
});
});
app.listen(3000, () => {
console.log("Relay listening on port 3000");
});
The relay makes the actual Volanea request. The essential API call is the POST /v1/send request with an Authorization: Bearer header, JSON body, and stable Idempotency-Key header. The send endpoint supports safe retries with that idempotency header, which is particularly important for form-triggered messages.
The example routes the email to an internal sales inbox and sets the submitter’s address as replyTo. That is usually better than setting from to an unverified visitor address. Your From address should belong to a domain you control and have authenticated for sending.
Send a confirmation to the submitter instead
An internal alert is only one use case. To send a confirmation email to the person who submitted the form, change the recipient and content in the API payload.
body: JSON.stringify({
from: EMAIL_FROM,
to: [contactEmail],
subject: "We received your request",
html: `
<p>Hi ${safeName},</p>
<p>Thanks for contacting us. We received your request and will reply soon.</p>
<p><strong>Your message:</strong><br>${safeMessage}</p>
`,
text: [
`Hi ${contactName},`,
"",
"Thanks for contacting us. We received your request and will reply soon.",
"",
`Your message: ${enquiry}`,
].join("\n"),
})
Use a different idempotency key suffix for this distinct email. For example:
const idempotencyKey = `gravity-forms:${formId}:entry:${entryId}:submitter-receipt:v1`;
That detail matters. An internal notification and a submitter confirmation are two different intended messages. They should not share one idempotency key, because provider-side deduplication should suppress only duplicate attempts to send the same message.
If you send both emails, either make two separate API requests with separate keys or place them in a queued workflow that records their individual status. Do not send the entire sales email to the visitor by accident; internal information, routing notes, and administrative links should never be copied into a customer receipt.
Where the Volanea API key belongs
The Volanea API key belongs in the relay’s server-side secret store or environment configuration. In the example, it is read from process.env.VOLANEA_API_KEY.
Good places for this secret include:
- Environment variables configured in your hosting platform.
- A cloud secret manager injected into the runtime.
- Encrypted deployment secrets for a serverless function or worker.
- A local
.envfile during development that is excluded from Git.
Bad places include:
- A Gravity Forms field, including a hidden field.
- Page HTML, a shortcode attribute, or a JavaScript bundle.
- A Git repository or copied code snippet.
- A browser-side
fetch()request. - An email template or webhook request body.
The Gravity Forms side needs only GRAVITY_FORMS_WEBHOOK_SECRET, the shared credential that authorizes it to call your relay. That value is still sensitive: restrict WordPress administrator access, rotate it if it is exposed, and do not log it. But it is not the provider key that can send mail across your Volanea project.
For higher assurance, replace the static shared secret with a relay that accepts requests only from your own WordPress infrastructure, uses an HMAC signature you compute in a custom WordPress integration, or verifies another authenticated server-to-server mechanism. The static header is a practical baseline for a simple Webhooks Add-On feed, not the final word in webhook security.
When this breaks: failures in the Gravity Forms-to-email hop
A form integration has more than one failure point: the visitor can submit incomplete data, WordPress can fail to make the outbound request, the relay can reject the payload, or the email API can decline the message. Design for those cases before the form becomes business-critical.
Duplicate sends caused by retries or reprocessing
HTTP delivery is not an exactly-once system. If the webhook request reaches the relay and the relay successfully sends the email but the response is lost or times out before Gravity Forms receives it, a later retry or manual reprocessing can create another request for the same entry.
The stable idempotency key solves the provider-side duplicate risk. The example derives it from:
gravity-forms:<form_id>:entry:<entry_id>:sales-alert:v1
That key remains identical for repeat attempts for the same Gravity Forms entry and message type. Do not use the current timestamp, a random UUID, or a request ID as the idempotency key; those would make every retry look like a new email.
Also avoid changing the message semantics under the same key. If you intentionally need to resend an updated version, increment the suffix deliberately, such as sales-alert:v2, and record why the resend occurred.
Webhook timeouts
The Webhooks Add-On sends an HTTP request as part of feed processing. If the relay waits on slow downstream work, DNS, template rendering, or an overloaded email provider before responding, the request can time out. A timeout creates ambiguity: the sender may not know whether the receiver completed the work.
Keep the relay fast. Validate the payload, make the send request, and return a short response. For higher-volume forms, the better pattern is to persist a job keyed by form_id and entry_id, immediately return a successful acknowledgement once the job is durable, then let a background worker call Volanea.
Do not return a success response before you have either sent the email or safely recorded the send job. Returning 200 or 202 without durable work means a process crash can silently lose the message.
Missing payload fields on some forms or plans
A missing key is usually a mapping issue, not an email API problem. The form field may have been deleted, the field ID may differ between staging and production, a conditional field may not have been shown, or the webhook feed may be using an All Fields payload while the relay expects named keys.
The Webhooks Add-On is an Elite Add-On. A site without that eligible license cannot use this direct feed configuration. Pro license holders can use the official Zapier Add-On as a middle layer, while any site with custom development can use Gravity Forms’ server-side hooks such as gform_after_submission to call its own relay from WordPress code.
At the relay, classify values into three groups:
- Required: entry ID, form ID, recipient email, and any content that makes the email valid.
- Optional: company name, phone number, free-text message, or additional context.
- Forbidden: passwords, payment data, internal fields, and values you should not send to an inbox.
Reject requests with missing required values using a clear 400 or 422 response. Handle optional fields with safe fallback text. That prevents an empty undefined value from appearing in an email and makes configuration errors visible during testing.
Authentication failures
A 401 from your relay means the X-Gravity-Forms-Secret header did not match. Confirm that the feed is using the intended header name, that the value has no accidental whitespace, and that the deployed environment has the current secret.
A 401 or 403 from Volanea generally means the API key is absent, invalid, revoked, or lacks access. Because the key remains only in the relay environment, you can rotate it without editing every form feed. Update the secret, redeploy if necessary, and test with a new Gravity Forms entry.
Sender and recipient problems
An API acceptance response is not the same as mailbox delivery. A message can later bounce, be suppressed, or be rejected by a receiving system. Use a verified From domain, a clear transactional subject, and an address collected through the intended form field.
For contact forms that accept arbitrary email text, validate the email address in Gravity Forms and validate it again at the relay. If bounce protection is important for a high-volume workflow, run addresses through an email address verification tool before using them for nonessential follow-ups. Do not block a requested receipt solely because a validator returns an uncertain result; choose the policy based on the message’s purpose and risk.
Test the integration before publishing the form
Testing should prove more than “a request returned 200.” Use a real test submission and inspect each hop.
- Submit the form with a mailbox you can access.
- Confirm that Gravity Forms created an entry with the expected values.
- Check the Webhooks Add-On logs, if logging is enabled, for the outbound request and HTTP response.
- Confirm that the relay received the named JSON properties, not numeric All Fields keys.
- Confirm that the relay accepted the shared secret and used the expected idempotency key.
- Confirm that Volanea accepted the send request.
- Check the mailbox, spam folder, reply-to behavior, sender display, text fallback, and HTML rendering.
- Submit the same test entry only through a controlled replay or retry path and confirm it does not create a duplicate send with the same idempotency key.
Use a test-specific subject while building, such as [TEST] New form enquiry from Jordan Lee. Remove the prefix only after you have tested a production-like sender domain and recipient mailbox.
Keep staging and production separate. They should use different relay URLs, different shared secrets, and ideally different Volanea projects or clearly separated sender identities. A staging form should never send test submissions to a live sales queue.
Alternatives when you do not have the Webhooks Add-On
The Webhooks Add-On is the cleanest no-custom-WordPress-code route for this pattern, but it is not the only route.
Use the Gravity Forms Zapier Add-On
The official Gravity Forms Zapier Add-On is available on Pro and Elite plans. In this route, the form submission triggers a Zap, and Zapier performs an outbound webhook action to your relay. The relay design remains the same: Zapier sends selected form data and a shared secret; the relay holds the Volanea API key and calls the API.
This is useful when your team already manages automations in Zapier. The tradeoff is another service, another execution history, possible task usage, and another place where retries and field mappings must be understood.
Use a Gravity Forms server-side hook
For a developer-managed WordPress site, gform_after_submission can trigger PHP code after a form is submitted. That code can call your relay with WordPress HTTP functions, or it can call Volanea directly if the API key is held in server-side WordPress configuration.
This option avoids the Webhooks Add-On requirement, but it moves integration ownership into your theme or, preferably, a custom functionality plugin. Do not put production integration code in a theme if a theme change could disable email sending.
Use Gravity Forms notifications for simple cases
If all you need is a basic internal notification and your WordPress mail transport is already dependable, Gravity Forms Notifications may be enough. It is simpler, but it does not provide the same explicit API payload, idempotency key strategy, or independent email infrastructure layer described in this guide.
For customer-facing confirmations, account events, receipts, or messages that need durable operational handling, use the webhook-and-relay design instead.
Operational practices that keep the workflow reliable
A reliable form email flow is not just code. It is a small operational system with ownership, monitoring, and retention decisions.
Log the minimum useful metadata:
- Gravity Forms
form_idandentry_id. - The message type, such as
sales-alertorsubmitter-receipt. - The idempotency key.
- Relay status and Volanea HTTP status.
- A provider message identifier when returned.
- Error category, without logging secrets or full sensitive form content.
Avoid storing raw form messages indefinitely in application logs. Email enquiries can contain personal information, private business details, or information that should remain only in Gravity Forms. Log identifiers and a short status trail; retrieve the full entry from Gravity Forms only when an authorized operator needs it.
Set an alert for repeated 5xx responses from the relay and repeated provider errors. A contact form that stops producing internal alerts can become a revenue or support issue quickly. Monitor the endpoint separately from website uptime: the public form page can load normally while the outbound integration fails.
Finally, document the mapping. Write down which form ID feeds which relay route, which sender it uses, who receives the alert, and what the idempotency key format means. Future changes to a field label, a WordPress administrator, or a deployment environment should not require reverse-engineering a live customer workflow.
Conclusion
To send email from Gravity Forms with Volanea, use the actual Gravity Forms event that exists: a successful form submission that creates an entry and processes a Webhooks feed. Send a deliberately small JSON payload to a relay you control, keep the Volanea API key in that relay’s environment, and call POST /v1/send with a stable idempotency key.
This approach is more robust than exposing email credentials in a browser flow or treating WordPress mail as the only delivery layer. It gives you clear field mapping, controlled authentication, safer duplicate prevention, and a practical troubleshooting boundary between the form, the relay, and the email API.
FAQ
Does Volanea have a native Gravity Forms plugin?
No. This integration uses Gravity Forms’ Webhooks Add-On and Volanea’s REST API through a server-side relay. There is no native Gravity Forms marketplace plugin to install for this workflow.
What Gravity Forms event triggers the email?
A successful form submission triggers the flow. Gravity Forms creates an entry, then processes the active Webhooks feed for that form.
Can I call Volanea directly from a Gravity Forms webhook?
You can configure an outbound webhook with custom headers, but using a relay is safer because the Volanea API key stays in your server-side environment rather than in a WordPress feed configuration. The relay also gives you validation, logging, and idempotency control.
How do I prevent duplicate emails after a webhook retry?
Use a stable Idempotency-Key for each intended message, derived from the Gravity Forms form ID, entry ID, and message type. Send the same key again for a retry of the same entry and email.
What if I do not have the Gravity Forms Webhooks Add-On?
Use the official Gravity Forms Zapier Add-On on eligible Pro or Elite plans and have Zapier call your relay, or build a custom WordPress integration with the server-side gform_after_submission hook.