Wistia can notify your application when activity happens in the platform, and Volanea can deliver the resulting email through its REST API. This guide shows how to send email from Wistia with Volanea without pretending there is a native Volanea marketplace app: Wistia sends a signed webhook to your server, and your server securely calls Volanea.

The example below sends an internal notification when Wistia emits the documented media.updated webhook event. That is a reliable starting point because the event envelope includes an event ID, type, timestamp, and a media payload. The same architecture can later support lead-capture or webinar workflows, but those workflows need stricter consent, payload validation, and plan-specific testing.

What this Wistia-to-Volanea integration does

This is a direct webhook-to-server integration, not a browser integration and not a Wistia marketplace installation. Wistia delivers an HTTP POST to an endpoint you control whenever the selected webhook event occurs. Your endpoint verifies that the request was genuinely sent by Wistia, extracts the fields needed for a message, then sends the email through Volanea.

The flow is intentionally simple:

  1. A Wistia media record changes and Wistia creates a media.updated event.
  2. Wistia posts a JSON webhook envelope to your HTTPS endpoint.
  3. Your server verifies the X-Wistia-Signature HMAC against the raw request body.
  4. Your server uses the event UUID as the logical-send identifier.
  5. Your server calls Volanea’s POST /v1/send endpoint with an Idempotency-Key.
  6. Volanea queues and dispatches one notification email from your verified sending domain.

The important boundary is step 4. Wistia is the event producer; Volanea is the email provider; your server is the trusted translator between them. That translation layer is where you validate data, decide who should receive the message, and keep email credentials private.

The concrete Wistia trigger: media.updated

For this implementation, the trigger is Wistia’s media.updated webhook event. Wistia describes webhooks as HTTP POST notifications sent whenever a configured event occurs. Every delivery contains a top-level hook object and an events array, even if there is only one event in the delivery.

A media.updated event is useful when you want to alert a content, marketing, localization, or production team that a Wistia video record has changed. For example, you might send an email when somebody updates a video’s title, description, CTA configuration, captions, privacy settings, or other media metadata that causes Wistia to emit the event.

This trigger is deliberately different from a video watch event. A viewing event can occur at high volume and is generally a poor reason to create a transactional email for every individual view. A media-update notification, by contrast, usually represents a meaningful internal operational change.

Why not call Volanea directly from Wistia?

Wistia webhooks can send an HTTP request to a URL you provide, but Volanea’s email API needs an authenticated server-side request. Sending directly from Wistia to Volanea would leave you without a safe place to transform the webhook payload, verify the source request, set an idempotency key, or protect the Volanea secret key.

It would also make recipient logic brittle. A Wistia webhook is an event notification, not an email template engine. Your relay should decide whether the update deserves an email, which inbox receives it, which sender identity to use, and what information is appropriate to expose in the message.

Wistia’s webhook request shape

Wistia sends JSON. The exact contents of payload vary by event type, but the outer envelope is consistent: a webhook UUID and an array of events. Each event includes its own UUID, a type, a UTC ISO-8601 generated_at timestamp, and an event-specific payload.

For a media update, the practical shape your code should expect looks like this:

{
  "hook": {
    "uuid": "bb812395-8d23-4285-9278-9ca014209ae7"
  },
  "events": [
    {
      "uuid": "09e67f16e673a000",
      "type": "media.updated",
      "generated_at": "2026-10-16T15:42:11Z",
      "payload": {
        "media": {
          "url": "https://your-account.wistia.com/medias/f5diqltruh"
        }
      }
    }
  ]
}

Treat this as an event envelope, not as a permanent contract for every media field. The fields outside payload are the stable routing fields your relay needs. The payload.media.url value is useful as a link in the notification, but code should still guard against it being absent or changed rather than blindly assuming every event contains a complete media record.

A single webhook request can contain more than one event. Do not write a handler that reads only events[0] and silently drops the rest. The example below loops through the array and creates a separate idempotent Volanea send for each qualifying event.

Set up the Wistia webhook endpoint

In Wistia, go to Account Settings, then open the Webhooks section. Create a webhook that points to a public HTTPS endpoint you control, such as:

https://automation.example.com/webhooks/wistia

Select the media-update event category and enable the media.updated event. Wistia supplies a secret key when you configure the webhook; retain it in your server-side secret manager or environment configuration as WISTIA_WEBHOOK_SECRET.

Do not use a temporary request-bin URL for production traffic. Request-bin tools are useful for inspecting a test payload during initial development, but a production webhook can contain information about your media library and operational activity. A stable endpoint also lets you enforce signature checks, logs, rate limits, replay protection, and alerting.

Configure the endpoint before enabling notifications

Your endpoint must be ready to return a successful response quickly. Webhook delivery is an HTTP request, so Wistia needs an answer before its request times out. Your code can send the Volanea request synchronously for a low-volume internal workflow, but production systems should make the acknowledgment path short and predictable.

A robust pattern is:

  • Verify the Wistia signature immediately.
  • Validate and persist each incoming event UUID.
  • Enqueue the email work or perform a bounded API send.
  • Return a 2xx response only after you know the event has been safely accepted for processing.

For a first implementation, the code below directly calls Volanea after verification. If updates are frequent or sending reliability is business-critical, replace that final step with a durable queue and a worker.

Store the Volanea API key in your server, not in Wistia

The Volanea API key does not belong in Wistia’s webhook configuration, a Wistia embed, an HTML customization field, a client-side script, or a public environment variable. It belongs only in the private configuration of the server or serverless function that receives the webhook.

Use server-side environment variables or your platform’s secret manager:

WISTIA_WEBHOOK_SECRET=the-secret-configured-for-this-webhook
VOLANEA_API_KEY=sk_...
VOLANEA_FROM_EMAIL=video-updates@example.com
WISTIA_NOTIFICATION_TO=video-team@example.com

The Wistia webhook secret proves that the request body came from a sender that knows the configured secret. The Volanea key authorizes the sending of email from your Volanea account. They solve different problems and should never be substituted for each other.

Exposing the Volanea key in browser-visible configuration is especially dangerous. Anyone who can inspect the page, browser bundle, frontend network requests, repository history, or an accidentally public environment setting could reuse it to send mail through your account. Keep it in the relay’s runtime only, rotate it if exposure is suspected, and use a verified from address on a domain your team controls.

For API field definitions, authentication details, domain setup, and send-response handling, consult the Volanea API reference and setup guides while implementing your relay.

Working Node.js webhook relay and Volanea API call

The following Express example performs the full hop: it receives Wistia’s raw JSON body, verifies the HMAC SHA-256 signature from X-Wistia-Signature, filters for media.updated, maps Wistia fields into a notification email, and calls Volanea’s REST send endpoint.

import "dotenv/config";
import crypto from "node:crypto";
import express from "express";

const app = express();

const {
  WISTIA_WEBHOOK_SECRET,
  VOLANEA_API_KEY,
  VOLANEA_FROM_EMAIL,
  WISTIA_NOTIFICATION_TO,
} = process.env;

for (const [name, value] of Object.entries({
  WISTIA_WEBHOOK_SECRET,
  VOLANEA_API_KEY,
  VOLANEA_FROM_EMAIL,
  WISTIA_NOTIFICATION_TO,
})) {
  if (!value) throw new Error(`Missing required environment variable: ${name}`);
}

function verifyWistiaSignature(rawBody, receivedSignature) {
  if (!receivedSignature) return false;

  const expectedSignature = crypto
    .createHmac("sha256", WISTIA_WEBHOOK_SECRET)
    .update(rawBody)
    .digest("hex");

  const expected = Buffer.from(expectedSignature, "utf8");
  const received = Buffer.from(receivedSignature, "utf8");

  return expected.length === received.length &&
    crypto.timingSafeEqual(expected, received);
}

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

app.post("/webhooks/wistia", express.raw({ type: "application/json" }), async (req, res) => {
  const rawBody = req.body;
  const signature = req.get("X-Wistia-Signature");

  if (!verifyWistiaSignature(rawBody, signature)) {
    return res.status(401).json({ error: "Invalid Wistia webhook signature" });
  }

  let webhook;
  try {
    webhook = JSON.parse(rawBody.toString("utf8"));
  } catch {
    return res.status(400).json({ error: "Invalid JSON payload" });
  }

  if (!Array.isArray(webhook.events)) {
    return res.status(400).json({ error: "Wistia payload did not contain an events array" });
  }

  try {
    for (const event of webhook.events) {
      if (event.type !== "media.updated") continue;

      // Wistia -> Volanea field mapping:
      // event.uuid                 -> Idempotency-Key
      // event.generated_at         -> email body timestamp
      // event.payload.media.url    -> email body media URL
      // static server configuration -> from and internal recipient
      const mediaUrl = event.payload?.media?.url;
      const generatedAt = event.generated_at || "an unknown time";
      const safeMediaUrl = mediaUrl ? escapeHtml(mediaUrl) : "No media URL was included.";
      const eventId = event.uuid;

      if (!eventId) {
        console.warn("Skipping Wistia event without an event UUID", event);
        continue;
      }

      const html = `
        <h1>Wistia media updated</h1>
        <p>Wistia recorded a <code>media.updated</code> event.</p>
        <ul>
          <li><strong>Event ID:</strong> ${escapeHtml(eventId)}</li>
          <li><strong>Occurred:</strong> ${escapeHtml(generatedAt)}</li>
          <li><strong>Media URL:</strong> ${safeMediaUrl}</li>
        </ul>
        ${mediaUrl ? `<p><a href="${escapeHtml(mediaUrl)}">Open the media in Wistia</a></p>` : ""}
      `;

      const volaneaResponse = await fetch("https://api.volanea.com/v1/send", {
        method: "POST",
        headers: {
          "Authorization": `Bearer ${VOLANEA_API_KEY}`,
          "Content-Type": "application/json",
          "Idempotency-Key": `wistia-media-updated-${eventId}`,
        },
        body: JSON.stringify({
          from: VOLANEA_FROM_EMAIL,
          to: [WISTIA_NOTIFICATION_TO],
          subject: "Wistia media was updated",
          html,
          text: `Wistia media updated\nEvent ID: ${eventId}\nOccurred: ${generatedAt}\nMedia URL: ${mediaUrl || "not supplied"}`,
        }),
      });

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

    return res.status(200).json({ received: true });
  } catch (error) {
    console.error(error);
    return res.status(500).json({ error: "Webhook processing failed" });
  }
});

app.listen(3000, () => {
  console.log("Listening for Wistia webhooks on port 3000");
});

Understand the field mapping

The mapping above is intentionally explicit:

Wistia valueVolanea request useWhy it matters
event.uuidIdempotency-Key suffixIdentifies one logical Wistia event and prevents duplicate sends on retries.
event.typeFilter condition and message copyEnsures this handler sends only for media.updated.
event.generated_atEmail body timestampTells the recipient when Wistia recorded the event in UTC.
event.payload.media.urlEmail body and linkGives the recipient a route back to the relevant Wistia media.
Server-side VOLANEA_FROM_EMAILfromMaintains control of the sending identity.
Server-side WISTIA_NOTIFICATION_TOtoAvoids trusting an unvalidated event field as a recipient.

Do not make payload.media.url a recipient address, and do not accept an arbitrary recipient from webhook JSON without authorization rules. Event data is input; it should never become an unrestricted email-routing instruction.

Test the integration before relying on it

Start with a test or non-production Volanea key where appropriate, and use a mailbox controlled by your team as the notification recipient. The objective of the first test is not just to see an email arrive. You want to confirm that the signature passes, the event filtering works, the sender identity is authorized, the link is correct, and duplicate delivery does not create duplicate mail.

Use this test sequence:

  1. Deploy the endpoint behind HTTPS.
  2. Set all four environment variables in the runtime, then restart or redeploy it.
  3. Create the Wistia webhook in Account Settings and save its secret in WISTIA_WEBHOOK_SECRET.
  4. Trigger a safe media update in a test Wistia project.
  5. Inspect relay logs for the received event UUID and Volanea response status.
  6. Confirm that exactly one email reaches WISTIA_NOTIFICATION_TO.
  7. Repeat the same delivery only if your test tooling permits it, and verify that the same idempotency key does not create another logical send.

Keep structured logs, but do not log the Volanea key, Wistia secret, or full payloads indefinitely. If you later adapt this flow to forms, webinar registrants, or visitor-email events, the webhook may include personal data. Log event IDs, event types, result codes, and carefully minimized metadata instead.

Adapting the relay for Wistia lead capture

Wistia’s webhook documentation specifically identifies a completed Wistia Form as a webhook use case. Wistia’s Zapier integration also exposes a New Visitor Email trigger when a visitor’s email address is captured. Those are useful signals for a consented follow-up journey, but they are not interchangeable with a generic media-update alert.

Before using a captured email address as Volanea’s to value, answer these questions:

  • Did the viewer expect the exact follow-up message you plan to send?
  • Does the form copy clearly explain what they are subscribing to or requesting?
  • Is the message transactional, sales follow-up, or ongoing marketing?
  • Does your privacy notice cover this data flow?
  • Are you prepared to honor suppression, unsubscribe, and regional privacy requirements where applicable?

For a direct-email workflow, create a separate Wistia webhook and capture a real sample payload from your own account. Then change only the mapping layer after you verify the actual event type and fields Wistia delivers for your form configuration. Do not copy a field path from an unrelated media event and assume it exists in a form-conversion event.

That distinction matters because Wistia payload structures are event-specific. A media event can contain media information, while a lead or visitor event can contain differently shaped identity and viewing information. Your code should reject missing recipient data rather than sending to undefined, falling back to a shared inbox, or accidentally treating a display name as an email address.

When this breaks: Wistia retries, timeouts, and incomplete payloads

Every webhook integration needs a failure model. The difficult cases are rarely “the code has a syntax error.” They are ambiguous network outcomes: Wistia may retry because it did not receive a successful response, your server may finish the Volanea API request but lose the response, or a payload may omit an optional field your template assumed existed.

Wistia retries can otherwise create duplicate emails

If Wistia sends an event, your relay calls Volanea, and the connection back to Wistia fails before your server returns 200, Wistia can treat the delivery as unsuccessful and send it again. Without protection, the same media update could create two or more messages.

The code uses event.uuid to construct the Volanea Idempotency-Key. The key must be stable for the same logical Wistia event. Do not generate a random UUID inside the handler for each API attempt; doing so defeats duplicate protection because every retry looks new.

For stronger guarantees, also store processed Wistia event UUIDs in your own database with a unique constraint. That gives you control beyond the email provider’s idempotency retention window and lets you inspect whether an event was received, queued, sent, or permanently failed.

Webhook timeouts need a faster acknowledgment path

Calling an external email API while Wistia waits for your webhook response introduces a chain of dependencies: Wistia to your endpoint, your endpoint to Volanea, Volanea back to your endpoint, and your endpoint back to Wistia. A slowdown in any link can make the outcome unclear.

At low volume, a direct API call can be acceptable. At higher volume, persist the verified event and return promptly, then use a worker to send email. A queue also gives you controlled retry rules, backoff, dead-letter handling, and visibility into events that failed because Volanea rejected a sender identity or an internal recipient was malformed.

Do not acknowledge a webhook before you have durably recorded it. Returning 200 immediately and then performing all work only in memory risks losing events if the process crashes a moment later.

Payload fields may be absent, event-specific, or plan-dependent

The outer Wistia webhook envelope is predictable, but payload contents are event-specific. Features such as forms, lead capture, webinar registration, and marketing automation can have different availability depending on your Wistia setup and plan. A field you see in one test account or event type is not a safe universal assumption.

Defensive code should:

  • Check that events is an array before iterating.
  • Filter explicitly by event.type.
  • Require event.uuid before creating an idempotency key.
  • Treat nested fields such as payload.media.url as optional until validated.
  • Send a clear internal error or queue item when a required field is missing.
  • Avoid mailing a person merely because an event contains an email-like string.

The example continues to send the internal media notification even if the media URL is unavailable, because the recipient is a static server-side address and the event UUID still identifies the update. A lead-email flow should be stricter: if a verified recipient email is absent, do not send.

Signature failures are usually raw-body failures

Wistia signs the POST body with an HMAC SHA-256 value in X-Wistia-Signature. Verify the signature against the exact raw bytes received. If a JSON body parser changes whitespace or reserializes the object before verification, the computed signature can differ even though the logical JSON looks the same.

This is why the example uses express.raw({ type: "application/json" }) on the webhook route and parses JSON only after signature verification. Keep the webhook secret separate per environment so a staging endpoint cannot validate production traffic and vice versa.

A successful API response is not the same as inbox placement

A successful Volanea API response means the message was accepted for processing; it does not guarantee that every recipient will see it in the inbox. Sending-domain authentication, message content, recipient reputation, mailbox filtering, and suppression status can all affect the final result.

Use a verified sender address, send only expected operational messages, and monitor the Volanea send log and event data for bounces, suppressions, complaints, and delivery outcomes. If this integration becomes a material workflow, define an owner for failed sends rather than treating email delivery as a fire-and-forget side effect.

Direct webhook relay versus Zapier or Make

Wistia supports native webhooks, and it also supports Zapier workflows. A direct webhook relay is the best fit when you need signature verification, custom application rules, full control over idempotency, and a private server-side location for your Volanea key.

Zapier or Make can be appropriate when the workflow is simple and a non-engineering team needs to own it. In that model, Wistia provides the event, the automation platform transforms it, and an HTTP request step can call your middleware or Volanea according to the automation platform’s credential and secret-storage controls.

Choose a direct relay when you need:

  • Exact verification of the Wistia signature.
  • Custom recipient authorization or business logic.
  • Durable deduplication keyed by Wistia event UUID.
  • Detailed logs and incident debugging.
  • A controlled deployment process for secrets and code changes.

Choose an automation platform when the operational simplicity outweighs the need for custom controls. Even then, avoid placing a long-lived Volanea key in a client-visible value or a broadly shared automation workspace. If your automation tool cannot keep the credential private and scoped, send to your own relay instead.

Operational checklist for production

Before you enable this for a live Wistia account, review the complete chain rather than only the JavaScript code.

  • Your webhook endpoint uses HTTPS and is publicly reachable.
  • Wistia is configured to send only the event types the endpoint supports.
  • The Wistia signing secret is stored privately and verified against raw request bytes.
  • The Volanea key is stored only in server-side secret configuration.
  • VOLANEA_FROM_EMAIL is a verified address on an authorized sending domain.
  • Recipient addresses are static or validated by strict business rules.
  • Every email-producing event receives a stable idempotency key.
  • The endpoint handles all events in Wistia’s events array.
  • Missing optional payload fields do not crash the handler or create malformed messages.
  • You have logs, alerting, and a replay or reconciliation plan for failures.

This architecture stays useful as your Wistia automation grows. You can add a queue, enrich a media event through the Wistia API, create a template-based Volanea message, or branch by video project. None of those changes require exposing your sending credentials to Wistia embeds or pretending a native Volanea plugin exists.

Conclusion

To send email from Wistia with Volanea, use Wistia’s native webhooks as the trigger and a server-side relay as the secure control point. In the implementation shown here, the real trigger is Wistia’s media.updated event; Wistia posts its signed event envelope to your endpoint; the endpoint maps the event UUID, timestamp, and media URL into a Volanea email request.

The small details are what make the integration production-worthy: verify the HMAC using the raw body, keep the Volanea key out of Wistia and out of the browser, process every event in the delivery array, and reuse the Wistia event UUID as an idempotency key. With those controls in place, a Wistia activity notification becomes a reliable email workflow rather than a fragile chain of unauthenticated HTTP calls.

FAQ

Does Volanea have a native Wistia app or marketplace plugin?

No. This setup uses Wistia’s native outbound webhooks and a server-side application or serverless function that calls Volanea’s REST API. There is no marketplace installation step.

What Wistia event starts the email in this guide?

The example uses Wistia’s media.updated webhook event. It sends an internal notification when Wistia reports that a media record was updated.

Where should I store the Volanea API key?

Store it only in the secret manager or private environment variables of the server or serverless function receiving the Wistia webhook. Do not put it in Wistia configuration fields, browser JavaScript, embed code, or a public repository.

How do I stop duplicate emails when Wistia retries a webhook?

Use the stable Wistia event.uuid as the basis for Volanea’s Idempotency-Key, and preferably persist processed event IDs in your own database as well. Reuse the same key for the same logical event; never generate a fresh key for a retry.

Can I email a viewer after they submit a Wistia Form?

Yes, but first capture and validate the real form-event payload in your Wistia account, confirm that the viewer consented to the message, and map the verified email field only after testing. Form and visitor payloads are event-specific and should not be assumed to match a media-update payload.