Send email from Formbricks without exposing an email API key by connecting its response webhook to a small server-side relay. That relay receives a completed response, maps its answers into a message, and calls Volanea’s REST API.
This is a webhook integration, not a native Formbricks marketplace app or plugin. Formbricks emits an outbound HTTP event when a survey response is finished; your application, serverless function, or automation endpoint decides whether an email should be sent and then makes the authenticated Volanea request. That extra hop is useful: it keeps credentials private, gives you a place to validate data, and makes duplicate prevention possible.
What this integration does
The practical flow is straightforward:
- A respondent completes a Formbricks survey.
- Formbricks sends a
responseFinishedwebhook to an HTTPS endpoint you control. - The endpoint reads
data.response.data, where Formbricks stores answers keyed by question ID. - The endpoint finds the respondent’s email address and the answers relevant to the message.
- It sends a transactional email through Volanea.
- It records that the Formbricks response ID has been handled so a retry does not create a second message.
A common example is a product-feedback survey. When somebody completes it, send them a thank-you message, a copy of their submitted request, or a follow-up based on a selected answer. Another is lead qualification: a finished response can alert a sales or customer-success inbox when the respondent has selected a high-intent option.
The key model term is important here. Formbricks surveys produce responses, and the outbound event for a completed response is responseFinished. Do not send immediately on a partially saved response unless your workflow truly needs that behavior. A response can be created or updated before the participant has reached the final step, so responseFinished is normally the safer trigger for an acknowledgement or follow-up.
The architecture: Formbricks, middleware, and Volanea
A direct connection from Formbricks to an email API is not the right design when the API requires a secret bearer token. Formbricks can make the outbound webhook request, but the Volanea credential must remain in a server-side secret store. Put a relay between the two services.
Formbricks survey completion
|
| POST responseFinished webhook
v
Your HTTPS webhook endpoint
- validates the request
- checks idempotency
- maps response answers
- applies consent and business rules
|
| POST Volanea email API with Bearer token
v
Volanea
|
v
Recipient inbox
That endpoint can be an API route in a Next.js app, a Cloudflare Worker, an AWS Lambda, a Vercel Function, a container service, or an automation platform’s webhook module. The code below uses a standard JavaScript HTTP handler so the integration logic is easy to transfer to any of those environments.
This arrangement also separates two kinds of trust:
- Formbricks-to-your-endpoint trust: confirm that the incoming request is expected before acting on it. Use the webhook verification approach available in your Formbricks deployment, plus an unguessable endpoint or an independently configured shared secret where appropriate.
- Your-endpoint-to-Volanea trust: keep the Volanea API key in a server-side environment variable or secret manager, then send it only in the server-to-server
Authorizationheader.
Never place VOLANEA_API_KEY in survey JavaScript, a Formbricks question’s HTML, a public environment variable, a client bundle, or a URL query string. Anyone who can inspect the survey page, browser network requests, logs, or a shared automation configuration could otherwise reuse the key to send mail from your account.
For API authentication, sending domains, and message options, consult the Volanea email API reference and setup guides alongside this implementation. The integration should use an API key with only the scope and environment needed by this relay.
Configure the Formbricks trigger
In Formbricks, create an outbound webhook for the survey and select the responseFinished event. Set its target to a public HTTPS URL controlled by your application, such as:
https://app.example.com/api/webhooks/formbricks
Use a staging survey and staging endpoint first. Complete a real test response, rather than testing with only a manually invented JSON request, because the question IDs and answer formats are survey-specific.
Know what Formbricks sends
Formbricks webhook bodies use an event envelope. For a finished response, the important parts are the event name, the response ID, the survey ID, the completion state, and the data object containing question answers. A representative responseFinished body has this shape:
{
"event": "responseFinished",
"data": {
"response": {
"id": "clx7r8response01",
"surveyId": "clx7r8survey01",
"createdAt": "2026-10-04T14:20:00.000Z",
"updatedAt": "2026-10-04T14:22:31.000Z",
"finished": true,
"data": {
"clx7q_email_question": "ada@example.com",
"clx7q_name_question": "Ada Lovelace",
"clx7q_topic_question": "Billing",
"clx7q_message_question": "Please contact me about annual invoicing."
}
},
"survey": {
"id": "clx7r8survey01",
"name": "Contact follow-up survey"
}
}
}
The strings such as clx7q_email_question are not labels such as “Email address.” They are Formbricks question IDs. That distinction prevents a frequent integration failure: changing the visible question title does not make a stable machine-readable key, and using a title as if it were an answer key will return undefined.
Before deploying, collect the IDs from a real webhook payload for your own survey. Keep them in configuration or constants with clear names. If you duplicate a survey, revise questions, or switch to a different survey, verify those mappings again.
Choose the recipient deliberately
The recipient does not have to be a value in the response payload. Depending on how the survey is distributed and how contacts are identified in your Formbricks setup, recipient information may be available from your own application rather than a survey question. For a public survey, asking for an email address can be simplest, but it raises consent and validation requirements.
For an email acknowledgement, make the email-address question required and clearly explain why the address is collected. For an internal alert, do not use a respondent-controlled address as the to value; send to a fixed team inbox and include sanitized answer content in the body.
Map a Formbricks response to a Volanea email
The following handler maps four Formbricks question IDs into a Volanea transactional email. It also shows the full REST call: POST https://api.volanea.com/v1/emails, a bearer token, and a JSON message payload.
Replace the four question IDs with IDs from your survey. Replace feedback@example.com with a verified sender on your Volanea account. The processedResponses functions represent durable storage; use a database table, KV store, or queue-backed idempotency store in production rather than an in-memory JavaScript Set.
// POST /api/webhooks/formbricks
// Server-side code only. Do not expose VOLANEA_API_KEY to the browser.
const QUESTION_IDS = {
email: "clx7q_email_question",
name: "clx7q_name_question",
topic: "clx7q_topic_question",
message: "clx7q_message_question"
};
function escapeHtml(value) {
return String(value ?? "")
.replaceAll("&", "&")
.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll('"', """)
.replaceAll("'", "'");
}
function firstText(value) {
// Formbricks answers can be scalar values or arrays for multi-select questions.
if (Array.isArray(value)) return value.join(", ");
if (value === null || value === undefined) return "";
return String(value).trim();
}
export async function POST(request) {
// Verify the Formbricks webhook here before processing it, according to the
// verification mechanism configured for your Formbricks deployment.
const payload = await request.json();
if (payload.event !== "responseFinished") {
return Response.json({ ignored: true }, { status: 200 });
}
const response = payload.data?.response;
if (!response?.id || response.finished !== true) {
return Response.json({ error: "Invalid finished response" }, { status: 400 });
}
// Idempotency: atomically reserve response.id in durable storage.
// If it was already reserved, return 200 so Formbricks does not keep retrying.
const reserved = await processedResponses.reserve(response.id);
if (!reserved) {
return Response.json({ duplicate: true }, { status: 200 });
}
const answers = response.data ?? {};
const recipient = firstText(answers[QUESTION_IDS.email]).toLowerCase();
const name = firstText(answers[QUESTION_IDS.name]);
const topic = firstText(answers[QUESTION_IDS.topic]);
const message = firstText(answers[QUESTION_IDS.message]);
if (!recipient || !recipient.includes("@")) {
await processedResponses.release(response.id);
return Response.json({ error: "No usable recipient email" }, { status: 422 });
}
const safeName = escapeHtml(name || "there");
const safeTopic = escapeHtml(topic || "your feedback");
const safeMessage = escapeHtml(message || "—");
const emailPayload = {
from: "Feedback Team <feedback@example.com>",
to: [recipient],
subject: `We received your ${topic || "feedback"}`,
html: `
<p>Hi ${safeName},</p>
<p>Thanks for completing our survey. We received your note about <strong>${safeTopic}</strong>.</p>
<blockquote>${safeMessage}</blockquote>
<p>Our team will review it and follow up if needed.</p>
`,
text: `Hi ${name || "there"},\n\nThanks for completing our survey. We received your note about ${topic || "your feedback"}.\n\n${message || ""}\n\nOur team will review it and follow up if needed.`
};
const volaneaResponse = await fetch("https://api.volanea.com/v1/emails", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.VOLANEA_API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify(emailPayload)
});
if (!volaneaResponse.ok) {
await processedResponses.release(response.id);
const detail = await volaneaResponse.text();
console.error("Volanea send failed", volaneaResponse.status, detail);
return Response.json({ error: "Email send failed" }, { status: 502 });
}
const sent = await volaneaResponse.json();
await processedResponses.markSent(response.id, sent.id);
return Response.json({ accepted: true, responseId: response.id }, { status: 200 });
}
The field mapping is the heart of the integration:
| Formbricks source | Meaning | Volanea destination |
|---|---|---|
data.response.data[QUESTION_IDS.email] | respondent email answer | to[0] |
data.response.data[QUESTION_IDS.topic] | selected topic | subject and HTML content |
data.response.data[QUESTION_IDS.message] | free-text response | HTML and plain-text content |
| fixed verified address | your sending identity | from |
The handler includes both html and text. HTML gives you layout and branding; the text alternative improves accessibility and gives receiving systems a sensible fallback. It also escapes respondent-provided content before inserting it into HTML. Without escaping, a participant could put markup into a free-text answer and change the outgoing email’s content.
Keep credentials and personal data safe
The Volanea API key belongs in the environment where the webhook handler runs. Examples include a deployment platform’s encrypted environment variables, a cloud secret manager, or an automation platform’s encrypted connection credential. It does not belong in Formbricks survey content or browser-visible configuration.
The Formbricks side should contain only what it needs to invoke your relay: the endpoint address and, if your deployment supports it, material used to authenticate the webhook request. Treat that endpoint configuration as sensitive too. A leaked endpoint URL is less serious than a leaked sending key, but an attacker may still generate unwanted traffic or attempt payload attacks.
Validate before sending
A syntactically plausible email address is not necessarily deliverable or authorized for mail. At minimum, reject empty and malformed values, and do not treat an email typed into an arbitrary survey as permission to send broad marketing campaigns. An acknowledgement related to the completed survey may be appropriate; newsletters and promotional sequences generally need a separate, auditable consent basis.
For high-value or externally distributed surveys, validate addresses before using them in automated follow-ups. Volanea’s email address verification tool can help screen obvious invalid or risky addresses, but it should complement—not replace—your own consent and suppression logic.
Limit what enters logs
Webhook payloads may contain names, email addresses, open-ended feedback, and potentially sensitive survey answers. Do not log complete request bodies in production. Log a response ID, survey ID, event name, decision outcome, and email-provider message ID where available; redact recipient addresses or hash them if your operational needs allow it.
Also make retention intentional. Your idempotency record only needs enough information to establish that response ID X was processed, when it was processed, and perhaps the resulting email message ID. It does not need to retain every answer forever.
When this breaks
This integration has two network hops, and each hop can fail differently. Designing for those failure modes is more valuable than making a successful first test send.
Formbricks retries can cause duplicate emails
A webhook sender generally cannot know whether your endpoint completed work if it times out or receives a non-success response. Formbricks may retry delivery. A retry can arrive after your relay has already sent the message but before it returned a successful HTTP response.
That is why response.id must be an idempotency key. Reserve it atomically before calling Volanea. If a second request carries the same completed response ID, return a success response without sending another email. Do not use the recipient email address as the idempotency key: the same person may legitimately submit multiple responses.
There is a subtle trade-off in the sample code. Releasing the reservation after an API failure permits a later retry to try again. In a stronger production design, store states such as processing, sent, and retryable_failure, record the provider message ID immediately after acceptance, and use a queue for controlled retries. This reduces the chance of both duplicate sends and permanently lost sends.
Webhook timeouts can make a successful send look unsuccessful
Calling an external email API inside the webhook request path increases latency. If your endpoint, database, or Volanea request is slow, Formbricks can time out waiting for the response and retry. A short, reliable handler is preferable.
For noncritical acknowledgements, the handler can validate and durably enqueue the work, return a 2xx response promptly, and let a worker make the Volanea call. The queue job should still use response.id as its idempotency key. For critical operational email, add monitoring for jobs that remain queued or fail repeatedly.
Fields can be missing or shaped differently
Not every Formbricks response will contain every answer. A question may be optional, hidden by survey logic, introduced after old responses were collected, or answered as an array rather than a string. A contact attribute available in one distribution method may not be present in another.
Build explicit handling for each case:
- Return a controlled error or skip an acknowledgement when the recipient is absent.
- Use a generic subject when an optional topic is missing.
- Convert multi-select arrays to a deliberate representation rather than relying on JavaScript’s implicit coercion.
- Test conditional logic paths, not just the happy path.
- Keep question IDs in per-survey configuration rather than scattering them through templates.
Webhook availability and configuration can also differ by Formbricks plan, hosted deployment, or self-hosted setup. If outbound webhooks are unavailable in the Formbricks environment you use, do not pretend a direct HTTP integration exists. Use the automation route described below, or upgrade/configure the deployment to enable the webhook capability.
A 4xx and a 5xx mean different things
A Volanea 4xx response usually indicates a request that should be fixed before retrying: an invalid recipient, unverified sender, malformed payload, or authorization problem. Repeating it automatically will not help. A 5xx, a connection failure, or a rate-limit response is more likely transient and should enter a bounded retry policy with exponential backoff.
Alert on sustained failures, not every single transient failure. Include the Formbricks response ID and provider response category in the alert, but exclude full survey content and bearer tokens.
Use an automation platform when direct webhooks are unavailable
If your Formbricks plan or deployment does not provide outbound webhooks, use an automation service that supports Formbricks as a trigger and can make an authenticated HTTP request. Zapier or Make can act as middleware, but the same security and reliability constraints remain.
The automation should be structured as:
- Trigger on a new finished Formbricks response, using the Formbricks integration available to the automation service.
- Filter out incomplete responses and responses that lack the recipient address.
- Map the response’s question-ID answer values into a JSON body.
- Use the automation platform’s secret/connection storage for the Volanea bearer token.
- Send the HTTP request to Volanea’s email endpoint.
- Store or check the Formbricks response ID to avoid duplicate sends on replay.
Automation platforms are convenient for a low-volume proof of concept, particularly when no application backend exists. They are less flexible for signature validation, escaping complex user-generated content, durable idempotency, and careful retry behavior. Once the workflow sends customer-facing mail at meaningful volume, a dedicated endpoint and queue are normally easier to test and operate.
Deliverability considerations for survey-triggered email
A technically valid API call is only the beginning. Survey-triggered email often has better engagement than bulk email because it follows a recent action, but it can still harm deliverability if it is vague, unwanted, or sent from an unprepared domain.
Use a sender domain you control and authenticate it before production sending. Keep the message clearly connected to the completed survey: mention the survey topic or the action the participant just took, avoid misleading subject lines, and use a recognizable sender name. A message saying “We received your billing feedback” is more understandable than “Important update.”
Separate message categories where appropriate. A respondent acknowledgement, an internal team notification, and a promotional nurture sequence have different recipients, consent expectations, and suppression requirements. Do not turn every completed response into a marketing subscription simply because an email field was present.
Watch for second-order effects too. If a survey contains an open-ended response, quoting it back in an email can reveal private information when a shared mailbox is used. If an internal alert includes a respondent’s email, limit who can access that inbox. If your survey supports multiple languages, select templates based on the response or survey locale rather than sending every recipient an English message.
Test the integration before production
A reliable test plan uses real Formbricks submissions and deliberately bad conditions. Start with a separate test survey, a verified test sender, and inboxes you control.
Test at least these cases:
- A normal finished response with all mapped questions answered.
- A response that omits an optional answer.
- A response with a multi-select answer.
- A free-text answer containing
<,>, quotes, and line breaks. - An invalid or missing email answer.
- The same webhook body delivered twice.
- A simulated Volanea failure followed by a retry.
- A slow downstream request that exercises your timeout strategy.
Confirm the email’s recipient, sender, subject, HTML rendering, plain-text rendering, and the absence of duplicate mail. Then inspect logs to ensure they contain useful operational identifiers but not the raw API key or a full copy of sensitive answers.
For observability, connect three IDs whenever possible: the Formbricks response ID, your internal job or idempotency record ID, and the Volanea message ID returned by the send request. That chain makes support investigations much faster when someone says they completed a survey but did not receive the expected acknowledgement.
Conclusion
To send email from Formbricks safely, use the responseFinished webhook as the completion signal and route it to server-side middleware. Map answers from data.response.data using your survey’s real question IDs, keep the Volanea key in a secret manager, and make the response ID idempotent before any email is sent.
The resulting workflow is more dependable than embedding credentials in a survey or relying on a fragile one-step connection. It gives you a clear place to enforce consent, validate addresses, escape content, manage retries, and keep survey follow-ups relevant to the person who just submitted a response.
FAQ
Can Formbricks send directly to Volanea?
Formbricks can emit the outbound webhook that starts the workflow, but the Volanea API key should not be placed in client-visible survey configuration. Use a server-side webhook endpoint or an automation platform as the authenticated relay.
Which Formbricks event should send the email?
Use responseFinished for an email that should be sent only after a respondent completes a survey. It is safer than acting on an earlier response creation or update event because those can represent incomplete work.
Where are Formbricks answers in the webhook payload?
Answers are in data.response.data, keyed by Formbricks question ID. Capture a real test webhook to identify the exact IDs for your email, name, and other mapped questions.
How do I prevent duplicate emails after a webhook retry?
Use data.response.id as a durable idempotency key. Atomically record or reserve it before calling the email API; if the same response ID arrives again, return success without sending another message.
What if my Formbricks setup does not offer webhooks?
Use a supported automation route such as Zapier or Make as middleware, with its Formbricks trigger and an authenticated HTTP request to Volanea. Keep the API key in the automation platform’s protected credential storage and add duplicate protection where the platform allows it.