Send email from Pipeliner when an important CRM event happens—without exposing an email API key or relying on a fictional marketplace plugin. The practical integration is Pipeliner CRM webhook → your server-side endpoint → Volanea’s REST API.
Pipeliner CRM does not need a native Volanea app for this workflow. Its API supports webhooks for entity events such as Contact creation, Opportunity movement, Opportunity won, Lead creation, and more. Your application receives that event, validates and maps the record, then sends a transactional email through Volanea.
This guide uses a concrete, useful trigger: Opportunity.Move, which Pipeliner defines as the event that occurs when an Opportunity’s sales step changes. That makes it a sensible foundation for a stage-based email such as a proposal follow-up, onboarding notification, internal handoff, or customer confirmation.
The direct route is deliberately server-side. Pipeliner posts CRM data to an HTTPS endpoint you control; that endpoint holds the Volanea secret key in server environment variables and calls POST /v1/send. There is no browser code, no client-visible API key, and no claim of a native Pipeliner marketplace installation.
What the Pipeliner-to-Volanea flow looks like
The architecture has three distinct responsibilities:
- Pipeliner CRM detects an entity event. In this guide, the event is
Opportunity.Move, triggered when an Opportunity changes sales step. - Your webhook endpoint applies business rules. It decides whether this movement should send an email, finds the recipient, validates the required fields, and creates a stable idempotency key.
- Volanea accepts the transactional message. The endpoint queues the message for delivery processing, including suppression checks, tracking instrumentation, and configured sender-domain rules.
That separation matters. A CRM event is not automatically an email instruction. An Opportunity can move because a representative corrected data, restored an archived record, ran an import, or advanced a deal accidentally. Your middleware is where you turn a raw CRM change into an intentional customer communication.
For example, a sales process could use these rules:
- Moving an Opportunity to Proposal Sent sends the customer a proposal-ready confirmation.
- Moving it to Closed Won sends an internal onboarding notification to the implementation team.
- Moving it to Discovery Complete creates a task but does not send an external email.
- Moving it backwards sends nothing, because the change may simply correct pipeline data.
The email logic therefore belongs in code you version, review, test, and monitor—not in an unprotected client configuration.
The concrete Pipeliner trigger: Opportunity.Move
Pipeliner CRM calls its main CRM objects entities. Supported webhook events include Account, Contact, Opportunity, Lead, Task, Email, Appointment, Product, Product Line Item, and Custom Entity activity. For opportunities, the supported events include Opportunity.Create, Opportunity.Update, Opportunity.Move, Opportunity.Lost, Opportunity.Won, and Opportunity.Qualify.
For this integration, register a webhook for:
Opportunity.Move
Pipeliner documents Opportunity.Move as the event raised when an Opportunity’s sales step changes. This is more precise than subscribing to every Opportunity.Update, which can fire for unrelated modifications such as a changed value, note, owner, or custom field.
Why stage movement is a better email trigger than a generic update
A generic update event often creates email risk. If a representative edits an Opportunity’s amount five times, corrects the close date, and adds a phone number, you do not want five nearly identical messages reaching the buyer.
A movement event is closer to a business milestone. Even so, it should not be treated as sufficient on its own. The webhook receiver should check a stable condition, such as the destination sales-step ID matching the configured Proposal Sent stage, before calling Volanea.
Do not compare only a display name such as Proposal Sent. Sales-step labels can be renamed, translated, or duplicated across pipelines. Store the immutable identifier you obtain from your own Pipeliner setup in a server environment variable such as PIPELINER_PROPOSAL_STEP_ID.
What Pipeliner actually posts
Pipeliner’s webhook documentation says that when a webhook is registered for an entity event, it sends JSON containing the body of the entity to the URL you specified. This is an important implementation detail: do not design around an invented universal envelope such as event.type, data, and previous_data unless your own captured webhook proves that shape.
The body is the Opportunity entity document for your Pipeliner space. Its available fields reflect your account’s field configuration, including custom fields. In other words, the record identifier and the fields you expose are real Pipeliner data, but custom-field keys are specific to your implementation.
Before enabling sending, point the webhook at a temporary request inspector or a non-production endpoint, move a test Opportunity once, and save the exact JSON body. Treat that captured payload as the contract for your integration tests.
A representative payload may look conceptually like this after you normalize the relevant entity fields in your application:
{
"id": "opp_01JQ6N8X5EKF4G3A2P7V",
"name": "Northwind annual platform rollout",
"sales_step_id": "step_proposal_sent",
"modified": "2026-10-11T15:24:30Z",
"contact_email": "ada@example.com",
"contact_first_name": "Ada",
"account_name": "Northwind Traders"
}
That example is a mapping target, not a claim that every Pipeliner space uses those exact property names. Pipeliner lets organizations define fields, so your webhook handler should isolate Pipeliner-specific field extraction in one mapping function. The rest of the email code should receive a small, predictable object: opportunity ID, step ID, recipient email, recipient name, account name, and modified timestamp.
Register the Pipeliner webhook safely
Pipeliner webhook registration is API-driven. The documented endpoint for creating a webhook is POST /entities/Webhooks. Pipeliner’s API uses an application-specific service URL and space ID, and its documentation describes Basic authentication with API access credentials generated for an application.
Your webhook registration needs:
- A Pipeliner API application with only the permissions it needs.
- The Pipeliner service URL and space ID for the relevant CRM space.
- A public HTTPS destination URL, such as
https://automation.example.com/webhooks/pipeliner/opportunity-moved. - An event subscription for
Opportunity.Move. - TLS certificate validation left enabled. Do not set an insecure SSL option merely to make a development endpoint work.
Keep the Pipeliner API username and password in your deployment secret manager or an encrypted CI/CD secret store. They are administrative integration credentials, not values for frontend code, email templates, or a shared spreadsheet.
Use a dedicated integration identity
Create or use a dedicated Pipeliner API application rather than an individual seller’s personal credentials. This improves revocation, auditability, continuity when staff change roles, and least-privilege access control.
The application’s access determines what records the webhook can observe. If your recipient data is stored on Contacts but the integration can only see Opportunities, the webhook may not contain enough data to send an email safely. Solve that with permissions and explicit record lookup in your middleware, not by copying sensitive customer data into unrelated fields.
Do not put the Volanea key in Pipeliner
The Volanea API key does not belong in Pipeliner webhook configuration, a Pipeliner custom field, a Pipeliner URL query string, or any client-visible configuration. Pipeliner’s job is to deliver the entity event to your endpoint. Your server’s job is to call Volanea.
Store the Volanea secret key in an environment variable or secret manager attached to the webhook service:
VOLANEA_API_KEY=your-secret-key
VOLANEA_FROM_EMAIL=notifications@example.com
VOLANEA_FROM_NAME=Example Co.
PIPELINER_PROPOSAL_STEP_ID=step_proposal_sent
A secret in a URL can leak through access logs, proxies, browser history, support screenshots, and monitoring tools. A secret embedded in client JavaScript can be extracted by anyone who loads the application. A server environment variable can be scoped to the service, masked in logs, rotated, and removed without changing CRM records.
Map the Opportunity to an email deliberately
An Opportunity is not necessarily a person. The recipient might be the primary Contact, a stakeholder in the Buying Center, an account-level email field, or an internal team alias. Decide this before writing the send code.
For an external customer message, a strong default is to use an explicit field such as Primary Contact Email that is populated by your sales process. Do not guess based on the Account name, scrape an email from notes, or send to every related Contact by default. Multiple recipients can create privacy and consent problems, especially if they can see each other in message headers.
Minimum mapping contract
The handler below expects this normalized input:
| Email field | Source in Pipeliner | Why it is needed |
|---|---|---|
opportunityId | Opportunity entity ID | Stable idempotency and tracing |
salesStepId | Current Opportunity sales-step field | Prevents sending on the wrong movement |
recipientEmail | Designated Contact or custom Opportunity field | Volanea recipient |
recipientName | Contact name or approved custom field | Personalization only |
opportunityName | Opportunity name | Context in subject and body |
accountName | Related Account or copied field | Context in body |
modifiedAt | Pipeliner entity modification timestamp | Diagnostics and idempotency versioning |
The important distinction is between identity fields and display fields. Email address and Opportunity ID determine whether a message should be sent. Name and account name improve the message but should not be required to make a safe send decision.
Validate before you send
A missing recipient email is not a reason to send to a fallback mailbox. Return a successful webhook response after recording the skipped event, then fix the CRM data. Sending a proposal notification to a generic sales inbox can conceal broken workflow data and cause duplicate internal work.
Validate at least the following:
- The Opportunity is in the intended sales step.
- A recipient email exists and passes basic syntactic validation.
- The address is appropriate for the message’s consent and business purpose.
- The configured sender address belongs to a verified Volanea sending domain.
- The event has not already been processed for this business transition.
For an extra pre-send signal, teams can use the email address verification tool during lead capture or prior to handoff. Verification is not consent, and it does not replace suppression handling, but it can reduce avoidable failures caused by malformed addresses.
Working webhook receiver and Volanea send call
The following Node.js example uses Express. It shows the actual boundary that matters: Pipeliner posts an Opportunity entity body to your server, the server extracts the approved field values, then it calls Volanea’s POST https://api.volanea.com/v1/send endpoint.
Replace extractPipelinerOpportunity() with field paths from a payload captured from your Pipeliner space. That is intentional: custom entity fields are tenant-specific, and hard-coding unverified Pipeliner field names is less reliable than making the mapping explicit in one function.
import crypto from "node:crypto";
import express from "express";
const app = express();
app.use(express.json({ limit: "1mb" }));
type PipelinerOpportunity = Record<string, unknown>;
type OpportunityEmailData = {
opportunityId: string;
salesStepId: string;
modifiedAt: string;
recipientEmail: string;
recipientName?: string;
opportunityName: string;
accountName?: string;
};
function requiredString(value: unknown, field: string): string {
if (typeof value !== "string" || value.trim() === "") {
throw new Error(`Missing required Pipeliner field: ${field}`);
}
return value.trim();
}
function extractPipelinerOpportunity(
payload: PipelinerOpportunity
): OpportunityEmailData {
// Map these paths to the exact JSON captured from your Pipeliner webhook.
// Keep all Pipeliner-specific field names in this function.
return {
opportunityId: requiredString(payload.id, "id"),
salesStepId: requiredString(payload.sales_step_id, "sales_step_id"),
modifiedAt: requiredString(payload.modified, "modified"),
recipientEmail: requiredString(payload.primary_contact_email, "primary_contact_email"),
recipientName:
typeof payload.primary_contact_name === "string"
? payload.primary_contact_name.trim()
: undefined,
opportunityName: requiredString(payload.name, "name"),
accountName:
typeof payload.account_name === "string"
? payload.account_name.trim()
: undefined
};
}
function isEmail(value: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
}
function escapeHtml(value: string): string {
return value.replace(/[&<>'"]/g, (char) => {
const entities: Record<string, string> = {
"&": "&",
"<": "<",
">": ">",
"'": "'",
"\"": """
};
return entities[char];
});
}
app.post("/webhooks/pipeliner/opportunity-moved", async (req, res) => {
try {
const opportunity = extractPipelinerOpportunity(req.body);
// Business rule: send only when the Opportunity reaches the configured step.
if (opportunity.salesStepId !== process.env.PIPELINER_PROPOSAL_STEP_ID) {
return res.status(204).end();
}
if (!isEmail(opportunity.recipientEmail)) {
console.warn("Skipping Opportunity: invalid recipient email", {
opportunityId: opportunity.opportunityId
});
return res.status(204).end();
}
// One logical message per Opportunity movement/version and recipient.
// Reuse the exact same value if this handler retries the Volanea request.
const idempotencyKey = crypto
.createHash("sha256")
.update([
"pipeliner-opportunity-move",
opportunity.opportunityId,
opportunity.salesStepId,
opportunity.modifiedAt,
opportunity.recipientEmail.toLowerCase()
].join(":"))
.digest("hex");
const recipientName = opportunity.recipientName || "there";
const accountText = opportunity.accountName
? ` for ${opportunity.accountName}`
: "";
const subject = `Your proposal is ready: ${opportunity.opportunityName}`;
const text = [
`Hi ${recipientName},`,
"",
`Your proposal for ${opportunity.opportunityName}${accountText} is ready.",
"Reply to this email if you have questions."
].join("\n");
const html = `
<p>Hi ${escapeHtml(recipientName)},</p>
<p>Your proposal for <strong>${escapeHtml(opportunity.opportunityName)}</strong>${escapeHtml(accountText)} is ready.</p>
<p>Reply to this email if you have questions.</p>
`;
const volaneaResponse = await fetch("https://api.volanea.com/v1/send", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.VOLANEA_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": idempotencyKey
},
body: JSON.stringify({
from: {
email: process.env.VOLANEA_FROM_EMAIL,
name: process.env.VOLANEA_FROM_NAME
},
to: [{
email: opportunity.recipientEmail,
name: opportunity.recipientName
}],
subject,
text,
html,
reply_to: {
email: "sales@example.com",
name: "Example Co. Sales"
},
tags: ["pipeliner", "opportunity-moved", "proposal-ready"]
})
});
const result = await volaneaResponse.json();
if (!volaneaResponse.ok) {
console.error("Volanea rejected the email", {
opportunityId: opportunity.opportunityId,
status: volaneaResponse.status,
result
});
return res.status(502).json({ error: "Email provider rejected request" });
}
console.info("Volanea accepted Pipeliner-triggered email", {
opportunityId: opportunity.opportunityId,
idempotencyKey,
result
});
return res.status(202).json({ accepted: true });
} catch (error) {
console.error("Pipeliner webhook processing failed", error);
return res.status(400).json({ error: "Invalid webhook payload" });
}
});
app.listen(process.env.PORT || 3000);
The critical field mapping happens in extractPipelinerOpportunity(). The Pipeliner entity fields are transformed into Volanea’s message fields:
Pipeliner Opportunity ID → idempotency input and logs
Pipeliner sales-step ID → send/no-send business rule
Pipeliner contact email → Volanea to[0].email
Pipeliner contact name → Volanea to[0].name and greeting
Pipeliner Opportunity name → Volanea subject and body
Pipeliner account name → Volanea message body
Pipeliner modified timestamp → idempotency input
If your Pipeliner webhook delivers only relationship IDs rather than the related Contact email, perform a server-side Pipeliner API lookup using the dedicated integration credentials. Do not send an email based on an unverified contact field and do not expose the Pipeliner API credentials to the browser.
For endpoint details, authentication, sender setup, and the complete send schema, use the Volanea API reference and setup guides.
Why idempotency is mandatory for this hop
Webhook delivery is an at-least-once integration pattern in practice. A sender can retry when the receiver times out, a network response can be lost after your server calls Volanea, or your own service can restart after dispatching the send request but before logging success.
Without duplicate protection, one Opportunity movement can produce two or more customer emails.
Volanea supports the Idempotency-Key request header on POST /v1/send. The receiver creates one stable key for one logical message, then uses that same value for every retry of that message. The code derives the key from the Opportunity ID, sales-step ID, modification time, and recipient address.
Choose the right idempotency scope
Do not use a random UUID generated inside each incoming webhook request. If Pipeliner retries the same event, a fresh random key tells Volanea that it is a new send.
Do not use just the Opportunity ID either. That would block a legitimate later email if the Opportunity moves through the same workflow again after meaningful changes.
A sound key describes the business event:
Opportunity ID + destination step + record version + recipient
If Pipeliner provides a durable event identifier in your captured payload, prefer that as the primary component. Store a local event ledger as well, particularly if you need audit records, reporting, or rules more complex than one send per event.
When this breaks
Every production integration eventually encounters malformed data, slow dependencies, retries, and changed CRM configuration. Plan for the exact failure modes in the Pipeliner → middleware → Volanea path.
Pipeliner retries after your endpoint times out
The most dangerous case is ambiguity: your middleware sends the email to Volanea, but Pipeliner does not receive your HTTP response in time. Pipeliner may deliver the webhook again. Your server may also retry the Volanea request after a connection error.
Use the deterministic Idempotency-Key shown above. Return a quick 2xx response only after your application has either durably queued the work or received the result needed for your operating model. For higher-volume systems, write the event to a database or queue transactionally, return success, and let a worker perform the Volanea call with the same stored key.
The webhook payload is missing fields
Pipeliner spaces can have different field arrangements, permissions, and custom entities. A record created through an import or mobile workflow may lack the contact email, account name, or sales-step metadata your normal sales process supplies.
Treat missing recipient data as a skip with an alert, not as an exception that produces a customer-facing email to a substitute address. Log the Opportunity ID, missing field name, webhook receipt time, and mapped stage. Then route the issue to the sales operations owner who can correct the CRM record or automation rule.
Also test payloads generated by all relevant paths: manual desktop edits, mobile changes, imports, API updates, bulk operations, and sales-step changes made by automated processes. A workflow proven with one manually edited sample record is not necessarily safe for all record sources.
A webhook destination becomes slow or unavailable
Your endpoint may be unavailable because of deployment errors, DNS failures, expired TLS certificates, cold starts, an overloaded database, or an upstream outage. Keep the webhook handler small: parse, validate, deduplicate, enqueue, and respond. Do not perform long-running enrichment, PDF generation, or CRM-wide searches synchronously if you can avoid it.
Add monitoring for response time, non-2xx rate, duplicate-event rate, skipped-event rate, Volanea rejection rate, and queue age. Alert on sudden changes rather than waiting for a sales representative to notice a missing message.
A Pipeliner admin changes a custom field or sales step
This failure can be silent. A renamed custom field, deleted field, changed field permission, or recreated sales step can make your extraction function return undefined or stop matching the configured target step.
Protect against it in three ways:
- Keep mapping configuration in version control or environment configuration, not only in comments.
- Run a scheduled synthetic test against a non-production Opportunity or a dedicated test pipeline.
- Alert when expected events arrive but are skipped because the mapped data is missing or the stage identifier is unknown.
Display labels are for humans; immutable IDs are for automation. If a sales process must be redesigned, update the ID configuration deliberately and test before enabling it in production.
Volanea accepts the request but the email is not delivered
A successful send API response means Volanea accepted the message for processing. It is not proof that a receiving mailbox accepted it or that the recipient saw it.
Delivery can still be affected by suppression status, unsubscribe rules where applicable, invalid recipients, mailbox-provider deferrals, domain authentication, reputation, or recipient-side filtering. Configure Volanea delivery event webhooks back to your application and record accepted, delivered, bounced, complained, and suppressed outcomes against the Opportunity or a dedicated communication record.
Do not automatically move an Opportunity forward just because the send endpoint returned success. That conflates API acceptance with business completion.
Testing the integration before production
Use a separate test pipeline or a clearly labeled test sales step. Production-like testing should cover more than one happy-path message.
Minimum test matrix
- Move a test Opportunity into the target step with a valid email address.
- Move the same Opportunity again and verify that duplicate protection behaves as expected.
- Send a payload with no recipient email and verify that the handler skips it safely.
- Send an invalid recipient value and verify that it does not reach Volanea.
- Trigger a non-target sales-step movement and verify that the response is a no-op.
- Force a Volanea error with a restricted test key or invalid sender in a non-production environment.
- Simulate a middleware timeout after the Volanea request and confirm a repeat event does not create a second email.
- Confirm that reply-to behavior routes replies to a monitored sales mailbox.
Test email content as carefully as the transport. Confirm that first names do not render as undefined, HTML escapes CRM text correctly, account names do not expose confidential notes, and the message makes sense if a recipient receives it without context.
Keep test and production senders separate
Use a non-production sender address or test domain when possible. A staging webhook accidentally connected to the production Volanea key is a common way to send confusing emails to real prospects.
Separate environments should have separate webhook destinations, Pipeliner applications, Volanea API keys, sender identities, and monitoring labels. This makes it possible to revoke a test integration without affecting production communication.
Direct webhook route versus Make or Zapier
The direct webhook route is the most flexible option when you can deploy and operate a small server-side endpoint. It lets you control authentication, idempotency, field validation, error policy, logging, delivery-event handling, and exactly what data reaches Volanea.
Pipeliner CRM also has integration options through automation platforms such as Make. Those can be appropriate for simpler internal notifications or early prototypes, but the same security boundary remains: the Volanea secret must be stored in the automation platform’s encrypted connection or secret mechanism, never in a field mapped into an outgoing client-visible payload.
Choose middleware when you need:
- Complex branching across sales steps or pipelines.
- A lookup of related Contacts, Accounts, or custom records.
- A durable audit trail of every send decision.
- Strong retry and deduplication behavior.
- Custom HTML rendering or template-data preparation.
- Delivery event processing back into your CRM or warehouse.
- More predictable control over secrets and operational logs.
Choose a no-code route only after confirming that it supports secure credential storage, stable idempotency behavior, error visibility, and the Pipeliner trigger you need. A quick automation that cannot explain why an email was sent is not a dependable customer-communication system.
Operational checklist
Before you enable the webhook in a live pipeline, verify each item below.
- A Pipeliner webhook is subscribed to
Opportunity.Move, not an unnecessarily broad update event. - The destination endpoint uses HTTPS with a valid certificate.
- Pipeliner integration credentials belong to a dedicated application identity.
- The Volanea API key exists only in server-side secrets or protected automation credentials.
- The From address uses a verified Volanea sending domain.
- A specific sales-step ID, not only a display label, controls the send rule.
- The recipient mapping is explicit and tested with a captured Pipeliner payload.
- Missing recipient information causes a logged skip, not a fallback send.
- Every Volanea send includes a deterministic
Idempotency-Key. - Logs include Pipeliner Opportunity ID, stage ID, idempotency key, Volanea response status, and message identifier when available.
- Volanea delivery events are monitored separately from send acceptance.
- A test pipeline and non-production credentials exist for future regression checks.
Conclusion
The reliable way to send email from Pipeliner is not to search for a nonexistent native plugin. Use Pipeliner CRM’s webhook support to react to a real entity event such as Opportunity.Move, receive the Opportunity body in a server-side endpoint, map the approved fields, and send through Volanea’s REST API.
The details that make the integration production-ready are not the HTTP POST alone. They are the business rule that selects the correct sales step, the explicit recipient mapping, server-side secret storage, deterministic idempotency, fast webhook responses, and delivery monitoring after the API accepts the message.
Build those safeguards from the beginning, and Pipeliner stage movements can become dependable email triggers rather than a source of duplicated, mistimed, or untraceable communication.
FAQ
Does Volanea have a native Pipeliner CRM app?
No. This integration uses Pipeliner CRM webhooks and a server-side relay that calls Volanea’s REST API. There is no marketplace installation flow to configure.
What Pipeliner event should trigger the email?
For stage-based messages, use Opportunity.Move, which Pipeliner documents as the event that occurs when an Opportunity’s sales step changes. Add a server-side check for the specific target sales-step ID.
Where should the Volanea API key be stored?
Store it only in your middleware’s environment variables or secret manager, or in a protected automation-platform connection. Do not place it in Pipeliner fields, webhook URLs, frontend code, or email templates.
How do I stop duplicate messages if Pipeliner retries a webhook?
Send a deterministic Idempotency-Key with the Volanea request. Derive it from the logical CRM event—such as Opportunity ID, sales-step ID, modification version, and recipient—and reuse it for every retry of that same message.
Does a successful Volanea API response mean the recipient received the email?
No. It means the message was accepted for processing. Monitor Volanea delivery, bounce, complaint, suppression, and other message events to understand the eventual outcome.