If you need to send transactional email from Gravity Forms, you do not need a native provider add-on to build a reliable workflow. Gravity Forms can send a JSON webhook after a successful submission, and a small server-side relay can validate that event, build an email safely, and call Volanea’s REST API.

There is an important limitation to state upfront: Volanea does not currently have a native “Send Email From Gravity Forms” app or Gravity Forms add-on. That is not a blocker, but it changes the implementation. Instead of installing a provider integration, you will use Gravity Forms’ Webhooks Add-On to post selected submission data to an endpoint you control, then that endpoint sends the email through Volanea.

This approach is usually better than placing an email API key in a form plugin configuration. Your Volanea secret stays on the server, you decide exactly which fields become email content, and you can add validation, rate controls, logging, and duplicate protection in one place.

What you are building

The completed flow has four parts:

  1. A visitor submits a Gravity Forms contact, quote, registration, or support form.
  2. Gravity Forms processes a Webhooks feed and sends a JSON payload to a private WordPress REST endpoint.
  3. The endpoint validates a shared webhook secret, validates the submitted values, and renders a plain-text and HTML message.
  4. The endpoint calls POST https://api.volanea.com/v1/send with the Volanea API key stored outside the form configuration.

The result is a transactional message such as a form receipt, sales inquiry confirmation, appointment request acknowledgment, or internal notification.

This is not the same as changing every email WordPress sends. If your goal is to route all WordPress mail—including password resets, WooCommerce receipts, and plugin notifications—through an SMTP relay, use an SMTP-oriented WordPress configuration instead. This guide is for a deliberate event-driven workflow where a specific Gravity Forms submission triggers a specific API email.

Why use a webhook relay instead of a direct API call?

Gravity Forms’ Webhooks Add-On can make remote HTTP requests after a form submission. It supports methods including POST and formats including JSON, and its request body can include all entry fields or only fields you select. That makes it suitable for connecting a form to a service that does not have an official add-on.

It may be tempting to configure the Webhooks feed to call Volanea directly and put Authorization: Bearer sk_... in the feed headers. Technically, a REST API request can be constructed that way. Operationally, it is the weaker design.

A direct call means the sending credential is stored in WordPress and is available to administrators or systems that can inspect the feed configuration or database. It also couples the form’s field mapping directly to the provider payload. A later form edit can inadvertently break a send request, and you have less room to normalize data, escape user content, or block abuse.

A relay creates a clean boundary:

  • Gravity Forms sends only the values the relay needs.
  • The relay owns the Volanea API key.
  • The relay creates the outbound email payload.
  • The relay can use a stable idempotency key for retries.
  • The relay can return safe errors without exposing provider credentials.

Volanea’s single-message endpoint is POST /v1/send, uses https://api.volanea.com as its base URL, authenticates with a secret key, and supports an Idempotency-Key header for safe retries. See the email API reference and setup guides when you need the complete send schema, templates, batch sending, and delivery event details.

Prerequisites before you configure Gravity Forms

You need the following before writing any code:

  • An active WordPress site running Gravity Forms.
  • Gravity Forms’ Webhooks Add-On. In WordPress admin, Webhooks feeds are configured per form under Form Settings → Webhooks after the add-on is installed and activated.
  • A Volanea account with a secret API key.
  • A verified sending domain and a sender address on that domain, such as forms@example.com.
  • Access to edit wp-config.php or another server-side secret store.
  • A form containing, at minimum, a recipient email field. This guide uses a Name field, Email field, and Message field.

Before going live, configure the sending identity in Volanea and complete the required DNS verification steps for your domain. Sending a form confirmation from a branded domain is much more reliable than using an arbitrary public mailbox as the From address. Keep the form submitter’s email in replyTo when you need your team to reply to the person who filled out the form.

Example form used in this guide

For clarity, assume your form has these fields:

FieldExample field IDPurpose
Name1Customer name
Email2Confirmation recipient and reply address
Message3Customer’s question or request
Consent checkbox4Optional condition for promotional follow-up—not needed for transactional confirmation

A Gravity Forms Name field has separate inputs. In this example, the first-name input is 1.3 and the last-name input is 1.6. Your form can use different IDs, so select fields from the Webhooks UI where possible rather than copying those numbers blindly.

The Gravity Forms webhook payload shape

Gravity Forms does not send one universal, fixed webhook JSON object. The payload shape depends on your Webhooks feed configuration.

If you choose Request Body → All Fields, Gravity Forms sends the unformatted entry data, using field or input IDs as keys. That can be useful for debugging, but it is a poor production contract because field IDs are difficult to read and can expose values the email workflow does not need.

For transactional email, select Request Body → Select Fields and define explicit keys. Gravity Forms lets you map each key to a submitted form field, entry meta, or an Add Custom Value using merge tags. With that configuration, the body your endpoint receives is predictable and intentionally minimal.

Recommended JSON body

Create these selected-field rows in the Webhooks feed:

JSON keyValue in Gravity Forms
eventCustom Value: contact_form_submission
entry_idCustom Value: {entry_id}
form_idCustom Value: {form_id}
first_nameName field’s First input, for example {Name (First):1.3}
last_nameName field’s Last input, for example {Name (Last):1.6}
emailYour Email field, for example {Email:2}
messageYour Message field, for example {Message:3}

For a successful submission, the relay receives JSON in this shape:

{
  "event": "contact_form_submission",
  "entry_id": "4821",
  "form_id": "7",
  "first_name": "Avery",
  "last_name": "Lee",
  "email": "avery@example.net",
  "message": "I would like a quote for a team plan."
}

The values will be strings because Gravity Forms is rendering form fields and merge tags into the configured webhook body. Treat them as untrusted user input regardless of whether the form already marked a field as required.

That distinction matters. Required-field validation improves the form experience, but server-side validation in the relay is still what prevents malformed recipient addresses, unexpected entry IDs, or a changed field mapping from resulting in an unintended API request.

Configure the Gravity Forms Webhooks feed

Open the relevant form in WordPress, then go to Settings → Webhooks for that form and add a new feed. Gravity Forms describes a feed as a configuration that tells an add-on what to do with form data after a successful entry is created.

Use these settings as the baseline:

SettingValue
NameVolanea transactional confirmation
Request URLhttps://your-site.example/wp-json/gf-volanea/v1/send-confirmation
Request MethodPOST
Request FormatJSON
Request BodySelect Fields
Webhook ConditionOptional; enable only when the form has a real condition for sending

When Request Format is JSON and the request method is POST, Gravity Forms sets the Content-Type header to application/json. Do not add a second conflicting Content-Type header.

Add a relay authentication header

Add one request header in the feed:

HeaderValue
X-GF-Volanea-SecretA long random shared secret

Generate a unique secret with a password manager or a cryptographically secure generator. This is not the Volanea API key. It is a separate credential that lets your endpoint distinguish a request from your configured feed from a random public request to the same URL.

Use the same value in your server configuration as GF_VOLANEA_WEBHOOK_SECRET. Never place the Volanea sk_... key in this feed.

Decide when the feed should run

A contact-form acknowledgment typically runs on every valid, non-spam submission. A workflow that sends a quote, status notice, or approval response may need a condition.

For example, only send the feed when:

  • a user selected “Request a quote,”
  • a payment or moderation workflow has approved the entry,
  • a consent field is checked for a non-transactional follow-up, or
  • an internal field indicates the entry is ready.

Do not use marketing consent as a condition for a purely transactional receipt. A receipt is tied to the action the user took. Marketing messages need a separate consent-aware workflow, audience policy, and unsubscribe handling.

Store credentials outside the form and theme

Put both secrets in wp-config.php, above the line that says to stop editing. In a managed host, use the equivalent environment-variable or secret-management mechanism if available.

define( 'VOLANEA_API_KEY', 'sk_replace_with_your_secret_key' );
define( 'GF_VOLANEA_WEBHOOK_SECRET', 'replace_with_a_long_random_shared_secret' );
define( 'VOLANEA_FORMS_FROM', 'forms@example.com' );
define( 'VOLANEA_FORMS_REPLY_TO', 'support@example.com' );

The VOLANEA_FORMS_FROM address must be an address you are authorized to send from in Volanea. It should be a stable branded sender, not the email address typed into the form.

Use the submitter’s address as a reply target only when it makes sense. A confirmation email can use the support mailbox as its reply-to address. An internal sales notification can use the submitter’s email in replyTo so that a sales rep can reply directly.

Do not put these constants in a child theme’s functions.php. Themes change, are often edited by multiple people, and are not an appropriate secret boundary. A small must-use plugin is a better location for the relay code because it remains active even if the theme changes.

Add the WordPress relay plugin

Create a file named wp-content/mu-plugins/gravity-forms-volanea-relay.php. If the mu-plugins directory does not exist, create it. Must-use plugins load automatically and are a practical place for site-specific integration code.

The following code registers a REST endpoint, verifies the webhook secret, checks the expected payload, safely escapes content, and sends the final API request to Volanea.

<?php
/**
 * Plugin Name: Gravity Forms Volanea Relay
 * Description: Receives a Gravity Forms webhook and sends a transactional confirmation through Volanea.
 */

add_action( 'rest_api_init', function () {
    register_rest_route(
        'gf-volanea/v1',
        '/send-confirmation',
        array(
            'methods'             => WP_REST_Server::CREATABLE,
            'callback'            => 'gf_volanea_send_confirmation',
            'permission_callback' => '__return_true',
        )
    );
} );

function gf_volanea_send_confirmation( WP_REST_Request $request ) {
    $provided_secret = (string) $request->get_header( 'x-gf-volanea-secret' );
    $expected_secret = defined( 'GF_VOLANEA_WEBHOOK_SECRET' )
        ? (string) GF_VOLANEA_WEBHOOK_SECRET
        : '';

    if ( $expected_secret === '' || ! hash_equals( $expected_secret, $provided_secret ) ) {
        return new WP_REST_Response(
            array( 'error' => 'Unauthorized webhook request.' ),
            401
        );
    }

    if ( ! defined( 'VOLANEA_API_KEY' ) || ! defined( 'VOLANEA_FORMS_FROM' ) ) {
        error_log( 'Gravity Forms Volanea relay is missing server configuration.' );
        return new WP_REST_Response(
            array( 'error' => 'Email service is not configured.' ),
            500
        );
    }

    $payload = $request->get_json_params();

    $event      = isset( $payload['event'] ) ? sanitize_key( $payload['event'] ) : '';
    $entry_id   = isset( $payload['entry_id'] ) ? absint( $payload['entry_id'] ) : 0;
    $form_id    = isset( $payload['form_id'] ) ? absint( $payload['form_id'] ) : 0;
    $first_name = isset( $payload['first_name'] ) ? sanitize_text_field( $payload['first_name'] ) : '';
    $last_name  = isset( $payload['last_name'] ) ? sanitize_text_field( $payload['last_name'] ) : '';
    $email      = isset( $payload['email'] ) ? sanitize_email( $payload['email'] ) : '';
    $message    = isset( $payload['message'] ) ? sanitize_textarea_field( $payload['message'] ) : '';

    if ( $event !== 'contact_form_submission' || ! $entry_id || ! $form_id || ! is_email( $email ) ) {
        return new WP_REST_Response(
            array( 'error' => 'Invalid webhook payload.' ),
            422
        );
    }

    $name = trim( $first_name . ' ' . $last_name );
    $greeting_name = $first_name !== '' ? $first_name : 'there';
    $safe_name = esc_html( $name !== '' ? $name : $email );
    $safe_message = nl2br( esc_html( $message ) );

    $subject = 'We received your message';
    $text = "Hi {$greeting_name},\n\n"
        . "Thanks for contacting us. We received your message and will reply as soon as possible.\n\n"
        . "Your message:\n{$message}\n\n"
        . "Reference: {$form_id}-{$entry_id}";

    $html = '<!doctype html><html><body>'
        . '<p>Hi ' . esc_html( $greeting_name ) . ',</p>'
        . '<p>Thanks for contacting us. We received your message and will reply as soon as possible.</p>'
        . '<p><strong>Your message</strong></p>'
        . '<blockquote>' . $safe_message . '</blockquote>'
        . '<p style="color:#666;font-size:12px">Reference: '
        . esc_html( $form_id . '-' . $entry_id ) . '</p>'
        . '</body></html>';

    $volanea_payload = array(
        'from'    => VOLANEA_FORMS_FROM,
        'to'      => $email,
        'replyTo' => defined( 'VOLANEA_FORMS_REPLY_TO' )
            ? VOLANEA_FORMS_REPLY_TO
            : VOLANEA_FORMS_FROM,
        'subject' => $subject,
        'text'    => $text,
        'html'    => $html,
    );

    $response = wp_remote_post(
        'https://api.volanea.com/v1/send',
        array(
            'timeout' => 15,
            'headers' => array(
                'Authorization'    => 'Bearer ' . VOLANEA_API_KEY,
                'Content-Type'     => 'application/json',
                'Idempotency-Key'  => 'gravity-forms-' . $form_id . '-entry-' . $entry_id . '-confirmation',
            ),
            'body' => wp_json_encode( $volanea_payload ),
        )
    );

    if ( is_wp_error( $response ) ) {
        error_log( 'Volanea request failed for Gravity Forms entry ' . $entry_id . ': ' . $response->get_error_message() );
        return new WP_REST_Response(
            array( 'error' => 'Email request could not be completed.' ),
            502
        );
    }

    $status_code = wp_remote_retrieve_response_code( $response );
    $response_body = wp_remote_retrieve_body( $response );

    if ( $status_code < 200 || $status_code >= 300 ) {
        error_log( 'Volanea API error for Gravity Forms entry ' . $entry_id . ': HTTP ' . $status_code . ' ' . $response_body );
        return new WP_REST_Response(
            array( 'error' => 'Email provider rejected the request.' ),
            502
        );
    }

    return new WP_REST_Response(
        array(
            'ok'       => true,
            'entry_id' => $entry_id,
        ),
        200
    );
}

The example deliberately does not accept from, subject, or arbitrary HTML from the webhook. Those values are business logic and should stay server-controlled. Letting a public-facing form determine them would make it easier for a changed feed or compromised form configuration to create misleading email.

The Volanea API call, broken down

The relay makes this logical request after it has validated the Gravity Forms payload:

curl --request POST 'https://api.volanea.com/v1/send' \
  --header 'Authorization: Bearer sk_your_secret_key' \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: gravity-forms-7-entry-4821-confirmation' \
  --data '{
    "from": "forms@example.com",
    "to": "avery@example.net",
    "replyTo": "support@example.com",
    "subject": "We received your message",
    "text": "Hi Avery,\n\nThanks for contacting us.",
    "html": "<!doctype html><html><body><p>Hi Avery,</p><p>Thanks for contacting us.</p></body></html>"
  }'

The key fields have separate jobs:

  • from is the authenticated sender identity your recipients see.
  • to is the confirmation recipient from the validated form email field.
  • replyTo controls where a recipient’s reply goes.
  • subject is controlled by your application, not the browser submission.
  • text gives recipients who prefer plain text a readable version.
  • html provides the formatted version.
  • Idempotency-Key identifies one logical event so a retry does not accidentally create another confirmation.

For this workflow, use the Gravity Forms form ID and entry ID in the idempotency key. The same entry should produce the same confirmation key every time. Do not add a timestamp, random UUID, or current request ID to the key; doing so would defeat duplicate protection.

Adapt the relay for common transactional messages

The example sends a confirmation to the person who completed the form. Most production forms need more than one type of email, but the architecture does not change.

Customer acknowledgment

This is the pattern already shown: send to the form email and let the customer know the message arrived. Keep it concise, identify the business, and include a reference ID when the conversation may require support.

Use it for:

  • contact forms,
  • demo requests,
  • appointment requests,
  • support inquiries,
  • application receipts, and
  • waitlist confirmations.

Internal team notification

For an internal notification, replace to => $email with a fixed business mailbox such as sales@example.com. Then use the customer email as replyTo.

'to'      => 'sales@example.com',
'replyTo' => $email,
'subject' => 'New website inquiry from ' . $name,

Do not send raw HTML supplied by a visitor into the internal notification. The example’s esc_html() call is important even for internal messages. Internal inboxes are still email clients, and user-submitted content should be treated as text.

One customer confirmation plus one internal alert

Make two API calls only when both messages are genuinely needed. Give each a distinct idempotency key suffix, such as -customer and -team, because they are different logical sends.

gravity-forms-7-entry-4821-customer
gravity-forms-7-entry-4821-team

Avoid putting multiple unrelated recipients into one message just because they originate from the same form. Customer and internal emails normally need different subjects, reply behavior, content, and retention expectations.

Templates for consistent branding

Inline HTML is ideal for showing the integration clearly and for a small number of simple notices. As your messages gain a logo, shared footer, support links, localization, or multiple product teams, use stored templates and pass variables from the relay instead of assembling all markup in PHP.

The boundary remains the same: the webhook supplies limited event data, your relay validates and maps it, and Volanea receives a provider-specific send payload. The benefit of templates is content governance, not a reason to trust arbitrary form input.

Security and deliverability considerations

Form-triggered email is a frequent target for abuse. A secure API key is necessary, but it is not the whole control plane.

Keep the API key server-side

The most important rule is simple: the browser must never receive the Volanea secret key, and the Gravity Forms feed should not need it. Store it in server configuration and send it only from code running on your server.

If you suspect an API key was exposed, rotate it promptly and replace the configured value. Review your server logs and Volanea activity around the time of exposure.

Authenticate the inbound webhook

The custom X-GF-Volanea-Secret header in this guide protects the relay from basic unauthorized requests. Use a unique high-entropy value and compare it with hash_equals() rather than a loose comparison.

A shared secret is not a substitute for broader form security. Keep HTTPS enabled, use Gravity Forms’ available anti-spam controls, add CAPTCHA or honeypot protections when appropriate, and rate-limit at the edge if a public form attracts automated traffic.

Do not turn a contact form into an open relay

Never let a visitor supply arbitrary to, from, subject, template ID, or raw HTML values without strict business rules. A form with a “recipient email” field is only safe when that field’s purpose is explicit and you validate its allowed use.

For example, a product quote form may legitimately send confirmation to the submitter’s email. It should not allow the submitter to enter 50 third-party recipients and use your domain to send a custom message.

Use a real From domain

The From address should belong to a domain you control and have verified for sending. Setting the visitor’s email as from is a common but harmful shortcut: it creates alignment and deliverability problems and makes replies and authentication harder to reason about.

Use a stable address such as forms@example.com, then set replyTo to the visitor’s address only for internal team alerts. That preserves a branded sender while still making replies convenient.

Testing the complete workflow

Test in a controlled order. It is much easier to locate a failure when you prove each handoff separately.

  1. Verify the Volanea API call independently. Run the cURL request with a test recipient and a verified From address. Confirm that the API accepts the request and that the message is visible in the recipient inbox or designated test mailbox.
  2. Deploy the relay. Call the WordPress endpoint with a test request and the X-GF-Volanea-Secret header. Confirm that it returns 200 only for a valid payload and 401 without the header.
  3. Configure the Webhooks feed. Use the selected fields listed earlier, not All Fields.
  4. Submit the form using a test email address you control. Confirm the customer acknowledgment arrives and renders correctly in both HTML and plain text.
  5. Inspect logs. Check the WordPress or PHP error log for rejected provider requests, timeouts, or payload validation errors.
  6. Test a duplicate scenario. Re-send the exact webhook payload or trigger a known retry path. The stable idempotency key should represent the same logical confirmation rather than create duplicate mail.

Do not use a production customer’s address while testing new content. A dedicated test mailbox lets you inspect authentication, spam placement, images, mobile rendering, and reply behavior without creating confusion for a real lead.

Useful failure signals

A 401 response from the relay indicates that the custom webhook secret is missing or does not match. A 422 response means the webhook payload did not pass application validation—usually an unexpected event, an invalid email, or missing entry identifiers.

A 502 response from the relay means WordPress could not complete the downstream Volanea request or Volanea returned a non-2xx response. The detailed provider response is intentionally logged on the server rather than returned to the public caller, where it could disclose implementation details.

Gravity Forms Webhooks feeds process after successful form submission and entry creation. That keeps the form confirmation flow separate from the outbound integration, but it also means you should monitor failures rather than assume a visitor-facing “thank you” page proves the email was sent.

When Zapier or Make is the better choice

The native Webhooks Add-On plus a small relay is the most direct option when you control WordPress and want the fewest moving parts. It is not the only option.

Use an automation tool such as Zapier or Make when the form event must also update a CRM, create a project, enrich a lead, open a support ticket, or notify several systems and your team prefers a visual workflow. In that setup, Gravity Forms sends the submission to the automation platform, and an HTTP module makes the Volanea API request.

The tradeoffs are straightforward:

  • Webhooks plus relay: strong secret isolation, low dependency count, code ownership, and flexible validation.
  • Zapier or Make: fast visual orchestration and easier multi-service workflows, with another vendor, another credential store, and potentially more execution cost.
  • Direct API call from the form feed: fewer components on paper, but unnecessarily exposes the email credential to WordPress configuration and leaves less room for controls.

Even when you use an automation platform, preserve the same principles: use a verified From address, map only necessary fields, validate recipient addresses, escape user-created content, and use a deterministic idempotency key based on the Gravity Forms entry.

Operational improvements for higher-volume forms

A low-volume contact form can use the example plugin almost unchanged. As volume or business impact rises, improve the workflow deliberately.

First, log a structured event after each successful send with the Gravity Forms entry ID, form ID, destination class such as customer_confirmation, Volanea response identifier if available, and status. Do not log full message bodies or more personal data than your operational needs justify.

Second, separate business email types. A contact receipt, an internal lead alert, an appointment reminder, and a password-related form event should not all share one generic feed and one generic subject line. Separate handlers or an allowlisted event value make review and future changes safer.

Third, consider queueing when an email needs complex processing or when your WordPress host has restrictive outbound request timing. The immediate relay remains suitable for many transactional cases, but a durable queue can improve retry handling for workflows where delivery requests must survive temporary provider or network failures.

Finally, keep form field changes under change control. A renamed field label may not break a merge tag if the input ID remains unchanged, but deleting or replacing an input can. After editing a production form, submit a test entry and confirm the exact webhook body and email output.

Conclusion

To send transactional email from Gravity Forms with Volanea today, use the integration pattern Gravity Forms already supports: a JSON Webhooks feed followed by a secure server-side REST relay. There is no native Volanea Gravity Forms app to install, and you do not need one to build a clean, maintainable workflow.

Map only the form fields you need, authenticate the inbound webhook with a separate shared secret, keep the Volanea API key in server configuration, render user content as escaped text, and create stable idempotency keys from the form and entry IDs. Those choices turn a simple form notification into dependable email infrastructure rather than a fragile plugin setting.

FAQ

Does Volanea have a native Gravity Forms add-on?

No. The practical integration is Gravity Forms’ Webhooks Add-On posting selected submission data to a secure endpoint that calls Volanea’s REST API.

Can Gravity Forms call Volanea’s API directly?

It can make JSON HTTP requests, but sending directly would require placing the Volanea secret API key in the Webhooks feed. A server-side relay is the safer pattern because the key remains outside the form configuration.

What JSON does Gravity Forms send to the webhook?

There is no single mandatory JSON schema. The Webhooks feed sends either all entry fields or the selected fields you configure. For this guide, the selected payload contains event, entry_id, form_id, first_name, last_name, email, and message.

Why should I use an Idempotency-Key?

It lets retries represent the same logical email send. Use a deterministic value such as the Gravity Forms form ID, entry ID, and message type so an integration retry does not create a duplicate confirmation.

Should the customer’s email address be the From address?

No. Use a verified address on your own sending domain as from. For internal notification emails, set the customer’s validated address as replyTo so your team can respond directly.