Calendly can schedule the meeting, but a transactional email triggered by the booking often needs to come from your own product domain, use your own template logic, and fit into your existing email delivery stack. This guide explains how to send transactional email from Calendly by receiving a Calendly webhook and turning that event into a Volanea REST API request.

There is no native Volanea-for-Calendly app or one-click installation flow in this pattern. Instead, Calendly remains the scheduling system, a webhook relay receives event notifications, and the relay decides whether to call Volanea. That separation is useful: it keeps credentials off the client, gives you a place to validate webhook signatures, and lets you handle cancellations, reschedules, duplicate deliveries, and business-specific rules consistently.

What this integration pattern does

A Calendly booking creates an event. Calendly sends a webhook notification to an HTTPS endpoint you control. Your endpoint validates the request, reads the invitee and event details, builds an email, then sends it through Volanea.

The end-to-end flow is:

  1. A prospect, customer, candidate, or teammate selects a time in Calendly.
  2. Calendly emits an invitee.created webhook for the newly scheduled event.
  3. Your webhook endpoint receives the JSON payload.
  4. The endpoint extracts the invitee’s name, email address, meeting time, event type, and identifiers.
  5. Your code calls Volanea’s REST email endpoint with the recipient, sender, subject, and HTML or text content.
  6. Volanea accepts the message for delivery, while your application records the result and the Calendly event identifier.

The same architecture can send a different message when Calendly emits invitee.canceled. For example, you might send a cancellation acknowledgement, notify an account owner, or create a support follow-up when a high-value consultation is cancelled.

This is more flexible than using only a calendar confirmation. Calendly’s own notifications are useful for basic scheduling communication, but a webhook-to-email flow can use your brand, application data, CRM context, localized content, internal routing rules, and delivery reporting.

Choose the right Calendly trigger

Calendly’s webhook API provides event notifications for activity in Calendly. For transactional booking emails, the two most relevant webhook event names are invitee.created and invitee.canceled.

invitee.created: a meeting was scheduled

Use invitee.created when you want an email after a person successfully books. Common examples include:

  • A branded booking confirmation that complements the calendar invitation.
  • A preparation email with an agenda, questionnaire, or account-specific resources.
  • A notification to an internal owner that a qualified lead booked.
  • A message to an alternative recipient, such as a sales representative or customer-success manager.
  • A product workflow email that opens a case, starts onboarding, or records consent.

Be deliberate about sending another confirmation to the invitee. Calendly may already send scheduling notifications and calendar invitations, depending on the account and event configuration. Your custom email should add value rather than repeat the same information. A useful confirmation might include what to prepare, the expected meeting outcome, a relevant guide, or a link to update account details.

invitee.canceled: a meeting was cancelled

Use invitee.canceled to react to cancellations. The payload includes the event and invitee context, and canceled-event data can include cancellation information. Your relay can email the original invitee, an internal team, or both.

A cancellation workflow should not blindly send a generic “sorry to see you go” message. Consider whether the cancellation was a reschedule, whether the appointment type warrants an outreach sequence, and whether the recipient has already booked a replacement time. A small delay or a lookup in your database can prevent an awkward email when someone cancels one slot only because they immediately chose another.

Webhooks versus no-code automation tools

Calendly also has integrations with automation platforms such as Zapier. Those tools can be appropriate for a simple workflow, especially when you need to pass booking data to a spreadsheet or CRM. However, sending directly from an automation platform to an email API can become difficult when you need webhook-signature verification, idempotency, HTML rendering, secret management, conditional routing, or reliable retries.

A pragmatic model is to use Calendly’s webhook capability to call a small endpoint you own. If you already use Zapier or Make, they can still be part of the workflow, but treat them as orchestration tools rather than a substitute for a secure server-side email sender. Never put a Volanea API key into browser JavaScript, a public booking page, or a client-visible automation configuration.

Understand the Calendly webhook payload

Calendly webhook deliveries have a top-level event name and a payload object. The details you use for an email generally live in payload.invitee, payload.event, and payload.event_type.

A representative invitee.created delivery has this structure. Values, optional fields, and URLs vary by account, event type, and booking.

{
  "created_at": "2024-06-20T16:20:00.000000Z",
  "created_by": "https://api.calendly.com/users/AAAAAAAAAAAAAAAA",
  "event": "invitee.created",
  "payload": {
    "event_type": {
      "uuid": "AAAAAAAAAAAAAAAA",
      "kind": "discovery-call",
      "slug": "discovery-call",
      "name": "Discovery Call",
      "scheduling_url": "https://calendly.com/acme/discovery-call",
      "duration": 30,
      "owner": "https://api.calendly.com/users/AAAAAAAAAAAAAAAA",
      "type": "https://api.calendly.com/event_types/AAAAAAAAAAAAAAAA"
    },
    "event": {
      "uuid": "BBBBBBBBBBBBBBBB",
      "assigned_to": [
        "sales@example.com"
      ],
      "start_time": "2024-06-24T14:00:00.000000Z",
      "start_time_pretty": "Jun 24, 2024 10:00am",
      "end_time": "2024-06-24T14:30:00.000000Z",
      "end_time_pretty": "Jun 24, 2024 10:30am",
      "created_at": "2024-06-20T16:20:00.000000Z",
      "location": {
        "type": "zoom",
        "location": "https://zoom.us/j/123456789"
      },
      "canceled": false,
      "event_memberships": [],
      "event_guests": []
    },
    "invitee": {
      "uuid": "CCCCCCCCCCCCCCCC",
      "first_name": "Avery",
      "last_name": "Nguyen",
      "name": "Avery Nguyen",
      "email": "avery@example.net",
      "timezone": "America/New_York",
      "created_at": "2024-06-20T16:20:00.000000Z",
      "is_reschedule": false,
      "cancel_url": "https://calendly.com/cancellations/...",
      "reschedule_url": "https://calendly.com/reschedulings/...",
      "tracking": {}
    }
  }
}

Do not build production code around the assumption that every nested object or field is always present. Location can be absent or use a format different from a video-conference URL. Teams may have multiple assignees. The invitee’s name may be incomplete. Custom questions, tracking data, guests, and payment-related data can vary by configuration.

The stable approach is to validate the fields your template truly needs, provide sensible fallbacks, and log unexpected payload variations without logging more personal data than necessary.

Map webhook fields to email data

For a booking confirmation, a practical mapping looks like this:

Calendly fieldEmail use
payload.invitee.emailPrimary recipient address
payload.invitee.namePersonal greeting
payload.event_type.nameMeeting type in subject and body
payload.event.start_timeCanonical time for formatting
payload.invitee.timezoneInvitee-local display timezone when available
payload.event.locationJoin instructions, if appropriate
payload.invitee.reschedule_urlRescheduling action link
payload.invitee.cancel_urlCancellation action link
payload.event.uuidIdempotency and audit key

Use the ISO-formatted start_time as the source of truth. The start_time_pretty field is convenient for human display, but server-side formatting from ISO time gives you control over locale, wording, and timezone. If you use the invitee timezone, handle invalid or missing timezone values safely.

Prepare your Volanea sending identity

Before wiring Calendly to an email API, set up the domain from which the messages will be sent. A transactional message should use an address your organization controls, such as appointments@example.com or hello@example.com, rather than an individual employee’s mailbox.

Authenticate the sending domain and use a clear, consistent sender identity. Domain authentication is central to deliverability because recipient systems evaluate whether the infrastructure is authorized to send mail for the visible From domain. It also gives recipients a more coherent experience than messages arriving from an unrelated or shared domain.

Your relay needs a Volanea API key stored as a server-side secret. Keep separate credentials for development, staging, and production where possible. Limit access to deployment secrets, rotate keys when an employee or integration loses access, and never commit a key to a repository.

When you are ready to select a plan or estimate sending volume, review transactional email pricing based on real booking volume, reminder traffic, cancellation messages, and internal notifications—not just the number of initial bookings.

Build a secure webhook relay

The relay is a small HTTP service. It may be a Node.js server, a serverless function, a containerized API route, or a worker runtime. Its job is not just to forward JSON; it is the policy and security boundary between a scheduling system and your email infrastructure.

A production relay should do the following:

  • Accept HTTPS POST requests at a dedicated route, such as /webhooks/calendly.
  • Preserve the raw request body before JSON parsing if signature validation requires it.
  • Validate Calendly’s webhook signature according to Calendly’s current webhook documentation.
  • Reject unknown event names and malformed payloads.
  • Return a successful response promptly after durable processing or queueing.
  • Deduplicate deliveries using a durable idempotency key.
  • Call Volanea only after the event passes your business rules.
  • Record a minimal audit record: event ID, event type, recipient, template name, send result, and timestamps.

Why signature verification matters

A public webhook URL is discoverable or guessable eventually. Without verification, anyone who can reach it may be able to cause your application to send email, probe recipient handling, or create unwanted delivery costs. IP allowlists can add defense in depth, but they are not a replacement for cryptographic verification because network ranges and delivery infrastructure can change.

Follow Calendly’s documented signing procedure exactly. In particular, signature schemes commonly depend on the unmodified raw body. Parsing JSON and serializing it again can change byte order or whitespace, making a valid signature fail. Verify first, parse second.

Why idempotency matters

Webhook delivery is generally at-least-once, not exactly-once. A sender may retry after a timeout, and your own system may retry a failed email request. If every repeat produces a new send, one appointment can generate duplicate confirmations.

For a booking email, store a key such as calendly:invitee.created:<event-uuid>:booking-confirmation-v1. Create it atomically before sending, or use a queue with a deduplication capability. If the key already exists, return success without sending another message. For cancellations, use a distinct event-type key.

Do not use the invitee email address alone as an idempotency key. The same person may legitimately book multiple meetings.

Send the Volanea email from Node.js

The following example shows the application logic after you have validated the Calendly request and parsed the JSON. Keep the Volanea URL and API key in environment variables rather than hard-coding credentials. The request shape uses a standard transactional-email payload: sender, recipient, subject, HTML, and text.

import express from "express";

const app = express();
app.use(express.json());

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

function formatMeetingTime(isoTime, timezone) {
  const date = new Date(isoTime);
  if (Number.isNaN(date.getTime())) return "the scheduled time in your calendar";

  try {
    return new Intl.DateTimeFormat("en-US", {
      dateStyle: "full",
      timeStyle: "short",
      timeZone: timezone || "UTC"
    }).format(date);
  } catch {
    return new Intl.DateTimeFormat("en-US", {
      dateStyle: "full",
      timeStyle: "short",
      timeZone: "UTC"
    }).format(date);
  }
}

async function sendWithVolanea({ to, subject, html, text }) {
  const response = await fetch(process.env.VOLANEA_EMAIL_API_URL, {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.VOLANEA_API_KEY}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      from: process.env.VOLANEA_FROM_EMAIL,
      to: [to],
      subject,
      html,
      text
    })
  });

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

  return response.json();
}

app.post("/webhooks/calendly", async (req, res) => {
  // Verify Calendly's signature against the raw request body before this point.
  // Also perform an atomic idempotency check using payload.event.uuid.
  const webhook = req.body;

  if (webhook.event !== "invitee.created") {
    return res.status(204).end();
  }

  const invitee = webhook.payload?.invitee;
  const event = webhook.payload?.event;
  const eventType = webhook.payload?.event_type;

  if (!invitee?.email || !event?.uuid || !event?.start_time) {
    return res.status(400).json({ error: "Missing required Calendly fields" });
  }

  const name = escapeHtml(invitee.name || "there");
  const meetingName = escapeHtml(eventType?.name || "meeting");
  const meetingTime = formatMeetingTime(event.start_time, invitee.timezone);
  const rescheduleUrl = invitee.reschedule_url || "";

  const subject = `Your ${eventType?.name || "meeting"} is booked`;
  const html = `
    <p>Hi ${name},</p>
    <p>Your <strong>${meetingName}</strong> is booked for ${escapeHtml(meetingTime)}.</p>
    ${rescheduleUrl ? `<p>If your plans change, you can <a href="${escapeHtml(rescheduleUrl)}">reschedule your meeting</a>.</p>` : ""}
    <p>We look forward to speaking with you.</p>
  `;
  const text = `Hi ${invitee.name || "there"},\n\nYour ${eventType?.name || "meeting"} is booked for ${meetingTime}.\n${rescheduleUrl ? `\nReschedule: ${rescheduleUrl}\n` : ""}\nWe look forward to speaking with you.`;

  try {
    const result = await sendWithVolanea({
      to: invitee.email,
      subject,
      html,
      text
    });

    // Persist event.uuid, invitee.uuid, and result before acknowledging in a real app.
    return res.status(202).json({ accepted: true, email: result });
  } catch (error) {
    console.error("Calendly email send error", {
      eventUuid: event.uuid,
      message: error.message
    });
    return res.status(500).json({ error: "Email could not be queued" });
  }
});

Set these environment variables in your deployment platform:

VOLANEA_EMAIL_API_URL="https://YOUR_VOLANEA_API_ENDPOINT"
VOLANEA_API_KEY="your-server-side-api-key"
VOLANEA_FROM_EMAIL="Appointments <appointments@example.com>"

Use the exact endpoint and payload fields documented for your Volanea account when setting VOLANEA_EMAIL_API_URL. The Volanea email API reference and setup guides are the source of truth for the current endpoint, authentication format, available message fields, and response object. Keeping the API URL in configuration makes the webhook application easier to test across environments without exposing a secret.

The example deliberately escapes dynamic values before placing them in HTML. Names and event-type labels are user-controlled or configuration-controlled input. Escaping them avoids turning a malicious or accidental value into markup in an email template.

Configure Calendly to deliver the webhook

Create a webhook subscription in Calendly for the organization or user scope that owns the event types you want to monitor. Subscribe to invitee.created for booking messages and add invitee.canceled only if you have a defined cancellation flow.

Use a production HTTPS URL, for example:

https://app.example.com/webhooks/calendly

During development, a tunneling tool can expose a local endpoint temporarily, but do not leave a development tunnel configured for production events. It is easy to lose the tunnel, and its logs may contain personal data from booking payloads.

Before enabling a production subscription, test with an event type reserved for internal bookings. Confirm that your relay receives the event, validates it, deduplicates it, and sends to a test mailbox. Inspect the actual received email in Gmail, Outlook, and an email-testing inbox if possible. HTML that looks fine in a browser can render differently in mailbox clients.

Make the email useful, not redundant

A calendar event already carries time, attendee, and often a meeting link. A custom transactional message should do work the calendar event does not do.

For a sales discovery call, add expectations: who will attend, the purpose of the call, and a short question that helps the representative prepare. For onboarding, include a checklist or a link to finish account setup. For an interview, explain the format, accessibility contact, and preparation materials. For support appointments, include the ticket number and a safe way to attach diagnostics.

Keep the primary purpose transactional. Do not turn a booking confirmation into a broad promotional campaign without considering consent, local law, and recipient expectations. Transactional and marketing messages have different audience rules, unsubscribe expectations, and deliverability consequences.

Handle meeting links carefully

A meeting URL can be sensitive. Include it only for the invitee or authenticated internal recipients who need it. Do not send an invitee’s private meeting link to a generic distribution list. If your workflow emails internal teams, send a separate internal template rather than forwarding the customer-facing confirmation.

Similarly, do not put raw answers to sensitive Calendly intake questions into an email unless the recipient needs them and your data-handling policy permits it. A webhook relay is a good place to filter fields before they enter email content or logs.

Add cancellation, rescheduling, and routing logic

A real scheduling workflow is rarely a single “booking equals email” rule. Add conditions that reflect the customer experience you want.

For example:

  • Send a confirmation only for externally booked event types, not internal standups.
  • Skip a custom confirmation when Calendly’s standard notification is sufficient.
  • Send a different template for a paid consultation versus a free introductory call.
  • Notify the assigned account owner when an enterprise lead books.
  • Suppress a cancellation email if a replacement event is found within a short window.
  • Use a different From address or reply-to route for support, recruiting, and sales.

The is_reschedule field can be useful, but do not assume it resolves every sequence of cancellation and rebooking events by itself. Persist your own association between invitee records, event UUIDs, and prior appointments when the distinction matters commercially.

For teams that route bookings to several hosts, inspect payload.event.assigned_to and, where necessary, use Calendly’s API or your own mapping to identify the responsible person. Avoid using the first array value as business truth unless your event configuration guarantees that behavior.

Reliability, observability, and delivery outcomes

A webhook handler that calls an email API synchronously can work at low volume, but a queue is usually the more resilient design. The webhook receiver validates and stores a normalized job, then responds promptly. A worker sends through Volanea, records the provider response, and retries transient failures with bounded backoff.

This design separates two failure domains. Calendly should not need to wait while an email provider is slow, and a temporary Volanea API failure should not cause an otherwise valid Calendly event to disappear. It also lets you replay a failed job after correcting a template or configuration issue.

Track at least these measurements:

  • Webhooks received, validated, rejected, and duplicated.
  • Emails queued, accepted by the API, failed, and retried.
  • Send latency from Calendly event creation to API acceptance.
  • Template version and event type for every send.
  • Delivery, bounce, complaint, and suppression outcomes where your email platform exposes them.

Do not log full webhook bodies indefinitely. They can contain names, email addresses, custom answers, and meeting details. Use structured logs with event IDs and redacted email addresses, set retention limits, and restrict access. For troubleshooting, store only the minimum payload data required to reproduce the issue.

Common implementation mistakes

The most common mistake is treating webhooks as trusted internal traffic. Validate signatures, enforce HTTPS, reject unsupported event types, and rate-limit the endpoint where appropriate.

The second is duplicate sending. Build idempotency before launch, not after the first customer forwards three identical confirmations. Webhook retries are normal behavior, and timeout-related duplicates can occur even when the email request actually succeeded.

Another mistake is relying on start_time_pretty without considering recipient timezone. Format the canonical timestamp using the invitee timezone when appropriate, and state the timezone in the email if there is any chance of confusion.

Finally, do not send from an unauthenticated or mismatched domain. Booking traffic is typically expected and valuable, so protect its inbox placement with a properly authenticated sender, stable templates, and clean recipient handling.

A practical launch checklist

Before connecting your live Calendly event types, confirm each item below.

  1. Your Volanea sending domain is authenticated and the From address is approved for your organization.
  2. The API key is stored only in server-side secret management.
  3. The Calendly webhook URL is HTTPS and points to the production service.
  4. Webhook signatures are validated from the raw request body.
  5. The service accepts only event names your workflow supports.
  6. Idempotency is durable and keyed to the actual booking event, not merely an email address.
  7. Template input is escaped and optional fields have fallbacks.
  8. Test bookings have been checked in representative mailbox providers.
  9. Cancellation and rescheduling behavior has been explicitly decided.
  10. Logs and database records minimize personal data and have a retention policy.

Conclusion

To send transactional email from Calendly, you do not need a native app integration. Calendly webhooks provide the scheduling event, and a small authenticated relay gives you full control over validation, personalization, business rules, and the Volanea send request.

Start with invitee.created, one useful confirmation template, signature verification, and durable deduplication. Then add cancellation handling, internal alerts, segmentation, and queue-based retries as the scheduling workflow becomes more important to your business.

FAQ

Can Calendly send directly to Volanea?

Not through a native Volanea Calendly app flow. Use a Calendly webhook or an automation layer to trigger a server-side endpoint, then have that endpoint call Volanea’s REST API.

Which Calendly webhook should trigger a booking email?

Use invitee.created for a new scheduled appointment. Use invitee.canceled only when you have a clear cancellation message or internal follow-up workflow.

Should I send another confirmation if Calendly already sends one?

Only if the custom email adds useful information, such as preparation instructions, account context, support details, or a branded next step. Avoid sending a near-duplicate of the calendar invitation.

How do I stop duplicate confirmation emails?

Store an idempotency key based on the Calendly event UUID and the template or event type. If the same webhook is delivered again, acknowledge it without sending a second message.

Is it safe to put the Volanea API key in a Zapier step or booking page?

Do not expose the key in browser code or public configuration. Keep it in a server-side secret store and send email through a protected webhook relay or trusted backend service.