If you need to send email from Workable when a new candidate enters your hiring pipeline, the reliable pattern is Workable webhook → your server-side endpoint → Volanea’s email API. It keeps recruiting events separate from email delivery, gives you control over content and recipients, and never exposes a sending credential in Workable or in a browser.

Workable does not need a native Volanea marketplace app for this integration. Its webhook capability can notify an HTTPS endpoint when a supported recruiting event occurs; your endpoint then decides whether an email should be sent and calls Volanea. This is a useful design for candidate acknowledgements, internal recruiting alerts, referral notifications, and narrowly targeted operational messages.

The important distinction is that Workable is the event source, while Volanea is the delivery service. Your middleware is the policy layer between them. It validates the incoming event, maps fields, avoids duplicate sends, and holds the Volanea API key safely on the server.

What this integration does—and does not do

This guide uses Workable’s candidate_created webhook event. In Workable’s candidate model, this event represents the creation of a candidate record. It is a sensible trigger for an immediate application-received acknowledgement or an internal notification that a new applicant needs review.

The flow is:

  1. A person becomes a candidate in Workable.
  2. Workable sends a candidate_created webhook payload to an HTTPS URL you control.
  3. Your endpoint verifies and records the event before doing slow work.
  4. Your endpoint maps candidate and job fields into an email request.
  5. Your endpoint calls Volanea’s REST email endpoint.
  6. Volanea accepts the message for delivery and returns a response that you log with the Workable event ID.

This is not a replacement for correspondence that recruiters deliberately send from Workable. It is better suited to repeatable, system-generated email where consistent timing, branding, deliverability controls, and an auditable send record matter.

It also should not be used to bypass consent or local hiring communications rules. An application acknowledgement is generally an expected service message, but recruiting teams should still decide which messages are appropriate, use an identified sender, provide required privacy information, and avoid sending sensitive application details by email.

Why use a middleware endpoint

A webhook is an outbound notification, not a general-purpose scripting environment. The endpoint between Workable and Volanea is where business logic belongs. It can reject an event with no usable email address, route a candidate to the correct regional sender, select a template based on job department, or notify a hiring manager instead of a candidate.

It also means you do not have to place an email API credential in a third-party automation field or in client-side JavaScript. That separation is a security boundary, not just an implementation preference.

The concrete Workable trigger: candidate_created

For this implementation, configure a Workable webhook subscription for the candidate created event, identified in the webhook payload as candidate_created. Point it at a publicly reachable HTTPS endpoint such as:

https://integrations.example.com/webhooks/workable

Workable webhook event bodies contain an event identifier and account context plus a data object for the event resource. For a candidate-created notification, the useful information is the candidate object beneath data.candidate, including the candidate’s identity and the job associated with the application.

A representative candidate_created payload has this shape:

{
  "id": "evt_01HZY7B9Q8K3M9P2",
  "event": "candidate_created",
  "account_id": 12345,
  "data": {
    "candidate": {
      "id": 987654,
      "name": "Avery Chen",
      "firstname": "Avery",
      "lastname": "Chen",
      "email": "avery.chen@example.net",
      "phone": "+1 555 0100",
      "source": "Applied",
      "stage": "Applied",
      "created_at": "2026-09-25T10:15:00Z",
      "job": {
        "shortcode": "ABCD123",
        "title": "Senior Platform Engineer",
        "department": "Engineering",
        "location": "Remote",
        "url": "https://apply.workable.com/example/j/ABCD123/"
      }
    }
  }
}

Treat this as a schema to validate rather than a guarantee that every optional property will exist. A candidate can lack an email address, a phone number, a department, or a location. Jobs can be configured differently across an account, and some fields depend on how a candidate entered Workable. Your code should require only the fields necessary for the particular message.

For example, an acknowledgement email needs data.candidate.email. An internal alert may only need the candidate name and job title, and it can still be sent if the applicant has no email address. Do not assume firstname exists simply because name does; derive a safe greeting when needed.

Select the event intentionally

candidate_created is a good event for a single acknowledgement because it occurs when the candidate record is created. It is not automatically the right trigger for every recruiting email.

Use a different supported candidate or job webhook event when the business moment is genuinely different. A stage-change message, for example, must be tied to the stage-related event that Workable emits—not inferred from the original candidate-created payload. That distinction prevents a system from sending an interview-related message immediately after an application arrives.

Before enabling a production rule, create a test candidate in the same Workable account and save the exact JSON delivered to your endpoint. Compare it with the current Workable webhook documentation and use the observed field names in your mapper. This is especially important if your workflow relies on custom answers or account-specific recruiting configuration.

Set up the webhook without placing an API key in Workable

Create the webhook in Workable’s webhook configuration for your account and subscribe the endpoint to candidate_created. Workable needs only the destination URL for this architecture. It does not need, and should not receive, your Volanea API key.

That answers a common implementation question directly: the Volanea API key lives nowhere on the Workable side. Store it in the secret manager or encrypted environment configuration for the server, serverless function, or automation service that receives the webhook. The only value Workable should know is the URL it is posting to.

If your Workable webhook configuration supports a request authentication mechanism for your account, enable it and validate it at the endpoint according to Workable’s current documentation. If it does not provide a signature scheme for your setup, use a dedicated endpoint, restrict access at your edge where possible, and make the endpoint harmless without a valid payload. Do not put a permanent Volanea bearer token into a URL query parameter as a substitute for request authentication: URLs can be exposed in logs, monitoring products, browser history, and intermediary systems.

Server-side secret handling

A minimal deployment needs these server-only variables:

VOLANEA_API_KEY=...
VOLANEA_FROM=Talent Team <talent@example.com>
WORKABLE_WEBHOOK_SECRET=...   # only if your verification design uses one

Use a dedicated Volanea API key for this integration rather than sharing a personal or all-purpose credential across unrelated systems. Give it the smallest practical scope if your account supports scoped credentials, name it for the Workable integration, and rotate it if a deployment, contractor, or log sink is suspected to have exposed it.

The from domain must be configured for sending in Volanea. A message can be accepted by an API but still perform poorly—or be rejected—if the sender identity and domain authentication are not properly configured. Follow the email API setup guides to configure the sending domain before turning the Workable webhook on for real candidates.

Working Node.js relay: map Workable fields to a Volanea email

The following Express example receives a Workable candidate-created payload, validates the fields this specific message needs, deduplicates on the Workable event ID, and sends an acknowledgement through Volanea. It deliberately does not make Workable wait for email delivery; it returns quickly after recording the event and processes the send in a background task.

import crypto from "node:crypto";
import express from "express";

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;

if (!VOLANEA_API_KEY || !VOLANEA_FROM) {
  throw new Error("VOLANEA_API_KEY and VOLANEA_FROM must be configured");
}

// Replace this Set with Redis, Postgres, or a queue-backed idempotency store.
// A process-memory Set is only suitable for a local demonstration.
const processedEvents = new Set();

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

function candidateEmailRequest(payload) {
  const candidate = payload?.data?.candidate;
  const job = candidate?.job;

  if (payload?.event !== "candidate_created") {
    return null;
  }
  if (!candidate?.email) {
    return null;
  }

  const candidateName = candidate.firstname || candidate.name || "there";
  const jobTitle = job?.title || "the role";
  const applicationUrl = job?.url;
  const safeName = escapeHtml(candidateName);
  const safeTitle = escapeHtml(jobTitle);
  const safeUrl = applicationUrl ? escapeHtml(applicationUrl) : null;

  return {
    from: VOLANEA_FROM,
    to: [candidate.email],
    subject: `We received your application for ${jobTitle}`,
    html: `
      <p>Hi ${safeName},</p>
      <p>Thanks for applying for <strong>${safeTitle}</strong>.</p>
      <p>Our team has received your application and will review it.</p>
      ${safeUrl ? `<p><a href="${safeUrl}">View the job posting</a></p>` : ""}
      <p>Talent Team</p>
    `,
    text: `Hi ${candidateName},\n\nThanks for applying for ${jobTitle}. Our team has received your application and will review it.\n\nTalent Team`,
    tags: [
      { name: "source", value: "workable" },
      { name: "event", value: "candidate_created" },
      { name: "job", value: String(job?.shortcode || "unknown") }
    ]
  };
}

async function sendWithVolanea(email) {
  const response = await fetch("https://api.volanea.com/v1/emails", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${VOLANEA_API_KEY}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify(email)
  });

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

  return responseBody ? JSON.parse(responseBody) : {};
}

app.post("/webhooks/workable", async (req, res) => {
  const payload = req.body;
  const eventId = payload?.id;

  if (payload?.event !== "candidate_created") {
    return res.status(204).end();
  }
  if (!eventId) {
    return res.status(400).json({ error: "Missing Workable event id" });
  }
  if (processedEvents.has(eventId)) {
    return res.status(200).json({ status: "already_processed" });
  }

  // Persist an event row with a unique constraint on eventId before replying.
  processedEvents.add(eventId);
  res.status(202).json({ status: "accepted", eventId });

  try {
    const email = candidateEmailRequest(payload);
    if (!email) {
      console.log("No email sent: candidate email or required event data missing", { eventId });
      return;
    }

    const result = await sendWithVolanea(email);
    console.log("Volanea email accepted", {
      workableEventId: eventId,
      workableCandidateId: payload.data.candidate.id,
      volaneaResult: result
    });
  } catch (error) {
    console.error("Workable-to-Volanea send failed", { eventId, error: error.message });
    // Mark the stored event as failed and let a worker retry with backoff.
  }
});

app.listen(process.env.PORT || 3000);

The mapping is explicit: data.candidate.email becomes to, data.candidate.job.title becomes part of the subject and message body, and data.candidate.job.shortcode becomes a tag. Keeping this transformation in code makes it reviewable. It is also safer than building a template from an unbounded set of fields or blindly inserting candidate-entered text into HTML.

The Volanea call is POST https://api.volanea.com/v1/emails, authenticated with a bearer token in the Authorization header. The request sends from, to, subject, html, and text; tags help connect delivery records back to the originating Workable event and job.

Build email content for a recruiting workflow

A first reply to an applicant should be short, clear, and predictable. It should confirm receipt without implying a decision, interview, or timeline that the recruiting team cannot consistently meet.

Useful acknowledgement content usually includes:

  • The role title the person applied for.
  • A simple confirmation that the application was received.
  • A statement about what happens next, only if it is accurate.
  • A monitored reply address or clear contact route where appropriate.
  • Required privacy or equal-opportunity language for your organization and jurisdiction.

Avoid including resume content, screening answers, interview feedback, compensation data, or internal hiring notes. Email is not the right channel for sending sensitive candidate information back to the candidate, and exposing those fields in a template also increases the impact of a mapping mistake.

Separate candidate messages from internal alerts

Do not reuse the same template and recipient logic for applicants and employees. Candidate email should generally go to candidate.email; an internal alert should go to a controlled company mailbox, distribution list, or recruiting operations address.

For an internal alert, map the candidate email as content rather than as the recipient. For example, set to to recruiting-ops@example.com, add the candidate’s name and job title in the body, and tag the send with the candidate ID. This prevents an accidental switch in a dynamic field from sending internal notes to an applicant.

Idempotency: stop webhook retries from creating duplicate email

The same Workable event can be delivered more than once. Network failures, endpoint timeouts, a deployment restart, or a response that does not reach Workable can all cause a sender to retry a webhook. Your system must assume at-least-once delivery, even when the normal case looks like exactly one HTTP request.

The sample uses payload.id as the idempotency key. In production, persist it in a durable datastore with a unique constraint. A typical table might contain:

FieldPurpose
workable_event_idUnique incoming event identifier; prevents duplicate processing.
event_typeLets workers select handling logic.
candidate_idSupports debugging and audit searches.
statusSuch as received, queued, sent, skipped, or failed.
volanea_message_idConnects the outgoing message to delivery activity.
attempt_countSupports controlled retry behavior.
received_atRecords when Workable first delivered the event.

Insert the event row before scheduling delivery. If a second request attempts to insert the same Workable event ID, return a successful response without sending again. If the first request fails after the event is recorded but before the Volanea call completes, a worker can retry safely according to the stored status.

There is a second duplicate risk: two different events might logically represent one desired message. A candidate could be created more than once through data imports, agency submissions, or workflow changes. If your policy is “one acknowledgement per candidate per job within 24 hours,” enforce that business rule separately with a key such as candidate ID + job shortcode + message type + date window. Event-level idempotency alone cannot solve a business-level duplication rule.

When this breaks

This integration has two HTTP hops: Workable to your endpoint, then your endpoint to Volanea. Diagnose failures by identifying which hop failed and whether an event was accepted, queued, or sent.

Workable retries cause duplicate sends

If the endpoint returns an error, takes too long, or the response is lost in transit, Workable may retry delivery. A webhook handler that sends email synchronously and only then returns a response is especially vulnerable: the email could be accepted by Volanea, but Workable might still see a timeout and send the same event again.

Fix this by acknowledging quickly after durably storing the event. Queue the Volanea work after the database write. Deduplicate on the Workable event ID and record the outgoing Volanea message identifier. Never rely on an in-memory set in a multi-instance deployment; each instance has different memory and loses it on restart.

Your webhook endpoint times out

Serverless cold starts, overloaded databases, slow DNS resolution, or waiting for a downstream API response can make a webhook endpoint slow. The remedy is architectural: make webhook receipt lightweight. Parse and validate the body, save the event, enqueue a job, and return a 2xx response. Let a worker with explicit timeout and retry policy call Volanea.

Set a reasonable outbound timeout for the Volanea request in the worker. Retry transient network errors and 5xx responses with exponential backoff, but do not endlessly retry malformed requests, invalid sender identities, or a recipient address you have already determined is unusable.

Expected payload fields are missing

Not every candidate record has every field. A candidate may be created without an email address, the job may have no department, or a field your team sees in the Workable UI may not be present in the webhook payload for that event or account configuration. Availability can also depend on your Workable plan, feature configuration, permissions, and the source of the candidate record.

Make missing fields an explicit outcome. For an acknowledgement, mark the event as skipped_missing_candidate_email and alert only if that rate is unexpected. For an internal alert, fall back from job.title to “a new application” rather than failing the entire event. Log the field path that was absent, but avoid writing full resumes, answers, or raw sensitive payloads to broad-access logs.

Volanea rejects or suppresses the message

A 4xx response usually indicates a request, authentication, sender, or recipient problem rather than a temporary outage. Check the exact response, confirm the API key is loaded in the worker environment, confirm the from identity is valid, and verify that to contains a clean address rather than an empty string or display name.

A message can also be accepted but not reach an inbox because the recipient address is invalid, suppressed, bounced, or filtered. Store the API response and use Volanea delivery events or logs to distinguish API acceptance from downstream delivery. For high-volume hiring workflows, validate addresses at the point of collection and avoid repeatedly sending to known-bad addresses.

Use a queue and observability from the start

The smallest proof of concept can call Volanea directly inside the webhook handler. A production hiring workflow should use a queue or durable job table. The webhook receiver has one responsibility—accept and record events. The worker has another—render and send email with retries, rate limits, and observability.

At minimum, log these identifiers together:

  • Workable event ID.
  • Workable candidate ID.
  • Workable job shortcode.
  • Your internal queue job ID.
  • Volanea message ID or send response ID.
  • Template or message version.
  • Final processing status and failure reason.

This record makes support questions answerable. When a recruiter asks whether Avery received an acknowledgement, you can trace the candidate event, find the email request, see whether Volanea accepted it, and distinguish “not sent because no email was supplied” from “sent but bounced.”

Use structured logs rather than plain text when possible. Redact API keys and avoid storing full candidate payloads by default. Candidate data is personally identifiable information; retention periods and access controls should follow the organization’s privacy policy rather than the convenience of debugging.

Direct webhook versus Zapier or Make middleware

A direct webhook relay is the best option when you need controlled templates, dependable deduplication, custom eligibility rules, or a clear security boundary around the Volanea key. It is also usually easier to test once your team has normal deployment and monitoring practices.

An automation platform can be useful when a recruiting operations team needs to prototype a simple workflow without maintaining code. In that model, Workable provides the trigger through the automation platform’s available Workable integration or webhook support, and the automation platform calls a secure custom webhook you own or an HTTP action that reaches Volanea.

However, do not assume an automation tool removes the core engineering concerns. You still need to understand retries, task replays, field availability, credential storage, and duplicate prevention. If the platform stores the Volanea key, store it in that platform’s encrypted connection or secrets facility—not in a visible field, code step output, browser-side app, or shared documentation.

For candidate-facing messages, a direct server-side relay remains the clearer long-term design because it gives you durable idempotency and avoids placing recruiting data across unnecessary systems.

Test the full path before enabling live sends

Create a test job and a test candidate using an address you control. Confirm the event reaches the endpoint and inspect the captured JSON. Then test the message with a verified Volanea sending domain before adding real candidate traffic.

Run these cases deliberately:

  1. A normal candidate with first name, email, job title, and job URL.
  2. A candidate with no email address, which should be skipped rather than crash the handler.
  3. A candidate with special characters in the name or job title, which should remain safe in HTML.
  4. A repeated delivery of the identical Workable event, which should produce one send.
  5. A temporary Volanea failure, which should move the job to a retry state without losing it.
  6. A malformed or unrelated webhook event, which should not create an email.

Inspect both the plain-text and HTML versions on desktop and mobile. Confirm that the sender name and reply address are correct, the job title maps correctly, and no internal identifiers are exposed. Then have recruiting operations approve the exact language before enabling the workflow for all roles.

Operational and deliverability considerations

Application acknowledgements can be high-volume during an active hiring campaign, but they are still expected communications. Consistent sender identity and a restrained cadence help them reach inboxes and build trust. Sending from the same authenticated recruiting domain, using a stable display name, and avoiding surprise promotional content are more useful than trying to make a transactional acknowledgement look like a marketing campaign.

Keep message categories separate. A candidate acknowledgement, a recruiter scheduling message, and a talent newsletter have different expectations and may require different consent, unsubscribe, and audience controls. The integration described here should send one event-driven message because a Workable candidate record was created; it should not silently add the candidate to promotional lists.

Review bounces and complaints. If a source or form produces a high rate of invalid recipient addresses, fix the collection workflow instead of repeatedly retrying email sends. If you plan to send at significant scale, estimate volume around hiring peaks and select an email plan that matches your expected throughput and retention needs; see transactional email pricing when you are ready to size the sending service.

A practical production checklist

Before enabling this workflow, confirm all of the following:

  • Workable is configured to deliver the candidate_created webhook to a dedicated HTTPS endpoint.
  • The endpoint validates the event type and required fields rather than trusting every incoming body.
  • The Volanea API key is only in server-side secrets or an encrypted automation credential store.
  • The Volanea sender domain and from address are configured and approved for use.
  • Incoming event IDs are stored durably with a uniqueness constraint.
  • The webhook handler returns quickly and an asynchronous worker performs the send.
  • Candidate email is treated as optional and missing fields have defined outcomes.
  • HTML content escapes candidate- and job-derived values.
  • Logs connect Workable event IDs to Volanea send results without retaining unnecessary sensitive data.
  • Recruiting, legal, privacy, and brand owners have approved the content and sending policy.

With those pieces in place, sending email from Workable becomes a controlled event-driven workflow rather than a brittle collection of manual rules. Workable remains the system that knows a candidate entered the pipeline; Volanea remains the system that delivers the message; your integration owns the decision and audit trail between them.

FAQ

Can Workable send directly to Volanea without a native app?

Not securely with this design. Workable sends an outbound webhook to an endpoint you control, and that server-side endpoint calls Volanea. This avoids exposing the Volanea API key and gives you a place to validate payloads, apply rules, and prevent duplicate sends.

What Workable event should send an application acknowledgement?

Use candidate_created when the intended message is a one-time confirmation that a candidate record was created. Do not use it for interview, rejection, or stage-specific messages; those should be tied to the applicable supported event and tested against its actual payload.

Where should I store the Volanea API key?

Store it in the environment variables or secret manager of your middleware, queue worker, serverless function, or encrypted automation connection. Do not store it in Workable webhook configuration, client-side code, a URL, or a shared spreadsheet.

How do I prevent duplicate emails when Workable retries a webhook?

Persist the Workable webhook event ID before sending and enforce uniqueness in a database or durable cache. Return a successful response for a repeated event ID, then use a queue worker and a stored send status to retry only unfinished work.

What if a candidate has no email address in the webhook payload?

Treat that as an expected branch. Skip a candidate-facing acknowledgement, record a clear status such as skipped_missing_candidate_email, and optionally send an internal alert if your recruiting process requires follow-up. Do not construct a message with an empty recipient field.