If you need to send email from PandaDoc after a document is completed, viewed, sent, or declined, connect PandaDoc’s webhook events to a secure middleware endpoint and let that service call Volanea’s REST API. This approach is reliable, keeps your Volanea credentials private, and does not require a native PandaDoc marketplace app.
PandaDoc does not provide a native Volanea app, listing, or marketplace plugin. The integration point is PandaDoc’s webhook capability: PandaDoc posts an event payload to an HTTPS endpoint when a document event occurs. Your endpoint decides whether that event merits an email, maps PandaDoc data into an email message, and sends it through Volanea.
That distinction matters. A webhook is an event notification, not an email template engine and not a safe place to store a third-party sending key. Treat PandaDoc as the source of document events, your middleware as the decision and security layer, and Volanea as the delivery layer.
What this integration does
The practical pattern is straightforward:
- A PandaDoc document changes state.
- PandaDoc sends a
document_state_changedwebhook to your HTTPS endpoint. - Your endpoint checks that the new document status is the one you care about, such as
document.completed. - It extracts safe fields from the webhook payload, such as the document name, document ID, recipient email, and status.
- It makes one authenticated REST request to Volanea.
- Volanea delivers the notification from a domain you have configured for sending.
A common example is a completion email. When every required recipient signs a proposal and its status changes to document.completed, send the signer a confirmation and send the deal owner an internal alert. Other useful event-to-email combinations include:
- A
document.sentevent that starts a customer-facing onboarding sequence. - A
document.viewedevent that alerts a sales owner that a high-value proposal was opened. - A
document.declinedevent that notifies an account team and creates a follow-up path. - A
document.expiredevent that sends an internal renewal or rescue alert.
Do not automatically turn every webhook into an email. PandaDoc documents can generate several legitimate state and recipient events during their lifetime. Decide explicitly which status transition represents a business event, then make that event idempotent.
The PandaDoc trigger: document state changed
For this integration, use PandaDoc’s document_state_changed webhook event. The event is appropriate when the email should depend on a document’s lifecycle state, rather than merely on a document record existing.
The field to test is the document status in the webhook’s data object. For a signed-and-finished agreement, the relevant value is typically document.completed. This is more precise than sending when a document is created or when the sender first dispatches it: a created document may remain a draft, and a sent document may never be signed.
Choose the status before writing code
Write the business rule in one sentence first. For example: “Send a receipt only after the agreement status is document.completed.” That sentence tells you what the middleware must enforce.
Here is a useful decision table:
| PandaDoc status | What it means for an automation | Typical Volanea email |
|---|---|---|
document.draft | A document exists but is not ready for recipients. | Usually none. |
document.sent | PandaDoc has sent the document to recipients. | Internal notification or a separate welcome message. |
document.viewed | A recipient has opened the document. | Optional internal sales alert. |
document.completed | The document workflow has been completed. | Customer confirmation and/or owner alert. |
document.declined | A recipient declined the document. | Internal escalation or customer follow-up. |
document.expired | The document expired before completion. | Internal recovery notification. |
Exact statuses and event availability should be confirmed against the PandaDoc API account and webhook documentation for your plan. Build the condition around the status value PandaDoc actually sends to your endpoint, not an assumed label from a CRM or a Zapier recipe.
Where to configure the PandaDoc side
Create a webhook subscription in PandaDoc’s developer/API webhook configuration and point it at a publicly reachable HTTPS URL that you control, such as:
https://automations.example.com/webhooks/pandadoc
Subscribe that endpoint to document_state_changed. PandaDoc’s configuration holds the destination URL and any PandaDoc-side webhook authentication or verification settings required by your account. It should not hold your Volanea API key.
If your PandaDoc plan or account setup does not expose API webhooks, use an automation intermediary that supports PandaDoc’s available triggers, such as Zapier or Make, and have that intermediary call your middleware endpoint. The same security and idempotency rules still apply. Do not put the Volanea key into a browser-side form, a public JavaScript bundle, or a client-visible automation parameter.
Understand the webhook payload before mapping it
PandaDoc sends JSON in a webhook POST. A document_state_changed delivery has an event name plus a data object representing document information. The exact object can vary by API version, event, account configuration, and document state, so log a redacted sample from your own test document before deploying production rules.
A representative completion event has this shape:
{
"event": "document_state_changed",
"data": {
"id": "L9M6dYwZVd2zYqLwY4aZqR",
"name": "Northwind Services Agreement",
"status": "document.completed",
"date_created": "2026-10-11T09:20:00.000000Z",
"date_modified": "2026-10-11T10:04:36.000000Z",
"expiration_date": null,
"version": "3",
"metadata": {
"account_id": "northwind-42"
},
"created_by": {
"id": "u_123",
"email": "owner@example.com",
"first_name": "Avery",
"last_name": "Ng"
},
"recipients": [
{
"id": "r_456",
"email": "signer@example.net",
"first_name": "Morgan",
"last_name": "Lee",
"role": "Client",
"has_completed": true
}
]
}
}
The sample is a mapping model, not a promise that every field will be present in every event. In particular, metadata is optional, expiration dates may be null, recipients can contain multiple people, and user profile fields may be absent. Your handler should not fail merely because a first name is unavailable.
Pick recipients deliberately
data.recipients is an array. “First recipient” is only a safe choice when your PandaDoc template guarantees one external signer and your process is designed around that person. For multi-party contracts, a better strategy is one of these:
- Send the customer confirmation to every recipient whose email is permitted by your policy.
- Send only an internal alert to
data.created_by.email. - Maintain the intended notification address in your application database, keyed by the PandaDoc document ID or your own metadata value.
- Fetch the document from PandaDoc’s API after receiving the webhook if the webhook does not contain the specific information your workflow needs.
Avoid inferring a customer’s identity from a recipient role name alone. Roles are template-specific, can be renamed, and may not have the same semantics across document types.
Keep document data out of subject lines where possible
A document name is useful in a message body and helpful in an internal subject line, but it may contain sensitive commercial information. Use a neutral external subject such as “Your agreement is complete” if subject-line exposure is a concern. Also escape dynamic values before inserting them into HTML so that a document name cannot alter your markup.
The secure architecture: PandaDoc, middleware, Volanea
The correct route is not PandaDoc directly to Volanea. PandaDoc webhooks are designed to post event data to an endpoint. Volanea expects an authenticated email-send request. A small server-side function bridges those two protocols and gives you control over validation, filtering, retries, observability, and secrets.
PandaDoc document status changes
|
v
PandaDoc document_state_changed webhook
|
v
Your HTTPS middleware endpoint
- validate request
- check event and status
- deduplicate
- map data and render content
|
v
Volanea REST email API
|
v
Recipient mailbox and delivery events
This middleware can be an Express app, a serverless function, a worker, or an automation service with a secure HTTP action. The implementation technology is less important than the properties: it must accept PandaDoc’s webhook quickly, protect secrets, tolerate repeated deliveries, and record enough information to diagnose failures.
Where the Volanea key belongs
The Volanea API key lives only in the middleware runtime’s secret store: for example, an environment variable configured in your cloud function, encrypted deployment secrets, or a dedicated secret manager. The process reads it as VOLANEA_API_KEY when it runs.
It does not live in PandaDoc. PandaDoc’s webhook setup should contain the URL of your receiver, not an API key for a separate email provider. It also must not appear in front-end JavaScript, an email template, a PandaDoc custom field, a client-accessible .env file, or a source repository.
An exposed sending key can allow an attacker to send mail from your authenticated domain, damage your sending reputation, and create costs. Restrict production keys to production infrastructure, rotate them when access changes, and keep separate keys for test and production workloads where your account setup supports that separation.
Working Node.js webhook-to-email example
The following Express example handles a PandaDoc document_state_changed delivery, sends an email only when data.status is document.completed, and maps fields into a Volanea REST API request. It uses a database-backed idempotency abstraction because webhook systems can redeliver an event.
import express from "express";
import crypto from "node:crypto";
const app = express();
app.use(express.json({ limit: "1mb" }));
const VOLANEA_API_KEY = process.env.VOLANEA_API_KEY;
const VOLANEA_FROM = process.env.VOLANEA_FROM; // e.g. "Northwind <contracts@mail.example.com>"
if (!VOLANEA_API_KEY || !VOLANEA_FROM) {
throw new Error("VOLANEA_API_KEY and VOLANEA_FROM must be configured");
}
function escapeHtml(value = "") {
return String(value)
.replaceAll("&", "&")
.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll('"', """)
.replaceAll("'", "'");
}
// Replace these two functions with a durable database table or queue.
const processed = new Set();
async function claimOnce(key) {
if (processed.has(key)) return false;
processed.add(key);
return true;
}
async function releaseClaim(key) {
processed.delete(key);
}
app.post("/webhooks/pandadoc", async (req, res) => {
const webhook = req.body;
const document = webhook?.data;
// Return a 2xx response for events this integration intentionally ignores.
if (webhook?.event !== "document_state_changed" ||
document?.status !== "document.completed") {
return res.sendStatus(204);
}
if (!document.id) {
console.error("PandaDoc webhook has no document id", { event: webhook?.event });
return res.sendStatus(400);
}
// A stable key prevents repeat completion deliveries from creating repeat mail.
const idempotencyKey = crypto
.createHash("sha256")
.update(`pandadoc:document.completed:${document.id}:${document.version ?? ""}`)
.digest("hex");
if (!(await claimOnce(idempotencyKey))) {
return res.sendStatus(204);
}
const recipient = document.recipients?.find((person) => person.email);
if (!recipient?.email) {
console.error("Completed PandaDoc document has no mappable recipient", {
documentId: document.id
});
return res.sendStatus(204);
}
const recipientName = [recipient.first_name, recipient.last_name]
.filter(Boolean)
.join(" ") || "there";
const documentName = document.name || "your document";
const safeName = escapeHtml(recipientName);
const safeDocumentName = escapeHtml(documentName);
const email = {
from: VOLANEA_FROM,
to: [recipient.email],
subject: "Your agreement is complete",
html: `
<p>Hi ${safeName},</p>
<p>Your document, <strong>${safeDocumentName}</strong>, is complete.</p>
<p>If you need a copy or have questions, reply to this email.</p>
`,
text: `Hi ${recipientName},\n\nYour document, ${documentName}, is complete.\n\nIf you need a copy or have questions, reply to this email.`,
headers: {
"X-PandaDoc-Document-ID": document.id
}
};
try {
const response = await fetch("https://api.volanea.com/v1/emails", {
method: "POST",
headers: {
"Authorization": `Bearer ${VOLANEA_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": idempotencyKey
},
body: JSON.stringify(email)
});
if (!response.ok) {
const detail = await response.text();
throw new Error(`Volanea returned ${response.status}: ${detail}`);
}
return res.sendStatus(202);
} catch (error) {
// Allow PandaDoc to retry a failed webhook delivery when applicable.
await releaseClaim(idempotencyKey);
console.error("Volanea send failed", { documentId: document.id, error: String(error) });
return res.sendStatus(500);
}
});
app.listen(process.env.PORT || 3000);
The field mapping in that example is explicit:
| PandaDoc field | Volanea email field | Purpose |
|---|---|---|
data.recipients[0].email | to[0] | Recipient address for the confirmation. |
data.recipients[0].first_name and last_name | html and text | Optional personal greeting. |
data.name | html and text | Identifies the completed document. |
data.id | X-PandaDoc-Document-ID and idempotency input | Correlates the email to the source document. |
data.status | Conditional logic | Ensures only completed documents send this message. |
Confirm the current request URL, request schema, supported headers, and response behavior in the email API reference and setup guides before deploying. Keep the API call isolated in one function so upgrades to your sending implementation do not affect webhook validation or business logic.
Validate and authenticate the webhook request
A public webhook URL is not automatically trustworthy. Anyone who discovers the URL could attempt to POST JSON to it. Before acting on an event, implement the webhook verification method PandaDoc makes available for your account and webhook configuration.
Webhook verification commonly depends on preserving the raw request body and validating a signature or secret according to the sender’s specification. Do not parse and reserialize JSON before a verification algorithm that expects the original bytes. In Express, that may mean capturing the raw body with a verify callback or using a route-specific raw-body parser.
Do not invent a verification header name or signature algorithm from another provider’s documentation. PandaDoc’s webhook verification configuration and documented request format are the source of truth. Test both a valid signed delivery and a deliberately invalid request before enabling real sends.
Add application-level checks too
Signature verification proves that the request came through the expected webhook mechanism. It does not replace workflow checks. Your handler should also:
- Require the expected event name,
document_state_changed. - Require the specific target status, such as
document.completed. - Require a nonempty document ID.
- Require a recipient selected by a documented business rule.
- Enforce document ownership, account metadata, or an allowlist where multiple PandaDoc workspaces feed one endpoint.
- Log the event type, document ID, delivery result, and Volanea message identifier without logging unnecessary personal data.
These checks prevent an unrelated PandaDoc event from triggering the wrong customer email and make production behavior auditable.
Build a production-safe email message
A webhook confirms an event happened; it does not necessarily carry everything a polished email needs. Decide whether your completion message can rely on webhook fields or needs data from another system.
For a simple confirmation, the recipient address, document name, and completion status may be enough. For a richer email with account branding, an invoice link, a document download link, or a named account manager, use data.id or a metadata identifier to look up authoritative data in your database.
Use an authenticated From domain
The from address in the Volanea request must use a sending domain you have set up and authenticated. Use a stable transactional identity such as contracts@mail.example.com or notifications@example.com, not the signer’s address. A sender identity aligned with your domain supports authentication, consistent user expectations, and easier reply handling.
Configure SPF, DKIM, and any required DNS records using the values shown in your Volanea account. DNS values are provider- and domain-specific, so copy them from the setup instructions rather than reusing a record from another service. Consider a monitored reply-to address if recipients may ask for copies, corrections, or assistance.
Include both HTML and text
The example includes html and text. HTML supports your brand and structured information; plain text gives recipients a clean fallback and makes the message usable in clients that restrict HTML. Keep both versions semantically equivalent.
Do not include a raw signed-document URL unless you understand its access controls and expiry behavior. A safer pattern is an authenticated link to your customer portal or a support route where the recipient can request a copy.
When this breaks: failures specific to this hop
A PandaDoc-to-Volanea workflow crosses multiple systems, and the failure mode often tells you where to look. Design for the following cases rather than treating a successful test delivery as proof of long-term reliability.
PandaDoc retries can create duplicate sends
Webhook delivery is at-least-once, not exactly-once. A network interruption, a slow response, or a non-2xx response can cause PandaDoc to try again. A retry can contain the same document state and payload as the earlier delivery.
Without deduplication, your customer may receive multiple “agreement complete” emails. Use a durable idempotency record with a unique key such as the PandaDoc document ID plus the target state and an event/version discriminator. Insert that record atomically before sending, or enqueue the job with a deduplication key supported by your queue.
The in-memory Set in the code is only instructional. It resets on restarts and is not shared across multiple server instances. Production systems need a database uniqueness constraint, Redis with careful persistence semantics, or a queue with durable deduplication.
Webhook timeouts can trigger a retry even if email was sent
Do not perform slow, optional work before acknowledging PandaDoc. If rendering, database queries, or the Volanea request makes your endpoint exceed the sender’s timeout, PandaDoc may retry. The first invocation could still finish after the timeout, producing a duplicate unless your idempotency design covers it.
For high reliability, acknowledge the validated event quickly after putting a durable job on a queue. A worker can then fetch additional data, render the message, call Volanea, and retry transient errors under your own controlled policy. Record both the inbound document event and the resulting Volanea send outcome.
Payload fields can be missing or different across document types and plans
Do not assume recipients, recipient names, metadata, or a specific custom value will be available in every webhook. Some fields can be null, optional, unavailable under account permissions, or differ with a document’s recipients and workflow. Test a draft, sent, viewed, completed, declined, and expired document where those states apply to your process.
If a required field is absent, choose a policy in advance. You might skip the customer email and alert an internal operator, fetch canonical details using the PandaDoc document ID, or send only to the document owner. Never substitute an unverified email address just to keep an automation moving.
Volanea can reject a request
A rejected email request is different from a bounced email. API rejection means the request could not be accepted, often because of credentials, a malformed payload, or a sender domain that is not ready. A later bounce or complaint concerns mailbox-level delivery after acceptance.
Log the HTTP status and a safely redacted response body from the Volanea API. Retry only errors that are plausibly transient, such as timeouts or server errors. Do not endlessly retry a bad recipient mapping, invalid sender, or authentication failure; route those to an alert and fix the configuration.
The wrong recipient receives the right message
This is the most serious logical failure. It happens when code chooses the first recipient even though the first recipient is an internal approver, when an old PandaDoc document is reused, or when metadata is associated with the wrong account.
Prevent it by documenting recipient selection in code, testing multi-recipient templates, and using a system-of-record lookup for sensitive workflows. For internal notifications, send to a fixed operational address or a validated owner record rather than trusting a template role alone.
Testing the workflow before production
Create a separate test document and use a mailbox you control. Avoid testing with live customer contracts or a production distribution list. Test each component in order so that a failure has a small search area.
- Confirm the PandaDoc webhook endpoint is reachable by HTTPS and receives an event.
- Save a redacted payload sample and verify its event name, document ID, status, and recipient structure.
- Verify that a non-completed event returns success without sending email.
- Complete a test document and verify exactly one Volanea request is made.
- Replay the same payload or trigger a redelivery scenario and verify no second message is sent.
- Remove an optional field, such as a recipient first name or metadata, and verify the fallback behavior.
- Force a Volanea API failure and verify alerting, retry policy, and idempotency behavior.
- Review the received message’s From address, subject, text version, HTML rendering, and reply path.
Use separate test sender identities and recipients if your environment supports them. This protects live sending reputation and makes it obvious whether an observed message came from a test run.
Alternatives when direct webhooks are not available
Webhooks plus middleware give the most control, but they are not the only route. If your PandaDoc account cannot create outbound API webhooks, or if your team does not maintain server-side code, an automation platform can provide the first hop.
With Zapier or Make, the conceptual flow remains: a PandaDoc document status trigger starts the scenario, a filter allows only the desired status, and a secure server-side HTTP step sends data onward. Prefer calling your own middleware endpoint rather than exposing your Volanea credential broadly inside a no-code workflow. Your endpoint can maintain the same signature checks where applicable, recipient rules, logging, and deduplication.
An automation-platform-only route may be sufficient for a low-volume internal alert. It is less attractive for high-value completion emails because debugging, replay behavior, data minimization, concurrency, and secret access become more dependent on the platform’s configuration. Review its task limits, retry semantics, data retention, and permission model before using it for contract events.
For teams migrating from a different sender, keep the PandaDoc webhook and middleware stable while changing only the email client module. That separation means document workflow behavior is not coupled to a particular delivery provider.
Operational checklist
Before enabling this integration for real documents, verify all of the following:
- PandaDoc is configured to send
document_state_changedto a TLS-protected endpoint you own. - Your endpoint validates PandaDoc deliveries using PandaDoc’s documented mechanism for your account.
- The handler acts only on the intended status, such as
document.completed. - Recipient selection is documented and tested for every relevant PandaDoc template.
- The Volanea API key is stored only as a server-side secret.
- The From domain is authenticated and appropriate for transactional mail.
- HTML dynamic fields are escaped, and a text alternative is provided.
- A durable idempotency key prevents duplicate completion emails.
- You log document IDs and delivery outcomes without exposing document contents or unnecessary recipient data.
- Failures alert a human or create a retriable job rather than silently disappearing.
This architecture makes it possible to send email from PandaDoc without pretending PandaDoc is an email API or exposing a delivery credential where it does not belong. It also leaves room to add more events later without making your email logic fragile.
FAQ
Can PandaDoc send directly to the Volanea email API?
Not safely for this use case. PandaDoc webhooks notify an HTTPS endpoint of document events, while Volanea needs an authenticated API request with email-specific field mapping. Put a server-side middleware endpoint between PandaDoc and Volanea so the API key stays private and the event can be validated and deduplicated.
Which PandaDoc event should trigger a signed-document email?
Use the document_state_changed event and check that data.status is document.completed. This ties the email to completion rather than to document creation or initial sending.
Where do I put the Volanea API key in PandaDoc?
Nowhere. Store the key in your middleware platform’s encrypted environment variables or secret manager. PandaDoc should have the webhook destination URL and its own webhook configuration only.
How do I stop duplicate emails after PandaDoc retries a webhook?
Create a durable idempotency key from the PandaDoc document ID and the target event/status, then enforce a unique insert before sending. Return a successful response for duplicate deliveries and retain enough state to cover retries and server restarts.
What if the PandaDoc webhook does not include the recipient field I need?
Do not guess. Use the document ID to look up the intended recipient in your own system or retrieve the necessary document details through PandaDoc’s API if your account and workflow support it. Otherwise, skip the external send and alert an internal owner for review.