Send email from Webflow without exposing an email API key by routing a Webflow form submission webhook through a small server-side endpoint. That endpoint validates the event, maps the submitted fields, prevents duplicates, and makes the authenticated Volanea REST request.

Webflow does not need a native marketplace app for this workflow. Its webhook system can POST form-submission events to a URL you control, while Volanea handles the transactional email delivery step. The important architectural detail is that Webflow is the trigger—not the place where your Volanea secret belongs.

The recommended Webflow-to-Volanea architecture

The safest integration has three pieces:

  1. A visitor submits a native Webflow form.
  2. Webflow sends a form_submission webhook to your server endpoint.
  3. Your endpoint validates and deduplicates the event, then calls the Volanea REST API with your server-side API key.

That separation matters. A Webflow form runs in a visitor’s browser, while an email API key can authorize sending from your domain. Putting a sending key into page-level JavaScript, an Embed element, a public site configuration object, or a browser request would make it extractable by anyone who opens developer tools.

The middleware can be a serverless function on Cloudflare Workers, Vercel, Netlify Functions, AWS Lambda, or your application backend. The platform is less important than the security model: Webflow sends public event data to your endpoint; your endpoint holds the Volanea credentials and sends the email.

For most teams, the result is useful for two separate messages:

  • Internal notifications, such as a sales alert when a lead submits a consultation form.
  • Customer confirmations, such as “We received your request” or a downloadable resource delivery email.

Keep those messages logically separate. An internal notification normally goes to a fixed company inbox. A customer confirmation goes to the address supplied by the visitor and needs more careful consent, validation, and content controls.

The concrete Webflow trigger: a form submission

For this integration, the trigger is a Webflow form submission. In Webflow’s webhook event model, that trigger type is named form_submission.

Build a native Form block in the Webflow Designer and give the fields deliberate, stable labels. A practical contact form might contain these fields:

  • First Name
  • Last Name
  • email
  • Company
  • Message
  • Marketing consent

Field names are not cosmetic in this design. Webflow’s form-submission webhook provides submitted values inside payload.data, using the field names from the form. If one environment calls the email field Email, another calls it email, and a third calls it Work email, your server mapping must accommodate those differences—or preferably, you standardize them before launch.

Webflow’s documented webhook envelope looks like this:

{
  "triggerType": "form_submission",
  "payload": {
    "name": "Contact Us",
    "siteId": "65427cf400e02b306eaa049c",
    "data": {
      "First Name": "Zaphod",
      "Last Name": "Beeblebrox",
      "email": "zaphod@heartofgold.ai",
      "Phone Number": 15550000000
    },
    "schema": [
      {
        "fieldName": "First Name",
        "fieldType": "FormTextInput",
        "fieldElementId": "285042f7-d554-dc7f-102c-aa10d6a2d2c4"
      }
    ],
    "submittedAt": "2022-09-14T12:35:16.117Z",
    "id": "6321ca84df3949bfc6752327",
    "formId": "65429eadebe8a9f3a30f62d0",
    "formElementId": "4e038d2c-6a1e-4953-7be9-a59a2b453177"
  }
}

The critical fields are payload.id, which is the submission identifier for deduplication, payload.name, which identifies the form, and payload.data, which contains the visitor-provided values. payload.schema is useful for debugging field mismatches, but should not be necessary for normal sends once your mapping is tested.

Register the Webflow webhook without exposing secrets

Webflow can deliver webhook events to an HTTPS endpoint. You can create webhook registrations through the Webflow Data API, and Webflow also documents dashboard configuration for webhooks. The API route is especially useful for an auditable, repeatable setup because it lets you create a form_submission registration with an optional form filter.

A Webflow Data API webhook registration uses a request in this shape:

curl -X POST "https://api.webflow.com/v2/sites/$WEBFLOW_SITE_ID/webhooks" \
  -H "Authorization: Bearer $WEBFLOW_DATA_CLIENT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "triggerType": "form_submission",
    "url": "https://example.com/api/webflow/lead",
    "filter": {
      "name": "Contact Us"
    }
  }'

Use a server-held Webflow Data Client token with the required site write permission when you automate registration. Do not add that token to Webflow custom code either. It can create or manage webhook registrations and deserves the same treatment as any privileged credential.

The filter is valuable when a site has multiple forms. A newsletter form, support form, booking request, and product inquiry can each require different email templates and recipients. Filtering at registration time reduces unnecessary endpoint traffic and reduces the chance that an unrelated form accidentally triggers a customer email.

If you configure the webhook in Webflow’s dashboard rather than through the API, document the target endpoint, the form selected, the date of change, and the owner. Dashboard-created webhooks have an important security limitation: Webflow’s documentation notes that they do not include the request headers needed for signature validation. If signed verification is part of your security requirement, use the documented API-created webhook path and validate the request at your endpoint.

Map the Webflow payload to a Volanea email

Do not send payload.data directly to an email API. It contains arbitrary visitor content and may include fields you never intended to place in a message. Instead, explicitly extract, validate, encode, and map only the fields your template needs.

The following Node.js example is an implementation pattern for a serverless endpoint. It accepts the real Webflow form-submission body, uses the Webflow submission ID as the idempotency key, and builds two deliberate messages: one internal notification and one customer confirmation.

Because API credentials and exact API contracts must come from your Volanea account, configure the Volanea REST URL, authorization header, and request body according to the current email API reference and setup guides. The server-side mapping below is the part that protects your Webflow integration from exposed secrets, arbitrary field injection, and repeated webhook delivery.

// api/webflow/lead.js
// Node 18+ / serverless-style example

const sentSubmissionIds = new Set(); // Replace with Redis, KV, or a database in production.

function escapeHtml(value = "") {
  return String(value)
    .replaceAll("&", "&")
    .replaceAll("<", "&lt;")
    .replaceAll(">", "&gt;")
    .replaceAll('"', "&quot;")
    .replaceAll("'", "&#039;");
}

function isEmail(value) {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(value || ""));
}

async function sendWithVolanea({ to, subject, html, text, idempotencyKey }) {
  // These values live only in your serverless platform's encrypted environment settings.
  const response = await fetch(process.env.VOLANEA_SEND_URL, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      [process.env.VOLANEA_AUTH_HEADER_NAME]: process.env.VOLANEA_API_KEY,
      "Idempotency-Key": idempotencyKey
    },
    body: JSON.stringify({
      // Map these keys to the current Volanea send-email request schema in your account.
      from: process.env.VOLANEA_FROM_EMAIL,
      to: [to],
      subject,
      html,
      text
    })
  });

  if (!response.ok) {
    const detail = await response.text();
    throw new Error(`Volanea send failed: ${response.status} ${detail}`);
  }

  return response.json().catch(() => ({}));
}

export default async function handler(req, res) {
  if (req.method !== "POST") {
    return res.status(405).json({ error: "Method not allowed" });
  }

  const event = req.body;
  if (event?.triggerType !== "form_submission" || !event?.payload?.id) {
    return res.status(400).json({ error: "Unexpected Webflow webhook payload" });
  }

  const submission = event.payload;
  const fields = submission.data || {};

  // Match these keys exactly to the labels in your published Webflow form.
  const firstName = String(fields["First Name"] || "").trim();
  const lastName = String(fields["Last Name"] || "").trim();
  const email = String(fields.email || "").trim().toLowerCase();
  const company = String(fields.Company || "").trim();
  const message = String(fields.Message || "").trim();
  const consent = fields["Marketing consent"] === true ||
    fields["Marketing consent"] === "true";

  if (!isEmail(email)) {
    return res.status(422).json({ error: "Submission did not include a valid email" });
  }

  // A durable store is required in production. Check before sending anything.
  if (sentSubmissionIds.has(submission.id)) {
    return res.status(200).json({ ok: true, duplicate: true });
  }
  sentSubmissionIds.add(submission.id);

  const safeName = escapeHtml(`${firstName} ${lastName}`.trim() || "Unknown visitor");
  const safeCompany = escapeHtml(company || "Not provided");
  const safeMessage = escapeHtml(message || "Not provided").replaceAll("\n", "<br>");

  try {
    await sendWithVolanea({
      to: process.env.LEAD_NOTIFICATION_TO,
      subject: `New Webflow lead: ${firstName || email}`,
      idempotencyKey: `webflow-${submission.id}-internal`,
      html: `<h1>New Webflow form submission</h1>
        <p><strong>Name:</strong> ${safeName}</p>
        <p><strong>Email:</strong> ${escapeHtml(email)}</p>
        <p><strong>Company:</strong> ${safeCompany}</p>
        <p><strong>Message:</strong><br>${safeMessage}</p>`,
      text: `New Webflow form submission\nName: ${firstName} ${lastName}\nEmail: ${email}\nCompany: ${company}\nMessage: ${message}`
    });

    // Send confirmation only when the form and your consent policy permit it.
    if (consent) {
      await sendWithVolanea({
        to: email,
        subject: "We received your request",
        idempotencyKey: `webflow-${submission.id}-confirmation`,
        html: `<p>Hi ${escapeHtml(firstName || "there")},</p>
          <p>Thanks for contacting us. We received your request and will reply soon.</p>`,
        text: `Hi ${firstName || "there"},\n\nThanks for contacting us. We received your request and will reply soon.`
      });
    }

    return res.status(200).json({ ok: true });
  } catch (error) {
    // Return a non-2xx response only if you want Webflow delivery to be retried.
    console.error({ submissionId: submission.id, error: error.message });
    return res.status(500).json({ error: "Email processing failed" });
  }
}

The deliberate environment variables in this example are:

VOLANEA_SEND_URL=
VOLANEA_AUTH_HEADER_NAME=
VOLANEA_API_KEY=
VOLANEA_FROM_EMAIL=
LEAD_NOTIFICATION_TO=

Set these in your hosting provider’s encrypted environment-variable or secret manager interface—not in a Webflow Embed, page custom code, CMS field, JavaScript bundle, or a client-side .env file. The VOLANEA_SEND_URL and VOLANEA_AUTH_HEADER_NAME variables should use the exact current values documented in your Volanea account. This avoids publishing unverified endpoint or authentication syntax and keeps changes to the provider contract isolated in one server-side function.

Why the API key cannot live in Webflow client code

A common first attempt is to listen for a browser form event and call the email provider directly with fetch(). That is not an acceptable production solution for transactional sending.

Anything that reaches the browser is visible. A visitor can inspect a minified JavaScript bundle, review outgoing network requests, copy a public configuration object, or search page source. Even if you obscure the key, the browser still needs to use it, which means an attacker can use it too.

A leaked sending key can create several expensive problems:

  • Unauthorized email can be sent from your authenticated domain.
  • Your sending reputation can be damaged by abuse, complaints, or invalid-recipient traffic.
  • A key rotation becomes urgent and can interrupt legitimate production sends.
  • Your audit trail becomes less trustworthy because legitimate and unauthorized calls share credentials.

The webhook endpoint eliminates that exposure. It also lets you enforce rules that do not belong in the browser: allowlisted form names, recipient restrictions, rate limits, validation, consent checks, spam screening, logging, and deduplication.

Set up the sending identity before testing

The webhook may be working perfectly while the email fails downstream because the sending identity is incomplete. Before you test the whole path, configure the sender identity and domain authentication required by your Volanea account.

Use a sender address that belongs to a domain you control, such as hello@yourdomain.com or notifications@yourdomain.com. Do not use a visitor’s submitted email address as the from address. Doing that breaks sender alignment and makes replies, spoofing controls, and deliverability harder to manage.

For lead notifications, put the visitor’s address in a reply-to field if your Volanea send request supports it. That gives your team a convenient reply path while preserving your own authenticated sender. Confirm the exact request field in the current Volanea documentation before adding it to the request body.

If you are comparing volumes, environments, or delivery needs before launch, review transactional email pricing alongside your expected form volume. A campaign that sends one internal alert per lead behaves very differently from a product workflow that sends multiple customer notifications per submission.

Test the field mapping, not just delivery

A successful HTTP status does not prove that the correct email was sent. Test with a published staging form and use realistic edge cases.

Start with this checklist:

  1. Submit the form with every expected field populated.
  2. Submit it with optional fields blank.
  3. Use characters such as apostrophes, ampersands, angle brackets, emoji, and line breaks in the message field.
  4. Submit with an invalid email address and confirm your endpoint handles it intentionally.
  5. Submit twice and verify that idempotency prevents a duplicate email.
  6. Confirm the internal recipient, sender, subject, and customer confirmation are correct.
  7. Check application logs for the Webflow submission ID and the Volanea response identifier, if returned.

HTML escaping deserves special attention. A form message is user-controlled input. If you interpolate it into an HTML email without escaping, a visitor can inject markup into the notification email. That may not execute as browser JavaScript in modern email clients, but it can still distort the message, conceal content, insert misleading links, or create an unsafe internal workflow. Escape user values before placing them into HTML and also provide a plain-text version.

When this breaks: retries, timeouts, and missing fields

Every webhook integration should be designed around the fact that delivery is not exactly once. Webflow expects a successful HTTP response from the receiving endpoint. If delivery fails, your endpoint is slow, or it returns a non-success response, retries can happen. A retry is not evidence that the form was submitted twice.

Webflow retries can cause duplicate sends

The submission ID in payload.id is your primary deduplication key. Save it in a durable data store before sending the email, preferably with a unique constraint or atomic “set if absent” operation. A process-local Set, as shown in the example, is only for illustrating the behavior; it resets when a serverless instance restarts and will not protect a multi-instance production deployment.

Use separate idempotency values for distinct messages generated from the same submission, such as:

webflow-<submission-id>-internal
webflow-<submission-id>-confirmation

This lets one submission legitimately produce two different emails while still preventing either one from being sent twice. If Volanea supports idempotency headers or keys, pass the same deterministic key through to the send request. That creates a second layer of protection if your application succeeds in sending but loses the response before recording success.

Webhook timeouts create ambiguous outcomes

A timeout is tricky because Webflow may not know whether your endpoint sent the message before timing out. Your endpoint might also time out while waiting for the Volanea API after the provider accepted the request.

The reliable pattern is to return quickly after recording the event, then process the email asynchronously through a queue or background worker. In the simplest version, the endpoint stores the submission ID and normalized payload, returns a 2xx response, and a worker sends the email. In the more advanced version, the worker retries provider failures with controlled backoff and records final delivery attempts.

Do not acknowledge an event before you have durably recorded enough information to process it later. A quick 200 OK followed by an in-memory send can silently lose messages if the runtime stops immediately afterward.

Payload fields can be missing or renamed

Webflow form data follows the form’s field labels. A marketer can rename a field in the Designer, a visitor can leave an optional field blank, and separate forms can use different labels for the same concept. Your endpoint should treat every field as potentially missing.

Avoid code that assumes fields.email always exists if your form’s label is actually Email address. Either standardize a canonical field label across forms or implement an explicit fallback map:

const email = String(
  fields.email || fields.Email || fields["Email address"] || ""
).trim().toLowerCase();

Use a form allowlist as well. If your endpoint is intended only for Contact Us, reject or ignore other payload.name values. That prevents a newly added form from unexpectedly triggering the same customer email flow.

Plan availability can also affect implementation choices. Webflow’s native form collection and publishing behavior depend on how the site is hosted and configured. If you export a site or choose a custom form action, verify that your final deployed form still sends submissions through the Webflow path that produces the webhook event. Test the production domain, not only the Designer preview.

Dashboard-created webhooks and request signatures

Webflow documents signature-related request headers for webhook delivery, including x-webflow-timestamp and x-webflow-signature, and it separately notes that dashboard-created webhooks do not include the headers needed for signature validation. Do not assume every webhook configuration path has identical verification capabilities.

If you create the webhook through the supported API route, validate the signature using Webflow’s current documented method before trusting the body. Also reject old timestamps to reduce replay risk. If signature verification is unavailable for your chosen configuration, compensate with a hard-to-guess endpoint path, strict form allowlisting, payload validation, rate limits, and monitoring—but recognize that these are compensating controls, not equivalent proof of origin.

Operational safeguards for a production integration

A form-to-email integration is small, but it touches public input, an external delivery service, and business-critical notifications. Treat it as production infrastructure.

At minimum, add these controls:

  • Structured logs: record submission ID, form name, event time, message type, outcome, and provider response ID. Do not log full message content or sensitive personal data by default.
  • Alerting: alert on repeated provider errors, increasing invalid-email rates, endpoint 5xx responses, and a sudden drop to zero submissions.
  • Rate limits: protect the endpoint against direct abuse and protect your sending account from a spam burst.
  • Spam defenses: enable Webflow’s available form-spam protections and keep server-side validation even when client-side controls are present.
  • Recipient allowlists: for internal notifications, use a fixed recipient or an approved list rather than taking the destination from a public form field.
  • Environment separation: use separate staging and production webhook endpoints, sender identities where appropriate, and API keys.
  • Retention rules: decide how long raw form submissions and logs should remain available, especially if forms collect personal information.

The second-order benefit is better debugging. When sales reports a missing lead, you can distinguish among “the visitor never submitted,” “Webflow did not deliver the webhook,” “the endpoint rejected the payload,” “the queue failed,” and “the email provider rejected the send.” Without this event trail, every failure looks like an email problem.

Alternatives: direct form action, Zapier, and Make

A native Webflow form can also be configured to use a custom action for some third-party form services. That option is not a safe direct path to Volanea if it requires placing an authenticated API credential in a browser-accessible form action or client-side script. It also makes robust retry handling and internal/customer branching harder.

Zapier or Make can be appropriate middleware when your team does not want to operate a serverless endpoint. The pattern remains the same: Webflow form submission triggers the automation; the automation securely stores the Volanea credential; the automation maps the form fields and sends the request. Before choosing it, confirm that the automation platform can securely hold the API credential, make the exact Volanea REST request your account requires, handle retries without duplicate sends, and provide enough observability for your volume.

For a simple low-volume internal alert, an automation tool can be faster to launch. For consent-driven customer messages, higher-volume lead routing, custom templates, or strict idempotency requirements, a dedicated endpoint and queue usually provide better control.

A practical launch plan

Launch this integration in deliberate stages rather than enabling every email at once.

First, send only to an internal test inbox from a staging Webflow form. Verify the webhook event, field mapping, sender identity, and logs. Next, enable internal production notifications while preserving the durable dedupe record. Finally, enable customer confirmations only after the exact consent condition, copy, sender, and reply handling have been approved.

Once the workflow is live, change form labels cautiously. Treat a label rename like an API change: update the mapping, deploy it, test it, and monitor the first real events. The simple habit of documenting field names prevents a surprising number of form automation failures.

Conclusion

To send email from Webflow with Volanea, use Webflow’s form_submission webhook as the trigger and a server-side endpoint as the security and reliability boundary. Map payload.data explicitly, store the Volanea API key only in server-side secrets, deduplicate with payload.id, and design for retries and missing fields from the start.

That approach is more dependable than embedding an email key into Webflow page code, and it leaves room to evolve from a single lead alert into a maintainable transactional email workflow.

FAQ

Can Webflow send a Volanea email directly from a form?

Not securely with a private API key in browser code. Use the Webflow form-submission webhook to call a server-side endpoint, then have that endpoint send through Volanea.

What exactly triggers the email?

A native Webflow form submission triggers the form_submission webhook event. Webflow POSTs the event to your configured endpoint, where your code decides which email or emails to send.

Where should the Volanea API key be stored?

Store it in the secret manager or encrypted environment variables of your serverless host, backend, Zapier connection, or Make connection. Never store it in Webflow page custom code, an Embed element, a CMS collection, or browser-visible JavaScript.

How do I stop duplicate Webflow emails?

Use payload.id as a durable deduplication key. Store it with a unique constraint before sending, and use separate deterministic idempotency keys for each message type generated from that submission.

Why did my Webflow webhook send no email?

Check the production form configuration, webhook registration and filter, endpoint logs, required fields, sender-domain setup, Volanea API response, and whether a retry or duplicate record was intentionally suppressed.