Gmail does not include a native outbound-webhook feature, but you can still send email from Gmail with Volanea reliably by using Google Apps Script as the controlled bridge. The practical pattern is simple: Gmail identifies a message with a label, Apps Script reads it on a schedule, and the script posts a mapped email payload to Volanea’s REST API.

This is not a Gmail Marketplace installation, a native Gmail app, or a browser-side integration. It is an automation you own: Gmail remains the source of the event, Apps Script performs the authenticated HTTP request, and Volanea handles delivery through your verified sending domain.

The honest Gmail integration model

It helps to start with what Gmail does and does not provide.

Gmail does not offer a standard “when an email arrives, POST this JSON to my URL” outbound-webhook setting. There is no Gmail screen where you paste a Volanea endpoint, enter an API key, and receive a direct message-event payload. Gmail also does not offer a native Volanea listing that you can install from a marketplace.

Gmail does provide two realistic integration paths:

  1. Google Apps Script with a time-driven trigger. A script periodically searches Gmail for messages carrying a particular user label, maps message data into an API request, and calls Volanea with UrlFetchApp.
  2. The Gmail API with push notifications. Gmail can publish mailbox-change notifications to a Google Cloud Pub/Sub topic. Your backend receives the Pub/Sub delivery, queries Gmail history to learn what changed, then sends through Volanea.

For an individual inbox or a small operational workflow, Apps Script is usually the clearest implementation. It has fewer moving parts, uses Gmail’s built-in label model, and can store configuration in the script project rather than exposing it in a browser extension or static page.

For high-volume, near-real-time, multi-user processing, the Gmail API plus Pub/Sub and a backend service is usually the more appropriate architecture. That route requires OAuth, a Google Cloud project, Pub/Sub configuration, watch renewal, history reconciliation, and server-side secret management.

This guide focuses on the Apps Script route because it is the shortest direct path to sending operational notifications from Gmail through Volanea.

What starts the send in Gmail?

The concrete Gmail-side signal in this implementation is a user-created Gmail label named volanea-send.

A message becomes eligible when the label is applied to its thread. You can apply that label manually, or create a Gmail filter that applies it automatically when an incoming message meets conditions such as sender address, recipient alias, subject text, or included words.

The actual trigger that runs the code is an Apps Script time-driven trigger. It is a scheduled trigger, not a Gmail message-received trigger. For example, you can run the script every five minutes. On each run, it searches the volanea-send label, processes unprocessed threads, sends the mapped content to Volanea, then applies a volanea-sent label.

That distinction matters:

  • Gmail’s label is the business signal: “this message should produce an outbound notification.”
  • The Apps Script clock trigger is the execution signal: “check Gmail now.”
  • Volanea receives a newly constructed REST request; Gmail itself does not emit that request.

This architecture creates a small delay determined by the schedule. A five-minute trigger means a newly labeled thread may wait up to roughly five minutes before it is picked up. If the workflow needs second-level responsiveness, use the Gmail API push-notification route instead.

Gmail does not send a direct HTTP payload

Because Gmail has no standard outbound webhook for inbox messages, there is no native Gmail JSON body to forward directly to Volanea in the Apps Script pattern. Apps Script reads the message using Gmail services and constructs the payload itself.

The important Gmail fields available to the script include the thread ID, message ID, sender, recipient fields, subject, plain-text body, HTML body, received date, and labels. Those are the fields you deliberately map into the email Volanea sends.

For the more advanced Gmail API push route, Gmail’s users.watch mechanism publishes mailbox-change notifications through Cloud Pub/Sub rather than posting a complete email to your application. The delivered Pub/Sub envelope has this general shape:

{
  "message": {
    "data": "eyJlbWFpbEFkZHJlc3MiOiJvcHNAZXhhbXBsZS5jb20iLCJoaXN0b3J5SWQiOiIxMjM0NTYifQ==",
    "messageId": "2078473629182736",
    "publishTime": "2026-10-05T12:00:00.000Z"
  },
  "subscription": "projects/example-project/subscriptions/gmail-changes"
}

The base64-decoded data is a small Gmail mailbox-change object, not a complete email:

{
  "emailAddress": "ops@example.com",
  "historyId": "12345"
}

Your backend must use the stored prior history ID and the Gmail History API to determine which messages changed, fetch the relevant message, decide whether it qualifies, and then call Volanea. That is powerful, but it is not the same as a direct Gmail webhook.

For the Apps Script approach, there is no incoming webhook envelope at all. The following implementation builds the Volanea send body directly from Gmail message methods.

The Gmail-to-Volanea field mapping

Before writing code, define what the source message means in your workflow.

A common pattern is an inbox for requests, approvals, support escalations, or monitoring messages. When a message gets the volanea-send label, the automation sends an internal notification to a fixed operations mailbox. The original Gmail sender becomes contextual information in the body, not necessarily the recipient of the new message.

Here is a practical mapping:

Gmail sourceVolanea request fieldWhy it is mapped this way
Script Property VOLANEA_FROM_EMAILfrom.emailThe sending address must belong to a verified Volanea sending domain.
Script Property VOLANEA_FROM_NAMEfrom.nameGives recipients a recognizable sender name.
Script Property NOTIFY_TO_EMAILto[0].emailAvoids trusting an arbitrary inbound email header as a notification destination.
Latest message subjectsubjectPreserves the original context with an operational prefix.
Latest message plain bodytextCreates a readable plain-text alternative.
Latest message HTML bodyhtmlPreserves basic formatting when available.
Gmail message IDIdempotency-KeyIdentifies one logical notification even if the script runs again.
Gmail sender, date, thread IDEmail body contentGives the receiving team enough context to investigate.

Do not automatically use a parsed address from the inbound message as the Volanea recipient unless that is explicitly the purpose of your workflow and you validate it. A Gmail inbox is an untrusted input boundary. A malicious sender can control message headers and body text; they should not be able to redirect internal alerts or cause your sender identity to be misused.

Set up Gmail labels and Script Properties

Create two Gmail labels before adding the code:

  • volanea-send: marks threads that should be sent through the automation.
  • volanea-sent: marks threads successfully handed to Volanea.

You can create the labels from Gmail’s label controls. If you use a Gmail filter, configure it to apply volanea-send to only the messages you want processed. Start narrowly: use a dedicated inbound alias, a trusted sender domain, or a precise subject prefix. Expanding an automation is easy; cleaning up accidental alerts is not.

Next, create a standalone Google Apps Script project. In the Apps Script editor, open Project Settings and add these Script Properties:

VOLANEA_API_KEY=sk_...
VOLANEA_FROM_EMAIL=alerts@your-verified-domain.example
VOLANEA_FROM_NAME=Inbox Alerts
NOTIFY_TO_EMAIL=operations@your-company.example

The VOLANEA_FROM_EMAIL value must be an address on a sending domain you have verified in Volanea. Do not set it to the random sender of an inbound Gmail message. The From address is part of your email identity and should be stable, authorized, and aligned with the domain configuration you control.

Keep the recipient address as a Script Property too. It makes routing explicit and keeps the destination out of source code, where it can be changed accidentally during an edit or copied into another project.

Working Google Apps Script code

The code below processes up to 50 labeled threads on each run. It takes the most recent message in each thread, assembles HTML and plain-text notification content, calls Volanea’s POST /v1/send endpoint, and applies the sent label only after a successful response.

const SOURCE_LABEL = 'volanea-send';
const SENT_LABEL = 'volanea-sent';
const MAX_THREADS_PER_RUN = 50;

function sendLabeledGmailMessagesWithVolanea() {
  const props = PropertiesService.getScriptProperties();
  const apiKey = requiredProperty_(props, 'VOLANEA_API_KEY');
  const fromEmail = requiredProperty_(props, 'VOLANEA_FROM_EMAIL');
  const fromName = requiredProperty_(props, 'VOLANEA_FROM_NAME');
  const notifyTo = requiredProperty_(props, 'NOTIFY_TO_EMAIL');

  const sourceLabel = GmailApp.getUserLabelByName(SOURCE_LABEL);
  if (!sourceLabel) {
    throw new Error(`Create the Gmail label: ${SOURCE_LABEL}`);
  }

  const sentLabel = GmailApp.getUserLabelByName(SENT_LABEL) ||
    GmailApp.createLabel(SENT_LABEL);

  const threads = sourceLabel.getThreads(0, MAX_THREADS_PER_RUN);

  threads.forEach((thread) => {
    // A prior successful run labels the whole thread as sent.
    if (thread.getLabels().some((label) => label.getName() === SENT_LABEL)) {
      return;
    }

    const messages = thread.getMessages();
    const message = messages[messages.length - 1];
    const messageId = message.getId();
    const subject = message.getSubject() || '(no subject)';
    const sender = message.getFrom() || '(unknown sender)';
    const receivedAt = message.getDate().toISOString();
    const textBody = message.getPlainBody() || '(no plain-text body)';
    const htmlBody = message.getBody() || escapeHtml_(textBody).replace(/\n/g, '<br>');

    // Field mapping from Gmail data to Volanea's send payload.
    const payload = {
      from: {
        email: fromEmail,
        name: fromName
      },
      to: [
        {
          email: notifyTo
        }
      ],
      subject: `[Gmail] ${subject}`,
      text: [
        `From: ${sender}`,
        `Received: ${receivedAt}`,
        `Gmail thread ID: ${thread.getId()}`,
        `Gmail message ID: ${messageId}`,
        '',
        textBody
      ].join('\n'),
      html: [
        '<h2>New Gmail notification</h2>',
        `<p><strong>From:</strong> ${escapeHtml_(sender)}<br>`,
        `<strong>Received:</strong> ${escapeHtml_(receivedAt)}<br>`,
        `<strong>Gmail thread ID:</strong> ${escapeHtml_(thread.getId())}<br>`,
        `<strong>Gmail message ID:</strong> ${escapeHtml_(messageId)}</p>`,
        '<hr>',
        htmlBody
      ].join('')
    };

    const response = UrlFetchApp.fetch('https://api.volanea.com/v1/send', {
      method: 'post',
      contentType: 'application/json',
      payload: JSON.stringify(payload),
      headers: {
        Authorization: `Bearer ${apiKey}`,
        'Idempotency-Key': `gmail-message:${messageId}`
      },
      muteHttpExceptions: true
    });

    const status = response.getResponseCode();
    if (status >= 200 && status < 300) {
      sentLabel.addToThread(thread);
      return;
    }

    throw new Error(
      `Volanea send failed for Gmail message ${messageId}: ` +
      `${status} ${response.getContentText()}`
    );
  });
}

function requiredProperty_(props, key) {
  const value = props.getProperty(key);
  if (!value) {
    throw new Error(`Missing Script Property: ${key}`);
  }
  return value;
}

function escapeHtml_(value) {
  return String(value)
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;');
}

The REST request uses Volanea’s single-message endpoint, POST https://api.volanea.com/v1/send. It sends one message to one address in this example, although the endpoint supports multiple recipients. The Idempotency-Key header is intentionally based on Gmail’s immutable message ID rather than a timestamp or a random UUID.

Use the Volanea API reference and setup guides when you need to extend this baseline with templates, attachments, metadata, scheduling, delivery events, or batch sends.

Install the actual trigger

The script will not run by itself after you paste the code. Install a time-driven trigger in Apps Script:

  1. Run sendLabeledGmailMessagesWithVolanea manually once and complete the requested authorization flow.
  2. Open the Apps Script Triggers area.
  3. Add a trigger for sendLabeledGmailMessagesWithVolanea.
  4. Choose Time-driven as the event source.
  5. Choose an interval appropriate for the workflow, such as every five minutes.

The first manual run is important because Apps Script needs authorization to read Gmail, access Script Properties, and make external HTTP requests. The HTTP capability comes from UrlFetchApp, which requires the external-request authorization scope.

Do not use a simple trigger for this job. Simple triggers have authorization restrictions and are not suitable for Gmail access plus authenticated external API calls. Use an installable time-driven trigger instead.

A five-minute schedule is a reasonable starting point for internal notifications. Move to a more frequent interval only after measuring real inbox volume, execution time, and quota use. Apps Script quotas differ between consumer Gmail accounts and Google Workspace accounts, and quotas can change over time.

Where the Volanea API key belongs

The Volanea API key belongs in Apps Script Script Properties for this direct Apps Script implementation.

That is the Gmail-side secret store for this architecture. Script Properties are persistent key-value storage attached to the Apps Script project. They are better than hardcoding a key in source code, placing it in a Gmail message template, or exposing it in a web page.

The API key must never appear in:

  • Client-side JavaScript delivered to a browser.
  • A Chrome extension configuration visible to end users.
  • A Gmail signature, canned response, draft, or message body.
  • A Google Sheet cell that broad collaborators can read.
  • A public Git repository, pasted code sample, or screenshot.

A Volanea secret key authorizes email sends. If a key reaches a client-visible location, anyone who can inspect that location can potentially use it to send messages through your account. That creates cost, abuse, deliverability, and incident-response risks.

Script Properties are suitable when the Apps Script project has tightly controlled editors. Review project access regularly. Every editor who can inspect or modify the script is part of your security boundary. For a broader team, a production environment, or a Gmail API/Pub/Sub architecture, keep the Volanea key in a server-side secret manager and have only the backend call Volanea.

Use a dedicated key for this automation if your Volanea account supports separate keys or scoped credentials. That makes rotation and revocation less disruptive. Record what workflow owns the key, rotate it if access changes, and revoke it immediately if you suspect exposure.

Why idempotency is non-negotiable

A mail-triggered automation must assume that a send attempt can become ambiguous.

For example, Apps Script may submit the Volanea request successfully but fail before it applies the volanea-sent Gmail label. At the next scheduled run, the same Gmail thread remains eligible. Without duplicate protection, the automation sends the same notification again.

The stable header below prevents that class of duplicate:

Idempotency-Key: gmail-message:<Gmail message ID>

This key represents one business event: “send the notification created from this specific Gmail message.” It does not represent one HTTP attempt. Every retry for that same message must reuse exactly the same idempotency key.

The script also adds volanea-sent only after a 2xx response. That label is a helpful operational marker, but it is not the primary duplicate-control mechanism. A label update can fail, a thread can be revisited, or two executions can overlap. The API-level idempotency key is the durable protection at the send boundary.

If your desired behavior is “notify on every new message in an ongoing Gmail conversation,” this implementation is appropriate because it keys sends to the latest Gmail message ID. If your desired behavior is “notify once per thread,” change the idempotency key to gmail-thread:<thread ID> and be aware that later replies will not create additional sends.

When this breaks

Every integration eventually meets incomplete data, ambiguous outcomes, quota limits, or configuration drift. Treat those conditions as expected operating cases rather than surprising exceptions.

A scheduled execution runs again and duplicates a send

The time-driven Apps Script model can revisit a thread after an exception or after a partial execution. Gmail does not directly retry an outbound Volanea webhook because Gmail is not making one, but the scheduled script can process the same source record more than once.

Use the Gmail message ID as the stable Volanea Idempotency-Key. Do not generate a fresh UUID for each attempt. A random key makes every retry look like a brand-new send.

Also leave the volanea-sent label in place as a visible audit signal. It helps operators search Gmail and understand which threads completed, even though idempotency remains the last line of defense.

The request times out or returns an uncertain error

A failed HTTP call does not always mean Volanea rejected the message. The request could have reached the API and the response could have been lost before Apps Script received it.

Keep the same idempotency key when retrying. If the first attempt was accepted, Volanea can recognize the repeated logical operation rather than creating another email. Log the Gmail message ID, HTTP status, and response body in Apps Script execution logs so you can correlate failures.

Do not mark the thread as sent after a non-2xx response. Let the next scheduled run retry it with the same key, or route the error to an operator if the failure is permanent, such as an invalid sender domain or revoked API key.

Expected Gmail fields are missing or unexpectedly formatted

Inbox messages are not normalized application records. getFrom() may contain a display name plus an address. A subject can be empty. A plain-text body may be blank or minimal. HTML may include images, quoted replies, tracking markup, or untrusted content.

The example has fallbacks for an empty subject and body, and it HTML-escapes fields that the script places in markup. Keep that escaping. Do not inject raw sender, subject, or plain-text values into HTML without escaping them.

Be cautious with attachments. The example deliberately does not forward them. Attachment forwarding adds message-size, content-safety, and data-handling considerations. Add it only after confirming the exact Volanea attachment schema and establishing what content your team is permitted to retransmit.

Gmail filters label more mail than intended

A loose Gmail filter can make an inbox automation noisy or unsafe. For example, a filter matching a common word might label vendor newsletters, forwarded conversations, and genuine action requests alike.

Start with a dedicated inbound alias or trusted sender list. Search Gmail periodically for label:volanea-send -label:volanea-sent to inspect the pending queue. If the automation should be manually approved, apply volanea-send manually and omit automatic Gmail filters altogether.

Apps Script reaches a quota or execution limit

Apps Script has quotas for Gmail reads, URL Fetch calls, property operations, and total trigger runtime. The limits vary by account type and may change. A busy inbox can also make getThreads() and message-body retrieval slower than expected.

Process a bounded number of threads each run, as the example does with MAX_THREADS_PER_RUN. If the label backlog grows, increase frequency carefully, lower the per-run batch size, or move to a backend that uses the Gmail API and a queue. Do not respond by removing limits and attempting to process an entire historical label in one execution.

The sender domain is not verified

A request can be structurally correct while still failing because VOLANEA_FROM_EMAIL is not authorized for your Volanea account. Configure and verify the sending domain before activating the trigger. Keep the From identity stable after launch so mailbox recipients, authentication alignment, and operational expectations remain consistent.

Choosing Apps Script, Zapier or Make, or Gmail API push

The right route depends on how much control, volume, and latency your workflow needs.

Use Apps Script when you want direct ownership

Apps Script is a strong fit when the workflow belongs to one Google account or Workspace team, a few-minute delay is acceptable, and you can maintain a small amount of JavaScript.

Its advantages are direct Gmail access, no third-party automation layer, clear label-based routing, and Script Properties for project configuration. The trade-off is that it is polling-based and subject to Apps Script execution and service quotas.

Use Zapier or Make when visual workflow design matters

An automation platform can make a Gmail-to-email workflow more accessible to non-developers, especially when several business tools participate. However, do not paste a long-lived Volanea secret key into a client-visible step or rely on an unreviewed generic webhook configuration.

If you use an automation platform, prefer a small server-side endpoint that accepts the automation payload, validates it, stores or derives a stable event ID, and calls Volanea. This gives you control over retries, recipient validation, audit logs, and secret rotation.

Use Gmail API push when near-real-time processing matters

The Gmail API route is the better choice when you need mailbox changes delivered quickly, need to support many connected inboxes, or want a backend that can queue, retry, observe, and scale independently.

It is more complex because Gmail watch notifications point to Pub/Sub, not directly to Volanea. Your service must renew watches, store history IDs, reconcile mailbox changes, and handle Pub/Sub redelivery. In return, you avoid periodic mailbox polling and gain a more production-oriented event pipeline.

Operational checklist before going live

Use this checklist before enabling an automatic Gmail filter or scheduling a frequent trigger:

  • Verify the Volanea sending domain and use a controlled From address.
  • Store the API key in Script Properties, not code or browser-visible configuration.
  • Create both volanea-send and volanea-sent Gmail labels.
  • Begin with manual labeling and a test recipient.
  • Confirm that the subject, sender, received time, body, and thread ID appear as expected.
  • Verify that the Idempotency-Key remains unchanged when you rerun the same Gmail message.
  • Test an invalid API key and confirm the source thread is not labeled sent.
  • Test a temporary failure and confirm a later run retries safely.
  • Review Apps Script execution logs and Gmail’s pending-label search regularly.
  • Restrict who can edit the Apps Script project and rotate the key when ownership changes.

Once the flow is stable, you can adjust the HTML notification layout, route different Gmail labels to different operational recipients, or replace inline email content with Volanea templates. Keep the core contract unchanged: one Gmail message ID should always map to one stable idempotency key.

Conclusion

The dependable way to send email from Gmail with Volanea is not to search for a nonexistent native Gmail plugin. It is to use Gmail labels as the routing decision, an authorized Apps Script time-driven trigger as the execution mechanism, and Volanea’s REST API as the email-delivery layer.

That design is explicit about where each responsibility lives. Gmail selects the work, Apps Script retrieves and maps the message, Script Properties protect the secret, Volanea sends from your verified domain, and the message ID protects retries from becoming duplicate mail. For teams that outgrow polling, the same mapping and idempotency principles carry forward to a Gmail API, Pub/Sub, and backend architecture.

FAQ

Can Gmail call the Volanea API directly?

Not through a standard native outbound-webhook feature. Gmail does not provide a direct “POST this incoming message to my URL” setting. Google Apps Script can call Volanea with UrlFetchApp, or a backend can use Gmail API push notifications through Pub/Sub.

What Gmail event triggers the send in this setup?

The workflow uses a Gmail user label named volanea-send as the business signal. An Apps Script time-driven trigger runs on a schedule, finds labeled threads, and sends the latest message through Volanea.

Where should I store my Volanea API key in Google?

Store it in Apps Script Script Properties for a tightly controlled Apps Script project. Never put it in front-end JavaScript, a Gmail message, a shared spreadsheet, or source code committed to a repository.

How do I prevent duplicate emails when Apps Script retries?

Use the Gmail message ID in Volanea’s Idempotency-Key header, such as gmail-message:<message ID>. Reuse the same key for every retry of that source message.

Can I use this for every incoming Gmail message?

Technically, you can use a Gmail filter to label matching messages automatically. Operationally, start with a narrow filter or manual labels, validate the results, and ensure the recipient, content handling, and sender identity match your security and deliverability requirements.