Tally is excellent at collecting information, but send email from Tally is not a native Volanea integration button or marketplace installation. The reliable pattern is to let Tally emit a form-submission webhook, map that submission in a server-side endpoint, and have that endpoint call Volanea’s email API.
This guide builds that route end to end. It uses Tally’s real FORM_RESPONSE webhook event, shows the submission payload you need to handle, and provides a deployable JavaScript example that sends a confirmation email through Volanea without exposing an API key in a browser, form, or public URL.
What the Tally-to-Volanea integration does
The integration has three distinct jobs:
- A respondent completes and submits a Tally form.
- Tally sends a
FORM_RESPONSEHTTP webhook to an endpoint you control. - That endpoint validates and maps the submission, then sends an email through Volanea’s REST API.
That separation matters. Tally’s webhook payload represents a form response: its answers are supplied as an array of fields. An email API needs a resolved recipient, sender, subject, and rendered text or HTML. A relay is where you safely make that translation.
The result works for several common workflows:
- A lead completes a demo-request or contact form and receives an acknowledgement.
- A customer applies for a beta, event, or waitlist and receives a tailored next-step message.
- An internal team receives a notification containing the form answers.
- One form submission produces both an internal alert and a customer confirmation.
- Form answers determine which transactional message a recipient should receive.
This is not an email marketing blast workflow. A Tally submission is an event initiated by an individual. Use it for messages that are directly related to that submission, and obtain appropriate consent before adding a person to any separate campaign audience.
The concrete Tally trigger: a form response
The trigger is a Tally form submission. In Tally’s webhook event format, this arrives as eventType: "FORM_RESPONSE". Configure a webhook on the specific form whose responses should start the email workflow, then provide the public HTTPS URL of your relay.
Tally posts a JSON event with envelope fields such as eventId, eventType, and createdAt. The submitted form data is under data; the answers are represented by data.fields, an array rather than a simple object keyed by question label.
A representative Tally FORM_RESPONSE payload looks like this:
{
"eventId": "evt_01HV8K3J8V4P7M2Q9B6R1A0CDE",
"eventType": "FORM_RESPONSE",
"createdAt": "2025-02-10T14:22:31.000Z",
"data": {
"responseId": "resp_01HV8K3M8XQY2R0D5N7J3B9F12",
"submissionId": "sub_01HV8K3M8XQY2R0D5N7J3B9F12",
"respondentId": "respondent_01HV8K3M8XQY2R0D5N7J3B9F12",
"formId": "form_01HV7ZZ2C6P8F3D4S5A6B7C8D9",
"formName": "Product demo request",
"createdAt": "2025-02-10T14:22:30.000Z",
"fields": [
{
"key": "question_email",
"label": "Work email",
"type": "INPUT_EMAIL",
"value": "alex@example.com"
},
{
"key": "question_name",
"label": "First name",
"type": "INPUT_TEXT",
"value": "Alex"
},
{
"key": "question_company",
"label": "Company",
"type": "INPUT_TEXT",
"value": "Northstar Labs"
}
]
}
}
Treat the sample as a shape to program against, not as a reason to hard-code Tally-generated identifiers. The key attached to each question is the useful durable mapping value. A human-facing label can be edited by a form owner, translated, or duplicated; using labels as production identifiers creates a subtle failure mode after harmless form copy changes.
Build the form for stable mappings
Before writing code, create the fields you intend to map. A simple demo form might have an email question, a name question, and an optional company question. In a test submission, inspect the actual received event and record the field keys that Tally sends.
The email address should come from an email-type question and should be required if an acknowledgement cannot be sent without it. A name may be optional, but your rendering code should have a fallback such as “there” rather than produce Hello undefined.
Conditional questions deserve special care. If a branch is not shown to a respondent, its question may not have a useful value in the submission. Your code must regard optional answers as optional instead of assuming every possible field is present.
Why a server-side relay is required
Tally has native outbound webhooks, so it can notify your endpoint when a response arrives. But a webhook does not by itself reshape an array of answers into the email request your provider expects, apply conditional business rules, escape user-provided content for HTML, deduplicate retries, and keep delivery credentials private.
A server-side relay can be a Cloudflare Worker, Vercel Function, Netlify Function, AWS Lambda, container endpoint, or an existing application API. The example below uses the standard Web Fetch API, which makes it portable to most JavaScript serverless environments.
Do not put a Volanea API key in any of these places:
- A Tally question default, hidden field, calculation, or redirect URL.
- JavaScript loaded in a public website where the Tally form is embedded.
- A client-side environment variable with a public prefix.
- A webhook URL query string.
- Source code committed to a repository.
Instead, store it as a server-side secret named VOLANEA_API_KEY. The relay reads that secret at runtime and sends it only in the outbound HTTPS request to Volanea. Tally never receives the Volanea credential, and the person completing the form cannot inspect it in page source or browser developer tools.
For Volanea endpoint details, payload options, domain setup, and authentication guidance, refer to the email API reference and setup guides. Keep the sending domain verified before testing the full workflow; an integration can return a successful API response while inbox placement still depends on domain authentication, recipient address quality, and message content.
Why not point Tally directly at the email API?
Even if an endpoint accepted a raw HTTP POST, a direct webhook is usually the wrong design. The payload contains Tally’s event structure, not a complete email schema. More importantly, a direct configuration either lacks a safe place for a sending credential or turns a long-lived API credential into configuration that is harder to rotate, audit, and protect.
The relay also gives you a place to return a fast 2xx response to Tally, record an event identifier, and decide whether a delivery error should be retried. Those capabilities become important as soon as a form receives more than a few real submissions.
Configure Tally’s webhook
In the form’s integrations area, add a webhook and enter the public HTTPS address for your relay, for example https://forms.example.com/tally/demo-request. Submit a test response after saving the webhook so you can confirm the endpoint sees the actual event shape from your form.
The exact webhook controls available in Tally can depend on the workspace and plan. Check the current Tally webhook documentation and your form’s integrations UI before designing a production process around it. If webhook access is not available in your workspace, use an automation intermediary such as Make or Zapier: connect its Tally form-submission trigger, then have it call a protected endpoint you own. Do not move the Volanea API key into a browser-visible form configuration merely to avoid middleware.
A direct relay should accept only the one form, or a small explicit allowlist of form IDs. An endpoint that accepts any Tally-like payload and sends to an address supplied by the caller can be abused as an open mail relay.
Test the webhook before enabling email
First deploy the relay in a diagnostic mode that logs the event ID, form ID, event type, and field keys—but never logs full email addresses or unrestricted answer text in a shared log. Submit the form once and verify all of the following:
- The request reaches the endpoint over HTTPS.
- The event type is
FORM_RESPONSE. data.formIdmatches the expected form.- The intended email question has the field key you configured in code.
- The address value is present and syntactically plausible.
- Optional fields are handled when blank or absent.
Only then turn on the email call. This staged approach prevents an early mapping mistake from sending emails to the wrong address or rendering empty personalization fields.
Map Tally fields into an email message
The key difference between a form response and an email request is the array lookup. Build a small helper that finds a field by its stable Tally key, then use the resulting values to compose recipient and content.
For the sample form, the mapping is:
| Tally payload location | Meaning | Email use |
|---|---|---|
data.fields[key=question_email].value | Work email answer | to recipient |
data.fields[key=question_name].value | First-name answer | Greeting personalization |
data.fields[key=question_company].value | Company answer | Internal notification content |
data.submissionId | Submission identifier | Idempotency/deduplication key |
data.formId | Originating form | Allowlist and routing decision |
Use field keys from your own received test payload. The placeholder strings question_email, question_name, and question_company below demonstrate the mapping pattern; replace them with the keys in the event from your production form.
Never use raw respondent input as HTML without escaping it. A name or company value can contain angle brackets, quotes, or other content that changes markup. Escape dynamic values for the HTML version, and keep a plain-text version for clients that do not render HTML.
Working relay code: Tally webhook to Volanea
The following JavaScript handler accepts Tally’s form-response event, validates the expected form and required address, prevents a repeated submission from producing a second email, then sends the confirmation through Volanea.
It uses a seen key-value binding for idempotency. On Cloudflare Workers, that can be a KV namespace; in another runtime, replace env.seen with Redis, a database table with a unique index, or a durable queue-backed store. The idempotency store is not optional in a serious workflow because webhook delivery can be retried.
// Environment secrets / bindings:
// VOLANEA_API_KEY = server-side Volanea API key
// TALLY_FORM_ID = accepted Tally form ID
// MAIL_FROM = "Demo team <demo@example.com>"
// seen = durable KV-like store with get() and put()
const VOLANEA_SEND_URL = "https://api.volanea.com/v1/email/send";
function fieldValue(fields, key) {
const field = fields.find((item) => item.key === key);
return typeof field?.value === "string" ? field.value.trim() : "";
}
function escapeHtml(value) {
return String(value)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
function validEmail(value) {
// Basic guard, not a deliverability guarantee.
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
}
export default {
async fetch(request, env) {
if (request.method !== "POST") {
return new Response("Method not allowed", { status: 405 });
}
let event;
try {
event = await request.json();
} catch {
return new Response("Invalid JSON", { status: 400 });
}
// Tally's webhook trigger for a submitted form response.
if (event.eventType !== "FORM_RESPONSE") {
return Response.json({ ignored: true }, { status: 200 });
}
const submission = event.data;
if (!submission || submission.formId !== env.TALLY_FORM_ID) {
return new Response("Unexpected form", { status: 403 });
}
const fields = Array.isArray(submission.fields) ? submission.fields : [];
// Replace these with keys from YOUR Tally test webhook payload.
const email = fieldValue(fields, "question_email").toLowerCase();
const firstName = fieldValue(fields, "question_name") || "there";
const company = fieldValue(fields, "question_company") || "Not provided";
if (!validEmail(email)) {
// Acknowledge a structurally valid event; log internally without PII.
return Response.json({ skipped: "missing_or_invalid_email" }, { status: 200 });
}
// Prefer submissionId; eventId is a fallback for event-level deduplication.
const idempotencyKey = submission.submissionId || event.eventId;
if (!idempotencyKey) {
return new Response("Missing event identifier", { status: 400 });
}
if (await env.seen.get(`tally:${idempotencyKey}`)) {
return Response.json({ duplicate: true }, { status: 200 });
}
const safeName = escapeHtml(firstName);
const safeCompany = escapeHtml(company);
const emailPayload = {
from: env.MAIL_FROM,
to: [email],
subject: "We received your demo request",
text: `Hi ${firstName},\n\nThanks for your interest in Volanea. We received your request from ${company} and will be in touch shortly.\n\n— The Volanea team`,
html: `<p>Hi ${safeName},</p><p>Thanks for your interest in Volanea. We received your request from <strong>${safeCompany}</strong> and will be in touch shortly.</p><p>— The Volanea team</p>`
};
const upstream = await fetch(VOLANEA_SEND_URL, {
method: "POST",
headers: {
"Authorization": `Bearer ${env.VOLANEA_API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify(emailPayload)
});
const responseText = await upstream.text();
if (!upstream.ok) {
// Return a non-2xx status so the webhook delivery can be retried.
console.error("Volanea send failed", {
status: upstream.status,
submissionId: idempotencyKey
});
return new Response("Email provider error", { status: 502 });
}
// Mark only after Volanea accepted the send request.
await env.seen.put(`tally:${idempotencyKey}`, "sent", {
expirationTtl: 60 * 60 * 24 * 30
});
return Response.json({ accepted: true, submissionId: idempotencyKey });
}
};
The Volanea call is the POST request to /v1/email/send. The mapping is explicit: the Tally email answer becomes the one-element to array; the name and company answers become safely rendered content; and MAIL_FROM is a verified sender stored in server configuration rather than supplied by the form respondent.
Add an internal notification carefully
A common next step is an internal notification to sales or support. Make this a separate Volanea send with a fixed internal recipient such as sales@example.com, not a recipient sourced from a free-text question. Include only the answers your team needs, and avoid unnecessarily placing sensitive answers in an inbox that may be broadly accessible.
If the customer confirmation and internal alert must either both happen or both be retried coherently, record message-level state separately. A single sent flag is sufficient for one confirmation email but is too coarse when one submission creates multiple messages.
Keep the Volanea API key private and rotatable
The relay’s secret manager is the correct location for VOLANEA_API_KEY. Add it through your deployment platform’s encrypted secrets interface or command-line secret mechanism, not in a .env file that gets shipped to a client bundle. Production and test environments should have separate keys when practical.
The sender identity belongs in a non-secret server variable such as MAIL_FROM, but it should still be controlled by your application. Do not derive the from value, reply-to address, or API endpoint from a form answer. Otherwise, an attacker could turn a public form into a sender-spoofing mechanism.
A useful operational policy is to scope credentials according to environment and rotate them on a schedule or immediately after suspected exposure. Rotation is much less disruptive when the key exists in exactly one server-side secret store rather than across form settings, automation steps, and frontend repositories.
Also consider validating the inbound webhook. At minimum, verify the expected form ID and reject unexpected methods. If your Tally webhook configuration supports an authentication mechanism you can verify at the endpoint, use it. Do not rely on an unguessable URL as the only control for an endpoint that can trigger email.
When this breaks: retries, timeouts, and missing fields
The fragile point in this integration is the handoff between Tally’s webhook delivery and your relay. A form can be successfully submitted while the email request fails, or an email request can succeed while the webhook sender does not receive the response it expected. Design for those two facts instead of assuming each request happens exactly once.
Retries can create duplicate sends
Webhook systems may retry delivery after a timeout, network interruption, or non-success status. A particularly awkward case is this: your relay sends the email successfully, but the network connection closes before Tally receives your 200 response. Tally can retry the same event, and without deduplication the respondent receives two confirmations.
The code uses submissionId (with eventId as a fallback) as an idempotency key. It checks durable storage before sending and writes the key after Volanea accepts the request. Retain keys long enough to cover the webhook provider’s retry window and your own replay procedures; 30 days is a reasonable starting retention for a form acknowledgement, subject to your data policy.
For higher-volume or high-value workflows, make the design more robust with a database transaction or queue:
- Persist the event ID and submission ID with a unique constraint.
- Return a success response after durable acceptance into your own queue.
- Have a worker send the email and record the provider response.
- Retry only the unfinished send state, with controlled backoff.
This pattern reduces the amount of work performed during the webhook request and makes failed sends observable without asking Tally to be your job queue.
Webhook timeouts should not hold up email rendering
A webhook endpoint should parse, validate, persist, and respond quickly. Slow template rendering, database calls, external CRM enrichment, or a temporarily slow email API can push the response beyond a sender’s timeout expectations.
Do not solve that by returning 200 before recording the event anywhere. That creates a silent-loss risk: Tally considers the webhook delivered, while your process has not retained enough information to send the message. The queue pattern above is the safer alternative. If you keep the synchronous design, keep its work limited and monitor both latency and non-2xx responses.
Fields can be missing, empty, or changed
A form response is not a guarantee that every question in the editor has a populated string. Conditional logic, optional questions, changed question types, copied forms, deleted questions, and plan or feature differences can leave a field unavailable to the workflow you expected.
Defend against this by treating the recipient email as required and skipping sends when it is missing or invalid. For optional data, provide a fallback. For essential routing data—such as a selected product line—use an explicit allowlist of expected values and route unknown values to review rather than guessing.
Keep a small test suite of representative payloads: a complete submission, one with blank optional fields, one where a conditional branch is absent, and one invalid email case. Re-run it whenever someone changes the Tally form. Form edits are production changes when downstream automation depends on field keys.
Deliverability and message design after the API call
A successful request to an email API is not the same as an inbox placement guarantee. The sending domain should have the authentication records required by your provider, and the from address should align with that verified domain. Use a recognizable sender name and a reply-capable address when a human response is expected.
The confirmation should match the user’s expectation at the moment they submit the form. “We received your request” is clearer than a vague promotional message. Include the requested next step, a realistic response-time expectation if you can honor it, and a way to contact support if the form was submitted in error.
Address quality also affects delivery and costs. Tally’s email question type helps collect a correctly formatted address, but format validation cannot establish whether a mailbox exists or can receive mail. For high-value forms, check addresses before sending follow-up sequences with the free address verification tool.
Avoid adding a large block of raw form answers to a customer-facing email. Aside from privacy concerns, unescaped text can break the layout, and excessively variable messages are harder to test. Prefer a stable template with only the personalization necessary to acknowledge the submission.
Alternatives when a custom relay is not the right fit
A small serverless relay is usually the best balance of control, security, and cost for developers. It is not the only route, however.
Use Make or Zapier as an intermediary
If your Tally workspace cannot use webhooks, or your team already operates automations in Make or Zapier, use Tally’s form-submission trigger there. The automation should then call an endpoint you own that performs the Volanea send, or use a securely configured server-side HTTP step where the platform’s credential handling and logs meet your requirements.
The trade-off is operational visibility. Automation platforms can make initial setup faster, but a multi-step scenario adds another retry policy, another place where mappings can drift, and potentially another billable task for every response. Capture the Tally submission ID in the automation and preserve it as your deduplication identifier.
Send through your application backend
If a user submits a Tally form as part of a logged-in product flow, it can be cleaner to have your backend own the business event. Tally can still collect the response, while your application receives the webhook, writes the event to its own database, and triggers a transactional email from a background job.
This approach is especially appropriate when sending depends on account status, consent state, territory assignment, or data held outside the form. It also gives you a single audit trail tying together the Tally submission, application record, and email provider response.
Use Tally only for collection, not message composition
For complex onboarding, do not turn a large form into an email template editor. Map a small set of explicit values into a known transactional template or application workflow. This keeps copy changes under version control, lets you test rendering, and prevents a field-label edit from changing message behavior.
Monitor the integration as a delivery pipeline
Monitor more than “webhook endpoint returned 200.” That only tells you that one hop completed. A healthy production setup tracks the path from form submission to provider acceptance and, where available, downstream delivery events.
At a minimum, record these fields in a privacy-conscious log or database:
- Tally event ID and submission ID.
- Form ID and event type.
- Timestamp received and processing duration.
- A hashed recipient identifier rather than the raw address where possible.
- Volanea request result and provider message identifier, if returned.
- Deduplicated, skipped, failed, and retried outcome states.
Alert on sustained increases in 4xx errors, 5xx errors, missing email mappings, and duplicate-event attempts. A sudden rise in missing fields often means somebody edited the Tally form. A spike in provider rejections can indicate a sender-domain issue, a malformed request after a deployment, or a poor-quality traffic source.
Keep a manual replay tool as well. An administrator should be able to select a failed submission and retry its email after fixing the root cause, without resubmitting the form and creating a second response. The replay must reuse the same idempotency policy so it does not produce an accidental duplicate.
A production checklist for sending email from Tally
Before making the flow live, verify this checklist:
- The webhook is attached to the intended Tally form and a real test submission reaches your HTTPS endpoint.
- The endpoint accepts only
POSTand onlyFORM_RESPONSEevents for the expectedformId. - Tally question keys—not display labels—are used for email, name, and routing mappings.
- Required recipient data is validated; absent or malformed addresses are skipped and recorded.
- The Volanea API key is stored only as a server-side secret.
- The sender address belongs to a verified sending domain.
- User-provided values are escaped before insertion into HTML.
- Submission IDs are persisted to prevent retry-driven duplicates.
- Provider failures return an appropriate error or enter a durable retry queue.
- Logs avoid exposing full form answers and API credentials.
- A form edit has a documented test and deployment process.
Once these controls are in place, the integration is intentionally simple: a Tally response creates a durable event, and that event requests one well-defined transactional email. The simplicity is valuable because it makes failures diagnosable and reduces the chance that a form configuration change becomes a sending incident.
FAQ
Can I send email from Tally without a native Volanea app?
Yes. Use Tally’s form-submission webhook to call a server-side relay, then have the relay call Volanea’s REST email API. There is no need for a native marketplace app, and a relay is the safer place for mapping and credentials.
What Tally event should start the email?
Use the form-submission webhook event, identified as FORM_RESPONSE. The submitted answers are available in data.fields, where each answer includes a Tally field key, label, type, and value.
Where should I store the Volanea API key?
Store it in the secret manager of your serverless function, backend, or automation environment. Never put it in Tally form content, frontend JavaScript, a public environment variable, or a webhook URL.
How do I stop duplicate confirmation emails?
Persist data.submissionId as an idempotency key before allowing a retry to send again. Webhook deliveries can be retried after timeouts or network failures, including cases where the first email was already accepted.
What happens if an optional Tally answer is not present?
Treat it as an expected condition. Use fallbacks for optional personalization, but skip and log the workflow when a required value such as the recipient email address is missing or invalid.