Send email from ZoomInfo with Volanea by receiving a ZoomInfo webhook in your own server-side endpoint, validating it, mapping the updated contact data, and calling Volanea’s transactional email API. This is a developer integration, not a native ZoomInfo marketplace app: Volanea does not ship a ZoomInfo plugin, and ZoomInfo should never receive your Volanea API key.
What this ZoomInfo-to-email integration actually does
The direct route is built around ZoomInfo’s Monitoring API webhooks. ZoomInfo can send an HTTP POST to a target URL when a previously enriched record changes. Your relay receives that callback, checks ZoomInfo’s verification token, decides whether the change merits an email, and creates the message with Volanea.
That distinction matters. ZoomInfo Workflows can automate exports of contacts and companies to configured destinations, but a Workflow is not a general-purpose place to paste a Volanea REST request and expose a sending credential. The supported direct HTTP mechanism documented for record changes is the ZoomInfo webhook target URL used with the Monitoring API.
For this integration, the concrete ZoomInfo trigger is:
- Object type:
Contact - Event type:
Update - Business event: a contact record that your organization previously enriched through the ZoomInfo Enterprise API is updated in ZoomInfo’s data
A title change is a practical example. If ZoomInfo updates an enriched contact from “Director of Operations” to “VP of Operations,” it can POST the updated contact to your relay. The relay can notify an assigned account owner, create an internal review email, or send a permitted customer-facing notice tied to your own relationship with that person.
Do not treat this trigger as permission to start cold-emailing ZoomInfo contacts. A data-update event is a data-maintenance signal, not consent. Use it for operational notifications, customer communications, account-owner alerts, or campaigns for recipients for whom you have a lawful and appropriate sending basis.
Prerequisites before you send email from ZoomInfo
This setup needs more than an email API key. It has three systems with distinct responsibilities:
- ZoomInfo Monitoring API access. ZoomInfo documents webhooks for organizations with the applicable Scaling API or Enterprise API access. The monitored records must be records your organization previously enriched through the API.
- A public HTTPS relay. This is a server endpoint you operate, such as an API route, serverless function, container service, or edge worker. It must accept ZoomInfo’s POST requests.
- A verified Volanea sending domain and secret API key. Your email must use a sender identity from a domain you have authenticated in Volanea.
Before building, verify the sending domain in Volanea and make a one-off test send from a controlled backend. The Volanea API reference and setup guides cover domain setup, API authentication, send requests, and delivery events.
You should also decide what a meaningful ZoomInfo update is. A contact’s title, company, department, direct dial, and email availability do not all carry the same operational meaning. Sending for every field change creates noisy notifications and can quickly become an unwanted-message generator.
A good initial policy is conservative:
- Send only for a change in
jobTitle,companyName, or another field relevant to the recipient. - Send only to internal account owners or known customers, not automatically to the updated contact.
- Require a usable recipient email address before creating any external email.
- Suppress sends when a record has already produced the same notification recently.
- Log the ZoomInfo record ID, changed fields, Volanea message ID, and decision outcome.
The ZoomInfo trigger and webhook payload
ZoomInfo’s Monitoring API webhook model is intentionally narrow. The supported monitored object types are Contact and Company, and the documented event type is Update. For this page, subscribe to Contact updates.
When an eligible enriched contact changes, ZoomInfo sends a POST request to the target URL configured for the webhook. The webhook uses the x-zoominfo-token request header for verification. ZoomInfo can be configured to deliver either a full profile payload with changed attributes or only the changed attributes. ZoomInfo also documents default mapping fields including the profile ID, email, and domain, and limits a single webhook payload to a maximum of 25 profiles.
That batch behavior changes how you should write the receiver. Do not assume one HTTP request equals one contact. Your endpoint should iterate through the profiles in the request, apply a decision for each profile, and send at most one message for each logical record-update event.
The fields to map
For a Contact update, the integration needs four categories of data:
| Purpose | ZoomInfo contact field | Volanea use |
|---|---|---|
| Stable record identity | id | Stable idempotency key component and audit correlation |
| Email identity | email | Optional recipient or review-data value |
| Contact name | firstName, lastName | Subject and message personalization |
| Company context | companyName or company field returned in your configured profile | Useful email context |
| Change context | changedAttributes | Filter updates and explain why the message was sent |
The exact full-profile fields available depend on your ZoomInfo subscription, object type, and webhook payload configuration. That is why the code below treats id and email as required routing data, handles optional fields defensively, and rejects records that do not meet your notification policy.
A real-world notification design
A safe first implementation sends an internal email to a record owner when an enriched contact’s title changes. It does not send a marketing message to the ZoomInfo contact.
For example:
- ZoomInfo detects an update for contact ID
123456789. - The contact’s
jobTitleis one of the changed attributes. - Your application looks up the account owner for the contact’s company.
- Your relay emails that owner: “ZoomInfo data update: Jordan Lee is now VP Operations at Northstar.”
- The stable idempotency key prevents a replayed webhook from producing another notification.
This pattern gives sales and customer-success teams timely intelligence while keeping control of sending rules in software you own.
Architecture: ZoomInfo posts, your relay decides, Volanea sends
The recommended flow has four hops:
- ZoomInfo detects a Contact
Updatefor an eligible monitored record. - ZoomInfo POSTs a JSON payload to your HTTPS target URL and includes
x-zoominfo-token. - Your relay validates the token, validates each profile, checks the changed fields, deduplicates the business event, and constructs an email.
- Your relay calls
POST https://api.volanea.com/v1/sendwith a server-sideAuthorization: BearerAPI key and anIdempotency-Key.
The relay is not ceremony. It is the security and reliability boundary between a third-party webhook producer and an email-sending system. It is where you can enforce recipient eligibility, prevent duplicate mail, escape untrusted text, apply rate limits, log audit data, and respond quickly to ZoomInfo.
A direct request from ZoomInfo to Volanea is not the right design even if you could construct one. ZoomInfo’s webhook sends its verification token to your endpoint; Volanea’s email endpoint requires a secret sending credential. Your application should verify one secret on ingress and use a different secret on egress.
Build the secure ZoomInfo webhook relay
The following TypeScript example uses a standard server-side Request handler. It is portable to most serverless and web frameworks with minor adaptation. It receives a JSON array of Contact profiles, checks the x-zoominfo-token header, maps each eligible record into an internal notification, and calls Volanea’s REST API.
The code assumes the ZoomInfo webhook is configured for Contact / Update with a payload that includes the full contact profile and changed attributes. It explicitly processes up to 25 profiles rather than pretending that webhook delivery is always one record at a time.
// app/api/webhooks/zoominfo/route.ts
// Server-side code only. Do not run this in a browser or client bundle.
type ZoomInfoContact = {
id: string | number;
email?: string | null;
firstName?: string | null;
lastName?: string | null;
jobTitle?: string | null;
companyName?: string | null;
domain?: string | null;
// ZoomInfo includes changed attributes when configured for that payload mode.
changedAttributes?: Record<string, unknown> | string[] | null;
};
const ZOOMINFO_WEBHOOK_TOKEN = process.env.ZOOMINFO_WEBHOOK_TOKEN!;
const VOLANEA_API_KEY = process.env.VOLANEA_API_KEY!;
const FROM_EMAIL = process.env.VOLANEA_FROM_EMAIL!;
const ALERT_RECIPIENT = process.env.ZOOMINFO_ALERT_RECIPIENT!;
function hasTitleChange(changedAttributes: ZoomInfoContact["changedAttributes"]) {
if (Array.isArray(changedAttributes)) {
return changedAttributes.includes("jobTitle");
}
if (changedAttributes && typeof changedAttributes === "object") {
return Object.prototype.hasOwnProperty.call(changedAttributes, "jobTitle");
}
// With a full-profile payload, choose the conservative path if the
// changed-attributes marker is absent rather than emailing on every update.
return false;
}
function text(value: string | null | undefined) {
return value?.trim() || "Unknown";
}
export async function POST(request: Request) {
// Verify that this request reached the endpoint from a ZoomInfo webhook
// configured with your private verification token.
const receivedToken = request.headers.get("x-zoominfo-token");
if (!receivedToken || receivedToken !== ZOOMINFO_WEBHOOK_TOKEN) {
return Response.json({ error: "Unauthorized webhook" }, { status: 401 });
}
const payload: unknown = await request.json();
// ZoomInfo can deliver up to 25 profiles in one webhook payload.
if (!Array.isArray(payload)) {
return Response.json(
{ error: "Expected an array of ZoomInfo profiles" },
{ status: 400 }
);
}
const results = await Promise.all(
payload.map(async (contact: ZoomInfoContact) => {
const zoomInfoId = String(contact.id || "");
if (!zoomInfoId) {
return { skipped: true, reason: "missing_contact_id" };
}
if (!hasTitleChange(contact.changedAttributes)) {
return { skipped: true, id: zoomInfoId, reason: "no_job_title_change" };
}
const fullName = `${text(contact.firstName)} ${text(contact.lastName)}`;
const company = text(contact.companyName);
const title = text(contact.jobTitle);
// One stable key per logical ZoomInfo record-update notification.
// If your payload supplies an event/update timestamp or revision ID,
// include it here so distinct future title changes remain distinct events.
const idempotencyKey = `zoominfo-contact-${zoomInfoId}-job-title`;
const subject = `ZoomInfo update: ${fullName} at ${company}`;
const emailText = [
"A monitored ZoomInfo contact record changed.",
"",
`Contact: ${fullName}`,
`Company: ${company}`,
`Current title: ${title}`,
`ZoomInfo contact ID: ${zoomInfoId}`,
`Contact email on record: ${contact.email || "not supplied"}`,
].join("\n");
const response = await fetch("https://api.volanea.com/v1/send", {
method: "POST",
headers: {
"Authorization": `Bearer ${VOLANEA_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": idempotencyKey,
},
body: JSON.stringify({
from: FROM_EMAIL,
to: ALERT_RECIPIENT,
subject,
text: emailText,
html: `
<p>A monitored ZoomInfo contact record changed.</p>
<ul>
<li><strong>Contact:</strong> ${escapeHtml(fullName)}</li>
<li><strong>Company:</strong> ${escapeHtml(company)}</li>
<li><strong>Current title:</strong> ${escapeHtml(title)}</li>
<li><strong>ZoomInfo contact ID:</strong> ${escapeHtml(zoomInfoId)}</li>
<li><strong>Contact email on record:</strong> ${escapeHtml(contact.email || "not supplied")}</li>
</ul>
`,
}),
});
const responseBody = await response.text();
if (!response.ok) {
throw new Error(
`Volanea send failed for ${zoomInfoId}: ${response.status} ${responseBody}`
);
}
return { sent: true, id: zoomInfoId, volanea: responseBody };
})
);
return Response.json({ received: payload.length, results }, { status: 200 });
}
function escapeHtml(value: string) {
return value
.replaceAll("&", "&")
.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll('"', """)
.replaceAll("'", "'");
}
This example intentionally sends to an internal alert address. If your use case is a permitted customer communication, replace ALERT_RECIPIENT with a recipient selected from your own CRM or application database—not automatically with contact.email just because ZoomInfo supplied it.
Why the example uses both text and HTML
Volanea accepts transactional message content through POST /v1/send. Supplying a readable text body alongside the HTML gives recipients with text-only clients or restrictive settings a usable message. The HTML is escaped because titles, company names, and other upstream fields are data, not trusted markup.
If you use templates, keep the same architecture: validate the inbound profile, prepare an allowlisted variable object, use a stable idempotency key, and call the Volanea endpoint from the relay.
Keep the Volanea API key out of ZoomInfo
Your Volanea API key does not belong in ZoomInfo. There is no native Volanea app configuration in ZoomInfo where you should paste it, and you should not attempt to place it in a webhook URL, a client-visible script, browser storage, a public automation field, or a query parameter.
Store it in the server-side secret store attached to the relay:
- An environment variable in your serverless platform.
- A managed secret store such as a cloud secret manager.
- An encrypted deployment secret injected only into the server runtime.
The ZoomInfo-side secret is separate: the webhook verification token. Configure the target URL in ZoomInfo’s Monitoring API setup and generate the verification token there. Save that token as ZOOMINFO_WEBHOOK_TOKEN in the same server-side secret store. Your relay compares it to the incoming x-zoominfo-token header before it does any parsing, database lookup, or email send.
This separation protects both directions:
- The ZoomInfo token helps your relay reject untrusted webhook calls.
- The Volanea key authorizes your relay to send mail.
- Neither credential is exposed to a browser or to another user who can merely view a ZoomInfo configuration.
Use a dedicated Volanea key for this integration where possible. That makes rotation, auditing, and revocation easier. If the ZoomInfo relay is compromised, rotate only the integration’s key rather than breaking unrelated mail flows.
Configure the ZoomInfo webhook carefully
ZoomInfo’s webhook workflow starts with Monitoring API access, not the general ZoomInfo Workflows builder. The configuration names can vary as the product evolves, but the configuration concepts are consistent: webhook title, target URL, verification token, subscription, object type, event type, changed attributes, and payload type.
For the narrow internal-alert design, use these choices:
- Create a webhook with a clear title, such as
volanea-contact-update-alerts. - Set the target URL to your HTTPS relay, for example
https://app.example.com/api/webhooks/zoominfo. - Generate and securely save the ZoomInfo verification token.
- Add a subscription for the Contact object type.
- Select the Update event type.
- Request the full payload with changed attributes if your email needs name, company, title, and other profile context.
- Restrict changed attributes to the smallest useful set, such as job title or company-related fields, if that configuration is available in your account.
- Activate only after the relay has passed a controlled test.
Avoid making an update to “email” itself a trigger for an automatic email to that same address. A new or changed address is not a confirmation that you should send to it. It is often better to treat an email-address update as a data-quality task for an internal owner.
Add idempotency before turning the webhook on
Webhooks are delivery attempts, not a guarantee of exactly-once business processing. Network failures can occur after Volanea accepts a send but before your relay returns a success to ZoomInfo. ZoomInfo may then retry, and your relay may see the same logical update more than once.
Volanea’s POST /v1/send supports the Idempotency-Key header. Use it on every webhook-driven send.
The key must represent the logical email event, not the HTTP request. A poor key is a randomly generated UUID produced inside the handler, because every retry gets a different key and produces another message. A better key combines stable business identifiers:
zoominfo-contact-{contact-id}-{changed-field}-{update-revision}
The Contact ID makes the key record-specific. The changed field distinguishes, for example, a title alert from a company alert. The update revision should be a stable event timestamp, version, or change identifier from the actual ZoomInfo payload when available. Persist that identifier in your own database if you need a durable event ledger.
The compact example above uses zoominfo-contact-{id}-job-title because it demonstrates the mechanism. In production, improve it with a stable version marker or a database record keyed by the contact ID and the normalized before/after state. Otherwise, a legitimate title change months later could be treated as the same idempotent request.
Filter, validate, and personalize without creating risky mail
The relay should not blindly transform every data field into email content. Upstream data is variable: a field can be blank, have an unexpected type, be stale, or be inappropriate for a particular recipient.
Validate before sending
At minimum, validate these conditions:
- The
x-zoominfo-tokenmatches the secret you configured. - The request body is a JSON array and does not exceed the supported batch expectation.
- Every profile has a usable
id. - The changed attributes include a field your policy allows.
- The intended recipient is resolved from a trusted system you control.
- Required message data is present or has a safe fallback.
- The sending domain is verified in Volanea.
Do not log raw webhook payloads indefinitely if they contain personal data. Store only the fields needed for debugging and audit, protect access, and apply a retention period appropriate to your organization.
Personalization should be modest and accurate
An internal update email can safely say that a contact record changed and show the current fields. A customer-facing message needs a stronger standard: it should be triggered by your own product or relationship event, contain only context the recipient expects, and include appropriate unsubscribe or preference handling if it is commercial or campaign mail.
Never paste unescaped contact or company data directly into HTML. The code example uses escapeHtml for that reason. Treat every webhook field as untrusted input even if it came from a respected provider.
For a recipient address captured through a form or application flow, verify the address before beginning a high-value workflow. The email verification tool can help check an address before it becomes a send target, but verification is not a substitute for consent, relevance, or suppression handling.
When this breaks
A reliable integration plans for the failure modes created by the ZoomInfo-to-relay-to-Volanea hop. The following issues are the ones most likely to cause duplicate, missing, or unexpectedly formatted messages.
ZoomInfo retries create duplicate sends
If ZoomInfo does not receive a successful response from your relay, it can make another delivery attempt. This may happen because your service timed out, an exception occurred after Volanea accepted the message, or an intermediary interrupted the response.
Fix: use a stable Idempotency-Key with every Volanea send, and maintain your own event ledger for higher assurance. Record the ZoomInfo contact ID, changed-field set, event revision if supplied, first processing time, Volanea response, and final outcome. Return a successful response only after the send outcome has been safely recorded.
Your endpoint takes too long
A webhook sender expects a prompt HTTP response. Slow database calls, expensive recipient lookups, a delayed Volanea call, or cold starts can turn a valid event into a retry cycle.
Fix: verify the header and minimally validate the payload immediately. For high-volume or complex processing, persist the validated event to a queue or database transaction, return success, and let a worker perform the recipient lookup and Volanea send. The queue record must preserve the stable idempotency key.
Do not respond with success before you have durably stored enough information to recover the event. Otherwise, an outage between acknowledgement and processing can silently lose the notification.
A field is missing on your plan or payload mode
ZoomInfo’s available attributes and payload details can differ by entitlement, object configuration, and whether you selected a full payload or changed-attributes-only delivery. A production handler that assumes companyName, jobTitle, or even a particular nesting style will eventually fail.
Fix: test using a real webhook delivery from your own account. Save a redacted fixture, validate it against a schema, and code optional fields as optional. Use fallback text for nonessential display fields. Reject only when a field is necessary for your business decision, such as the stable record ID.
The changed field is represented differently than expected
Your update filtering may fail if the changed-attributes structure is configured differently than your initial fixture or if your team changes webhook payload mode.
Fix: log the names of changed attributes, not just a yes/no result. Keep the filter in a single tested function, as in hasTitleChange. Add regression fixtures for array-style and object-style changed-attribute representations if your account can produce both.
A malformed record blocks a batch
ZoomInfo can send multiple profiles in one webhook payload. If your code throws while processing the first malformed profile, valid records later in the batch never get processed.
Fix: process each profile independently, capture per-record results, and isolate errors. Decide whether to return an error for the whole webhook only when the payload itself is invalid or the service cannot persist any work. For per-profile validation failures, log the record ID and skip it according to policy.
Volanea rejects the message
A Volanea call can fail because the sender domain is unverified, the API key is invalid or revoked, the request is malformed, the recipient is suppressed, or the service returns a transient error.
Fix: log the Volanea HTTP status and error body securely, distinguish retryable from non-retryable failures, and alert on sustained authentication or sender-domain errors. Do not retry a malformed request forever. For transient errors, retry through a queue while preserving the same idempotency key.
A title update becomes an inappropriate external campaign
The technical flow can work perfectly while the message policy is wrong. A changed ZoomInfo title is not a permission signal for an outreach sequence.
Fix: keep contact-data updates separate from campaign eligibility. Use your CRM or consent system as the authority for audience membership, opt-outs, legal basis, frequency caps, and regional rules. Let ZoomInfo provide data context; let your owned systems decide whether email is appropriate.
Operational checklist for production
Before enabling the production webhook, run this checklist:
- Confirm your ZoomInfo package includes the required Monitoring API webhook access.
- Configure a Contact subscription with the
Updateevent only. - Use a unique HTTPS webhook endpoint with TLS enabled.
- Save the ZoomInfo verification token as a server-side secret.
- Save the Volanea API key as a separate server-side secret.
- Verify the Volanea sender domain and test a direct server-side send.
- Capture and redact a real test payload from your ZoomInfo account.
- Validate the payload schema and optional-field behavior.
- Use a durable idempotency strategy based on a logical update event.
- Escape all untrusted fields before rendering HTML.
- Send initial alerts internally, not automatically to enriched contacts.
- Monitor webhook authorization failures, handler latency, Volanea errors, skipped records, and duplicate replays.
A small amount of observability is especially valuable here. Track the count of ZoomInfo webhook deliveries, accepted records, skipped records, Volanea send attempts, successful sends, idempotent replays, and failures by reason. Those metrics tell you whether the issue is data coverage, webhook authentication, your business rules, or email delivery.
Alternatives when ZoomInfo Monitoring webhooks are not available
If your ZoomInfo package does not include the Monitoring API webhook capability, do not pretend that a standard ZoomInfo Workflow can directly execute arbitrary Volanea REST calls. Use a middleware architecture instead.
One option is to have ZoomInfo export qualifying contacts or companies to a CRM or other supported destination through Workflows, then trigger an automation from the destination system. Your middleware can receive the downstream event, apply the same policy checks, and call Volanea.
Another option is scheduled processing. Use the ZoomInfo API to retrieve or enrich records on an approved schedule, compare them to a stored previous state, enqueue only meaningful changes, and send through Volanea from the same secure worker. This is less immediate than webhooks, but it preserves the critical controls: secret storage, audit records, idempotency, recipient rules, and suppression-aware sending.
The key principle does not change: ZoomInfo data enters a backend process you control; that process determines whether a message is warranted; Volanea sends only after that decision.
Conclusion
To send email from ZoomInfo with Volanea, use a ZoomInfo Contact Update webhook as the data-change trigger and place a secure backend relay between ZoomInfo and Volanea. Verify the x-zoominfo-token, process batched profiles defensively, map only the fields your use case needs, and send through POST /v1/send with a stable Idempotency-Key.
The relay is where this integration becomes dependable. It keeps the Volanea API key out of ZoomInfo and out of the browser, prevents duplicate sends during webhook retries, handles missing fields, applies recipient and consent rules, and gives your team a durable record of what happened. Start with internal notifications for meaningful record updates, then expand only when your data, policy, and delivery controls are ready.
FAQ
Does Volanea have a native ZoomInfo integration?
No. Volanea does not provide a native ZoomInfo marketplace app or plugin. The integration uses ZoomInfo Monitoring API webhooks, your own server-side relay, and Volanea’s REST email API.
What ZoomInfo event starts the email send?
For the direct webhook route, the trigger is a monitored Contact record receiving an Update event. This applies to eligible records previously enriched through the relevant ZoomInfo API access.
Can I put my Volanea API key in ZoomInfo?
No. Store the Volanea key only in a server-side secret store used by your webhook relay. ZoomInfo should send its webhook to your endpoint; your endpoint then authenticates separately to Volanea.
How do I prevent duplicate emails if ZoomInfo retries a webhook?
Use Volanea’s Idempotency-Key header with a key derived from stable identifiers for the logical ZoomInfo update. For stronger guarantees, also store processed event identifiers in your own database or queue.
Can I automatically email the ZoomInfo contact whose record changed?
Technically, your relay can map a contact email address into a recipient field, but a data update is not permission to send outreach. Use only recipients who meet your consent, relationship, suppression, and campaign-eligibility rules.